### Bug Summary

File: | builds/wireshark/wireshark/wiretap/cllog.c
Warning: | line 425, column 33 Addition of a null pointer (from variable 'pFieldEnd') and a nonzero integer value results in undefined behavior

### Annotated Source Code

Press '?' to see keyboard shortcuts

Show analyzer invocation
clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cllog.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-22/lib/clang/22 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /usr/include/libxml2 -D CARES_NO_DEPRECATED -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D WS_BUILD_DLL -D WS_DEBUG -D WS_DEBUG_UTF_8 -D wiretap_EXPORTS -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -I /builds/wireshark/wireshark/wiretap -I /builds/wireshark/wireshark/build/wiretap -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/llvm-22/lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-nonliteral -std=gnu17 -ferror-limit 19 -fvisibility=hidden -fwrapv -fwrapv-pointer -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fexceptions -fcolor-diagnostics -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /builds/wireshark/wireshark/sbout/2026-07-21-100400-3661-1 -x c /builds/wireshark/wireshark/wiretap/cllog.c

Keyboard shortcuts:

- Use 'j/k' keys for keyboard navigation
- Use 'Shift+S' to show/hide relevant lines
- Use '?' to toggle this window

Close
Show only relevant lines Show control flow arrows

1 | /* cllog.c
2 | *
3 | * Wiretap Library
4 | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
5 | *
6 | * SPDX-License-Identifier: GPL-2.0-or-later
7 | */
8 |
9 | /*
10 | * Reads log files from CLX000 CAN loggers from CSS Electronics:
11 | *
12 | * https://canlogger.csselectronics.com/clx000-docs/cl1000/log/index.html
13 | * https://canlogger.csselectronics.com/clx000-docs/cl2000/log/index.html
14 | *
15 | * Based on the cCLLog.c, cCLLog.h, and wtap-cllog.c source files from
16 | * the WS_v2.4-Plugin_v7.1.zip version of the CSS Electronics plugin at
17 | *
18 | * https://canlogger.csselectronics.com/downloads.php?q=wireshark
19 | *
20 | * with the files combined into one source file, modernized to
21 | * fit into an up-to-date version of Wireshark, and cleaned up
22 | * not to, for example, do seeks by rewinding and reading to
23 | * get to the seek target.
24 | *
25 | * It could probably use some further cleanup.
26 | */
27 |
28 | #include "config.h"
29 |
30 | #include <stdio.h>
31 | #include <stdbool.h>
32 | #include <stdint.h>
33 | #include <string.h>
34 | #include <stdlib.h>
35 | #include <time.h>
36 |
37 | #include <wsutil/array.h>
38 | #include <wsutil/str_util.h>
39 | #include <wsutil/strtoi.h>
40 |
41 | #include "socketcan.h"
42 | #include "file_wrappers.h"
43 | #include "cllog.h"
44 |
45 | static int cllog_file_type_subtype = -1;
46 |
47 | /***********************************************************************************************************************
48 | * Public definitions
49 | **********************************************************************************************************************/
50 | #define MAX_LOG_LINE_FIELDS7 7 /*( seqNo, timestamp, lost, SE, ID, length, data) */
51 | /***********************************************************************************************************************
52 | * Public type declarations
53 | **********************************************************************************************************************/
54 |
55 | /* Message type */
56 | typedef enum
57 | {
58 | msg_rx_standard_e = 0,
59 | msg_rx_extended_e = 1,
60 | msg_tx_standard_e = 7,
61 | msg_tx_extended_e = 8,
62 | } cCLLog_messageType_t;
63 |
64 | /* Typedef CAN-bus message type */
65 | typedef struct
66 | {
67 | uint32_t lost;
68 | cCLLog_messageType_t msgType;
69 | wtap_can_msg_t msg;
70 | } cCLLog_message_t;
71 |
72 | /* Silent-mode*/
73 | typedef enum { silent_disabled_e = 0, silent_enabled_e } cCLLog_silentMode_t;
74 |
75 | /* Cyclic-mode*/
76 | typedef enum { cyclic_disabled_e = 0, cyclic_enabled_e } cCLLog_cyclicMode_t;
77 |
78 | /* Logger type */
79 | typedef enum { type_CL1000_e = 0, type_CL2000_e, type_CL3000_e } cCLLog_loggerType_t;
80 |
81 | typedef char * (*CLLog_gets_t)(char *s, int size, void *stream);
82 | typedef int (*CLLog_rewind_t)(void *stream);
83 |
84 | typedef struct cLLog_private cCLLog_logFileInfo_t;
85 |
86 | /* Type used to parse a field in a log line */
87 | typedef bool_Bool (*parseFieldFunc_t)(cCLLog_logFileInfo_t *pInfo, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info);
88 |
89 | /* Log file information */
90 | struct cLLog_private
91 | {
92 | uint32_t firstLogRow;
93 | cCLLog_loggerType_t loggerType;
94 | char hwrev[5];
95 | char fwrev[5];
96 | char id[20];
97 | uint32_t sessionNo;
98 | uint32_t splitNo;
99 | nstime_t logStartTime;
100 | char logStartTimeString[ 20 ];
101 | char separator;
102 | uint8_t timeFormat;
103 | char timeSeparator;
104 | char timeSeparatorMs;
105 | char dateSeparator;
106 | char dateAndTimeSeparator;
107 | uint32_t bitRate;
108 | cCLLog_silentMode_t silentMode;
109 | cCLLog_cyclicMode_t cyclicMode;
110 |
111 | parseFieldFunc_t parseFieldFunc[ MAX_LOG_LINE_FIELDS7 ];
112 |
113 | /* First log time stamp as relative offset */
114 | nstime_t firstTimeStampAbs;
115 | };
116 |
117 | /***********************************************************************************************************************
118 | * Private definitions
119 | **********************************************************************************************************************/
120 | #define HEADER_LINE_PARSE_MAPPING_LENGTH(sizeof (headerLineParseMapping) / sizeof (headerLineParseMapping )[0]) array_length(headerLineParseMapping)(sizeof (headerLineParseMapping) / sizeof (headerLineParseMapping )[0])
121 | #define MAX_LOG_LINE_LENGTH200 200
122 | #define TIME_STAMP_STRING_MAX_LENGTH( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) ) ( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )
123 | #define TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) ( sizeof( "YYYYMMDDhhmmsskkk" ) )
124 |
125 | /***********************************************************************************************************************
126 | * Private type definitions
127 | **********************************************************************************************************************/
128 | /* Function type to parse a single log file line */
129 | typedef bool_Bool (*parseFunc_t)(cCLLog_logFileInfo_t *pInfo, char *pLine, int *err, char **err_info);
130 |
131 | /* Structure of the header parse mapping. A match string is paired with a parse function */
132 | typedef struct
133 | {
134 | const char *pMatchString;
135 | parseFunc_t parseFunc;
136 | } headerLineParseMapping_t;
137 |
138 | /***********************************************************************************************************************
139 | * Private function declarations
140 | **********************************************************************************************************************/
141 | static bool_Bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine );
142 | static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString );
143 |
144 | /* Parse header lines functions */
145 | static bool_Bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
146 | static bool_Bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
147 | static bool_Bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
148 | static bool_Bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
149 | static bool_Bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
150 | static bool_Bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
151 | static bool_Bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
152 | static bool_Bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
153 | static bool_Bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
154 | static bool_Bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
155 | static bool_Bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
156 | static bool_Bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
157 | static bool_Bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
158 | static bool_Bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
159 | static bool_Bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
160 | static bool_Bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info);
161 | /***********************************************************************************************************************
162 | * Private variable definitions
163 | **********************************************************************************************************************/
164 |
165 | /* Array of header line match strings and associated parse functions */
166 | static const headerLineParseMapping_t headerLineParseMapping[] =
167 | {
168 | { .pMatchString = "Logger type: ", .parseFunc = parseLogFileHeaderLine_type},
169 | { .pMatchString = "HW rev: ", .parseFunc = parseLogFileHeaderLine_hwrev },
170 | { .pMatchString = "FW rev: ", .parseFunc = parseLogFileHeaderLine_fwrev },
171 | { .pMatchString = "Logger ID: ", .parseFunc = parseLogFileHeaderLine_id},
172 | { .pMatchString = "Session No.: ", .parseFunc = parseLogFileHeaderLine_sessionNo},
173 | { .pMatchString = "Split No.: ", .parseFunc = parseLogFileHeaderLine_splitNo},
174 | { .pMatchString = "Time: ", .parseFunc = parseLogFileHeaderLine_time},
175 | { .pMatchString = "Value separator: ", .parseFunc = parseLogFileHeaderLine_valueSeparator},
176 | { .pMatchString = "Time format: ", .parseFunc = parseLogFileHeaderLine_timeFormat},
177 | { .pMatchString = "Time separator: ", .parseFunc = parseLogFileHeaderLine_timeSeparator},
178 | { .pMatchString = "Time separator ms: ", .parseFunc = parseLogFileHeaderLine_timeSeparatorMs},
179 | { .pMatchString = "Date separator: ", .parseFunc = parseLogFileHeaderLine_dateSeparator},
180 | { .pMatchString = "Time and date separator: ", .parseFunc = parseLogFileHeaderLine_timeAndDateSeparator},
181 | { .pMatchString = "Bit-rate: ", .parseFunc = parseLogFileHeaderLine_bitRate},
182 | { .pMatchString = "Silent mode: ", .parseFunc = parseLogFileHeaderLine_silentMode},
183 | { .pMatchString = "Cyclic mode: ", .parseFunc = parseLogFileHeaderLine_cyclicMode},
184 | };
185 |
186 | /*
187 | * Do a string copy to a buffer of a specified length.
188 | * If the string will fit, return true.
189 | * If the string won't fit, return false.
190 | */
191 | static bool_Bool
192 | checked_strcpy(char *dest, size_t destlen, const char *src)
193 | {
194 | size_t srclen;
195 |
196 | srclen = strlen(src) + 1; // count the trailing '\0'
197 | if (srclen > destlen)
198 | return false0;
199 | memcpy(dest, src, srclen);
200 | return true1;
201 | }
202 |
203 | /* TODO: Does not support separators set to numbers (will remove part of the time stamp also */
204 | /* TODO: Does not support time stamps without ms, as given in the header */
205 | /* TODO: Alot of copying slows down the parsing */
206 | static bool_Bool parseFieldTS(cCLLog_logFileInfo_t *pInfo, char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
207 | {
208 | struct tm tm;
209 | int ms;
210 |
211 | /* Copy the string to not modify the original */
212 | char timeStampCopy[TIME_STAMP_STRING_MAX_LENGTH( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )];
213 | if (!checked_strcpy(timeStampCopy, sizeof timeStampCopy, pField))
214 | {
215 | *err = WTAP_ERR_BAD_FILE-13;
216 | *err_info = g_strdup("cllog: time stamp is too long")g_strdup_inline ("cllog: time stamp is too long");
217 | return false0;
218 | }
219 |
220 | /* Copy the header time stamp string to not modify the original */
221 | char timeStampHeaderCopy[TIME_STAMP_STRING_MAX_LENGTH( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )];
222 | if (!checked_strcpy(timeStampHeaderCopy, sizeof timeStampHeaderCopy, pInfo->logStartTimeString))
223 | {
224 | *err = WTAP_ERR_BAD_FILE-13;
225 | *err_info = g_strdup("cllog: header time stamp too long")g_strdup_inline ("cllog: header time stamp too long");
226 | return false0;
227 | }
228 |
229 | /* Strip the delimiters from the time strings */
230 | uint8_t msgTimeStrippedLen = stripTimeStamp(pInfo, timeStampCopy);
231 | if (msgTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1) {
232 | *err = WTAP_ERR_BAD_FILE-13;
233 | *err_info = g_strdup("cllog: time stamp incorrectly formatted")g_strdup_inline ("cllog: time stamp incorrectly formatted");
234 | return false0;
235 | }
236 |
237 | uint8_t headerTimeStrippedLen = stripTimeStamp(pInfo, timeStampHeaderCopy);
238 | if (headerTimeStrippedLen > TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1) {
239 | *err = WTAP_ERR_BAD_FILE-13;
240 | *err_info = g_strdup("cllog: header time stamp incorrectly formatted")g_strdup_inline ("cllog: header time stamp incorrectly formatted" );
241 | return false0;
242 | }
243 |
244 | /* Set time string (YYYYMMDDhhmmsskkk) to the epoch */
245 | char timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) )] = "19700101000000000";
246 |
247 | /* Copy the header time to the template */
248 | memcpy(timeStampStringFull, timeStampHeaderCopy, headerTimeStrippedLen);
249 |
250 | /* Copy the stripped timestamp into the full template */
251 | memcpy(&timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1 - msgTimeStrippedLen], timeStampCopy, msgTimeStrippedLen);
252 | timeStampStringFull[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH( sizeof( "YYYYMMDDhhmmsskkk" ) ) - 1] = '\0';
253 |
254 | memset(&tm, 0, sizeof tm);
255 |
256 | /* YYYYMMDDThhmmss */
257 | sscanf(timeStampStringFull, "%4u%2u%2u%2u%2u%2u%3d",
258 | &tm.tm_year,
259 | &tm.tm_mon,
260 | &tm.tm_mday,
261 | &tm.tm_hour,
262 | &tm.tm_min,
263 | &tm.tm_sec,
264 | &ms
265 | );
266 | tm.tm_mon -= 1;
267 | tm.tm_year -= 1900;
268 |
269 | /* To Epoch (mktime converts to epoch from local (!!!) timezone) */
270 | pLogEntry->msg.ts.secs = mktime(&tm);
271 | pLogEntry->msg.ts.nsecs = ms*1000 * 1000;
272 |
273 | /* Is first time stamp ? */
274 | if (pInfo->firstTimeStampAbs.secs == 0 && pInfo->firstTimeStampAbs.nsecs == 0)
275 | pInfo->firstTimeStampAbs = pLogEntry->msg.ts;
276 |
277 | return true1;
278 | }
279 |
280 | static bool_Bool parseFieldLost(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
281 | {
282 | uint32_t lost;
283 |
284 | if (!ws_strtou32(pField, NULL((void*)0), &lost)) {
285 | *err = WTAP_ERR_BAD_FILE-13;
286 | *err_info = g_strdup_printf("cllog: lost packet count value is not valid");
287 | return false0;
288 | }
289 | pLogEntry->lost = lost;
290 | return true1;
291 | }
292 |
293 | static bool_Bool parseFieldMsgType(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
294 | {
295 | switch (pField[0])
296 | {
297 | case '0':
298 | pLogEntry->msgType = msg_rx_standard_e;
299 | pLogEntry->msg.type = MSG_TYPE_STD;
300 | return true1;
301 | case '1':
302 | pLogEntry->msgType = msg_rx_extended_e;
303 | pLogEntry->msg.type = MSG_TYPE_EXT;
304 | return true1;
305 | case '8':
306 | pLogEntry->msgType = msg_tx_standard_e;
307 | pLogEntry->msg.type = MSG_TYPE_STD;
308 | return true1;
309 | case '9':
310 | pLogEntry->msgType = msg_tx_extended_e;
311 | pLogEntry->msg.type = MSG_TYPE_EXT;
312 | return true1;
313 | default:
314 | *err = WTAP_ERR_BAD_FILE-13;
315 | *err_info = g_strdup("cllog: unknown message type")g_strdup_inline ("cllog: unknown message type");
316 | return false0;
317 | }
318 | }
319 |
320 | static bool_Bool parseFieldID(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
321 | {
322 | uint32_t id;
323 |
324 | if (!ws_hexstrtou32(pField, NULL((void*)0), &id)) {
325 | *err = WTAP_ERR_BAD_FILE-13;
326 | *err_info = g_strdup_printf("cllog: ID value is not valid");
327 | return false0;
328 | }
329 | pLogEntry->msg.id = id;
330 | return true1;
331 | }
332 |
333 | static bool_Bool parseFieldLength(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
334 | {
335 | uint32_t length;
336 |
337 | if (!ws_strtou32(pField, NULL((void*)0), &length)) {
338 | *err = WTAP_ERR_BAD_FILE-13;
339 | *err_info = g_strdup_printf("cllog: length value is not valid");
340 | return false0;
341 | }
342 | if (length > array_length(pLogEntry->msg.data.data)(sizeof (pLogEntry->msg.data.data) / sizeof (pLogEntry-> msg.data.data)[0])) {
343 | *err = WTAP_ERR_BAD_FILE-13;
344 | *err_info = g_strdup_printf("cllog: length value %u > maximum length %zu",
345 | length, array_length(pLogEntry->msg.data.data)(sizeof (pLogEntry->msg.data.data) / sizeof (pLogEntry-> msg.data.data)[0]));
346 | return false0;
347 | }
348 | pLogEntry->msg.data.length = length;
349 | return true1;
350 | }
351 |
352 | static bool_Bool parseFieldData(cCLLog_logFileInfo_t *pInfo _U___attribute__((unused)), char *pField, cCLLog_message_t *pLogEntry, int *err, char **err_info)
353 | {
354 | char *pFieldStart = pField;
355 |
356 | /* Set data length in case length field is not set explicitly in the log file */
357 | pLogEntry->msg.data.length = 0;
358 |
359 | /* Loop all data bytes */
360 | while (pLogEntry->msg.data.length < array_length(pLogEntry->msg.data.data)(sizeof (pLogEntry->msg.data.data) / sizeof (pLogEntry-> msg.data.data)[0]))
361 | {
362 | int hexdigit;
363 | uint8_t data;
364 |
365 | if (*pFieldStart == '\n' || *pFieldStart == '\r' || *pFieldStart == '\0')
366 | {
367 | break;
368 | }
369 |
370 | hexdigit = ws_xton(*pFieldStart);
371 | if (hexdigit < 0) {
372 | *err = WTAP_ERR_BAD_FILE-13;
373 | *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
374 | return false0;
375 | }
376 | data = (uint8_t)hexdigit << 4U;
377 | pFieldStart++;
378 | hexdigit = ws_xton(*pFieldStart);
379 | if (hexdigit < 0) {
380 | *err = WTAP_ERR_BAD_FILE-13;
381 | *err_info = g_strdup_printf("cllog: packet byte value 0x%02x is not valid", *pFieldStart);
382 | return false0;
383 | }
384 | data = data | (uint8_t)hexdigit;
385 | pFieldStart++;
386 | pLogEntry->msg.data.data[pLogEntry->msg.data.length++] = data;
387 | }
388 | return true1;
389 | }
390 |
391 | static bool_Bool parseLogLine(cCLLog_logFileInfo_t *pInfo, char *pLine, cCLLog_message_t *pLogEntry, int *err, char **err_info)
392 | {
393 | char *pFieldStart = pLine;
394 |
395 | /* Loop all fields in log line */
396 | for (unsigned int fieldNo = 0, finalField = 0; fieldNo
6.1 | 'fieldNo' is < MAX_LOG_LINE_FIELDS
< MAX_LOG_LINE_FIELDS7 && finalField == 0; fieldNo++)
7 | ← | Loop condition is true. Entering loop body | →
397 | {
398 | /* Find field end by separator */
399 | char *pFieldEnd = strchr(pFieldStart, pInfo->separator);
400 |
401 | /* If final field, then EOL marks the end of the field */
402 | if (pFieldEnd == NULL((void*)0))
8 | ← | Assuming 'pFieldEnd' is equal to NULL | →
9 | ← | Taking true branch | →
403 | {
404 | pFieldEnd = strchr(pFieldStart, '\n');
10 | ← | Value assigned to 'pFieldEnd' | →
405 | finalField = 1;
406 | }
407 |
408 | /* Replace separator or terminator with string termination */
409 | if (pFieldEnd != NULL((void*)0))
11 | ← | Assuming 'pFieldEnd' is equal to NULL | →
12 | ← | Taking false branch | →
410 | {
411 | *pFieldEnd = '\0';
412 | }
413 |
414 | /* Is parse function assigned to field? */
415 | if (pInfo->parseFieldFunc[fieldNo] != NULL((void*)0))
13 | ← | Assuming the condition is false | →
14 | ← | Taking false branch | →
416 | {
417 | /* Parse field */
418 | if (!pInfo->parseFieldFunc[fieldNo](pInfo, pFieldStart, pLogEntry, err, err_info))
419 | {
420 | return false0;
421 | }
422 | }
423 |
424 | /* Skip over the separator */
425 | pFieldStart = pFieldEnd + 1;
15 | ← | Addition of a null pointer (from variable 'pFieldEnd') and a nonzero integer value results in undefined behavior
426 | }
427 | return true1;
428 | }
429 |
430 | /***********************************************************************************************************************
431 | * parseColumnHeaderFields
432 | *
433 | * Parse the column fields and determine which fields are present and the position of the fields
434 | *
435 | * @param[ in ] pInfo Pointer to the CLLog object
436 | * @param[ in ] pColLine The column line
437 | **********************************************************************************************************************/
438 | static bool_Bool parseColumnHeaderFields( cCLLog_logFileInfo_t *pInfo, char *pColLine )
439 | {
440 | bool_Bool resultFlag = false0;
441 |
442 | /* Initialise field start */
443 | char *pFieldStart = pColLine;
444 |
445 | /* Loop all fields in line */
446 | for ( uint8_t fieldNo = 0, finalField = 0 ; fieldNo < MAX_LOG_LINE_FIELDS7 && finalField == 0 ; fieldNo++ )
447 | {
448 | /* Find field end */
449 | char *pFieldEnd = strchr( pFieldStart, pInfo->separator );
450 |
451 | /* If final field, then EOL marks the end of the field */
452 | if( pFieldEnd == NULL((void*)0) )
453 | {
454 | pFieldEnd = strchr( pFieldStart, '\n' );
455 | finalField = 1;
456 | }
457 |
458 | /* Replace separator or terminator with string termination */
459 | if (pFieldEnd != NULL((void*)0))
460 | {
461 | *pFieldEnd = '\0';
462 | }
463 |
464 | /* Set field number */
465 | if( strcmp( pFieldStart, "Timestamp" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldTS; resultFlag = true1; }
466 | if( strcmp( pFieldStart, "Lost" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLost; resultFlag = true1; }
467 | if( strcmp( pFieldStart, "Type" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldMsgType; resultFlag = true1; }
468 | if( strcmp( pFieldStart, "ID" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldID; resultFlag = true1; }
469 | if( strcmp( pFieldStart, "Length" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldLength; resultFlag = true1; }
470 | if( strcmp( pFieldStart, "Data" ) == 0 ) { pInfo->parseFieldFunc[ fieldNo ] = parseFieldData; resultFlag = true1; }
471 |
472 | /* Set start of next field to end of previous + 1 */
473 | pFieldStart = pFieldEnd + 1;
474 | }
475 |
476 | return resultFlag;
477 | }
478 |
479 | /***********************************************************************************************************************
480 | * stripTimeStamp
481 | *
482 | * Strips a time stamp string for any delimiters
483 | **********************************************************************************************************************/
484 | static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t *pInfo, char *pTimeStampString )
485 | {
486 | uint8_t strippedLength = 0U;
487 |
488 | /* Char by char, strip the delimiters from the time stamp string */
489 | size_t timeStampStringLen = strlen( pTimeStampString );
490 | for (size_t i = 0U; i < timeStampStringLen; i++ )
491 | {
492 | /* Get char */
493 | char charTmp = pTimeStampString[i];
494 |
495 | /* If delimiter, skip */
496 | if( charTmp == pInfo->separator ){ continue; }
497 | if( charTmp == pInfo->timeSeparator ){ continue; }
498 | if( charTmp == pInfo->timeSeparatorMs ){ continue; }
499 | if( charTmp == pInfo->dateSeparator ){ continue; }
500 | if( charTmp == pInfo->dateAndTimeSeparator ){ continue; }
501 |
502 | /* Not a delimiter, keep char */
503 | pTimeStampString[ strippedLength++ ] = charTmp;
504 | }
505 | pTimeStampString[ strippedLength ] = '\0';
506 |
507 | return strippedLength;
508 | }
509 |
510 | static bool_Bool parseString(const char *pFieldValue, char *valuep, size_t valueSize, char *fieldName, int *err, char **err_info)
511 | {
512 | if (!checked_strcpy(valuep, valueSize, pFieldValue))
513 | {
514 | *err = WTAP_ERR_BAD_FILE-13;
515 | *err_info = ws_strdup_printf("cllog: %s is too long",wmem_strdup_printf(((void*)0), "cllog: %s is too long", fieldName )
516 | fieldName)wmem_strdup_printf(((void*)0), "cllog: %s is too long", fieldName );
517 | return false0;
518 | }
519 | return true1;
520 | }
521 |
522 | static bool_Bool parseUnsigned(const char *pFieldValue, uint32_t *valuep, char *fieldName, int *err, char **err_info)
523 | {
524 | uint32_t value;
525 |
526 | if (!ws_strtou32(pFieldValue, NULL((void*)0), &value)) {
527 | *err = WTAP_ERR_BAD_FILE-13;
528 | *err_info = ws_strdup_printf("cllog: %s value is not valid",wmem_strdup_printf(((void*)0), "cllog: %s value is not valid" , fieldName)
529 | fieldName)wmem_strdup_printf(((void*)0), "cllog: %s value is not valid" , fieldName);
530 | return false0;
531 | }
532 | *valuep = value;
533 | return true1;
534 | }
535 |
536 | static bool_Bool parseSeparator(const char *pFieldValue, char *separatorp, char *fieldName, int *err, char **err_info)
537 | {
538 | char separator = '\0';
539 |
540 | /* Separator field is if set e.g. ";" - that is 3 chars. Else it is "" */
541 | if (strlen( pFieldValue) == 3)
542 | {
543 | if (pFieldValue[0] != '"' || !g_ascii_isprint(pFieldValue[1])((g_ascii_table[(guchar) (pFieldValue[1])] & G_ASCII_PRINT ) != 0) ||
544 | pFieldValue[2] != '"')
545 | {
546 | *err = WTAP_ERR_BAD_FILE-13;
547 | *err_info = ws_strdup_printf("cllog: %s separator is not valid",wmem_strdup_printf(((void*)0), "cllog: %s separator is not valid" , fieldName)
548 | fieldName)wmem_strdup_printf(((void*)0), "cllog: %s separator is not valid" , fieldName);
549 | return false0;
550 | }
551 | separator = pFieldValue[1];
552 | }
553 | *separatorp = separator;
554 | return true1;
555 | }
556 |
557 | static bool_Bool parseBoolean(const char *pFieldValue, bool_Bool *value, char *fieldName, int *err, char **err_info)
558 | {
559 | if (strcmp(pFieldValue, "true") == 0)
560 | {
561 | *value = true1;
562 | }
563 | else if (strcmp(pFieldValue, "false") == 0)
564 | {
565 | *value = false0;
566 | }
567 | else
568 | {
569 | *err = WTAP_ERR_BAD_FILE-13;
570 | *err_info = ws_strdup_printf("cllog: %s value is not valid",wmem_strdup_printf(((void*)0), "cllog: %s value is not valid" , fieldName)
571 | fieldName)wmem_strdup_printf(((void*)0), "cllog: %s value is not valid" , fieldName);
572 | return false0;
573 | }
574 | return true1;
575 | }
576 |
577 | static bool_Bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
578 | {
579 | if (strcmp(pFieldValue, "CANLogger1000") == 0 || strcmp(pFieldValue, "CL1000") == 0)
580 | {
581 | pInfo->loggerType = type_CL1000_e;
582 | }
583 | else if (strcmp(pFieldValue, "CANLogger2000") == 0 || strcmp(pFieldValue, "CL2000") == 0)
584 | {
585 | pInfo->loggerType = type_CL2000_e;
586 | }
587 | else if (strcmp(pFieldValue, "CANLogger3000") == 0 || strcmp(pFieldValue, "CL3000") == 0)
588 | {
589 | pInfo->loggerType = type_CL3000_e;
590 | }
591 | else
592 | {
593 | *err = WTAP_ERR_BAD_FILE-13;
594 | *err_info = g_strdup("cllog: logger type value is not valid")g_strdup_inline ("cllog: logger type value is not valid");
595 | return false0;
596 | }
597 | return true1;
598 | }
599 |
600 | static bool_Bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
601 | {
602 | return parseString(pFieldValue, pInfo->hwrev, sizeof pInfo->hwrev, "hardware revision", err, err_info);
603 | }
604 |
605 | static bool_Bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
606 | {
607 | return parseString(pFieldValue, pInfo->fwrev, sizeof pInfo->fwrev, "firmware revision", err, err_info);
608 | }
609 |
610 | static bool_Bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
611 | {
612 | return parseString(pFieldValue, pInfo->id, sizeof pInfo->id, "ID", err, err_info);
613 | }
614 |
615 | static bool_Bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
616 | {
617 | return parseUnsigned(pFieldValue, &pInfo->sessionNo, "session number", err, err_info);
618 | }
619 |
620 | static bool_Bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
621 | {
622 | return parseUnsigned(pFieldValue, &pInfo->splitNo, "split number", err, err_info);
623 | }
624 |
625 | static bool_Bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
626 | {
627 | struct tm tm;
628 |
629 | memset(&tm, 0, sizeof tm);
630 | /* YYYYMMDDThhmmss */
631 | sscanf(pFieldValue,
632 | "%4u%2u%2uT%2u%2u%2u",
633 | &tm.tm_year,
634 | &tm.tm_mon,
635 | &tm.tm_mday,
636 | &tm.tm_hour,
637 | &tm.tm_min,
638 | &tm.tm_sec);
639 | tm.tm_mon -= 1;
640 | tm.tm_year -= 1900;
641 |
642 | /* To Epoch ( mktime converts to epoch from local (!!!) timezone )*/
643 | pInfo->logStartTime.secs = mktime(&tm);
644 | pInfo->logStartTime.nsecs = 0;
645 |
646 | if (!checked_strcpy(pInfo->logStartTimeString, sizeof pInfo->logStartTimeString, pFieldValue))
647 | {
648 | *err = WTAP_ERR_BAD_FILE-13;
649 | *err_info = g_strdup("cllog: time is too long")g_strdup_inline ("cllog: time is too long");
650 | return false0;
651 | }
652 | return true1;
653 | }
654 |
655 | static bool_Bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
656 | {
657 | return parseSeparator(pFieldValue, &pInfo->separator, "value", err, err_info);
658 | }
659 |
660 | static bool_Bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
661 | {
662 | uint32_t format;
663 |
664 | if (!ws_strtou32(pFieldValue, NULL((void*)0), &format))
665 | {
666 | *err = WTAP_ERR_BAD_FILE-13;
667 | *err_info = g_strdup("cllog: time format value is not valid")g_strdup_inline ("cllog: time format value is not valid");
668 | return false0;
669 | }
670 | if (format > 6)
671 | {
672 | *err = WTAP_ERR_BAD_FILE-13;
673 | *err_info = g_strdup("cllog: time format value is not valid")g_strdup_inline ("cllog: time format value is not valid");
674 | return false0;
675 | }
676 | pInfo->timeFormat = (uint8_t)format;
677 | return true1;
678 | }
679 |
680 | static bool_Bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
681 | {
682 | return parseSeparator(pFieldValue, &pInfo->timeSeparator, "time", err, err_info);
683 | }
684 |
685 | static bool_Bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
686 | {
687 | return parseSeparator(pFieldValue, &pInfo->timeSeparatorMs, "time millisecond", err, err_info);
688 | }
689 |
690 | static bool_Bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
691 | {
692 | return parseSeparator(pFieldValue, &pInfo->dateSeparator, "date", err, err_info);
693 | }
694 |
695 | static bool_Bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
696 | {
697 | return parseSeparator(pFieldValue, &pInfo->dateAndTimeSeparator, "date and time", err, err_info);
698 | }
699 |
700 | static bool_Bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
701 | {
702 | return parseUnsigned(pFieldValue, &pInfo->bitRate, "bit rate", err, err_info);
703 | }
704 |
705 | static bool_Bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
706 | {
707 | bool_Bool silentMode;
708 |
709 | if (!parseBoolean(pFieldValue, &silentMode, "silent mode", err, err_info))
710 | {
711 | return false0;
712 | }
713 |
714 | if (silentMode)
715 | {
716 | pInfo->silentMode = silent_enabled_e;
717 | }
718 | else
719 | {
720 | pInfo->silentMode = silent_disabled_e;
721 | }
722 | return true1;
723 | }
724 |
725 | static bool_Bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t *pInfo, char *pFieldValue, int *err, char **err_info)
726 | {
727 | bool_Bool cyclicMode;
728 |
729 | if (!parseBoolean(pFieldValue, &cyclicMode, "silent mode", err, err_info))
730 | {
731 | return false0;
732 | }
733 |
734 | if (cyclicMode)
735 | {
736 | pInfo->cyclicMode = cyclic_enabled_e;
737 | }
738 | else
739 | {
740 | pInfo->cyclicMode = cyclic_disabled_e;
741 | }
742 | return true1;
743 | }
744 |
745 | static bool_Bool
746 | cllog_read_common(wtap *wth, FILE_T fh, wtap_rec *rec, int *err, char **err_info)
747 | {
748 | cCLLog_logFileInfo_t* clLog = (cCLLog_logFileInfo_t*)wtap_socketcan_get_private_data(wth);
749 | char line[MAX_LOG_LINE_LENGTH200];
750 | cCLLog_message_t logEntry;
751 |
752 | /* Read a line */
753 | if (file_gets(line, sizeof(line), fh) == NULL((void*)0))
4 | ← | Assuming the condition is false | →
5 | ← | Taking false branch | →
754 | {
755 | /* EOF or error. */
756 | *err = file_error(fh, err_info);
757 | return false0;
758 | }
759 |
760 | /* Default the log entry structure */
761 | memset(&logEntry, 0, sizeof(logEntry));
762 |
763 | /* Parse the line */
764 | if (!parseLogLine(clLog, line, &logEntry, err, err_info))
6 | ← | Calling 'parseLogLine' | →
765 | {
766 | return false0;
767 | }
768 |
769 | logEntry.msg.interface_id = wtap_socketcan_find_or_create_new_interface(wth, clLog->id);
770 | if (!wtap_socketcan_gen_packet(wth, rec, &logEntry.msg, "cllog", err, err_info))
771 | return false0;
772 |
773 |
774 | if (logEntry.msgType == msg_tx_standard_e || logEntry.msgType == msg_tx_extended_e)
775 | {
776 | wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS2, PACK_FLAGS_DIRECTION_OUTBOUND2);
777 | }
778 | else if (logEntry.msgType == msg_rx_standard_e || logEntry.msgType == msg_rx_extended_e)
779 | {
780 | wtap_block_add_uint32_option(rec->block, OPT_PKT_FLAGS2, PACK_FLAGS_DIRECTION_INBOUND1);
781 | }
782 |
783 | return true1;
784 | }
785 |
786 | static bool_Bool
787 | cllog_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, int64_t *data_offset)
788 | {
789 | *data_offset = file_tell(wth->fh);
790 |
791 | return cllog_read_common(wth, wth->fh, rec, err, err_info);
792 | }
793 |
794 | static bool_Bool
795 | cllog_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info)
796 | {
797 | if (file_seek(wth->random_fh, seek_off, SEEK_SET0, err) == -1)
1 | Assuming the condition is false | →
2 | ← | Taking false branch | →
798 | return false0;
799 |
800 | return cllog_read_common(wth, wth->random_fh, rec, err, err_info);
3 | ← | Calling 'cllog_read_common' | →
801 | }
802 |
803 | static void
804 | cllog_close(void* tap_data)
805 | {
806 | /* Clean up the file information */
807 | g_free((cCLLog_logFileInfo_t*)tap_data)(__builtin_object_size (((cCLLog_logFileInfo_t*)tap_data), 0) != ((size_t) - 1)) ? g_free_sized ((cCLLog_logFileInfo_t*)tap_data , __builtin_object_size (((cCLLog_logFileInfo_t*)tap_data), 0 )) : (g_free) ((cCLLog_logFileInfo_t*)tap_data);
808 | }
809 |
810 | wtap_open_return_val
811 | cllog_open(wtap *wth, int *err, char **err_info)
812 | {
813 | cCLLog_logFileInfo_t *clLog;
814 | char line[ MAX_LOG_LINE_LENGTH200 ];
815 | char *linep;
816 |
817 | clLog = g_new0(cCLLog_logFileInfo_t, 1)((cCLLog_logFileInfo_t *) g_malloc0_n ((1), sizeof (cCLLog_logFileInfo_t )));
818 |
819 | /*
820 | * We're at the beginning of the file. The header is a set
821 | * of comment lines, each beginning with a '#'. Read each line,
822 | * stopping if we see a non-comment line, and parse each
823 | * comment line; if any aren't valid, quit and indicate that
824 | * this isn't a CLX log file.
825 | */
826 | while ((linep = file_gets(line, sizeof(line), wth->fh)) != NULL((void*)0) &&
827 | linep[0] == '#')
828 | {
829 | /*
830 | * Skip the comment character and white space following it.
831 | */
832 | linep++;
833 | while (*linep == ' ' || *linep == '\t')
834 | linep++;
835 |
836 | if (*linep == '\0')
837 | {
838 | /*
839 | * Skip over empty comment lines.
840 | * XXX - should we treat that as an indication of an
841 | * invalid file?
842 | */
843 | continue;
844 | }
845 |
846 | /*
847 | * Look for the handler for this particular header line.
848 | */
849 | for (unsigned int i = 0U; i < HEADER_LINE_PARSE_MAPPING_LENGTH(sizeof (headerLineParseMapping) / sizeof (headerLineParseMapping )[0]); i++)
850 | {
851 | const headerLineParseMapping_t *pHeaderMapping = &headerLineParseMapping[i];
852 | size_t matchStringLen = strlen(pHeaderMapping->pMatchString);
853 |
854 | if (strncmp(linep, pHeaderMapping->pMatchString, matchStringLen) == 0 &&
855 | pHeaderMapping->parseFunc != NULL((void*)0))
856 | {
857 | /*
858 | * This matches this header value.
859 | * Skip past the tag.
860 | */
861 | linep += matchStringLen;
862 |
863 | /* Replace any newline chars with end of line */
864 | for (char *pChar = linep; ; pChar++)
865 | {
866 | if (*pChar == '\n' || *pChar == '\r' || *pChar == '\0')
867 | {
868 | *pChar = '\0';
869 | break;
870 | }
871 | }
872 |
873 | /*
874 | * Call the handler.
875 | */
876 | if (!pHeaderMapping->parseFunc(clLog, linep, err, err_info))
877 | {
878 | /*
879 | * XXX - should this file be rejected as not
880 | * one of ours? Given the line looks like
881 | * a comment that begins with a valid header
882 | * field tag, it may be likely to be one of
883 | * ours.
884 | */
885 | g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized (clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog );
886 | if (*err == WTAP_ERR_BAD_FILE-13)
887 | {
888 | wmem_free(NULL((void*)0), *err_info);
889 | *err_info = NULL((void*)0);
890 | }
891 | return WTAP_OPEN_NOT_MINE;
892 | }
893 | }
894 | }
895 | }
896 |
897 | /*
898 | * Did file_gets() fail?
899 | */
900 | if (linep == NULL((void*)0))
901 | {
902 | /*
903 | * Yes - file_gets() didn't return a line.
904 | * Did it get an error?
905 | */
906 | *err = file_error(wth->fh, err_info);
907 | if (*err != 0)
908 | {
909 | /* Yes. What was it? */
910 | if (*err == WTAP_ERR_SHORT_READ-12)
911 | {
912 | /* Incomplete header, so not ours. */
913 | g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized (clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog );
914 | return WTAP_OPEN_NOT_MINE;
915 | }
916 | else
917 | {
918 | /* I/O error. */
919 | g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized (clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog );
920 | return WTAP_OPEN_ERROR;
921 | }
922 | }
923 |
924 | /*
925 | * No, it just got an EOF; treat it as our file, as
926 | * older versions did so.
927 | *
928 | * XXX - should we treat it as not our file, as it lacks
929 | * the column header line?
930 | */
931 | }
932 | else
933 | {
934 | /*
935 | * We've read the first line after the header, so it's the column
936 | * header line. Parse it. This requires that we have been provided with a
937 | * valid separator.
938 | */
939 | if (clLog->separator == '\0' || !parseColumnHeaderFields(clLog, linep))
940 | {
941 | g_free(clLog)(__builtin_object_size ((clLog), 0) != ((size_t) - 1)) ? g_free_sized (clLog, __builtin_object_size ((clLog), 0)) : (g_free) (clLog );
942 | return WTAP_OPEN_NOT_MINE;
943 | }
944 | }
945 |
946 | wtap_set_as_socketcan(wth, cllog_file_type_subtype, WTAP_TSPREC_MSEC3, clLog, cllog_close);
947 | wth->snapshot_length = 0;
948 |
949 | wth->subtype_read = cllog_read;
950 | wth->subtype_seek_read = cllog_seek_read;
951 |
952 | return WTAP_OPEN_MINE;
953 | }
954 |
955 | /* Options for packet blocks. */
956 | static const struct supported_option_type packet_block_options_supported[] = {
957 | { OPT_PKT_FLAGS2, ONE_OPTION_SUPPORTED },
958 | };
959 |
960 | static const struct supported_block_type cllog_blocks_supported[] = {
961 | /*
962 | * We support packet blocks, with only the flags option supported.
963 | */
964 | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, OPTION_TYPES_SUPPORTED(packet_block_options_supported)(sizeof (packet_block_options_supported) / sizeof (packet_block_options_supported )[0]), packet_block_options_supported }
965 | };
966 |
967 | static const struct file_type_subtype_info cllog_info = {
968 | "CSS Electronics CLX000 CAN log", "cllog", "txt", NULL((void*)0),
969 | false0, BLOCKS_SUPPORTED(cllog_blocks_supported)(sizeof (cllog_blocks_supported) / sizeof (cllog_blocks_supported )[0]), cllog_blocks_supported,
970 | NULL((void*)0), NULL((void*)0), NULL((void*)0)
971 | };
972 |
973 | void
974 | register_canlogger(void)
975 | {
976 | cllog_file_type_subtype = wtap_register_file_type_subtype(&cllog_info);
977 | }