4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0-or-later
10 * Reads log files from CLX000 CAN loggers from CSS Electronics:
12 * https://canlogger.csselectronics.com/clx000-docs/cl1000/log/index.html
13 * https://canlogger.csselectronics.com/clx000-docs/cl2000/log/index.html
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
18 * https://canlogger.csselectronics.com/downloads.php?q=wireshark
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.
25 * It could probably use some further cleanup.
37 #include <wsutil/str_util.h>
38 #include <wsutil/strtoi.h>
41 #include "file_wrappers.h"
43 /***********************************************************************************************************************
45 **********************************************************************************************************************/
46 #define MAX_LOG_LINE_FIELDS 7 /*( seqNo, timestamp, lost, SE, ID, length, data) */
47 /***********************************************************************************************************************
48 * Public type declarations
49 **********************************************************************************************************************/
50 /* Time stamp structure type (sec since start + ms resolution) */
51 typedef struct { time_t epoch
; uint16_t ms
; } cCLLog_timeStamp_t
;
56 msg_rx_standard_e
= 0,
57 msg_rx_extended_e
= 1,
58 msg_tx_standard_e
= 7,
59 msg_tx_extended_e
= 8,
60 } cCLLog_messageType_t
;
62 /* Typedef CAN-bus message type */
65 cCLLog_timeStamp_t timestamp
;
67 cCLLog_messageType_t msgType
;
74 typedef enum { silent_disabled_e
= 0, silent_enabled_e
} cCLLog_silentMode_t
;
77 typedef enum { cyclic_disabled_e
= 0, cyclic_enabled_e
} cCLLog_cyclicMode_t
;
80 typedef enum { type_CL1000_e
= 0, type_CL2000_e
, type_CL3000_e
} cCLLog_loggerType_t
;
82 typedef char * (*CLLog_gets_t
)(char *s
, int size
, void *stream
);
83 typedef int (*CLLog_rewind_t
)(void *stream
);
85 typedef struct cLLog_private cCLLog_logFileInfo_t
;
87 /* Type used to parse a field in a log line */
88 typedef bool (*parseFieldFunc_t
)(cCLLog_logFileInfo_t
*pInfo
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
);
90 /* Log file information */
94 cCLLog_loggerType_t loggerType
;
100 cCLLog_timeStamp_t logStartTime
;
101 char logStartTimeString
[ 20 ];
105 char timeSeparatorMs
;
107 char dateAndTimeSeparator
;
109 cCLLog_silentMode_t silentMode
;
110 cCLLog_cyclicMode_t cyclicMode
;
112 parseFieldFunc_t parseFieldFunc
[ MAX_LOG_LINE_FIELDS
];
114 /* First log time stamp as relative offset */
115 cCLLog_timeStamp_t firstTimeStampAbs
;
118 /***********************************************************************************************************************
119 * Private definitions
120 **********************************************************************************************************************/
121 #define HEADER_LINE_PARSE_MAPPING_LENGTH array_length(headerLineParseMapping)
122 #define MAX_LOG_LINE_LENGTH 200
123 #define TIME_STAMP_STRING_MAX_LENGTH ( sizeof( "YYYY/MM/DDThh:mm:ss.kkk" ) )
124 #define TIME_STAMP_STRING_STRIPPED_MAX_LENGTH ( sizeof( "YYYYMMDDhhmmsskkk" ) )
126 /***********************************************************************************************************************
127 * Private type definitions
128 **********************************************************************************************************************/
129 /* Function type to parse a single log file line */
130 typedef bool (*parseFunc_t
)(cCLLog_logFileInfo_t
*pInfo
, char *pLine
, int *err
, char **err_info
);
132 /* Structure of the header parse mapping. A match string is paired with a parse function */
135 const char *pMatchString
;
136 parseFunc_t parseFunc
;
137 } headerLineParseMapping_t
;
139 /***********************************************************************************************************************
140 * Private function declarations
141 **********************************************************************************************************************/
142 static bool parseColumnHeaderFields( cCLLog_logFileInfo_t
*pInfo
, char *pColLine
);
143 static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t
*pInfo
, char *pTimeStampString
);
145 /* Parse header lines functions */
146 static bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
147 static bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
148 static bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
149 static bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
150 static bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
151 static bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
152 static bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
153 static bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
154 static bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
155 static bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
156 static bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
157 static bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
158 static bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
159 static bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
160 static bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
161 static bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
);
162 /***********************************************************************************************************************
163 * Private variable definitions
164 **********************************************************************************************************************/
166 /* Array of header line match strings and associated parse functions */
167 static const headerLineParseMapping_t headerLineParseMapping
[] =
169 { .pMatchString
= "Logger type: ", .parseFunc
= parseLogFileHeaderLine_type
},
170 { .pMatchString
= "HW rev: ", .parseFunc
= parseLogFileHeaderLine_hwrev
},
171 { .pMatchString
= "FW rev: ", .parseFunc
= parseLogFileHeaderLine_fwrev
},
172 { .pMatchString
= "Logger ID: ", .parseFunc
= parseLogFileHeaderLine_id
},
173 { .pMatchString
= "Session No.: ", .parseFunc
= parseLogFileHeaderLine_sessionNo
},
174 { .pMatchString
= "Split No.: ", .parseFunc
= parseLogFileHeaderLine_splitNo
},
175 { .pMatchString
= "Time: ", .parseFunc
= parseLogFileHeaderLine_time
},
176 { .pMatchString
= "Value separator: ", .parseFunc
= parseLogFileHeaderLine_valueSeparator
},
177 { .pMatchString
= "Time format: ", .parseFunc
= parseLogFileHeaderLine_timeFormat
},
178 { .pMatchString
= "Time separator: ", .parseFunc
= parseLogFileHeaderLine_timeSeparator
},
179 { .pMatchString
= "Time separator ms: ", .parseFunc
= parseLogFileHeaderLine_timeSeparatorMs
},
180 { .pMatchString
= "Date separator: ", .parseFunc
= parseLogFileHeaderLine_dateSeparator
},
181 { .pMatchString
= "Time and date separator: ", .parseFunc
= parseLogFileHeaderLine_timeAndDateSeparator
},
182 { .pMatchString
= "Bit-rate: ", .parseFunc
= parseLogFileHeaderLine_bitRate
},
183 { .pMatchString
= "Silent mode: ", .parseFunc
= parseLogFileHeaderLine_silentMode
},
184 { .pMatchString
= "Cyclic mode: ", .parseFunc
= parseLogFileHeaderLine_cyclicMode
},
188 * Do a string copy to a buffer of a specified length.
189 * If the string will fit, return true.
190 * If the string won't fit, return false.
193 checked_strcpy(char *dest
, size_t destlen
, const char *src
)
197 srclen
= strlen(src
) + 1; // count the trailing '\0'
198 if (srclen
> destlen
)
200 memcpy(dest
, src
, srclen
);
204 /* TODO: Does not support separators set to numbers (will remove part of the time stamp also */
205 /* TODO: Does not support time stamps without ms, as given in the header */
206 /* TODO: Alot of copying slows down the parsing */
207 static bool parseFieldTS(cCLLog_logFileInfo_t
*pInfo
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
212 /* Copy the string to not modify the original */
213 char timeStampCopy
[TIME_STAMP_STRING_MAX_LENGTH
];
214 if (!checked_strcpy(timeStampCopy
, sizeof timeStampCopy
, pField
))
216 *err
= WTAP_ERR_BAD_FILE
;
217 *err_info
= g_strdup("cllog: time stamp is too long");
221 /* Copy the header time stamp string to not modify the original */
222 char timeStampHeaderCopy
[TIME_STAMP_STRING_MAX_LENGTH
];
223 if (!checked_strcpy(timeStampHeaderCopy
, sizeof timeStampHeaderCopy
, pInfo
->logStartTimeString
))
225 *err
= WTAP_ERR_BAD_FILE
;
226 *err_info
= g_strdup("cllog: header time stamp too long");
230 /* Strip the delimiters from the time strings */
231 uint8_t msgTimeStrippedLen
= stripTimeStamp(pInfo
, timeStampCopy
);
232 if (msgTimeStrippedLen
> TIME_STAMP_STRING_STRIPPED_MAX_LENGTH
- 1) {
233 *err
= WTAP_ERR_BAD_FILE
;
234 *err_info
= g_strdup("cllog: time stamp incorrectly formatted");
238 uint8_t headerTimeStrippedLen
= stripTimeStamp(pInfo
, timeStampHeaderCopy
);
239 if (headerTimeStrippedLen
> TIME_STAMP_STRING_STRIPPED_MAX_LENGTH
- 1) {
240 *err
= WTAP_ERR_BAD_FILE
;
241 *err_info
= g_strdup("cllog: header time stamp incorrectly formatted");
245 /* Set time string (YYYYMMDDhhmmsskkk) to the epoch */
246 char timeStampStringFull
[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH
] = "19700101000000000";
248 /* Copy the header time to the template */
249 memcpy(timeStampStringFull
, timeStampHeaderCopy
, headerTimeStrippedLen
);
251 /* Copy the stripped timestamp into the full template */
252 memcpy(&timeStampStringFull
[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH
- 1 - msgTimeStrippedLen
], timeStampCopy
, msgTimeStrippedLen
);
253 timeStampStringFull
[TIME_STAMP_STRING_STRIPPED_MAX_LENGTH
- 1] = '\0';
255 memset(&tm
, 0, sizeof tm
);
257 /* YYYYMMDDThhmmss */
258 sscanf(timeStampStringFull
, "%4u%2u%2u%2u%2u%2u%3d",
270 /* To Epoch (mktime converts to epoch from local (!!!) timezone) */
271 pLogEntry
->timestamp
.epoch
= mktime(&tm
);
272 pLogEntry
->timestamp
.ms
= ms
;
274 /* Is first time stamp ? */
275 if (pInfo
->firstTimeStampAbs
.epoch
== 0 && pInfo
->firstTimeStampAbs
.ms
== 0)
277 pInfo
->firstTimeStampAbs
.epoch
= pLogEntry
->timestamp
.epoch
;
278 pInfo
->firstTimeStampAbs
.ms
= pLogEntry
->timestamp
.ms
;
284 static bool parseFieldLost(cCLLog_logFileInfo_t
*pInfo _U_
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
288 if (!ws_strtou32(pField
, NULL
, &lost
)) {
289 *err
= WTAP_ERR_BAD_FILE
;
290 *err_info
= g_strdup_printf("cllog: lost packet count value is not valid");
293 pLogEntry
->lost
= lost
;
297 static bool parseFieldMsgType(cCLLog_logFileInfo_t
*pInfo _U_
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
302 pLogEntry
->msgType
= msg_rx_standard_e
;
305 pLogEntry
->msgType
= msg_rx_extended_e
;
308 pLogEntry
->msgType
= msg_tx_standard_e
;
311 pLogEntry
->msgType
= msg_tx_extended_e
;
314 *err
= WTAP_ERR_BAD_FILE
;
315 *err_info
= g_strdup("cllog: unknown message type");
320 static bool parseFieldID(cCLLog_logFileInfo_t
*pInfo _U_
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
324 if (!ws_hexstrtou32(pField
, NULL
, &id
)) {
325 *err
= WTAP_ERR_BAD_FILE
;
326 *err_info
= g_strdup_printf("cllog: ID value is not valid");
333 static bool parseFieldLength(cCLLog_logFileInfo_t
*pInfo _U_
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
337 if (!ws_strtou32(pField
, NULL
, &length
)) {
338 *err
= WTAP_ERR_BAD_FILE
;
339 *err_info
= g_strdup_printf("cllog: length value is not valid");
342 pLogEntry
->length
= length
;
346 static bool parseFieldData(cCLLog_logFileInfo_t
*pInfo _U_
, char *pField
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
348 char *pFieldStart
= pField
;
350 /* Set data length in case length field is not set explicitly in the log file */
351 pLogEntry
->length
= 0;
353 /* Loop all data bytes */
354 for (unsigned int dataByte
= 0; dataByte
< 8; dataByte
++)
359 if (*pFieldStart
== '\n' || *pFieldStart
== '\r')
364 hexdigit
= ws_xton(*pFieldStart
);
366 *err
= WTAP_ERR_BAD_FILE
;
367 *err_info
= g_strdup_printf("cllog: packet byte value is not valid");
370 data
= (uint8_t)hexdigit
<< 4U;
372 hexdigit
= ws_xton(*pFieldStart
);
374 *err
= WTAP_ERR_BAD_FILE
;
375 *err_info
= g_strdup("cllog: packet byte value is not valid");
378 data
= data
| (uint8_t)hexdigit
;
380 pLogEntry
->data
[dataByte
] = data
;
387 static bool parseLogLine(cCLLog_logFileInfo_t
*pInfo
, char *pLine
, cCLLog_message_t
*pLogEntry
, int *err
, char **err_info
)
389 char *pFieldStart
= pLine
;
391 /* Loop all fields in log line */
392 for (unsigned int fieldNo
= 0, finalField
= 0; fieldNo
< MAX_LOG_LINE_FIELDS
&& finalField
== 0; fieldNo
++)
394 /* Find field end by separator */
395 char *pFieldEnd
= strchr(pFieldStart
, pInfo
->separator
);
397 /* If final field, then EOL marks the end of the field */
398 if (pFieldEnd
== NULL
)
400 pFieldEnd
= strchr(pFieldStart
, '\n');
404 /* Replace separator or terminator with string termination */
405 if (pFieldEnd
!= NULL
)
410 /* Is parse function assigned to field? */
411 if (pInfo
->parseFieldFunc
[fieldNo
] != NULL
)
414 if (!pInfo
->parseFieldFunc
[fieldNo
](pInfo
, pFieldStart
, pLogEntry
, err
, err_info
))
420 /* Skip over the separator */
421 pFieldStart
= pFieldEnd
+ 1;
426 /***********************************************************************************************************************
427 * parseColumnHeaderFields
429 * Parse the column fields and determine which fields are present and the position of the fields
431 * @param[ in ] pInfo Pointer to the CLLog object
432 * @param[ in ] pColLine The column line
433 **********************************************************************************************************************/
434 static bool parseColumnHeaderFields( cCLLog_logFileInfo_t
*pInfo
, char *pColLine
)
436 bool resultFlag
= false;
438 /* Initialise field start */
439 char *pFieldStart
= pColLine
;
441 /* Loop all fields in line */
442 for ( uint8_t fieldNo
= 0, finalField
= 0 ; fieldNo
< MAX_LOG_LINE_FIELDS
&& finalField
== 0 ; fieldNo
++ )
445 char *pFieldEnd
= strchr( pFieldStart
, pInfo
->separator
);
447 /* If final field, then EOL marks the end of the field */
448 if( pFieldEnd
== NULL
)
450 pFieldEnd
= strchr( pFieldStart
, '\n' );
454 /* Replace separator or terminator with string termination */
455 if (pFieldEnd
!= NULL
)
460 /* Set field number */
461 if( strcmp( pFieldStart
, "Timestamp" ) == 0 ) { pInfo
->parseFieldFunc
[ fieldNo
] = parseFieldTS
; resultFlag
= true; }
462 if( strcmp( pFieldStart
, "Lost" ) == 0 ) { pInfo
->parseFieldFunc
[ fieldNo
] = parseFieldLost
; resultFlag
= true; }
463 if( strcmp( pFieldStart
, "Type" ) == 0 ) { pInfo
->parseFieldFunc
[ fieldNo
] = parseFieldMsgType
; resultFlag
= true; }
464 if( strcmp( pFieldStart
, "ID" ) == 0 ) { pInfo
->parseFieldFunc
[ fieldNo
] = parseFieldID
; resultFlag
= true; }
465 if( strcmp( pFieldStart
, "Length" ) == 0 ) { pInfo
->parseFieldFunc
[ fieldNo
] = parseFieldLength
; resultFlag
= true; }
466 if( strcmp( pFieldStart
, "Data" ) == 0 ) { pInfo
->parseFieldFunc
[ fieldNo
] = parseFieldData
; resultFlag
= true; }
468 /* Set start of next field to end of previous + 1 */
469 pFieldStart
= pFieldEnd
+ 1;
475 /***********************************************************************************************************************
478 * Strips a time stamp string for any delimiters
479 **********************************************************************************************************************/
480 static uint8_t stripTimeStamp( const cCLLog_logFileInfo_t
*pInfo
, char *pTimeStampString
)
482 uint8_t strippedLength
= 0U;
484 /* Char by char, strip the delimiters from the time stamp string */
485 size_t timeStampStringLen
= strlen( pTimeStampString
);
486 for (size_t i
= 0U; i
< timeStampStringLen
; i
++ )
489 char charTmp
= pTimeStampString
[i
];
491 /* If delimiter, skip */
492 if( charTmp
== pInfo
->separator
){ continue; }
493 if( charTmp
== pInfo
->timeSeparator
){ continue; }
494 if( charTmp
== pInfo
->timeSeparatorMs
){ continue; }
495 if( charTmp
== pInfo
->dateSeparator
){ continue; }
496 if( charTmp
== pInfo
->dateAndTimeSeparator
){ continue; }
498 /* Not a delimiter, keep char */
499 pTimeStampString
[ strippedLength
++ ] = charTmp
;
501 pTimeStampString
[ strippedLength
] = '\0';
503 return strippedLength
;
506 static bool parseString(const char *pFieldValue
, char *valuep
, size_t valueSize
, char *fieldName
, int *err
, char **err_info
)
508 if (!checked_strcpy(valuep
, valueSize
, pFieldValue
))
510 *err
= WTAP_ERR_BAD_FILE
;
511 *err_info
= ws_strdup_printf("cllog: %s is too long",
518 static bool parseUnsigned(const char *pFieldValue
, uint32_t *valuep
, char *fieldName
, int *err
, char **err_info
)
522 if (!ws_strtou32(pFieldValue
, NULL
, &value
)) {
523 *err
= WTAP_ERR_BAD_FILE
;
524 *err_info
= ws_strdup_printf("cllog: %s value is not valid",
532 static bool parseSeparator(const char *pFieldValue
, char *separatorp
, char *fieldName
, int *err
, char **err_info
)
534 char separator
= '\0';
536 /* Separator field is if set e.g. ";" - that is 3 chars. Else it is "" */
537 if (strlen( pFieldValue
) == 3)
539 if (pFieldValue
[0] != '"' || !g_ascii_isprint(pFieldValue
[1]) ||
540 pFieldValue
[2] != '"')
542 *err
= WTAP_ERR_BAD_FILE
;
543 *err_info
= ws_strdup_printf("cllog: %s separator is not valid",
547 separator
= pFieldValue
[1];
549 *separatorp
= separator
;
553 static bool parseBoolean(const char *pFieldValue
, bool *value
, char *fieldName
, int *err
, char **err_info
)
555 if (strcmp(pFieldValue
, "true") == 0)
559 else if (strcmp(pFieldValue
, "false") == 0)
565 *err
= WTAP_ERR_BAD_FILE
;
566 *err_info
= ws_strdup_printf("cllog: %s value is not valid",
573 static bool parseLogFileHeaderLine_type(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
575 if (strcmp(pFieldValue
, "CANLogger1000") == 0 )
577 pInfo
->loggerType
= type_CL1000_e
;
579 else if (strcmp(pFieldValue
, "CANLogger2000") == 0)
581 pInfo
->loggerType
= type_CL2000_e
;
583 else if (strcmp(pFieldValue
, "CANLogger3000") == 0 )
585 pInfo
->loggerType
= type_CL3000_e
;
589 *err
= WTAP_ERR_BAD_FILE
;
590 *err_info
= g_strdup("cllog: logger type value is not valid");
596 static bool parseLogFileHeaderLine_hwrev(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
598 return parseString(pFieldValue
, pInfo
->hwrev
, sizeof pInfo
->hwrev
, "hardware revision", err
, err_info
);
601 static bool parseLogFileHeaderLine_fwrev(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
603 return parseString(pFieldValue
, pInfo
->fwrev
, sizeof pInfo
->fwrev
, "firmware revision", err
, err_info
);
606 static bool parseLogFileHeaderLine_id(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
608 return parseString(pFieldValue
, pInfo
->id
, sizeof pInfo
->id
, "ID", err
, err_info
);
611 static bool parseLogFileHeaderLine_sessionNo(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
613 return parseUnsigned(pFieldValue
, &pInfo
->sessionNo
, "session number", err
, err_info
);
616 static bool parseLogFileHeaderLine_splitNo(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
618 return parseUnsigned(pFieldValue
, &pInfo
->splitNo
, "split number", err
, err_info
);
621 static bool parseLogFileHeaderLine_time(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
625 memset(&tm
, 0, sizeof tm
);
626 /* YYYYMMDDThhmmss */
628 "%4u%2u%2uT%2u%2u%2u",
638 /* To Epoch ( mktime converts to epoch from local (!!!) timezone )*/
639 pInfo
->logStartTime
.epoch
= mktime(&tm
);
640 pInfo
->logStartTime
.ms
= 0;
642 if (!checked_strcpy(pInfo
->logStartTimeString
, sizeof pInfo
->logStartTimeString
, pFieldValue
))
644 *err
= WTAP_ERR_BAD_FILE
;
645 *err_info
= g_strdup("cllog: time is too long");
651 static bool parseLogFileHeaderLine_valueSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
653 return parseSeparator(pFieldValue
, &pInfo
->separator
, "value", err
, err_info
);
656 static bool parseLogFileHeaderLine_timeFormat(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
660 if (!ws_strtou32(pFieldValue
, NULL
, &format
))
662 *err
= WTAP_ERR_BAD_FILE
;
663 *err_info
= g_strdup("cllog: time format value is not valid");
668 *err
= WTAP_ERR_BAD_FILE
;
669 *err_info
= g_strdup("cllog: time format value is not valid");
672 pInfo
->timeFormat
= (uint8_t)format
;
676 static bool parseLogFileHeaderLine_timeSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
678 return parseSeparator(pFieldValue
, &pInfo
->timeSeparator
, "time", err
, err_info
);
681 static bool parseLogFileHeaderLine_timeSeparatorMs(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
683 return parseSeparator(pFieldValue
, &pInfo
->timeSeparatorMs
, "time millisecond", err
, err_info
);
686 static bool parseLogFileHeaderLine_dateSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
688 return parseSeparator(pFieldValue
, &pInfo
->dateSeparator
, "date", err
, err_info
);
691 static bool parseLogFileHeaderLine_timeAndDateSeparator(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
693 return parseSeparator(pFieldValue
, &pInfo
->dateAndTimeSeparator
, "date and time", err
, err_info
);
696 static bool parseLogFileHeaderLine_bitRate(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
698 return parseUnsigned(pFieldValue
, &pInfo
->bitRate
, "bit rate", err
, err_info
);
701 static bool parseLogFileHeaderLine_silentMode(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
705 if (!parseBoolean(pFieldValue
, &silentMode
, "silent mode", err
, err_info
))
712 pInfo
->silentMode
= silent_enabled_e
;
716 pInfo
->silentMode
= silent_disabled_e
;
721 static bool parseLogFileHeaderLine_cyclicMode(cCLLog_logFileInfo_t
*pInfo
, char *pFieldValue
, int *err
, char **err_info
)
725 if (!parseBoolean(pFieldValue
, &cyclicMode
, "silent mode", err
, err_info
))
732 pInfo
->cyclicMode
= cyclic_enabled_e
;
736 pInfo
->cyclicMode
= cyclic_disabled_e
;
743 c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(248): warning C4
744 477: 'sscanf' : format string '%i' requires an argument of type 'int *',
745 but variadic argument 1 has type 'uint8_t *'
746 c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(274): warning C4
747 477: 'sscanf' : format string '%i' requires an argument of type 'int *',
748 but variadic argument 1 has type 'uint8_t *'
749 c:\development\wireshark\plugins\wimaxmacphy\cCLLog.c(288): warning C4
750 477: 'sscanf' : format string '%2x' requires an argument of type 'unsign
751 ed int *', but variadic argument 1 has type 'uint8_t *
758 static int cllog_file_type_subtype
= -1;
760 #define CAN_EFF_MASK 0x1FFFFFFF /* extended frame format (EFF) */
761 #define CAN_SFF_MASK 0x000007FF /* standard frame format (SFF) */
764 cllog_read_common(wtap
*wth
, FILE_T fh
, wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
766 cCLLog_logFileInfo_t
*clLog
= (cCLLog_logFileInfo_t
*) wth
->priv
;
767 char line
[MAX_LOG_LINE_LENGTH
];
768 cCLLog_message_t logEntry
;
772 if (file_gets(line
, sizeof(line
), fh
) == NULL
)
775 *err
= file_error(wth
->fh
, err_info
);
779 /* Default the log entry structure */
780 memset(&logEntry
, 0, sizeof(logEntry
));
783 if (!parseLogLine(clLog
, line
, &logEntry
, err
, err_info
))
788 rec
->rec_type
= REC_TYPE_PACKET
;
789 rec
->block
= wtap_block_create(WTAP_BLOCK_PACKET
);
790 rec
->presence_flags
= WTAP_HAS_TS
;
792 rec
->ts
.secs
= logEntry
.timestamp
.epoch
;
793 rec
->ts
.nsecs
= logEntry
.timestamp
.ms
* 1000U * 1000U;
795 rec
->rec_header
.packet_header
.caplen
= 8 + logEntry
.length
;
796 rec
->rec_header
.packet_header
.len
= 8 + logEntry
.length
;
798 if (logEntry
.msgType
== msg_tx_standard_e
|| logEntry
.msgType
== msg_tx_extended_e
)
800 wtap_block_add_uint32_option(rec
->block
, OPT_PKT_FLAGS
, PACK_FLAGS_DIRECTION_OUTBOUND
);
802 else if (logEntry
.msgType
== msg_rx_standard_e
|| logEntry
.msgType
== msg_rx_extended_e
)
804 wtap_block_add_uint32_option(rec
->block
, OPT_PKT_FLAGS
, PACK_FLAGS_DIRECTION_INBOUND
);
807 ws_buffer_assure_space(buf
, rec
->rec_header
.packet_header
.caplen
);
808 can_data
= ws_buffer_start_ptr(buf
);
810 can_data
[0] = (logEntry
.id
>> 24);
811 can_data
[1] = (logEntry
.id
>> 16);
812 can_data
[2] = (logEntry
.id
>> 8);
813 can_data
[3] = (logEntry
.id
>> 0);
814 can_data
[4] = logEntry
.length
;
819 if (logEntry
.msgType
== msg_tx_extended_e
|| logEntry
.msgType
== msg_rx_extended_e
|| (logEntry
.id
& CAN_EFF_MASK
) > CAN_SFF_MASK
)
822 memcpy(&can_data
[8], logEntry
.data
, logEntry
.length
);
827 cllog_read(wtap
*wth
, wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
, int64_t *data_offset
)
829 *data_offset
= file_tell(wth
->fh
);
831 return cllog_read_common(wth
, wth
->fh
, rec
, buf
, err
, err_info
);
835 cllog_seek_read(wtap
*wth
, int64_t seek_off
, wtap_rec
*rec
, Buffer
*buf
, int *err
, char **err_info
)
837 if (file_seek(wth
->random_fh
, seek_off
, SEEK_SET
, err
) == -1)
840 return cllog_read_common(wth
, wth
->random_fh
, rec
, buf
, err
, err_info
);
844 cllog_open(wtap
*wth
, int *err
, char **err_info
)
846 cCLLog_logFileInfo_t
*clLog
;
847 char line
[ MAX_LOG_LINE_LENGTH
];
850 clLog
= g_new0(cCLLog_logFileInfo_t
, 1);
852 /* Initialize the header information */
853 clLog
->loggerType
= 0;
854 clLog
->hwrev
[0] = '\0';
855 clLog
->fwrev
[0] = '\0';
857 clLog
->sessionNo
= 0;
859 clLog
->logStartTime
.epoch
= 0;
860 clLog
->logStartTime
.ms
= 0;
861 clLog
->logStartTimeString
[0] = '\0';
862 clLog
->separator
= '\0';
863 clLog
->timeFormat
= 0;
864 clLog
->timeSeparator
= '\0';
865 clLog
->timeSeparatorMs
= '\0';
866 clLog
->dateSeparator
= '\0';
867 clLog
->dateAndTimeSeparator
= '\0';
869 clLog
->silentMode
= 0;
870 clLog
->cyclicMode
= 0;
872 /* Set parse function pointers */
873 memset(clLog
->parseFieldFunc
, 0, sizeof( clLog
->parseFieldFunc
));
876 * We're at the beginning of the file. The header is a set
877 * of comment lines, each beginning with a '#'. Read each line,
878 * stopping if we see a non-comment line, and parse each
879 * comment line; if any aren't valid, quit and indicate that
880 * this isn't a CLX log file.
882 while ((linep
= file_gets(line
, sizeof(line
), wth
->fh
)) != NULL
&&
886 * Skip the comment character and white space following it.
889 while (*linep
== ' ' || *linep
== '\t')
895 * Skip over empty comment lines.
896 * XXX - should we treat that as an indication of an
903 * Look for the handler for this particular header line.
905 for (unsigned int i
= 0U; i
< HEADER_LINE_PARSE_MAPPING_LENGTH
; i
++)
907 const headerLineParseMapping_t
*pHeaderMapping
= &headerLineParseMapping
[i
];
908 size_t matchStringLen
= strlen(pHeaderMapping
->pMatchString
);
910 if (strncmp(linep
, pHeaderMapping
->pMatchString
, matchStringLen
) == 0 &&
911 pHeaderMapping
->parseFunc
!= NULL
)
914 * This matches this header value.
917 linep
+= matchStringLen
;
919 /* Replace any newline chars with end of line */
920 for (char *pChar
= linep
; ; pChar
++)
922 if (*pChar
== '\n' || *pChar
== '\r' || *pChar
== '\0')
932 if (!pHeaderMapping
->parseFunc(clLog
, linep
, err
, err_info
))
935 * XXX - should this file be rejected as not
936 * one of ours? Given the line looks like
937 * a comment that begins with a valid header
938 * field tag, it may be likely to be one of
942 if (*err
== WTAP_ERR_BAD_FILE
)
944 wmem_free(NULL
, *err_info
);
947 return WTAP_OPEN_NOT_MINE
;
954 * Did file_gets() fail?
959 * Yes - file_gets() didn't return a line.
960 * Did it get an error?
962 *err
= file_error(wth
->fh
, err_info
);
965 /* Yes. What was it? */
966 if (*err
== WTAP_ERR_SHORT_READ
)
968 /* Incomplete header, so not ours. */
970 return WTAP_OPEN_NOT_MINE
;
976 return WTAP_OPEN_ERROR
;
981 * No, it just got an EOF; treat it as our file, as
982 * older versions did so.
984 * XXX - should we treat it as not our file, as it lacks
985 * the column header line?
991 * We've read the first line after the header, so it's the column
992 * header line. Parse it.
994 if (!parseColumnHeaderFields(clLog
, linep
))
997 return WTAP_OPEN_NOT_MINE
;
1003 wth
->file_type_subtype
= cllog_file_type_subtype
;
1004 wth
->file_encap
= WTAP_ENCAP_SOCKETCAN
;
1005 wth
->snapshot_length
= 0;
1007 wth
->subtype_read
= cllog_read
;
1008 wth
->subtype_seek_read
= cllog_seek_read
;
1009 wth
->file_tsprec
= WTAP_TSPREC_MSEC
;
1011 return WTAP_OPEN_MINE
;
1014 /* Options for packet blocks. */
1015 static const struct supported_option_type packet_block_options_supported
[] = {
1016 { OPT_PKT_FLAGS
, ONE_OPTION_SUPPORTED
},
1019 static const struct supported_block_type cllog_blocks_supported
[] = {
1021 * We support packet blocks, with only the flags option supported.
1023 { WTAP_BLOCK_PACKET
, MULTIPLE_BLOCKS_SUPPORTED
, OPTION_TYPES_SUPPORTED(packet_block_options_supported
) }
1026 static const struct file_type_subtype_info cllog_info
= {
1027 "CSS Electronics CLX000 CAN log", "cllog", "txt", NULL
,
1028 false, BLOCKS_SUPPORTED(cllog_blocks_supported
),
1033 register_canlogger(void)
1035 cllog_file_type_subtype
= wtap_register_file_type_subtype(&cllog_info
);