4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * SPDX-License-Identifier: GPL-2.0+
15 #include "file_wrappers.h"
16 #include <wsutil/strtoi.h>
18 #include "catapult_dct2000.h"
20 #define MAX_FIRST_LINE_LENGTH 200
21 #define MAX_TIMESTAMP_LINE_LENGTH 100
22 #define MAX_LINE_LENGTH 65536
23 #define MAX_TIMESTAMP_LEN 32
24 #define MAX_SECONDS_CHARS 16
25 #define MAX_SUBSECOND_DECIMALS 4
26 #define MAX_CONTEXT_NAME 64
27 #define MAX_PROTOCOL_NAME 64
28 #define MAX_PORT_DIGITS 2
29 #define MAX_VARIANT_DIGITS 32
30 #define MAX_OUTHDR_NAME 256
31 #define AAL_HEADER_CHARS 12
34 - support for FP over AAL0
35 - support for IuR interface FP
39 /* 's' or 'r' of a packet as read from .out file */
40 typedef enum packet_direction_t
47 /***********************************************************************/
48 /* For each line, store (in case we need to dump): */
49 /* - String before time field */
50 /* - String beween time field and data (if NULL assume " l ") */
58 /*******************************************************************/
59 /* Information stored external to a file (wtap) needed for reading and dumping */
60 typedef struct dct2000_file_externals
62 /* Remember the time at the start of capture */
67 * The following information is needed only for dumping.
69 * XXX - Wiretap is not *supposed* to require that a packet being
70 * dumped come from a file of the same type that you currently have
71 * open; this should be fixed.
74 /* Buffer to hold first line, including magic and format number */
75 gchar firstline[MAX_FIRST_LINE_LENGTH];
76 gint firstline_length;
78 /* Buffer to hold second line with formatted file creation data/time */
79 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
80 gint secondline_length;
82 /* Hash table to store text prefix data part of displayed packets.
83 Records (file offset -> line_prefix_info_t)
85 GHashTable *packet_prefix_table;
86 } dct2000_file_externals_t;
88 /* 'Magic number' at start of Catapult DCT2000 .out files. */
89 static const gchar catapult_dct2000_magic[] = "Session Transcript";
91 /************************************************************/
92 /* Functions called from wiretap core */
93 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
95 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
96 struct wtap_pkthdr *phdr,
97 Buffer *buf, int *err,
99 static void catapult_dct2000_close(wtap *wth);
101 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
102 const guint8 *pd, int *err, gchar **err_info);
105 /************************************************************/
106 /* Private helper functions */
107 static gboolean read_new_line(FILE_T fh, gint *length,
108 gchar *buf, size_t bufsize, int *err,
110 static gboolean parse_line(char *linebuff, gint line_length,
111 gint *seconds, gint *useconds,
112 long *before_time_offset, long *after_time_offset,
115 packet_direction_t *direction,
116 int *encap, int *is_comment, int *is_sprint,
117 gchar *aal_header_chars,
118 gchar *context_name, guint8 *context_portp,
119 gchar *protocol_name, gchar *variant_name,
121 static gboolean process_parsed_line(wtap *wth,
122 dct2000_file_externals_t *file_externals,
123 struct wtap_pkthdr *phdr,
124 Buffer *buf, gint64 file_offset,
125 char *linebuff, long dollar_offset,
126 int seconds, int useconds,
127 gchar *timestamp_string,
128 packet_direction_t direction, int encap,
129 gchar *context_name, guint8 context_port,
130 gchar *protocol_name, gchar *variant_name,
131 gchar *outhdr_name, gchar *aal_header_chars,
132 gboolean is_comment, int data_chars,
133 int *err, gchar **err_info);
134 static guint8 hex_from_char(gchar c);
135 static void prepare_hex_byte_from_chars_table(void);
136 static guint8 hex_byte_from_chars(gchar *c);
137 static gchar char_from_hex(guint8 hex);
139 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
140 packet_direction_t direction,
141 gchar *aal_header_chars);
142 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
143 packet_direction_t direction);
144 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
145 packet_direction_t direction);
147 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
148 static guint packet_offset_hash_func(gconstpointer v);
150 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
151 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
155 /********************************************/
156 /* Open file (for reading) */
157 /********************************************/
159 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
163 gint firstline_length = 0;
164 dct2000_file_externals_t *file_externals;
165 static gchar linebuff[MAX_LINE_LENGTH];
166 static gboolean hex_byte_table_values_set = FALSE;
168 /* Clear errno before reading from the file */
172 /********************************************************************/
173 /* First line needs to contain at least as many characters as magic */
175 if (!read_new_line(wth->fh, &firstline_length, linebuff,
176 sizeof linebuff, err, err_info)) {
177 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
178 return WTAP_OPEN_ERROR;
179 return WTAP_OPEN_NOT_MINE;
181 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
182 firstline_length >= MAX_FIRST_LINE_LENGTH) {
184 return WTAP_OPEN_NOT_MINE;
187 /* This file is not for us if it doesn't match our signature */
188 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
189 return WTAP_OPEN_NOT_MINE;
192 /* Make sure table is ready for use */
193 if (!hex_byte_table_values_set) {
194 prepare_hex_byte_from_chars_table();
195 hex_byte_table_values_set = TRUE;
198 /*********************************************************************/
199 /* Need entry in file_externals table */
201 /* Allocate a new file_externals structure for this file */
202 file_externals = g_new(dct2000_file_externals_t,1);
203 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
205 /* Copy this first line into buffer so could write out later */
206 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
207 file_externals->firstline_length = firstline_length;
210 /***********************************************************/
211 /* Second line contains file timestamp */
212 /* Store this offset in in file_externals */
214 if (!read_new_line(wth->fh, &(file_externals->secondline_length),
215 linebuff, sizeof linebuff, err, err_info)) {
216 g_free(file_externals);
217 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
218 return WTAP_OPEN_ERROR;
219 return WTAP_OPEN_NOT_MINE;
221 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
222 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
224 /* Give up if file time line wasn't valid */
225 g_free(file_externals);
226 return WTAP_OPEN_NOT_MINE;
229 /* Fill in timestamp */
230 file_externals->start_secs = timestamp;
231 file_externals->start_usecs = usecs;
233 /* Copy this second line into buffer so could write out later */
234 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
237 /************************************************************/
238 /* File is for us. Fill in details so packets can be read */
240 /* Set our file type */
241 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000;
243 /* Use our own encapsulation to send all packets to our stub dissector */
244 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
246 /* Callbacks for reading operations */
247 wth->subtype_read = catapult_dct2000_read;
248 wth->subtype_seek_read = catapult_dct2000_seek_read;
249 wth->subtype_close = catapult_dct2000_close;
251 /* Choose microseconds (have 4 decimal places...) */
252 wth->file_tsprec = WTAP_TSPREC_USEC;
255 /***************************************************************/
256 /* Initialise packet_prefix_table (index is offset into file) */
257 file_externals->packet_prefix_table =
258 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
260 /* Set this wtap to point to the file_externals */
261 wth->priv = (void*)file_externals;
264 return WTAP_OPEN_MINE;
267 /* Ugly, but much faster than using g_snprintf! */
268 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
274 timestamp_string[idx++] = ((secs % 10)) + '0';
276 else if (secs < 100) {
277 timestamp_string[idx++] = ( secs / 10) + '0';
278 timestamp_string[idx++] = ((secs % 10)) + '0';
280 else if (secs < 1000) {
281 timestamp_string[idx++] = ((secs) / 100) + '0';
282 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
283 timestamp_string[idx++] = ((secs % 10)) + '0';
285 else if (secs < 10000) {
286 timestamp_string[idx++] = ((secs) / 1000) + '0';
287 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
288 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
289 timestamp_string[idx++] = ((secs % 10)) + '0';
291 else if (secs < 100000) {
292 timestamp_string[idx++] = ((secs) / 10000) + '0';
293 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
294 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
295 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
296 timestamp_string[idx++] = ((secs % 10)) + '0';
298 else if (secs < 1000000) {
299 timestamp_string[idx++] = ((secs) / 100000) + '0';
300 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
301 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
302 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
303 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
304 timestamp_string[idx++] = ((secs % 10)) + '0';
307 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
311 timestamp_string[idx++] = '.';
312 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
313 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
314 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
315 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
316 timestamp_string[idx++] = '\0';
319 /**************************************************/
320 /* Read packet function. */
321 /* Look for and read the next usable packet */
322 /* - return TRUE and details if found */
323 /**************************************************/
325 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
328 long dollar_offset, before_time_offset, after_time_offset;
329 packet_direction_t direction;
332 /* Get wtap external structure for this wtap */
333 dct2000_file_externals_t *file_externals =
334 (dct2000_file_externals_t*)wth->priv;
336 /* Search for a line containing a usable packet */
338 int line_length, seconds, useconds, data_chars;
339 int is_comment = FALSE;
340 int is_sprint = FALSE;
342 static gchar linebuff[MAX_LINE_LENGTH+1];
343 gchar aal_header_chars[AAL_HEADER_CHARS];
344 gchar context_name[MAX_CONTEXT_NAME];
345 guint8 context_port = 0;
346 gchar protocol_name[MAX_PROTOCOL_NAME+1];
347 gchar variant_name[MAX_VARIANT_DIGITS+1];
348 gchar outhdr_name[MAX_OUTHDR_NAME+1];
350 /* Get starting offset of the line we're about to read */
351 this_offset = file_tell(wth->fh);
353 /* Read a new line from file into linebuff */
354 if (!read_new_line(wth->fh, &line_length, linebuff,
355 sizeof linebuff, err, err_info)) {
357 return FALSE; /* error */
358 /* No more lines can be read, so quit. */
362 /* Try to parse the line as a frame record */
363 if (parse_line(linebuff, line_length, &seconds, &useconds,
364 &before_time_offset, &after_time_offset,
366 &data_chars, &direction, &encap, &is_comment, &is_sprint,
368 context_name, &context_port,
369 protocol_name, variant_name, outhdr_name)) {
370 line_prefix_info_t *line_prefix_info;
371 char timestamp_string[MAX_TIMESTAMP_LEN+1];
374 write_timestamp_string(timestamp_string, seconds, useconds/100);
376 /* Set data_offset to the beginning of the line we're returning.
377 This will be the seek_off parameter when this frame is re-read.
379 *data_offset = this_offset;
381 if (!process_parsed_line(wth, file_externals,
383 wth->frame_buffer, this_offset,
384 linebuff, dollar_offset,
388 context_name, context_port,
389 protocol_name, variant_name,
390 outhdr_name, aal_header_chars,
391 is_comment, data_chars,
395 /* Store the packet prefix in the hash table */
396 line_prefix_info = g_new(line_prefix_info_t,1);
398 /* Create and use buffer for contents before time */
399 line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
400 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
401 line_prefix_info->before_time[before_time_offset] = '\0';
403 /* Create and use buffer for contents before time.
404 Do this only if it doesn't correspond to " l ", which is by far the most
406 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
407 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
409 line_prefix_info->after_time = NULL;
412 /* Allocate & write buffer for line between timestamp and data */
413 line_prefix_info->after_time = (gchar *)g_malloc(dollar_offset - after_time_offset);
414 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
415 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
418 /* Add packet entry into table */
419 pkey = (gint64 *)g_malloc(sizeof(*pkey));
421 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
423 /* OK, we have packet details to return */
428 /* No packet details to return... */
433 /**************************************************/
434 /* Read & seek function. */
435 /**************************************************/
437 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
438 struct wtap_pkthdr *phdr, Buffer *buf,
439 int *err, gchar **err_info)
442 long dollar_offset, before_time_offset, after_time_offset;
443 static gchar linebuff[MAX_LINE_LENGTH+1];
444 gchar aal_header_chars[AAL_HEADER_CHARS];
445 gchar context_name[MAX_CONTEXT_NAME];
446 guint8 context_port = 0;
447 gchar protocol_name[MAX_PROTOCOL_NAME+1];
448 gchar variant_name[MAX_VARIANT_DIGITS+1];
449 gchar outhdr_name[MAX_OUTHDR_NAME+1];
450 int is_comment = FALSE;
451 int is_sprint = FALSE;
452 packet_direction_t direction;
454 int seconds, useconds, data_chars;
456 /* Get wtap external structure for this wtap */
457 dct2000_file_externals_t *file_externals =
458 (dct2000_file_externals_t*)wth->priv;
463 /* Seek to beginning of packet */
464 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
468 /* Re-read whole line (this really should succeed) */
469 if (!read_new_line(wth->random_fh, &length, linebuff,
470 sizeof linebuff, err, err_info)) {
474 /* Try to parse this line again (should succeed as re-reading...) */
475 if (parse_line(linebuff, length, &seconds, &useconds,
476 &before_time_offset, &after_time_offset,
478 &data_chars, &direction, &encap, &is_comment, &is_sprint,
480 context_name, &context_port,
481 protocol_name, variant_name, outhdr_name)) {
482 char timestamp_string[MAX_TIMESTAMP_LEN+1];
484 write_timestamp_string(timestamp_string, seconds, useconds/100);
486 if (!process_parsed_line(wth, file_externals,
488 linebuff, dollar_offset,
492 context_name, context_port,
493 protocol_name, variant_name,
494 outhdr_name, aal_header_chars,
495 is_comment, data_chars,
503 /* If get here, must have failed */
505 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
506 "line at position %" G_GINT64_MODIFIER "d",
512 /***************************************************************************/
513 /* Free dct2000-specific capture info from file that was open for reading */
514 /***************************************************************************/
516 catapult_dct2000_close(wtap *wth)
518 /* Get externals for this file */
519 dct2000_file_externals_t *file_externals =
520 (dct2000_file_externals_t*)wth->priv;
522 /* Free up its line prefix values */
523 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
524 free_line_prefix_info, NULL);
525 /* Free up its line prefix table */
526 g_hash_table_destroy(file_externals->packet_prefix_table);
532 /***************************/
534 /***************************/
537 gboolean first_packet_written;
541 /*****************************************************/
542 /* The file that we are writing to has been opened. */
543 /* Set other dump callbacks. */
544 /*****************************************************/
546 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
548 /* Fill in other dump callbacks */
549 wdh->subtype_write = catapult_dct2000_dump;
554 /*********************************************************/
555 /* Respond to queries about which encap types we support */
557 /*********************************************************/
559 catapult_dct2000_dump_can_write_encap(int encap)
562 case WTAP_ENCAP_CATAPULT_DCT2000:
563 /* We support this */
567 /* But don't write to any other formats... */
568 return WTAP_ERR_UNWRITABLE_ENCAP;
573 /*****************************************/
574 /* Write a single packet out to the file */
575 /*****************************************/
578 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
579 const guint8 *pd, int *err, gchar **err_info _U_)
581 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
583 line_prefix_info_t *prefix = NULL;
584 gchar time_string[16];
586 gboolean is_sprint = FALSE;
587 dct2000_dump_t *dct2000;
588 int consecutive_slashes=0;
591 /******************************************************/
592 /* Get the file_externals structure for this file */
593 /* Find wtap external structure for this wtap */
594 dct2000_file_externals_t *file_externals =
595 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
597 /* We can only write packet records. */
598 if (phdr->rec_type != REC_TYPE_PACKET) {
599 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
603 dct2000 = (dct2000_dump_t *)wdh->priv;
604 if (dct2000 == NULL) {
606 /* Write out saved first line */
607 if (!wtap_dump_file_write(wdh, file_externals->firstline,
608 file_externals->firstline_length, err)) {
611 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
615 /* Also write out saved second line with timestamp corresponding to the
616 opening time of the log.
618 if (!wtap_dump_file_write(wdh, file_externals->secondline,
619 file_externals->secondline_length, err)) {
622 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
626 /* Allocate the dct2000-specific dump structure */
627 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
628 wdh->priv = (void *)dct2000;
630 /* Copy time of beginning of file */
631 dct2000->start_time.secs = file_externals->start_secs;
632 dct2000->start_time.nsecs =
633 (file_externals->start_usecs * 1000);
635 /* Set flag so don't write header out again */
636 dct2000->first_packet_written = TRUE;
640 /******************************************************************/
641 /* Write out this packet's prefix, including calculated timestamp */
643 /* Look up line data prefix using stored offset */
644 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
645 (const void*)&(pseudo_header->dct2000.seek_off));
647 /* Write out text before timestamp */
648 if (!wtap_dump_file_write(wdh, prefix->before_time,
649 strlen(prefix->before_time), err)) {
653 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
654 /* This is much faster than strstr() for "/////" */
655 p_c = prefix->before_time;
656 while (p_c && (*p_c != '/')) {
659 while (p_c && (*p_c == '/')) {
660 consecutive_slashes++;
663 is_comment = (consecutive_slashes == 5);
665 /* Calculate time of this packet to write, relative to start of dump */
666 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
667 write_timestamp_string(time_string,
668 (int)(phdr->ts.secs - dct2000->start_time.secs),
669 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
672 write_timestamp_string(time_string,
673 (int)(phdr->ts.secs - dct2000->start_time.secs-1),
674 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
677 /* Write out the calculated timestamp */
678 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
682 /* Write out text between timestamp and start of hex data */
683 if (prefix->after_time == NULL) {
684 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
689 if (!wtap_dump_file_write(wdh, prefix->after_time,
690 strlen(prefix->after_time), err)) {
696 /****************************************************************/
697 /* Need to skip stub header at start of pd before we reach data */
700 for (n=0; pd[n] != '\0'; n++);
703 /* Context port number */
707 for (; pd[n] != '\0'; n++);
712 is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
714 for (; pd[n] != '\0'; n++);
717 /* Variant number (as string) */
718 for (; pd[n] != '\0'; n++);
721 /* Outhdr (as string) */
722 for (; pd[n] != '\0'; n++);
725 /* Direction & encap */
729 /**************************************/
730 /* Remainder is encapsulated protocol */
731 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
736 /* Each binary byte is written out as 2 hex string chars */
737 for (; n < phdr->len; n++) {
739 c[0] = char_from_hex((guint8)(pd[n] >> 4));
740 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
742 /* Write both hex chars of byte together */
743 if (!wtap_dump_file_write(wdh, c, 2, err)) {
749 for (; n < phdr->len; n++) {
753 /* Write both hex chars of byte together */
754 if (!wtap_dump_file_write(wdh, c, 1, err)) {
761 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
769 /****************************/
770 /* Private helper functions */
771 /****************************/
773 /**********************************************************************/
774 /* Read a new line from the file, starting at offset. */
775 /* - writes data to its argument linebuff */
776 /* - on return 'offset' will point to the next position to read from */
777 /* - return TRUE if this read is successful */
778 /**********************************************************************/
780 read_new_line(FILE_T fh, gint *length,
781 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
784 gint64 pos_before = file_tell(fh);
786 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
787 /* No characters found, or error */
788 *err = file_error(fh, err_info);
792 /* Set length (avoiding strlen()) and offset.. */
793 *length = (gint)(file_tell(fh) - pos_before);
795 /* ...but don't want to include newline in line length */
796 if (*length > 0 && linebuff[*length-1] == '\n') {
797 linebuff[*length-1] = '\0';
798 *length = *length - 1;
800 /* Nor do we want '\r' (as will be written when log is created on windows) */
801 if (*length > 0 && linebuff[*length-1] == '\r') {
802 linebuff[*length-1] = '\0';
803 *length = *length - 1;
810 /**********************************************************************/
811 /* Parse a line from buffer, by identifying: */
812 /* - context, port and direction of packet */
814 /* - data position and length */
815 /* Return TRUE if this packet looks valid and can be displayed */
816 /**********************************************************************/
818 parse_line(gchar *linebuff, gint line_length,
819 gint *seconds, gint *useconds,
820 long *before_time_offset, long *after_time_offset,
821 long *data_offset, gint *data_chars,
822 packet_direction_t *direction,
823 int *encap, int *is_comment, int *is_sprint,
824 gchar *aal_header_chars,
825 gchar *context_name, guint8 *context_portp,
826 gchar *protocol_name, gchar *variant_name,
831 char port_number_string[MAX_PORT_DIGITS+1];
832 int variant_digits = 0;
834 int protocol_chars = 0;
835 int outhdr_chars = 0;
837 char seconds_buff[MAX_SECONDS_CHARS+1];
839 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
840 int subsecond_decimals_chars;
841 int skip_first_byte = FALSE;
842 gboolean atm_header_present = FALSE;
847 /* Read context name until find '.' */
848 for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
849 if (linebuff[n] == '/') {
850 context_name[n] = '\0';
852 /* If not a comment (/////), not a valid line */
853 if (strncmp(linebuff+n, "/////", 5) != 0) {
857 /* There is no variant, outhdr, etc. Set protocol to be a comment */
858 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
862 if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
865 context_name[n] = linebuff[n];
867 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
871 /* Reset strings (that won't be set by comments) */
872 variant_name[0] = '\0';
873 outhdr_name[0] = '\0';
874 port_number_string[0] = '\0';
876 if (!(*is_comment)) {
877 /* '.' must follow context name */
878 if (linebuff[n] != '.') {
881 context_name[n] = '\0';
885 /* Now read port number */
886 for (port_digits = 0;
887 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
888 n++, port_digits++) {
890 if (!g_ascii_isdigit(linebuff[n])) {
893 port_number_string[port_digits] = linebuff[n];
895 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
899 /* Slash char must follow port number */
900 if (linebuff[n] != '/')
904 port_number_string[port_digits] = '\0';
905 if (port_digits == 1) {
906 *context_portp = port_number_string[0] - '0';
909 /* Everything in here is a digit, so we don't need to check
910 whether what follows the number is anything other than
912 if (!ws_strtou8(port_number_string, NULL, context_portp)) {
919 /* Now for the protocol name */
920 for (protocol_chars = 0;
921 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
922 n++, protocol_chars++) {
924 if (!g_ascii_isalnum(linebuff[n]) && linebuff[n] != '_') {
927 protocol_name[protocol_chars] = linebuff[n];
929 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
930 /* If doesn't fit, fail rather than truncate */
933 protocol_name[protocol_chars] = '\0';
935 /* Slash char must follow protocol name */
936 if (linebuff[n] != '/') {
943 /* Following the / is the variant number. No digits indicate 1 */
944 for (variant_digits = 0;
945 (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
946 n++, variant_digits++) {
948 if (!g_ascii_isdigit(linebuff[n])) {
951 variant_name[variant_digits] = linebuff[n];
953 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
957 if (variant_digits > 0) {
958 variant_name[variant_digits] = '\0';
959 if (variant_digits == 1) {
960 variant = variant_name[0] - '0';
963 if (!ws_strtoi32(variant_name, NULL, &variant)) {
969 variant_name[0] = '1';
970 variant_name[1] = '\0';
974 /* Outheader values may follow */
975 outhdr_name[0] = '\0';
976 if (linebuff[n] == ',') {
980 for (outhdr_chars = 0;
981 (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
982 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
983 n++, outhdr_chars++) {
985 if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
988 outhdr_name[outhdr_chars] = linebuff[n];
990 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
993 /* Terminate (possibly empty) string */
994 outhdr_name[outhdr_chars] = '\0';
999 /******************************************************************/
1000 /* Now check whether we know how to use a packet of this protocol */
1002 if ((strcmp(protocol_name, "ip") == 0) ||
1003 (strcmp(protocol_name, "sctp") == 0) ||
1004 (strcmp(protocol_name, "gre") == 0) ||
1005 (strcmp(protocol_name, "mipv6") == 0) ||
1006 (strcmp(protocol_name, "igmp") == 0)) {
1008 *encap = WTAP_ENCAP_RAW_IP;
1012 /* FP may be carried over ATM, which has separate atm header to parse */
1013 if ((strcmp(protocol_name, "fp") == 0) ||
1014 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1016 if ((variant > 256) && (variant % 256 == 3)) {
1017 /* FP over udp is contained in IPPrim... */
1021 /* FP over AAL0 or AAL2 */
1022 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1023 atm_header_present = TRUE;
1026 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1027 /* FP (IuR) over AAL2 */
1028 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1029 atm_header_present = TRUE;
1033 if (strcmp(protocol_name, "ppp") == 0) {
1034 *encap = WTAP_ENCAP_PPP;
1037 if (strcmp(protocol_name, "isdn_l3") == 0) {
1038 /* TODO: find out what this byte means... */
1039 skip_first_byte = TRUE;
1040 *encap = WTAP_ENCAP_ISDN;
1043 if (strcmp(protocol_name, "isdn_l2") == 0) {
1044 *encap = WTAP_ENCAP_ISDN;
1047 if (strcmp(protocol_name, "ethernet") == 0) {
1048 *encap = WTAP_ENCAP_ETHERNET;
1051 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1052 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1054 *encap = DCT2000_ENCAP_SSCOP;
1057 if (strcmp(protocol_name, "frelay_l2") == 0) {
1058 *encap = WTAP_ENCAP_FRELAY;
1061 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1062 *encap = DCT2000_ENCAP_MTP2;
1065 if ((strcmp(protocol_name, "nbap") == 0) ||
1066 (strcmp(protocol_name, "nbap_r4") == 0) ||
1067 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1069 /* The entire message in these cases is nbap, so use an encap value. */
1070 *encap = DCT2000_ENCAP_NBAP;
1073 /* Not a supported board port protocol/encap, but can show as raw data or
1074 in some cases find protocol embedded inside primitive */
1075 *encap = DCT2000_ENCAP_UNHANDLED;
1079 /* Find separate ATM header if necessary */
1080 if (atm_header_present) {
1081 int header_chars_seen = 0;
1083 /* Scan ahead to the next $ */
1084 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1087 if (n+1 >= line_length) {
1091 /* Read consecutive hex chars into atm header buffer */
1093 ((n < line_length) &&
1094 (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1095 (header_chars_seen < AAL_HEADER_CHARS));
1096 n++, header_chars_seen++) {
1098 aal_header_chars[header_chars_seen] = linebuff[n];
1099 /* Next 6 characters after '9' are mapped to a->f */
1100 if (!g_ascii_isdigit(linebuff[n])) {
1101 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1105 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1113 /* If there is a number, skip all info to next '/'.
1114 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1115 and display dct2000 dissector... */
1116 if (g_ascii_isdigit(linebuff[n])) {
1117 while ((n+1 < line_length) && linebuff[n] != '/') {
1123 while ((n+1 < line_length) && linebuff[n] == '/') {
1127 /* Skip a space that may happen here */
1128 if ((n+1 < line_length) && linebuff[n] == ' ') {
1132 /* Next character gives direction of message (must be 's' or 'r') */
1133 if (!(*is_comment)) {
1134 if (linebuff[n] == 's') {
1138 if (linebuff[n] == 'r') {
1139 *direction = received;
1152 /*********************************************************************/
1153 /* Find and read the timestamp */
1155 /* Now scan to the next digit, which should be the start of the timestamp */
1156 /* This will involve skipping " tm " */
1158 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1159 if (n >= line_length) {
1163 for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
1164 if (n >= line_length) {
1168 *before_time_offset = n;
1171 for (seconds_chars = 0;
1172 (linebuff[n] != '.') &&
1173 (seconds_chars <= MAX_SECONDS_CHARS) &&
1175 n++, seconds_chars++) {
1177 if (!g_ascii_isdigit(linebuff[n])) {
1178 /* Found a non-digit before decimal point. Fail */
1181 seconds_buff[seconds_chars] = linebuff[n];
1183 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1184 /* Didn't fit in buffer. Fail rather than use truncated */
1188 /* Convert found value into number */
1189 seconds_buff[seconds_chars] = '\0';
1190 if (!ws_strtoi32(seconds_buff, NULL, seconds)) {
1194 /* The decimal point must follow the last of the seconds digits */
1195 if (linebuff[n] != '.') {
1201 /* Subsecond decimal digits (expect 4-digit accuracy) */
1202 for (subsecond_decimals_chars = 0;
1203 (linebuff[n] != ' ') &&
1204 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1206 n++, subsecond_decimals_chars++) {
1208 if (!g_ascii_isdigit(linebuff[n])) {
1211 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1213 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1214 /* More numbers than expected - give up */
1217 /* Convert found value into microseconds */
1218 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1219 if (!ws_strtoi32(subsecond_decimals_buff, NULL, useconds)) {
1224 /* Space character must follow end of timestamp */
1225 if (linebuff[n] != ' ') {
1229 *after_time_offset = n++;
1231 /* If we have a string message, it could either be a comment (with '$') or
1232 a sprint line (no '$') */
1234 if (strncmp(linebuff+n, "l $", 3) != 0) {
1236 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1240 if (!(*is_sprint)) {
1241 /* Now skip ahead to find start of data (marked by '$') */
1242 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1243 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1250 /* Set offset to data start within line */
1253 /* Set number of chars that comprise the hex string protocol data */
1254 *data_chars = line_length - n;
1256 /* May need to skip first byte (2 hex string chars) */
1257 if (skip_first_byte) {
1265 /***********************************/
1266 /* Process results of parse_line() */
1267 /***********************************/
1269 process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
1270 struct wtap_pkthdr *phdr,
1271 Buffer *buf, gint64 file_offset,
1272 char *linebuff, long dollar_offset,
1273 int seconds, int useconds, gchar *timestamp_string,
1274 packet_direction_t direction, int encap,
1275 gchar *context_name, guint8 context_port,
1276 gchar *protocol_name, gchar *variant_name,
1277 gchar *outhdr_name, gchar *aal_header_chars,
1278 gboolean is_comment, int data_chars,
1279 int *err, gchar **err_info)
1282 int stub_offset = 0;
1284 guint8 *frame_buffer;
1286 phdr->rec_type = REC_TYPE_PACKET;
1287 phdr->presence_flags = WTAP_HAS_TS;
1289 /* Make sure all packets go to Catapult DCT2000 dissector */
1290 phdr->pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1292 /* Fill in timestamp (capture base + packet offset) */
1293 phdr->ts.secs = file_externals->start_secs + seconds;
1294 if ((file_externals->start_usecs + useconds) >= 1000000) {
1298 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1301 * Calculate the length of the stub info and the packet data.
1302 * The packet data length is half bytestring length.
1304 phdr->caplen = (guint)strlen(context_name)+1 + /* Context name */
1306 (guint)strlen(timestamp_string)+1 + /* timestamp */
1307 (guint)strlen(variant_name)+1 + /* variant */
1308 (guint)strlen(outhdr_name)+1 + /* outhdr */
1309 (guint)strlen(protocol_name)+1 + /* Protocol name */
1312 (is_comment ? data_chars : (data_chars/2));
1313 if (phdr->caplen > WTAP_MAX_PACKET_SIZE_STANDARD) {
1315 * Probably a corrupt capture file; return an error,
1316 * so that our caller doesn't blow up trying to allocate
1317 * space for an immensely-large packet.
1319 *err = WTAP_ERR_BAD_FILE;
1320 *err_info = g_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
1321 phdr->caplen, WTAP_MAX_PACKET_SIZE_STANDARD);
1324 phdr->len = phdr->caplen;
1326 /*****************************/
1327 /* Get the data buffer ready */
1328 ws_buffer_assure_space(buf, phdr->caplen);
1329 frame_buffer = ws_buffer_start_ptr(buf);
1331 /******************************************/
1332 /* Write the stub info to the data buffer */
1335 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1336 stub_offset += (int)(length + 1);
1338 /* Context port number */
1339 frame_buffer[stub_offset] = context_port;
1342 /* Timestamp within file */
1343 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1344 stub_offset += (int)(length + 1);
1347 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1348 stub_offset += (int)(length + 1);
1350 /* Protocol variant number (as string) */
1351 length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1352 stub_offset += (int)(length + 1);
1355 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1356 stub_offset += (int)(length + 1);
1359 frame_buffer[stub_offset] = direction;
1363 frame_buffer[stub_offset] = (guint8)encap;
1367 /***********************************************************/
1368 /* Copy packet data into buffer, converting from ascii hex */
1369 for (n=0; n < data_chars; n+=2) {
1370 frame_buffer[stub_offset + n/2] =
1371 hex_byte_from_chars(linebuff+dollar_offset+n);
1375 /***********************************************************/
1376 /* Copy packet data into buffer, just copying ascii chars */
1377 for (n=0; n < data_chars; n++) {
1378 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1382 /*****************************************/
1383 /* Set packet pseudo-header if necessary */
1384 phdr->pseudo_header.dct2000.seek_off = file_offset;
1385 phdr->pseudo_header.dct2000.wth = wth;
1388 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1389 set_aal_info(&phdr->pseudo_header, direction, aal_header_chars);
1391 case WTAP_ENCAP_ISDN:
1392 set_isdn_info(&phdr->pseudo_header, direction);
1394 case WTAP_ENCAP_PPP:
1395 set_ppp_info(&phdr->pseudo_header, direction);
1399 /* Other supported types don't need to set anything here... */
1406 /*********************************************/
1407 /* Fill in atm pseudo-header with known info */
1408 /*********************************************/
1410 set_aal_info(union wtap_pseudo_header *pseudo_header,
1411 packet_direction_t direction,
1412 gchar *aal_header_chars)
1414 /* 'aal_head_chars' has this format (for AAL2 at least):
1415 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1416 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1417 Channel Identifier (8 bits) | ...
1420 /* Indicate that this is a reassembled PDU */
1421 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1423 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1424 TODO: Can we infer the correct value here?
1425 Meanwhile, just use the direction to make them distinguishable...
1427 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1429 /* Assume always AAL2 for FP */
1430 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1432 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1433 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1435 /* vpi is 8 bits (2nd & 3rd nibble) */
1436 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1437 hex_byte_from_chars(aal_header_chars+1);
1439 /* vci is next 16 bits */
1440 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1441 ((hex_from_char(aal_header_chars[3]) << 12) |
1442 (hex_from_char(aal_header_chars[4]) << 8) |
1443 (hex_from_char(aal_header_chars[5]) << 4) |
1444 hex_from_char(aal_header_chars[6]));
1446 /* 0 means we don't know how many cells the frame comprises. */
1447 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1449 /* cid is usually last byte. Unless last char is not hex digit, in which
1450 case cid is derived from last char in ascii */
1451 if (g_ascii_isalnum(aal_header_chars[11])) {
1452 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1453 hex_byte_from_chars(aal_header_chars+10);
1456 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1457 (int)aal_header_chars[11] - '0';
1462 /**********************************************/
1463 /* Fill in isdn pseudo-header with known info */
1464 /**********************************************/
1466 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1467 packet_direction_t direction)
1469 /* This field is used to set the 'Source' and 'Destination' columns to
1470 'User' or 'Network'. If we assume that we're simulating the network,
1471 treat Received messages as being destined for the network.
1473 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1475 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1476 everything else would be treated as a B-channel
1478 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1482 /*********************************************/
1483 /* Fill in ppp pseudo-header with known info */
1484 /*********************************************/
1486 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1487 packet_direction_t direction)
1489 /* Set direction. */
1490 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1494 /********************************************************/
1495 /* Return hex nibble equivalent of hex string character */
1496 /********************************************************/
1498 hex_from_char(gchar c)
1500 if ((c >= '0') && (c <= '9')) {
1504 if ((c >= 'a') && (c <= 'f')) {
1505 return 0x0a + (c - 'a');
1508 /* Not a valid hex string character */
1514 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1515 static guint8 s_tableValues[256][256];
1517 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1518 static void prepare_hex_byte_from_chars_table(void)
1520 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1521 'a', 'b', 'c', 'd', 'e', 'f' };
1524 for (i=0; i < 16; i++) {
1525 for (j=0; j < 16; j++) {
1526 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1531 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1532 static guint8 hex_byte_from_chars(gchar *c)
1534 /* Return value from quick table lookup */
1535 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1540 /********************************************************/
1541 /* Return character corresponding to hex nibble value */
1542 /********************************************************/
1544 char_from_hex(guint8 hex)
1546 static const char hex_lookup[16] =
1547 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1553 return hex_lookup[hex];
1556 /***********************************************/
1557 /* Equality test for packet prefix hash tables */
1558 /***********************************************/
1560 packet_offset_equal(gconstpointer v, gconstpointer v2)
1562 /* Dereferenced pointers must have same gint64 offset value */
1563 return (*(const gint64*)v == *(const gint64*)v2);
1567 /********************************************/
1568 /* Hash function for packet-prefix hash table */
1569 /********************************************/
1571 packet_offset_hash_func(gconstpointer v)
1573 /* Use low-order bits of gint64 offset value */
1574 return (guint)(*(const gint64*)v);
1578 /************************************************************************/
1579 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1580 /* Set secs and usecs as output */
1581 /* Return FALSE if no valid time can be read */
1582 /************************************************************************/
1584 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1587 #define MAX_MONTH_LETTERS 9
1588 char month[MAX_MONTH_LETTERS+1];
1590 int day, year, hour, minute, second;
1593 /* If line longer than expected, file is probably not correctly formatted */
1594 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1598 /********************************************************/
1599 /* Scan for all fields */
1600 scan_found = sscanf(linebuff, "%9s %2d, %4d %2d:%2d:%2d.%4u",
1601 month, &day, &year, &hour, &minute, &second, usecs);
1602 if (scan_found != 7) {
1603 /* Give up if not all found */
1607 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1608 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1609 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1610 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1611 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1612 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1613 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1614 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1615 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1616 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1617 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1618 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1620 /* Give up if not found a properly-formatted date */
1624 /******************************************************/
1625 /* Fill in remaining fields and return it in a time_t */
1626 tm.tm_year = year - 1900;
1631 tm.tm_isdst = -1; /* daylight saving time info not known */
1633 /* Get seconds from this time */
1634 *secs = mktime(&tm);
1636 /* Multiply 4 digits given to get micro-seconds */
1637 *usecs = *usecs * 100;
1642 /* Free the data allocated inside a line_prefix_info_t */
1644 free_line_prefix_info(gpointer key, gpointer value,
1645 gpointer user_data _U_)
1647 line_prefix_info_t *info = (line_prefix_info_t*)value;
1649 /* Free the 64-bit key value */
1652 /* Free the strings inside */
1653 g_free(info->before_time);
1654 g_free(info->after_time);
1656 /* And the structure itself */
1659 /* Item will always be removed from table */
1664 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1669 * indent-tabs-mode: nil
1672 * vi: set shiftwidth=4 tabstop=8 expandtab:
1673 * :indentSize=4:tabSize=8:noTabs=true: