| File: | wiretap/peak-trc.c |
| Warning: | line 800, column 9 Attempt to free released memory |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* peak_trc.c | |||
| 2 | * | |||
| 3 | * Wiretap Library | |||
| 4 | * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> | |||
| 5 | * | |||
| 6 | * Support for TRC log file format generated by several CAN tools from PEAK Gmbh | |||
| 7 | * Copyright (c) 2024 by Miklos Marton <martonmiklosqdev@gmail.com> | |||
| 8 | * | |||
| 9 | * File format description: | |||
| 10 | * https://www.peak-system.com/produktcd/Pdf/English/PEAK_CAN_TRC_File_Format.pdf | |||
| 11 | * | |||
| 12 | * SPDX-License-Identifier: GPL-2.0-or-later | |||
| 13 | */ | |||
| 14 | ||||
| 15 | #define WS_LOG_DOMAIN"peak-trc" "peak-trc" | |||
| 16 | #include <config.h> | |||
| 17 | #include <wireshark.h> | |||
| 18 | #include <file_wrappers.h> | |||
| 19 | #include <epan/dissectors/packet-socketcan.h> | |||
| 20 | #include <wctype.h> | |||
| 21 | #include <string.h> | |||
| 22 | #include <stdlib.h> | |||
| 23 | #include <errno(*__errno_location ()).h> | |||
| 24 | #include <time.h> | |||
| 25 | #include "peak-trc.h" | |||
| 26 | #include "socketcan.h" | |||
| 27 | ||||
| 28 | #define PEAK_TRC_MAX_LINE_SIZE4096 4096 // J1939 logs could contain long lines | |||
| 29 | ||||
| 30 | typedef enum { | |||
| 31 | FileVersion, | |||
| 32 | StartTime, | |||
| 33 | ColumnHeader, | |||
| 34 | Comment, | |||
| 35 | Data | |||
| 36 | } peak_trc_parse_state_t; | |||
| 37 | ||||
| 38 | typedef enum { | |||
| 39 | Col_BusNumber, // 0 | |||
| 40 | Col_BusNumber_v1, // 1 | |||
| 41 | Col_Direction, // 2 | |||
| 42 | Col_CanId, // 3 | |||
| 43 | Col_DLC, // 4 | |||
| 44 | Col_MessageNumber, // 5 | |||
| 45 | Col_TimeOffset_v1_0, // 6 | |||
| 46 | Col_TimeOffset, // 7 | |||
| 47 | Col_Reserved, // 8 | |||
| 48 | Col_MessageType, // 9 | |||
| 49 | Col_ErrorOrRtr_v1p0, // 10 | |||
| 50 | // keep it to the last one to get the | |||
| 51 | Col_Data_v1_0, // 11 | |||
| 52 | Col_Data, // 12 | |||
| 53 | Col_Invalid // 13 | |||
| 54 | } peak_trc_column_type_t; | |||
| 55 | ||||
| 56 | typedef struct { | |||
| 57 | peak_trc_column_type_t type; | |||
| 58 | char code; | |||
| 59 | char* regex; | |||
| 60 | } peak_trc_column_map_t; | |||
| 61 | ||||
| 62 | typedef struct { | |||
| 63 | time_t trace_start; | |||
| 64 | time_t trace_start_nsec; | |||
| 65 | ||||
| 66 | uint8_t column_positions[Col_Invalid]; | |||
| 67 | ||||
| 68 | uint8_t file_version_major, file_version_minor; | |||
| 69 | GRegex* data_matcher; | |||
| 70 | } peak_trc_state_t; | |||
| 71 | ||||
| 72 | static int peak_trc_file_type_subtype = -1; | |||
| 73 | ||||
| 74 | static peak_trc_column_map_t colmap[] = { | |||
| 75 | {Col_BusNumber, 'B', "\\s*(([0-9]*)|-)"}, | |||
| 76 | /* This isn't a real column type, so give it a "type" that shouldn't be used */ | |||
| 77 | {Col_BusNumber_v1, 1, "\\s*([0-9]*)"}, | |||
| 78 | {Col_Direction, 'd', "\\s*(Rx|Tx|Warng|Error)"}, | |||
| 79 | /* This isn't a real column type, so give it a "type" that shouldn't be used */ | |||
| 80 | {Col_Data_v1_0, 2, "\\s?(((--|[0-9A-F]{2})\\s?)*)"}, | |||
| 81 | {Col_Data, 'D', "\\s?((((--|[0-9A-F]{2})\\s?))*)"}, | |||
| 82 | {Col_CanId, 'I', "\\s*([0-9A-F]*)"}, | |||
| 83 | {Col_DLC, 'l', "\\s*([0-9]*)"}, | |||
| 84 | {Col_DLC, 'L', "\\s*([0-9]*)"}, | |||
| 85 | {Col_MessageNumber, 'N', "\\s*([0-9]*)\\)"}, | |||
| 86 | {Col_TimeOffset, 'O', "\\s*([0-9]*\\.[0-9]*)"}, | |||
| 87 | /* This isn't a real column type, so give it a "type" that shouldn't be used */ | |||
| 88 | {Col_TimeOffset_v1_0, 3, "\\s*([0-9]*)"}, | |||
| 89 | {Col_Reserved, 'R', "\\s*(-)"}, | |||
| 90 | {Col_MessageType, 'T', "\\s*([A-Z]{2})"}, | |||
| 91 | /* This isn't a real column, so give it a "type" that shouldn't be used */ | |||
| 92 | {Col_ErrorOrRtr_v1p0, 4, "\\s*(ERROR|RTR)?"}, | |||
| 93 | {Col_Invalid, 0, ""}, | |||
| 94 | }; | |||
| 95 | ||||
| 96 | void register_peak_trc(void); | |||
| 97 | ||||
| 98 | static wtap_open_return_val | |||
| 99 | peak_trc_parse(wtap* wth, peak_trc_state_t* state, gint64* offset, int* err, char** err_info) | |||
| 100 | { | |||
| 101 | gint64 seek_off = 0; | |||
| 102 | bool_Bool found_timestamp = false0; | |||
| 103 | char line_buffer[PEAK_TRC_MAX_LINE_SIZE4096]; | |||
| 104 | ||||
| 105 | /* 1.0 file format does not have version info or column line, | |||
| 106 | * so assume that until told otherwise | |||
| 107 | */ | |||
| 108 | int major = 1; | |||
| 109 | int minor = 0; | |||
| 110 | ||||
| 111 | ws_debug("%s: Trying peak_trc file decoder\n", G_STRFUNC)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 111, __func__, "%s: Trying peak_trc file decoder\n", ((const char*) (__func__))); } } while (0); | |||
| 112 | ||||
| 113 | /* Initial start time until we find it in the header */ | |||
| 114 | state->trace_start = time(NULL((void*)0)); | |||
| 115 | state->trace_start_nsec = 0; | |||
| 116 | ||||
| 117 | state->file_version_major = 1; | |||
| 118 | state->file_version_minor = 0; | |||
| 119 | ||||
| 120 | /* Initialize columns to "invalid" */ | |||
| 121 | for (int i = 0; i < Col_Invalid; i++) | |||
| 122 | state->column_positions[i] = Col_Invalid; | |||
| 123 | ||||
| 124 | /* Bail if the end of the file is here and it hasn't been determined it's a PEAK file */ | |||
| 125 | while (!file_eof(wth->fh)) | |||
| 126 | { | |||
| 127 | seek_off = file_tell(wth->fh); | |||
| 128 | ||||
| 129 | ws_debug("%s: Starting parser at offset %" PRIi64 "\n", G_STRFUNC, seek_off)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 129, __func__, "%s: Starting parser at offset %" "l" "i" "\n" , ((const char*) (__func__)), seek_off); } } while (0); | |||
| 130 | ||||
| 131 | if (file_gets(line_buffer, PEAK_TRC_MAX_LINE_SIZE4096, wth->fh) == NULL((void*)0)) | |||
| 132 | { | |||
| 133 | /* Error reading file, bail out */ | |||
| 134 | *err = file_error(wth->fh, err_info); | |||
| 135 | if (*err != 0 && *err != WTAP_ERR_SHORT_READ-12) | |||
| 136 | return WTAP_OPEN_ERROR; | |||
| 137 | ||||
| 138 | return WTAP_OPEN_NOT_MINE; | |||
| 139 | } | |||
| 140 | ||||
| 141 | g_strstrip(line_buffer)g_strchomp (g_strchug (line_buffer)); | |||
| 142 | size_t line_len = strlen(line_buffer); | |||
| 143 | ||||
| 144 | /* Ignore empty lines */ | |||
| 145 | if (line_len == 0) | |||
| 146 | continue; | |||
| 147 | ||||
| 148 | /* The intention here is to get through all of the "header" which consists of | |||
| 149 | * "commented" lines (notated with a ;) | |||
| 150 | * Once the header is complete, parsing can be confident it's a PEAK file | |||
| 151 | */ | |||
| 152 | if (line_buffer[0] != ';') | |||
| 153 | { | |||
| 154 | /* Two possibilities: | |||
| 155 | * 1. This is the start of the actual packet data and header parsing is complete | |||
| 156 | * 2. This isn't a PEAK file | |||
| 157 | * | |||
| 158 | * Determine this by if the timestamp has been found | |||
| 159 | */ | |||
| 160 | if (!found_timestamp) | |||
| 161 | return WTAP_OPEN_NOT_MINE; | |||
| 162 | ||||
| 163 | /* Valid PEAK file */ | |||
| 164 | GString* regex_str = g_string_new(NULL((void*)0)); | |||
| 165 | ||||
| 166 | *offset = seek_off; | |||
| 167 | ||||
| 168 | /* Save off the data found in the header */ | |||
| 169 | state->file_version_major = (uint8_t)major; | |||
| 170 | state->file_version_minor = (uint8_t)minor; | |||
| 171 | ||||
| 172 | /* File version 1.x doesn't explicitly provide column order information */ | |||
| 173 | if (state->file_version_major == 1) | |||
| 174 | { | |||
| 175 | state->column_positions[Col_MessageNumber] = 1; | |||
| 176 | ||||
| 177 | switch(state->file_version_minor) | |||
| 178 | { | |||
| 179 | case 0: | |||
| 180 | state->column_positions[Col_TimeOffset_v1_0] = 2; | |||
| 181 | state->column_positions[Col_CanId] = 3; | |||
| 182 | state->column_positions[Col_DLC] = 4; | |||
| 183 | state->column_positions[Col_ErrorOrRtr_v1p0] = 5; | |||
| 184 | state->column_positions[Col_Data_v1_0] = 6; | |||
| 185 | break; | |||
| 186 | case 1: | |||
| 187 | state->column_positions[Col_TimeOffset] = 2; | |||
| 188 | state->column_positions[Col_Direction] = 3; | |||
| 189 | state->column_positions[Col_CanId] = 4; | |||
| 190 | state->column_positions[Col_DLC] = 5; | |||
| 191 | state->column_positions[Col_ErrorOrRtr_v1p0] = 6; | |||
| 192 | state->column_positions[Col_Data] = 7; | |||
| 193 | break; | |||
| 194 | case 2: | |||
| 195 | state->column_positions[Col_TimeOffset] = 2; | |||
| 196 | state->column_positions[Col_BusNumber_v1] = 3; | |||
| 197 | state->column_positions[Col_Direction] = 4; | |||
| 198 | state->column_positions[Col_CanId] = 5; | |||
| 199 | state->column_positions[Col_DLC] = 6; | |||
| 200 | state->column_positions[Col_ErrorOrRtr_v1p0] = 7; | |||
| 201 | state->column_positions[Col_Data] = 8; | |||
| 202 | break; | |||
| 203 | default: | |||
| 204 | state->column_positions[Col_TimeOffset] = 2; | |||
| 205 | state->column_positions[Col_BusNumber_v1] = 3; | |||
| 206 | state->column_positions[Col_Direction] = 4; | |||
| 207 | state->column_positions[Col_CanId] = 5; | |||
| 208 | state->column_positions[Col_Reserved] = 6; | |||
| 209 | state->column_positions[Col_DLC] = 7; | |||
| 210 | state->column_positions[Col_ErrorOrRtr_v1p0] = 8; | |||
| 211 | state->column_positions[Col_Data] = 9; | |||
| 212 | break; | |||
| 213 | } | |||
| 214 | ||||
| 215 | for (int column_index = 0; column_index < Col_Invalid; column_index++) | |||
| 216 | { | |||
| 217 | for (int col_type = Col_BusNumber; col_type < Col_Invalid; col_type++) | |||
| 218 | { | |||
| 219 | if (state->column_positions[col_type] == column_index) | |||
| 220 | { | |||
| 221 | for (peak_trc_column_map_t* item = colmap; item->type != Col_Invalid; item++) | |||
| 222 | { | |||
| 223 | if ((int)item->type == col_type) | |||
| 224 | { | |||
| 225 | g_string_append(regex_str, item->regex)(__builtin_constant_p (item->regex) ? __extension__ ({ const char * const __val = (item->regex); g_string_append_len_inline (regex_str, __val, (__val != ((void*)0)) ? (gssize) strlen ( ((__val) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline (regex_str, item->regex, (gssize) -1)); | |||
| 226 | break; | |||
| 227 | } | |||
| 228 | } | |||
| 229 | } | |||
| 230 | } | |||
| 231 | } | |||
| 232 | ||||
| 233 | state->data_matcher = g_regex_new(regex_str->str, (GRegexCompileFlags)(G_REGEX_OPTIMIZE | G_REGEX_RAW), (GRegexMatchFlags)0, NULL((void*)0)); | |||
| 234 | g_string_free(regex_str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) ( (regex_str), ((!(0)))) : g_string_free_and_steal (regex_str)) : (g_string_free) ((regex_str), ((!(0))))); | |||
| 235 | } | |||
| 236 | ||||
| 237 | return WTAP_OPEN_MINE; | |||
| 238 | } | |||
| 239 | ||||
| 240 | if (g_str_has_prefix(line_buffer, ";$FILEVERSION=")(__builtin_constant_p (";$FILEVERSION=")? __extension__ ({ const char * const __str = (line_buffer); const char * const __prefix = (";$FILEVERSION="); gboolean __result = (0); if (__str == ( (void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix ) (__str, __prefix); else { const size_t __str_len = strlen ( ((__str) + !(__str))); const size_t __prefix_len = strlen ((( __prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len ) == 0; } __result; }) : (g_str_has_prefix) (line_buffer, ";$FILEVERSION=" ) )) | |||
| 241 | { | |||
| 242 | if (sscanf(line_buffer, ";$FILEVERSION=%d.%d\r\n", &major, &minor) != 2) | |||
| 243 | return WTAP_OPEN_NOT_MINE; | |||
| 244 | } | |||
| 245 | else if (g_str_has_prefix(line_buffer, ";$STARTTIME=")(__builtin_constant_p (";$STARTTIME=")? __extension__ ({ const char * const __str = (line_buffer); const char * const __prefix = (";$STARTTIME="); gboolean __result = (0); if (__str == (( void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix ) (__str, __prefix); else { const size_t __str_len = strlen ( ((__str) + !(__str))); const size_t __prefix_len = strlen ((( __prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len ) == 0; } __result; }) : (g_str_has_prefix) (line_buffer, ";$STARTTIME=" ) )) | |||
| 246 | { | |||
| 247 | /* $STARTTIME keyword to store the absolute start time of the trace file: | |||
| 248 | Format: Floating point, decimal separator is a point. | |||
| 249 | Value: Integral part = Number of days that have passed since 30. December 1899. | |||
| 250 | Fractional Part = Fraction of a 24-hour day that has elapsed, resolution is 1 millisecond. */ | |||
| 251 | ||||
| 252 | gchar** parts = g_strsplit(line_buffer, "=", 2); | |||
| 253 | if (parts[1] == NULL((void*)0)) | |||
| 254 | { | |||
| 255 | /* At this point, the file is probably a PEAK file that is poorly formatted, so bail with an error */ | |||
| 256 | *err_info = ws_strdup("peak_trc: unable to parse start time")wmem_strdup(((void*)0), "peak_trc: unable to parse start time" ); | |||
| 257 | g_strfreev(parts); | |||
| 258 | return WTAP_OPEN_ERROR; | |||
| 259 | } | |||
| 260 | ||||
| 261 | /* Convert to a more "standard" timestamp */ | |||
| 262 | double days_since_epoch = g_strtod(parts[1], NULL((void*)0)); | |||
| 263 | days_since_epoch -= 25569.0; | |||
| 264 | time_t ts = 0; | |||
| 265 | struct tm* t = gmtime(&ts); | |||
| 266 | t->tm_sec += (int)(days_since_epoch * 24 * 3600); | |||
| 267 | state->trace_start = mktime(t); | |||
| 268 | state->trace_start_nsec = 0; | |||
| 269 | ||||
| 270 | found_timestamp = true1; | |||
| 271 | ||||
| 272 | g_strfreev(parts); | |||
| 273 | } | |||
| 274 | else if (g_str_has_prefix(line_buffer, ";$COLUMNS=")(__builtin_constant_p (";$COLUMNS=")? __extension__ ({ const char * const __str = (line_buffer); const char * const __prefix = (";$COLUMNS="); gboolean __result = (0); if (__str == ((void *)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix); else { const size_t __str_len = strlen (( (__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix ) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len ) == 0; } __result; }) : (g_str_has_prefix) (line_buffer, ";$COLUMNS=" ) )) | |||
| 275 | { | |||
| 276 | gchar** parts = g_strsplit(line_buffer, "=", 2); | |||
| 277 | if (parts[1] == NULL((void*)0)) | |||
| 278 | { | |||
| 279 | /* At this point, the file is probably a PEAK file that is poorly formatted, so bail with an error */ | |||
| 280 | *err_info = ws_strdup("peak_trc: unable to parse column definitions")wmem_strdup(((void*)0), "peak_trc: unable to parse column definitions" ); | |||
| 281 | g_strfreev(parts); | |||
| 282 | return WTAP_OPEN_ERROR; | |||
| 283 | } | |||
| 284 | ||||
| 285 | gchar** columns = g_strsplit(parts[1], ",", 0); | |||
| 286 | int column_index = 0; | |||
| 287 | for (gchar** iter = columns; *iter != NULL((void*)0); iter++) | |||
| 288 | { | |||
| 289 | size_t col_length = strlen(*iter); | |||
| 290 | if (col_length > 1) { | |||
| 291 | *err_info = ws_strdup_printf("peak_trc: unknown column definition in the $COLUMNS line: '%s'", *iter)wmem_strdup_printf(((void*)0), "peak_trc: unknown column definition in the $COLUMNS line: '%s'" , *iter); | |||
| 292 | g_strfreev(columns); | |||
| 293 | g_strfreev(parts); | |||
| 294 | return WTAP_OPEN_ERROR; | |||
| 295 | } | |||
| 296 | if (col_length == 0) | |||
| 297 | { | |||
| 298 | //Done ? | |||
| 299 | break; | |||
| 300 | } | |||
| 301 | ||||
| 302 | /* Assign the column value if found */ | |||
| 303 | for (peak_trc_column_map_t* item = colmap; item->type != Col_Invalid; item++) | |||
| 304 | { | |||
| 305 | if ((*iter)[0] == item->code) | |||
| 306 | { | |||
| 307 | // column type matched | |||
| 308 | state->column_positions[item->type] = column_index; | |||
| 309 | break; | |||
| 310 | } | |||
| 311 | } | |||
| 312 | column_index++; | |||
| 313 | } | |||
| 314 | ||||
| 315 | g_strfreev(columns); | |||
| 316 | g_strfreev(parts); | |||
| 317 | } | |||
| 318 | else if (line_len >= 2) | |||
| 319 | { | |||
| 320 | /* The rest of the "keywords" are separated by whitespace after the initial ';' */ | |||
| 321 | char* keyword = &line_buffer[1]; | |||
| 322 | while (iswspace(*keyword)) | |||
| 323 | keyword++; | |||
| 324 | ||||
| 325 | if (g_str_has_prefix(keyword, "Start time:")(__builtin_constant_p ("Start time:")? __extension__ ({ const char * const __str = (keyword); const char * const __prefix = ("Start time:"); gboolean __result = (0); if (__str == ((void *)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix); else { const size_t __str_len = strlen (( (__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix ) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len ) == 0; } __result; }) : (g_str_has_prefix) (keyword, "Start time:" ) )) | |||
| 326 | { | |||
| 327 | /* Use the $STARTTIME if it has already been found */ | |||
| 328 | if (!found_timestamp) | |||
| 329 | { | |||
| 330 | int day, month, year, hour, minute, second, millisecond; | |||
| 331 | if (sscanf(keyword, "Start time: %d.%d.%d %d:%d:%d.%d", &month, &day, &year, &hour, &minute, &second, &millisecond) != 7) | |||
| 332 | { | |||
| 333 | *err_info = ws_strdup("peak_trc: unable to parse start time")wmem_strdup(((void*)0), "peak_trc: unable to parse start time" ); | |||
| 334 | return WTAP_OPEN_ERROR; | |||
| 335 | } | |||
| 336 | ||||
| 337 | struct tm t; | |||
| 338 | t.tm_sec = second; | |||
| 339 | t.tm_min = minute; | |||
| 340 | t.tm_hour = hour; | |||
| 341 | t.tm_mday = day; | |||
| 342 | t.tm_mon = month-1; | |||
| 343 | t.tm_year = year-1900; | |||
| 344 | t.tm_wday = 0; | |||
| 345 | t.tm_yday = 0; | |||
| 346 | t.tm_isdst = -1; | |||
| 347 | ||||
| 348 | state->trace_start = mktime(&t); | |||
| 349 | state->trace_start_nsec = millisecond*1000000; | |||
| 350 | ||||
| 351 | found_timestamp = true1; | |||
| 352 | } | |||
| 353 | } | |||
| 354 | } | |||
| 355 | } | |||
| 356 | ||||
| 357 | return WTAP_OPEN_NOT_MINE; | |||
| 358 | } | |||
| 359 | ||||
| 360 | static bool_Bool peak_trc_read_packet_v1(wtap* wth, peak_trc_state_t* state, wtap_can_msg_t* peak_msg, char* line_buffer) | |||
| 361 | { | |||
| 362 | // pre 2.0 packets use regexp with fixed column order with a regexp | |||
| 363 | GMatchInfo* match_info = NULL((void*)0); | |||
| 364 | bool_Bool found = g_regex_match(state->data_matcher, line_buffer, 0, &match_info); | |||
| 365 | int column_count = g_match_info_get_match_count(match_info); | |||
| 366 | if (!found || column_count < 5) | |||
| 367 | { | |||
| 368 | /* Not a valid v1 packet */ | |||
| 369 | g_match_info_free(match_info); | |||
| 370 | return false0; | |||
| 371 | } | |||
| 372 | ||||
| 373 | for (int col = Col_BusNumber; col < Col_Invalid; col++) | |||
| 374 | { | |||
| 375 | if (state->column_positions[col] >= column_count || state->column_positions[col] == Col_Invalid) | |||
| 376 | continue; | |||
| 377 | ||||
| 378 | gchar* column_text = g_match_info_fetch(match_info, state->column_positions[col]); | |||
| 379 | if (column_text == NULL((void*)0)) | |||
| 380 | { | |||
| 381 | g_match_info_free(match_info); | |||
| 382 | return false0; | |||
| 383 | } | |||
| 384 | ||||
| 385 | switch (col) | |||
| 386 | { | |||
| 387 | case Col_Direction: | |||
| 388 | if (strcmp(column_text, "Warng") == 0) | |||
| 389 | { | |||
| 390 | // Warning lines cannot be interpreted in Wireshark needs to be skipped | |||
| 391 | g_free(column_text); | |||
| 392 | g_match_info_free(match_info); | |||
| 393 | return false0; | |||
| 394 | } | |||
| 395 | if (strcmp(column_text, "Error") == 0) | |||
| 396 | { | |||
| 397 | peak_msg->type = MSG_TYPE_ERR; | |||
| 398 | } | |||
| 399 | break; | |||
| 400 | case Col_CanId: | |||
| 401 | peak_msg->id = (guint32)g_ascii_strtoull(column_text, NULL((void*)0), 16); | |||
| 402 | break; | |||
| 403 | case Col_ErrorOrRtr_v1p0: | |||
| 404 | if (state->file_version_minor == 1 && strcmp(column_text, "RTR") == 0) | |||
| 405 | { | |||
| 406 | peak_msg->type = MSG_TYPE_STD_RTR; | |||
| 407 | } | |||
| 408 | break; | |||
| 409 | case Col_TimeOffset: | |||
| 410 | case Col_TimeOffset_v1_0: | |||
| 411 | { | |||
| 412 | double tsd = g_ascii_strtod(column_text, NULL((void*)0)) / 1000.0; // parse to seconds | |||
| 413 | tsd += state->trace_start; | |||
| 414 | peak_msg->ts.secs = (guint64)tsd; | |||
| 415 | peak_msg->ts.nsecs = (int)((tsd - (guint64)tsd) * 1000000000); | |||
| 416 | } | |||
| 417 | break; | |||
| 418 | case Col_DLC: | |||
| 419 | peak_msg->data.length = (uint8_t)g_ascii_strtoull(column_text, NULL((void*)0), 10); | |||
| 420 | break; | |||
| 421 | case Col_BusNumber_v1: | |||
| 422 | { | |||
| 423 | char* interface_name = wmem_strdup_printf(NULL((void*)0), "interface%s", column_text); | |||
| 424 | peak_msg->interface_id = wtap_socketcan_find_or_create_new_interface(wth, interface_name); | |||
| 425 | wmem_free(NULL((void*)0), interface_name); | |||
| 426 | break; | |||
| 427 | } | |||
| 428 | ||||
| 429 | case Col_Data: | |||
| 430 | case Col_Data_v1_0: | |||
| 431 | { | |||
| 432 | gchar* err_or_rtr = g_match_info_fetch(match_info, state->column_positions[Col_ErrorOrRtr_v1p0]); | |||
| 433 | gchar** bytes = g_strsplit(g_strstrip(column_text)g_strchomp (g_strchug (column_text)), " ", CAN_MAX_DLEN8); | |||
| 434 | // 1.0 format has an ERROR prefix in the data bytes, 1.1 has a separate columns for message type | |||
| 435 | if ((g_str_has_prefix(err_or_rtr, "ERROR")(__builtin_constant_p ("ERROR")? __extension__ ({ const char * const __str = (err_or_rtr); const char * const __prefix = ("ERROR" ); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (err_or_rtr, "ERROR") )) || | |||
| 436 | (state->file_version_minor == 1 && peak_msg->type == MSG_TYPE_ERR)) | |||
| 437 | { | |||
| 438 | peak_msg->type = MSG_TYPE_ERR; | |||
| 439 | ||||
| 440 | /* For LINUX_CAN_ERR the length has to be CAN_MAX_DLEN */ | |||
| 441 | peak_msg->data.length = CAN_MAX_DLEN8; | |||
| 442 | /* Clear the data of the message to populate it with SocketCAN meta data */ | |||
| 443 | memset(peak_msg->data.data, 0, CAN_MAX_DLEN8); | |||
| 444 | ||||
| 445 | guint32 old_id = peak_msg->id; | |||
| 446 | peak_msg->id = 0; | |||
| 447 | // ID: Type of Error Frame | |||
| 448 | switch (old_id) | |||
| 449 | { | |||
| 450 | case 1: // 1 = Bit Error | |||
| 451 | peak_msg->id |= CAN_ERR_PROT_BIT0x01; | |||
| 452 | break; | |||
| 453 | case 2: // 2 = Form Error | |||
| 454 | peak_msg->id |= CAN_ERR_PROT_FORM0x02; | |||
| 455 | break; | |||
| 456 | case 4: // 4 = Stuff Error | |||
| 457 | peak_msg->id |= CAN_ERR_PROT_STUFF0x04; | |||
| 458 | break; | |||
| 459 | } | |||
| 460 | ||||
| 461 | //Translate PEAK error data into SocketCAN meta data for supported error types | |||
| 462 | if ((peak_msg->id | (CAN_ERR_PROT_FORM0x02& CAN_ERR_PROT_STUFF0x04)) != 0) | |||
| 463 | { | |||
| 464 | // Data Byte 0: Direction | |||
| 465 | uint8_t byte0 = (uint8_t)g_ascii_strtoull(bytes[0], NULL((void*)0), 16); | |||
| 466 | if (byte0 < 2) | |||
| 467 | { | |||
| 468 | //0 = Error occurred while sending | |||
| 469 | //1 = Error occurred while receiving. | |||
| 470 | peak_msg->data.data[2] |= CAN_ERR_PROT_TX0x80; | |||
| 471 | } | |||
| 472 | ||||
| 473 | // Data Byte 1: Current Position in Bit Stream | |||
| 474 | uint8_t bit_pos = (uint8_t)g_ascii_strtoull(bytes[1], NULL((void*)0), 16); | |||
| 475 | if (bit_pos == 28) | |||
| 476 | { | |||
| 477 | peak_msg->data.data[2] |= CAN_ERR_PROT_OVERLOAD0x20; | |||
| 478 | } | |||
| 479 | else | |||
| 480 | { | |||
| 481 | // magically SocketCAN and PEAK error bit positions are identical with some exceptions | |||
| 482 | peak_msg->data.data[3] = bit_pos; | |||
| 483 | if (peak_msg->data.data[3] == 23) | |||
| 484 | peak_msg->data.data[3] = 0; // Error position not present in SocketCAN | |||
| 485 | } | |||
| 486 | } | |||
| 487 | } | |||
| 488 | else if (g_str_has_prefix(err_or_rtr, "RTR")(__builtin_constant_p ("RTR")? __extension__ ({ const char * const __str = (err_or_rtr); const char * const __prefix = ("RTR"); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void*)0)) __result = (g_str_has_prefix) (__str, __prefix ); else { const size_t __str_len = strlen (((__str) + !(__str ))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix ))); if (__str_len >= __prefix_len) __result = memcmp (((__str ) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0 ; } __result; }) : (g_str_has_prefix) (err_or_rtr, "RTR") ) || (state->file_version_minor == 1 && peak_msg->type == MSG_TYPE_STD_RTR)) | |||
| 489 | { | |||
| 490 | peak_msg->type = MSG_TYPE_STD_RTR; | |||
| 491 | peak_msg->data.length = 0; | |||
| 492 | } | |||
| 493 | else | |||
| 494 | { | |||
| 495 | peak_msg->type = MSG_TYPE_STD; | |||
| 496 | if (bytes) | |||
| 497 | { | |||
| 498 | for (int byte_i = 0; ((byte_i < CAN_MAX_DLEN8) && (bytes[byte_i] != 0)); byte_i++) | |||
| 499 | peak_msg->data.data[byte_i] = (uint8_t)g_ascii_strtoull(bytes[byte_i], NULL((void*)0), 16); | |||
| 500 | } | |||
| 501 | } | |||
| 502 | g_strfreev(bytes); | |||
| 503 | g_free(err_or_rtr); | |||
| 504 | break; | |||
| 505 | } | |||
| 506 | } | |||
| 507 | g_free(column_text); | |||
| 508 | } | |||
| 509 | ||||
| 510 | #ifdef WS_DEBUG1 | |||
| 511 | for (int i = 0; i < column_count; i++) | |||
| 512 | ws_debug("%d: %s\n", i, g_match_info_fetch(match_info, i))do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 512, __func__, "%d: %s\n", i, g_match_info_fetch(match_info , i)); } } while (0); | |||
| 513 | #endif | |||
| 514 | ||||
| 515 | g_match_info_free(match_info); | |||
| 516 | return true1; | |||
| 517 | } | |||
| 518 | ||||
| 519 | static bool_Bool peak_trc_read_packet_v2(wtap* wth, peak_trc_state_t* state, wtap_can_msg_t* peak_msg, char* line_buffer) | |||
| 520 | { | |||
| 521 | int i = 0; | |||
| 522 | int column_i = 0; | |||
| 523 | int column_start = 0; | |||
| 524 | bool_Bool last_char_is_ws = true1; | |||
| 525 | peak_trc_column_type_t current_column = Col_Invalid; | |||
| 526 | while (line_buffer[i] != 0) | |||
| 527 | { | |||
| 528 | bool_Bool is_whitespace = iswspace(line_buffer[i]); | |||
| 529 | if (current_column == Col_Data) | |||
| 530 | { | |||
| 531 | // data always the last and could contain spaces | |||
| 532 | is_whitespace = (line_buffer[i] == '\r' || line_buffer[i] == '\n'); | |||
| 533 | } | |||
| 534 | ||||
| 535 | if (is_whitespace) | |||
| 536 | { | |||
| 537 | if (!last_char_is_ws) | |||
| 538 | { | |||
| 539 | // column closed -> process data | |||
| 540 | gchar* column_text = g_utf8_substring(line_buffer, column_start, i); | |||
| 541 | ||||
| 542 | ws_debug("Column %d: %s\n", current_column, column_text)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 542, __func__, "Column %d: %s\n", current_column, column_text ); } } while (0); | |||
| 543 | ||||
| 544 | switch (current_column) { | |||
| 545 | case Col_BusNumber: | |||
| 546 | { | |||
| 547 | char* interface_name = wmem_strdup_printf(NULL((void*)0), "interface%s", column_text); | |||
| 548 | peak_msg->interface_id = wtap_socketcan_find_or_create_new_interface(wth, interface_name); | |||
| 549 | wmem_free(NULL((void*)0), interface_name); | |||
| 550 | break; | |||
| 551 | } | |||
| 552 | case Col_Direction: | |||
| 553 | break; | |||
| 554 | case Col_Data: { | |||
| 555 | if (peak_msg->type == MSG_TYPE_ERR) | |||
| 556 | { | |||
| 557 | // FIXME fill data and msg id | |||
| 558 | } | |||
| 559 | else | |||
| 560 | { | |||
| 561 | gchar** bytes = g_strsplit(g_strstrip(column_text)g_strchomp (g_strchug (column_text)), " ", CANFD_MAX_DLEN64); | |||
| 562 | int byte_i = 0; | |||
| 563 | while (byte_i < CANFD_MAX_DLEN64) { | |||
| 564 | if (bytes[byte_i] == 0) | |||
| 565 | break; | |||
| 566 | peak_msg->data.data[byte_i] = (uint8_t)g_ascii_strtoull(bytes[byte_i], NULL((void*)0), 16); | |||
| 567 | byte_i++; | |||
| 568 | } | |||
| 569 | } | |||
| 570 | break; | |||
| 571 | } | |||
| 572 | case Col_CanId: | |||
| 573 | peak_msg->id = (guint)g_ascii_strtoull(column_text, NULL((void*)0), 16); | |||
| 574 | break; | |||
| 575 | case Col_DLC: | |||
| 576 | peak_msg->data.length = (guint)g_ascii_strtoull(column_text, NULL((void*)0), 10); | |||
| 577 | break; | |||
| 578 | case Col_TimeOffset: | |||
| 579 | { | |||
| 580 | double tsd = g_ascii_strtod(column_text, NULL((void*)0)) / 1000.0; // parse to seconds | |||
| 581 | tsd += state->trace_start; | |||
| 582 | peak_msg->ts.secs = (guint64)tsd; | |||
| 583 | peak_msg->ts.nsecs = (int)((tsd - (guint64)tsd) * 1000000000); | |||
| 584 | break; | |||
| 585 | } | |||
| 586 | case Col_MessageType: | |||
| 587 | if (strcmp(column_text, "DT") == 0) | |||
| 588 | { | |||
| 589 | peak_msg->type = MSG_TYPE_STD; | |||
| 590 | } | |||
| 591 | else if (strcmp(column_text, "FD") == 0) | |||
| 592 | { | |||
| 593 | peak_msg->type = MSG_TYPE_STD_FD; | |||
| 594 | } | |||
| 595 | else if (strcmp(column_text, "FB") == 0) | |||
| 596 | { | |||
| 597 | // CAN FD data frame with BRS bit set (Bit Rate Switch). | |||
| 598 | peak_msg->type = MSG_TYPE_STD_FD; // TODO | |||
| 599 | } | |||
| 600 | else if (strcmp(column_text, "FE") == 0) | |||
| 601 | { | |||
| 602 | // CAN FD data frame with ESI bit set (Error State Indicator). | |||
| 603 | peak_msg->type = MSG_TYPE_ERR; | |||
| 604 | } | |||
| 605 | else if (strcmp(column_text, "BI") == 0) | |||
| 606 | { | |||
| 607 | // CAN FD data frame with both BRS and ESI bits set. | |||
| 608 | peak_msg->type = MSG_TYPE_ERR; | |||
| 609 | } | |||
| 610 | else if (strcmp(column_text, "RR") == 0) | |||
| 611 | { | |||
| 612 | peak_msg->type = MSG_TYPE_STD_RTR; | |||
| 613 | } | |||
| 614 | else if (strcmp(column_text, "ST") == 0) | |||
| 615 | { | |||
| 616 | // TODO: Hardware Status change. | |||
| 617 | // Currently not supported in Wireshark | |||
| 618 | return false0; | |||
| 619 | } | |||
| 620 | else if (strcmp(column_text, "ER") == 0) | |||
| 621 | { | |||
| 622 | peak_msg->type = MSG_TYPE_ERR; | |||
| 623 | } | |||
| 624 | else if (strcmp(column_text, "EC") == 0) | |||
| 625 | { | |||
| 626 | // TODO: Error Counter change | |||
| 627 | // Currently not supported in Wireshark | |||
| 628 | return false0; | |||
| 629 | } | |||
| 630 | else if (strcmp(column_text, "EV") == 0) | |||
| 631 | { | |||
| 632 | // Event. User-defined text, begins directly after bus specifier | |||
| 633 | // TODO add support for this event type | |||
| 634 | return false0; | |||
| 635 | } | |||
| 636 | break; | |||
| 637 | case Col_MessageNumber: | |||
| 638 | case Col_Reserved: | |||
| 639 | case Col_Invalid: | |||
| 640 | break; | |||
| 641 | /* Version 1 column types ignored */ | |||
| 642 | case Col_ErrorOrRtr_v1p0: | |||
| 643 | case Col_BusNumber_v1: | |||
| 644 | case Col_TimeOffset_v1_0: | |||
| 645 | case Col_Data_v1_0: | |||
| 646 | break; | |||
| 647 | } | |||
| 648 | g_free(column_text); | |||
| 649 | } | |||
| 650 | } | |||
| 651 | else | |||
| 652 | { | |||
| 653 | // non whitespace read | |||
| 654 | if (last_char_is_ws) | |||
| 655 | { | |||
| 656 | for (int lut_i = 0; lut_i < Col_Invalid; lut_i++) | |||
| 657 | { | |||
| 658 | if (state->column_positions[lut_i] == column_i) | |||
| 659 | { | |||
| 660 | current_column = lut_i; | |||
| 661 | break; | |||
| 662 | } | |||
| 663 | } | |||
| 664 | column_i++; | |||
| 665 | column_start = i; | |||
| 666 | } | |||
| 667 | } | |||
| 668 | last_char_is_ws = is_whitespace; | |||
| 669 | i++; | |||
| 670 | } | |||
| 671 | ||||
| 672 | return true1; | |||
| 673 | } | |||
| 674 | ||||
| 675 | static bool_Bool | |||
| 676 | peak_trc_read_packet(wtap *wth, FILE_T fh, peak_trc_state_t* state, | |||
| 677 | wtap_rec *rec, int *err, gchar **err_info, | |||
| 678 | gint64 *data_offset) | |||
| 679 | { | |||
| 680 | wtap_can_msg_t peak_msg = {0}; | |||
| 681 | char line_buffer[PEAK_TRC_MAX_LINE_SIZE4096]; | |||
| 682 | ||||
| 683 | while (!file_eof(fh)) | |||
| 684 | { | |||
| 685 | if (data_offset) | |||
| 686 | *data_offset = file_tell(fh); | |||
| 687 | ||||
| 688 | /* Read a line */ | |||
| 689 | if (file_gets(line_buffer, PEAK_TRC_MAX_LINE_SIZE4096, fh) == NULL((void*)0)) | |||
| 690 | { | |||
| 691 | *err = file_error(fh, err_info); | |||
| 692 | return false0; | |||
| 693 | } | |||
| 694 | ||||
| 695 | /* Ignore empty or commented lines */ | |||
| 696 | if ((strlen(line_buffer) == 0) || | |||
| 697 | (line_buffer[0] == ';')) | |||
| 698 | continue; | |||
| 699 | ||||
| 700 | //Start with unknown interface ID | |||
| 701 | peak_msg.interface_id = WTAP_SOCKETCAN_INVALID_INTERFACE_ID0xFFFFFFFF; | |||
| 702 | ||||
| 703 | if (state->file_version_major >= 2) | |||
| 704 | { | |||
| 705 | if (!peak_trc_read_packet_v2(wth, state, &peak_msg, line_buffer)) | |||
| 706 | continue; | |||
| 707 | } | |||
| 708 | else | |||
| 709 | { | |||
| 710 | if (!peak_trc_read_packet_v1(wth, state, &peak_msg, line_buffer)) | |||
| 711 | continue; | |||
| 712 | } | |||
| 713 | ||||
| 714 | if (peak_msg.id > 0x7FF) | |||
| 715 | { | |||
| 716 | //Translate from 11-bit to 32-bit IDs | |||
| 717 | switch (peak_msg.type) | |||
| 718 | { | |||
| 719 | case MSG_TYPE_STD_FD: | |||
| 720 | peak_msg.type = MSG_TYPE_EXT_FD; | |||
| 721 | break; | |||
| 722 | case MSG_TYPE_STD: | |||
| 723 | peak_msg.type = MSG_TYPE_EXT; | |||
| 724 | break; | |||
| 725 | case MSG_TYPE_STD_RTR: | |||
| 726 | peak_msg.type = MSG_TYPE_EXT_RTR; | |||
| 727 | break; | |||
| 728 | default: | |||
| 729 | //Ignore other types | |||
| 730 | break; | |||
| 731 | } | |||
| 732 | } | |||
| 733 | ||||
| 734 | return wtap_socketcan_gen_packet(wth, rec, &peak_msg, "peak", err, err_info); | |||
| 735 | } | |||
| 736 | ||||
| 737 | return false0; | |||
| 738 | } | |||
| 739 | ||||
| 740 | static bool_Bool | |||
| 741 | peak_trc_read(wtap *wth, wtap_rec *rec, int *err, char **err_info, | |||
| 742 | int64_t *data_offset) | |||
| 743 | { | |||
| 744 | peak_trc_state_t* state = (peak_trc_state_t*)wtap_socketcan_get_private_data(wth); | |||
| 745 | ||||
| 746 | ws_debug("%s: Try reading at offset %" PRIi64 "\n", G_STRFUNC, file_tell(wth->fh))do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 746, __func__, "%s: Try reading at offset %" "l" "i" "\n", ( (const char*) (__func__)), file_tell(wth->fh)); } } while ( 0); | |||
| 747 | ||||
| 748 | return peak_trc_read_packet(wth, wth->fh, state, rec, err, err_info, data_offset); | |||
| 749 | } | |||
| 750 | ||||
| 751 | static bool_Bool peak_trc_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, int *err, char **err_info) | |||
| 752 | { | |||
| 753 | ws_debug("%s: Read at offset %" PRIi64 "\n", G_STRFUNC, seek_off)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 753, __func__, "%s: Read at offset %" "l" "i" "\n", ((const char*) (__func__)), seek_off); } } while (0); | |||
| 754 | ||||
| 755 | peak_trc_state_t* state = (peak_trc_state_t*)wtap_socketcan_get_private_data(wth); | |||
| 756 | ||||
| 757 | if (file_seek(wth->random_fh, seek_off, SEEK_SET0, err) == -1) | |||
| 758 | { | |||
| 759 | *err = errno(*__errno_location ()); | |||
| 760 | *err_info = g_strdup(g_strerror(errno))g_strdup_inline (g_strerror((*__errno_location ()))); | |||
| 761 | ||||
| 762 | return false0; | |||
| 763 | } | |||
| 764 | ||||
| 765 | return peak_trc_read_packet(wth, wth->random_fh, state, rec, err, err_info, NULL((void*)0)); | |||
| 766 | } | |||
| 767 | ||||
| 768 | static void | |||
| 769 | clean_trc_state(peak_trc_state_t* state) | |||
| 770 | { | |||
| 771 | if (state != NULL((void*)0)) | |||
| 772 | { | |||
| 773 | if (state->data_matcher != NULL((void*)0)) | |||
| 774 | g_regex_unref(state->data_matcher); | |||
| 775 | ||||
| 776 | g_free(state); | |||
| 777 | } | |||
| 778 | } | |||
| 779 | ||||
| 780 | static void | |||
| 781 | peak_trc_close(void* tap_data) | |||
| 782 | { | |||
| 783 | clean_trc_state((peak_trc_state_t*)tap_data); | |||
| 784 | } | |||
| 785 | ||||
| 786 | wtap_open_return_val | |||
| 787 | peak_trc_open(wtap* wth, int* err, char** err_info) | |||
| 788 | { | |||
| 789 | gint64 data_offset = 0; | |||
| 790 | peak_trc_state_t* trc_state = g_new0(peak_trc_state_t, 1)((peak_trc_state_t *) g_malloc0_n ((1), sizeof (peak_trc_state_t ))); | |||
| ||||
| 791 | ||||
| 792 | /* wth->priv stores a pointer to the general file properties. | |||
| 793 | * It it updated when the header data is parsed */ | |||
| 794 | wth->priv = trc_state; | |||
| 795 | ||||
| 796 | wtap_open_return_val open_val = peak_trc_parse(wth, trc_state, &data_offset, err, err_info); | |||
| 797 | if (open_val != WTAP_OPEN_MINE) | |||
| 798 | { | |||
| 799 | clean_trc_state(trc_state); | |||
| 800 | g_free(trc_state); | |||
| ||||
| 801 | wth->priv = NULL((void*)0); | |||
| 802 | return open_val; | |||
| 803 | } | |||
| 804 | ||||
| 805 | ws_debug("%s: This is our file\n", G_STRFUNC)do { if (1) { ws_log_full("peak-trc", LOG_LEVEL_DEBUG, "wiretap/peak-trc.c" , 805, __func__, "%s: This is our file\n", ((const char*) (__func__ ))); } } while (0); | |||
| 806 | ||||
| 807 | /* Go to the start of the real packet data since header is now done */ | |||
| 808 | if (file_seek(wth->fh, data_offset, SEEK_SET0, err) == -1) | |||
| 809 | { | |||
| 810 | *err = errno(*__errno_location ()); | |||
| 811 | *err_info = g_strdup(g_strerror(errno))g_strdup_inline (g_strerror((*__errno_location ()))); | |||
| 812 | return WTAP_OPEN_ERROR; | |||
| 813 | } | |||
| 814 | ||||
| 815 | wtap_set_as_socketcan(wth, peak_trc_file_type_subtype, WTAP_TSPREC_USEC6, trc_state, peak_trc_close); | |||
| 816 | wth->subtype_read = peak_trc_read; | |||
| 817 | wth->subtype_seek_read = peak_trc_seek_read; | |||
| 818 | ||||
| 819 | return WTAP_OPEN_MINE; | |||
| 820 | } | |||
| 821 | ||||
| 822 | static const struct supported_block_type peak_trc_blocks_supported[] = { | |||
| 823 | /* | |||
| 824 | * We support packet blocks, with no comments or other options. | |||
| 825 | */ | |||
| 826 | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED0, ((void*)0) } | |||
| 827 | }; | |||
| 828 | ||||
| 829 | static const struct file_type_subtype_info peak_trc_info = { | |||
| 830 | "PEAK CAN TRC file", "peak-trc", "trc", NULL((void*)0), | |||
| 831 | false0, BLOCKS_SUPPORTED(peak_trc_blocks_supported)(sizeof (peak_trc_blocks_supported) / sizeof (peak_trc_blocks_supported )[0]), peak_trc_blocks_supported, | |||
| 832 | NULL((void*)0), NULL((void*)0), NULL((void*)0) | |||
| 833 | }; | |||
| 834 | ||||
| 835 | void register_peak_trc(void) | |||
| 836 | { | |||
| 837 | peak_trc_file_type_subtype = wtap_register_file_type_subtype(&peak_trc_info); | |||
| 838 | } | |||
| 839 | ||||
| 840 | /* | |||
| 841 | * Editor modelines - https://www.wireshark.org/tools/modelines.html | |||
| 842 | * | |||
| 843 | * Local variables: | |||
| 844 | * c-basic-offset: 4 | |||
| 845 | * tab-width: 8 | |||
| 846 | * indent-tabs-mode: nil | |||
| 847 | * End: | |||
| 848 | * | |||
| 849 | * vi: set shiftwidth=4 tabstop=8 expandtab: | |||
| 850 | * :indentSize=4:tabSize=8:noTabs=true: | |||
| 851 | */ |