4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "file_wrappers.h"
28 #include <wsutil/strtoi.h>
30 #include "catapult_dct2000.h"
32 #define MAX_FIRST_LINE_LENGTH 200
33 #define MAX_TIMESTAMP_LINE_LENGTH 100
34 #define MAX_LINE_LENGTH 65536
35 #define MAX_TIMESTAMP_LEN 32
36 #define MAX_SECONDS_CHARS 16
37 #define MAX_SUBSECOND_DECIMALS 4
38 #define MAX_CONTEXT_NAME 64
39 #define MAX_PROTOCOL_NAME 64
40 #define MAX_PORT_DIGITS 2
41 #define MAX_VARIANT_DIGITS 32
42 #define MAX_OUTHDR_NAME 256
43 #define AAL_HEADER_CHARS 12
46 - support for FP over AAL0
47 - support for IuR interface FP
51 /* 's' or 'r' of a packet as read from .out file */
52 typedef enum packet_direction_t
59 /***********************************************************************/
60 /* For each line, store (in case we need to dump): */
61 /* - String before time field */
62 /* - String beween time field and data (if NULL assume " l ") */
70 /*******************************************************************/
71 /* Information stored external to a file (wtap) needed for reading and dumping */
72 typedef struct dct2000_file_externals
74 /* Remember the time at the start of capture */
79 * The following information is needed only for dumping.
81 * XXX - Wiretap is not *supposed* to require that a packet being
82 * dumped come from a file of the same type that you currently have
83 * open; this should be fixed.
86 /* Buffer to hold first line, including magic and format number */
87 gchar firstline[MAX_FIRST_LINE_LENGTH];
88 gint firstline_length;
90 /* Buffer to hold second line with formatted file creation data/time */
91 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
92 gint secondline_length;
94 /* Hash table to store text prefix data part of displayed packets.
95 Records (file offset -> line_prefix_info_t)
97 GHashTable *packet_prefix_table;
98 } dct2000_file_externals_t;
100 /* 'Magic number' at start of Catapult DCT2000 .out files. */
101 static const gchar catapult_dct2000_magic[] = "Session Transcript";
103 /************************************************************/
104 /* Functions called from wiretap core */
105 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
106 gint64 *data_offset);
107 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
108 struct wtap_pkthdr *phdr,
109 Buffer *buf, int *err,
111 static void catapult_dct2000_close(wtap *wth);
113 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
114 const guint8 *pd, int *err, gchar **err_info);
117 /************************************************************/
118 /* Private helper functions */
119 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
120 gchar *buf, size_t bufsize, int *err,
122 static gboolean parse_line(char *linebuff, gint line_length,
123 gint *seconds, gint *useconds,
124 long *before_time_offset, long *after_time_offset,
127 packet_direction_t *direction,
128 int *encap, int *is_comment, int *is_sprint,
129 gchar *aal_header_chars,
130 gchar *context_name, guint8 *context_portp,
131 gchar *protocol_name, gchar *variant_name,
133 static gboolean process_parsed_line(wtap *wth,
134 dct2000_file_externals_t *file_externals,
135 struct wtap_pkthdr *phdr,
136 Buffer *buf, gint64 file_offset,
137 char *linebuff, long dollar_offset,
138 int seconds, int useconds,
139 gchar *timestamp_string,
140 packet_direction_t direction, int encap,
141 gchar *context_name, guint8 context_port,
142 gchar *protocol_name, gchar *variant_name,
143 gchar *outhdr_name, gchar *aal_header_chars,
144 gboolean is_comment, int data_chars,
145 int *err, gchar **err_info);
146 static guint8 hex_from_char(gchar c);
147 static void prepare_hex_byte_from_chars_table(void);
148 static guint8 hex_byte_from_chars(gchar *c);
149 static gchar char_from_hex(guint8 hex);
151 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
152 packet_direction_t direction,
153 gchar *aal_header_chars);
154 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
155 packet_direction_t direction);
156 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
157 packet_direction_t direction);
159 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
160 static guint packet_offset_hash_func(gconstpointer v);
162 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
163 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
167 /********************************************/
168 /* Open file (for reading) */
169 /********************************************/
171 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
176 gint firstline_length = 0;
177 dct2000_file_externals_t *file_externals;
178 static gchar linebuff[MAX_LINE_LENGTH];
179 static gboolean hex_byte_table_values_set = FALSE;
181 /* Clear errno before reading from the file */
185 /********************************************************************/
186 /* First line needs to contain at least as many characters as magic */
188 if (!read_new_line(wth->fh, &offset, &firstline_length, linebuff,
189 sizeof linebuff, err, err_info)) {
190 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
191 return WTAP_OPEN_ERROR;
192 return WTAP_OPEN_NOT_MINE;
194 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
195 firstline_length >= MAX_FIRST_LINE_LENGTH) {
197 return WTAP_OPEN_NOT_MINE;
200 /* This file is not for us if it doesn't match our signature */
201 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
202 return WTAP_OPEN_NOT_MINE;
205 /* Make sure table is ready for use */
206 if (!hex_byte_table_values_set) {
207 prepare_hex_byte_from_chars_table();
208 hex_byte_table_values_set = TRUE;
211 /*********************************************************************/
212 /* Need entry in file_externals table */
214 /* Allocate a new file_externals structure for this file */
215 file_externals = g_new(dct2000_file_externals_t,1);
216 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
218 /* Copy this first line into buffer so could write out later */
219 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
220 file_externals->firstline_length = firstline_length;
223 /***********************************************************/
224 /* Second line contains file timestamp */
225 /* Store this offset in in file_externals */
227 if (!read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
228 linebuff, sizeof linebuff, err, err_info)) {
229 g_free(file_externals);
230 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
231 return WTAP_OPEN_ERROR;
232 return WTAP_OPEN_NOT_MINE;
234 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
235 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
237 /* Give up if file time line wasn't valid */
238 g_free(file_externals);
239 return WTAP_OPEN_NOT_MINE;
242 /* Fill in timestamp */
243 file_externals->start_secs = timestamp;
244 file_externals->start_usecs = usecs;
246 /* Copy this second line into buffer so could write out later */
247 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
250 /************************************************************/
251 /* File is for us. Fill in details so packets can be read */
253 /* Set our file type */
254 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000;
256 /* Use our own encapsulation to send all packets to our stub dissector */
257 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
259 /* Callbacks for reading operations */
260 wth->subtype_read = catapult_dct2000_read;
261 wth->subtype_seek_read = catapult_dct2000_seek_read;
262 wth->subtype_close = catapult_dct2000_close;
264 /* Choose microseconds (have 4 decimal places...) */
265 wth->file_tsprec = WTAP_TSPREC_USEC;
268 /***************************************************************/
269 /* Initialise packet_prefix_table (index is offset into file) */
270 file_externals->packet_prefix_table =
271 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
273 /* Set this wtap to point to the file_externals */
274 wth->priv = (void*)file_externals;
277 return WTAP_OPEN_MINE;
280 /* Ugly, but much faster than using g_snprintf! */
281 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
287 timestamp_string[idx++] = ((secs % 10)) + '0';
289 else if (secs < 100) {
290 timestamp_string[idx++] = ( secs / 10) + '0';
291 timestamp_string[idx++] = ((secs % 10)) + '0';
293 else if (secs < 1000) {
294 timestamp_string[idx++] = ((secs) / 100) + '0';
295 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
296 timestamp_string[idx++] = ((secs % 10)) + '0';
298 else if (secs < 10000) {
299 timestamp_string[idx++] = ((secs) / 1000) + '0';
300 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
301 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
302 timestamp_string[idx++] = ((secs % 10)) + '0';
304 else if (secs < 100000) {
305 timestamp_string[idx++] = ((secs) / 10000) + '0';
306 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
307 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
308 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
309 timestamp_string[idx++] = ((secs % 10)) + '0';
311 else if (secs < 1000000) {
312 timestamp_string[idx++] = ((secs) / 100000) + '0';
313 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
314 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
315 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
316 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
317 timestamp_string[idx++] = ((secs % 10)) + '0';
320 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
324 timestamp_string[idx++] = '.';
325 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
326 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
327 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
328 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
329 timestamp_string[idx++] = '\0';
332 /**************************************************/
333 /* Read packet function. */
334 /* Look for and read the next usable packet */
335 /* - return TRUE and details if found */
336 /**************************************************/
338 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
341 gint64 offset = file_tell(wth->fh);
342 long dollar_offset, before_time_offset, after_time_offset;
343 packet_direction_t direction;
346 /* Get wtap external structure for this wtap */
347 dct2000_file_externals_t *file_externals =
348 (dct2000_file_externals_t*)wth->priv;
350 /* Search for a line containing a usable packet */
352 int line_length, seconds, useconds, data_chars;
353 int is_comment = FALSE;
354 int is_sprint = FALSE;
355 gint64 this_offset = offset;
356 static gchar linebuff[MAX_LINE_LENGTH+1];
357 gchar aal_header_chars[AAL_HEADER_CHARS];
358 gchar context_name[MAX_CONTEXT_NAME];
359 guint8 context_port = 0;
360 gchar protocol_name[MAX_PROTOCOL_NAME+1];
361 gchar variant_name[MAX_VARIANT_DIGITS+1];
362 gchar outhdr_name[MAX_OUTHDR_NAME+1];
364 /* Are looking for first packet after 2nd line */
365 if (file_tell(wth->fh) == 0) {
366 this_offset += (file_externals->firstline_length+1+
367 file_externals->secondline_length+1);
370 /* Read a new line from file into linebuff */
371 if (!read_new_line(wth->fh, &offset, &line_length, linebuff,
372 sizeof linebuff, err, err_info)) {
374 return FALSE; /* error */
375 /* No more lines can be read, so quit. */
379 /* Try to parse the line as a frame record */
380 if (parse_line(linebuff, line_length, &seconds, &useconds,
381 &before_time_offset, &after_time_offset,
383 &data_chars, &direction, &encap, &is_comment, &is_sprint,
385 context_name, &context_port,
386 protocol_name, variant_name, outhdr_name)) {
387 line_prefix_info_t *line_prefix_info;
388 char timestamp_string[MAX_TIMESTAMP_LEN+1];
391 write_timestamp_string(timestamp_string, seconds, useconds/100);
393 /* Set data_offset to the beginning of the line we're returning.
394 This will be the seek_off parameter when this frame is re-read.
396 *data_offset = this_offset;
398 if (!process_parsed_line(wth, file_externals,
400 wth->frame_buffer, this_offset,
401 linebuff, dollar_offset,
405 context_name, context_port,
406 protocol_name, variant_name,
407 outhdr_name, aal_header_chars,
408 is_comment, data_chars,
412 /* Store the packet prefix in the hash table */
413 line_prefix_info = g_new(line_prefix_info_t,1);
415 /* Create and use buffer for contents before time */
416 line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
417 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
418 line_prefix_info->before_time[before_time_offset] = '\0';
420 /* Create and use buffer for contents before time.
421 Do this only if it doesn't correspond to " l ", which is by far the most
423 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
424 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
426 line_prefix_info->after_time = NULL;
429 /* Allocate & write buffer for line between timestamp and data */
430 line_prefix_info->after_time = (gchar *)g_malloc(dollar_offset - after_time_offset);
431 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
432 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
435 /* Add packet entry into table */
436 pkey = (gint64 *)g_malloc(sizeof(*pkey));
438 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
440 /* OK, we have packet details to return */
445 /* No packet details to return... */
450 /**************************************************/
451 /* Read & seek function. */
452 /**************************************************/
454 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
455 struct wtap_pkthdr *phdr, Buffer *buf,
456 int *err, gchar **err_info)
460 long dollar_offset, before_time_offset, after_time_offset;
461 static gchar linebuff[MAX_LINE_LENGTH+1];
462 gchar aal_header_chars[AAL_HEADER_CHARS];
463 gchar context_name[MAX_CONTEXT_NAME];
464 guint8 context_port = 0;
465 gchar protocol_name[MAX_PROTOCOL_NAME+1];
466 gchar variant_name[MAX_VARIANT_DIGITS+1];
467 gchar outhdr_name[MAX_OUTHDR_NAME+1];
468 int is_comment = FALSE;
469 int is_sprint = FALSE;
470 packet_direction_t direction;
472 int seconds, useconds, data_chars;
474 /* Get wtap external structure for this wtap */
475 dct2000_file_externals_t *file_externals =
476 (dct2000_file_externals_t*)wth->priv;
481 /* Seek to beginning of packet */
482 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
486 /* Re-read whole line (this really should succeed) */
487 if (!read_new_line(wth->random_fh, &offset, &length, linebuff,
488 sizeof linebuff, err, err_info)) {
492 /* Try to parse this line again (should succeed as re-reading...) */
493 if (parse_line(linebuff, length, &seconds, &useconds,
494 &before_time_offset, &after_time_offset,
496 &data_chars, &direction, &encap, &is_comment, &is_sprint,
498 context_name, &context_port,
499 protocol_name, variant_name, outhdr_name)) {
500 char timestamp_string[MAX_TIMESTAMP_LEN+1];
502 write_timestamp_string(timestamp_string, seconds, useconds/100);
504 if (!process_parsed_line(wth, file_externals,
506 linebuff, dollar_offset,
510 context_name, context_port,
511 protocol_name, variant_name,
512 outhdr_name, aal_header_chars,
513 is_comment, data_chars,
521 /* If get here, must have failed */
523 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
524 "line at position %" G_GINT64_MODIFIER "d",
530 /***************************************************************************/
531 /* Free dct2000-specific capture info from file that was open for reading */
532 /***************************************************************************/
534 catapult_dct2000_close(wtap *wth)
536 /* Get externals for this file */
537 dct2000_file_externals_t *file_externals =
538 (dct2000_file_externals_t*)wth->priv;
540 /* Free up its line prefix values */
541 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
542 free_line_prefix_info, NULL);
543 /* Free up its line prefix table */
544 g_hash_table_destroy(file_externals->packet_prefix_table);
550 /***************************/
552 /***************************/
555 gboolean first_packet_written;
559 /*****************************************************/
560 /* The file that we are writing to has been opened. */
561 /* Set other dump callbacks. */
562 /*****************************************************/
564 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
566 /* Fill in other dump callbacks */
567 wdh->subtype_write = catapult_dct2000_dump;
572 /*********************************************************/
573 /* Respond to queries about which encap types we support */
575 /*********************************************************/
577 catapult_dct2000_dump_can_write_encap(int encap)
580 case WTAP_ENCAP_CATAPULT_DCT2000:
581 /* We support this */
585 /* But don't write to any other formats... */
586 return WTAP_ERR_UNWRITABLE_ENCAP;
591 /*****************************************/
592 /* Write a single packet out to the file */
593 /*****************************************/
596 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
597 const guint8 *pd, int *err, gchar **err_info _U_)
599 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
601 line_prefix_info_t *prefix = NULL;
602 gchar time_string[16];
604 gboolean is_sprint = FALSE;
605 dct2000_dump_t *dct2000;
606 int consecutive_slashes=0;
609 /******************************************************/
610 /* Get the file_externals structure for this file */
611 /* Find wtap external structure for this wtap */
612 dct2000_file_externals_t *file_externals =
613 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
615 /* We can only write packet records. */
616 if (phdr->rec_type != REC_TYPE_PACKET) {
617 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
621 dct2000 = (dct2000_dump_t *)wdh->priv;
622 if (dct2000 == NULL) {
624 /* Write out saved first line */
625 if (!wtap_dump_file_write(wdh, file_externals->firstline,
626 file_externals->firstline_length, err)) {
629 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
633 /* Also write out saved second line with timestamp corresponding to the
634 opening time of the log.
636 if (!wtap_dump_file_write(wdh, file_externals->secondline,
637 file_externals->secondline_length, err)) {
640 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
644 /* Allocate the dct2000-specific dump structure */
645 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
646 wdh->priv = (void *)dct2000;
648 /* Copy time of beginning of file */
649 dct2000->start_time.secs = file_externals->start_secs;
650 dct2000->start_time.nsecs =
651 (file_externals->start_usecs * 1000);
653 /* Set flag so don't write header out again */
654 dct2000->first_packet_written = TRUE;
658 /******************************************************************/
659 /* Write out this packet's prefix, including calculated timestamp */
661 /* Look up line data prefix using stored offset */
662 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
663 (const void*)&(pseudo_header->dct2000.seek_off));
665 /* Write out text before timestamp */
666 if (!wtap_dump_file_write(wdh, prefix->before_time,
667 strlen(prefix->before_time), err)) {
671 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
672 /* This is much faster than strstr() for "/////" */
673 p_c = prefix->before_time;
674 while (p_c && (*p_c != '/')) {
677 while (p_c && (*p_c == '/')) {
678 consecutive_slashes++;
681 is_comment = (consecutive_slashes == 5);
683 /* Calculate time of this packet to write, relative to start of dump */
684 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
685 write_timestamp_string(time_string,
686 (int)(phdr->ts.secs - dct2000->start_time.secs),
687 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
690 write_timestamp_string(time_string,
691 (int)(phdr->ts.secs - dct2000->start_time.secs-1),
692 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
695 /* Write out the calculated timestamp */
696 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
700 /* Write out text between timestamp and start of hex data */
701 if (prefix->after_time == NULL) {
702 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
707 if (!wtap_dump_file_write(wdh, prefix->after_time,
708 strlen(prefix->after_time), err)) {
714 /****************************************************************/
715 /* Need to skip stub header at start of pd before we reach data */
718 for (n=0; pd[n] != '\0'; n++);
721 /* Context port number */
725 for (; pd[n] != '\0'; n++);
730 is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
732 for (; pd[n] != '\0'; n++);
735 /* Variant number (as string) */
736 for (; pd[n] != '\0'; n++);
739 /* Outhdr (as string) */
740 for (; pd[n] != '\0'; n++);
743 /* Direction & encap */
747 /**************************************/
748 /* Remainder is encapsulated protocol */
749 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
754 /* Each binary byte is written out as 2 hex string chars */
755 for (; n < phdr->len; n++) {
757 c[0] = char_from_hex((guint8)(pd[n] >> 4));
758 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
760 /* Write both hex chars of byte together */
761 if (!wtap_dump_file_write(wdh, c, 2, err)) {
767 for (; n < phdr->len; n++) {
771 /* Write both hex chars of byte together */
772 if (!wtap_dump_file_write(wdh, c, 1, err)) {
779 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
787 /****************************/
788 /* Private helper functions */
789 /****************************/
791 /**********************************************************************/
792 /* Read a new line from the file, starting at offset. */
793 /* - writes data to its argument linebuff */
794 /* - on return 'offset' will point to the next position to read from */
795 /* - return TRUE if this read is successful */
796 /**********************************************************************/
798 read_new_line(FILE_T fh, gint64 *offset, gint *length,
799 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
802 gint64 pos_before = file_tell(fh);
804 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
805 /* No characters found, or error */
806 *err = file_error(fh, err_info);
810 /* Set length (avoiding strlen()) and offset.. */
811 *length = (gint)(file_tell(fh) - pos_before);
812 *offset = *offset + *length;
814 /* ...but don't want to include newline in line length */
815 if (*length > 0 && linebuff[*length-1] == '\n') {
816 linebuff[*length-1] = '\0';
817 *length = *length - 1;
819 /* Nor do we want '\r' (as will be written when log is created on windows) */
820 if (*length > 0 && linebuff[*length-1] == '\r') {
821 linebuff[*length-1] = '\0';
822 *length = *length - 1;
829 /**********************************************************************/
830 /* Parse a line from buffer, by identifying: */
831 /* - context, port and direction of packet */
833 /* - data position and length */
834 /* Return TRUE if this packet looks valid and can be displayed */
835 /**********************************************************************/
837 parse_line(gchar *linebuff, gint line_length,
838 gint *seconds, gint *useconds,
839 long *before_time_offset, long *after_time_offset,
840 long *data_offset, gint *data_chars,
841 packet_direction_t *direction,
842 int *encap, int *is_comment, int *is_sprint,
843 gchar *aal_header_chars,
844 gchar *context_name, guint8 *context_portp,
845 gchar *protocol_name, gchar *variant_name,
850 char port_number_string[MAX_PORT_DIGITS+1];
851 int variant_digits = 0;
853 int protocol_chars = 0;
854 int outhdr_chars = 0;
856 char seconds_buff[MAX_SECONDS_CHARS+1];
858 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
859 int subsecond_decimals_chars;
860 int skip_first_byte = FALSE;
861 gboolean atm_header_present = FALSE;
866 /* Read context name until find '.' */
867 for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
868 if (linebuff[n] == '/') {
869 context_name[n] = '\0';
871 /* If not a comment (/////), not a valid line */
872 if (strncmp(linebuff+n, "/////", 5) != 0) {
876 /* There is no variant, outhdr, etc. Set protocol to be a comment */
877 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
881 if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
884 context_name[n] = linebuff[n];
886 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
890 /* Reset strings (that won't be set by comments) */
891 variant_name[0] = '\0';
892 outhdr_name[0] = '\0';
893 port_number_string[0] = '\0';
895 if (!(*is_comment)) {
896 /* '.' must follow context name */
897 if (linebuff[n] != '.') {
900 context_name[n] = '\0';
904 /* Now read port number */
905 for (port_digits = 0;
906 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
907 n++, port_digits++) {
909 if (!g_ascii_isdigit(linebuff[n])) {
912 port_number_string[port_digits] = linebuff[n];
914 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
918 /* Slash char must follow port number */
919 if (linebuff[n] != '/')
923 port_number_string[port_digits] = '\0';
924 if (port_digits == 1) {
925 *context_portp = port_number_string[0] - '0';
928 /* Everything in here is a digit, so we don't need to check
929 whether what follows the number is anything other than
931 if (!ws_strtou8(port_number_string, NULL, context_portp)) {
938 /* Now for the protocol name */
939 for (protocol_chars = 0;
940 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
941 n++, protocol_chars++) {
943 if (!g_ascii_isalnum(linebuff[n]) && linebuff[n] != '_') {
946 protocol_name[protocol_chars] = linebuff[n];
948 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
949 /* If doesn't fit, fail rather than truncate */
952 protocol_name[protocol_chars] = '\0';
954 /* Slash char must follow protocol name */
955 if (linebuff[n] != '/') {
962 /* Following the / is the variant number. No digits indicate 1 */
963 for (variant_digits = 0;
964 (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
965 n++, variant_digits++) {
967 if (!g_ascii_isdigit(linebuff[n])) {
970 variant_name[variant_digits] = linebuff[n];
972 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
976 if (variant_digits > 0) {
977 variant_name[variant_digits] = '\0';
978 if (variant_digits == 1) {
979 variant = variant_name[0] - '0';
982 if (!ws_strtoi32(variant_name, NULL, &variant)) {
988 variant_name[0] = '1';
989 variant_name[1] = '\0';
993 /* Outheader values may follow */
994 outhdr_name[0] = '\0';
995 if (linebuff[n] == ',') {
999 for (outhdr_chars = 0;
1000 (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
1001 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
1002 n++, outhdr_chars++) {
1004 if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
1007 outhdr_name[outhdr_chars] = linebuff[n];
1009 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
1012 /* Terminate (possibly empty) string */
1013 outhdr_name[outhdr_chars] = '\0';
1018 /******************************************************************/
1019 /* Now check whether we know how to use a packet of this protocol */
1021 if ((strcmp(protocol_name, "ip") == 0) ||
1022 (strcmp(protocol_name, "sctp") == 0) ||
1023 (strcmp(protocol_name, "gre") == 0) ||
1024 (strcmp(protocol_name, "mipv6") == 0) ||
1025 (strcmp(protocol_name, "igmp") == 0)) {
1027 *encap = WTAP_ENCAP_RAW_IP;
1031 /* FP may be carried over ATM, which has separate atm header to parse */
1032 if ((strcmp(protocol_name, "fp") == 0) ||
1033 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1035 if ((variant > 256) && (variant % 256 == 3)) {
1036 /* FP over udp is contained in IPPrim... */
1040 /* FP over AAL0 or AAL2 */
1041 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1042 atm_header_present = TRUE;
1045 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1046 /* FP (IuR) over AAL2 */
1047 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1048 atm_header_present = TRUE;
1052 if (strcmp(protocol_name, "ppp") == 0) {
1053 *encap = WTAP_ENCAP_PPP;
1056 if (strcmp(protocol_name, "isdn_l3") == 0) {
1057 /* TODO: find out what this byte means... */
1058 skip_first_byte = TRUE;
1059 *encap = WTAP_ENCAP_ISDN;
1062 if (strcmp(protocol_name, "isdn_l2") == 0) {
1063 *encap = WTAP_ENCAP_ISDN;
1066 if (strcmp(protocol_name, "ethernet") == 0) {
1067 *encap = WTAP_ENCAP_ETHERNET;
1070 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1071 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1073 *encap = DCT2000_ENCAP_SSCOP;
1076 if (strcmp(protocol_name, "frelay_l2") == 0) {
1077 *encap = WTAP_ENCAP_FRELAY;
1080 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1081 *encap = DCT2000_ENCAP_MTP2;
1084 if ((strcmp(protocol_name, "nbap") == 0) ||
1085 (strcmp(protocol_name, "nbap_r4") == 0) ||
1086 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1088 /* The entire message in these cases is nbap, so use an encap value. */
1089 *encap = DCT2000_ENCAP_NBAP;
1092 /* Not a supported board port protocol/encap, but can show as raw data or
1093 in some cases find protocol embedded inside primitive */
1094 *encap = DCT2000_ENCAP_UNHANDLED;
1098 /* Find separate ATM header if necessary */
1099 if (atm_header_present) {
1100 int header_chars_seen = 0;
1102 /* Scan ahead to the next $ */
1103 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1106 if (n+1 >= line_length) {
1110 /* Read consecutive hex chars into atm header buffer */
1112 ((n < line_length) &&
1113 (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1114 (header_chars_seen < AAL_HEADER_CHARS));
1115 n++, header_chars_seen++) {
1117 aal_header_chars[header_chars_seen] = linebuff[n];
1118 /* Next 6 characters after '9' are mapped to a->f */
1119 if (!g_ascii_isdigit(linebuff[n])) {
1120 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1124 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1132 /* If there is a number, skip all info to next '/'.
1133 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1134 and display dct2000 dissector... */
1135 if (g_ascii_isdigit(linebuff[n])) {
1136 while ((n+1 < line_length) && linebuff[n] != '/') {
1142 while ((n+1 < line_length) && linebuff[n] == '/') {
1146 /* Skip a space that may happen here */
1147 if ((n+1 < line_length) && linebuff[n] == ' ') {
1151 /* Next character gives direction of message (must be 's' or 'r') */
1152 if (!(*is_comment)) {
1153 if (linebuff[n] == 's') {
1157 if (linebuff[n] == 'r') {
1158 *direction = received;
1171 /*********************************************************************/
1172 /* Find and read the timestamp */
1174 /* Now scan to the next digit, which should be the start of the timestamp */
1175 /* This will involve skipping " tm " */
1177 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1178 if (n >= line_length) {
1182 for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
1183 if (n >= line_length) {
1187 *before_time_offset = n;
1190 for (seconds_chars = 0;
1191 (linebuff[n] != '.') &&
1192 (seconds_chars <= MAX_SECONDS_CHARS) &&
1194 n++, seconds_chars++) {
1196 if (!g_ascii_isdigit(linebuff[n])) {
1197 /* Found a non-digit before decimal point. Fail */
1200 seconds_buff[seconds_chars] = linebuff[n];
1202 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1203 /* Didn't fit in buffer. Fail rather than use truncated */
1207 /* Convert found value into number */
1208 seconds_buff[seconds_chars] = '\0';
1209 if (!ws_strtoi32(seconds_buff, NULL, seconds)) {
1213 /* The decimal point must follow the last of the seconds digits */
1214 if (linebuff[n] != '.') {
1220 /* Subsecond decimal digits (expect 4-digit accuracy) */
1221 for (subsecond_decimals_chars = 0;
1222 (linebuff[n] != ' ') &&
1223 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1225 n++, subsecond_decimals_chars++) {
1227 if (!g_ascii_isdigit(linebuff[n])) {
1230 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1232 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1233 /* More numbers than expected - give up */
1236 /* Convert found value into microseconds */
1237 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1238 if (!ws_strtoi32(subsecond_decimals_buff, NULL, useconds)) {
1243 /* Space character must follow end of timestamp */
1244 if (linebuff[n] != ' ') {
1248 *after_time_offset = n++;
1250 /* If we have a string message, it could either be a comment (with '$') or
1251 a sprint line (no '$') */
1253 if (strncmp(linebuff+n, "l $", 3) != 0) {
1255 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1259 if (!(*is_sprint)) {
1260 /* Now skip ahead to find start of data (marked by '$') */
1261 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1262 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1269 /* Set offset to data start within line */
1272 /* Set number of chars that comprise the hex string protocol data */
1273 *data_chars = line_length - n;
1275 /* May need to skip first byte (2 hex string chars) */
1276 if (skip_first_byte) {
1284 /***********************************/
1285 /* Process results of parse_line() */
1286 /***********************************/
1288 process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
1289 struct wtap_pkthdr *phdr,
1290 Buffer *buf, gint64 file_offset,
1291 char *linebuff, long dollar_offset,
1292 int seconds, int useconds, gchar *timestamp_string,
1293 packet_direction_t direction, int encap,
1294 gchar *context_name, guint8 context_port,
1295 gchar *protocol_name, gchar *variant_name,
1296 gchar *outhdr_name, gchar *aal_header_chars,
1297 gboolean is_comment, int data_chars,
1298 int *err, gchar **err_info)
1301 int stub_offset = 0;
1303 guint8 *frame_buffer;
1305 phdr->rec_type = REC_TYPE_PACKET;
1306 phdr->presence_flags = WTAP_HAS_TS;
1308 /* Make sure all packets go to Catapult DCT2000 dissector */
1309 phdr->pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1311 /* Fill in timestamp (capture base + packet offset) */
1312 phdr->ts.secs = file_externals->start_secs + seconds;
1313 if ((file_externals->start_usecs + useconds) >= 1000000) {
1317 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1320 * Calculate the length of the stub info and the packet data.
1321 * The packet data length is half bytestring length.
1323 phdr->caplen = (guint)strlen(context_name)+1 + /* Context name */
1325 (guint)strlen(timestamp_string)+1 + /* timestamp */
1326 (guint)strlen(variant_name)+1 + /* variant */
1327 (guint)strlen(outhdr_name)+1 + /* outhdr */
1328 (guint)strlen(protocol_name)+1 + /* Protocol name */
1331 (is_comment ? data_chars : (data_chars/2));
1332 if (phdr->caplen > WTAP_MAX_PACKET_SIZE) {
1334 * Probably a corrupt capture file; return an error,
1335 * so that our caller doesn't blow up trying to allocate
1336 * space for an immensely-large packet.
1338 *err = WTAP_ERR_BAD_FILE;
1339 *err_info = g_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
1340 phdr->caplen, WTAP_MAX_PACKET_SIZE);
1343 phdr->len = phdr->caplen;
1345 /*****************************/
1346 /* Get the data buffer ready */
1347 ws_buffer_assure_space(buf, phdr->caplen);
1348 frame_buffer = ws_buffer_start_ptr(buf);
1350 /******************************************/
1351 /* Write the stub info to the data buffer */
1354 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1355 stub_offset += (int)(length + 1);
1357 /* Context port number */
1358 frame_buffer[stub_offset] = context_port;
1361 /* Timestamp within file */
1362 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1363 stub_offset += (int)(length + 1);
1366 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1367 stub_offset += (int)(length + 1);
1369 /* Protocol variant number (as string) */
1370 length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1371 stub_offset += (int)(length + 1);
1374 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1375 stub_offset += (int)(length + 1);
1378 frame_buffer[stub_offset] = direction;
1382 frame_buffer[stub_offset] = (guint8)encap;
1386 /***********************************************************/
1387 /* Copy packet data into buffer, converting from ascii hex */
1388 for (n=0; n < data_chars; n+=2) {
1389 frame_buffer[stub_offset + n/2] =
1390 hex_byte_from_chars(linebuff+dollar_offset+n);
1394 /***********************************************************/
1395 /* Copy packet data into buffer, just copying ascii chars */
1396 for (n=0; n < data_chars; n++) {
1397 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1401 /*****************************************/
1402 /* Set packet pseudo-header if necessary */
1403 phdr->pseudo_header.dct2000.seek_off = file_offset;
1404 phdr->pseudo_header.dct2000.wth = wth;
1407 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1408 set_aal_info(&phdr->pseudo_header, direction, aal_header_chars);
1410 case WTAP_ENCAP_ISDN:
1411 set_isdn_info(&phdr->pseudo_header, direction);
1413 case WTAP_ENCAP_PPP:
1414 set_ppp_info(&phdr->pseudo_header, direction);
1418 /* Other supported types don't need to set anything here... */
1425 /*********************************************/
1426 /* Fill in atm pseudo-header with known info */
1427 /*********************************************/
1429 set_aal_info(union wtap_pseudo_header *pseudo_header,
1430 packet_direction_t direction,
1431 gchar *aal_header_chars)
1433 /* 'aal_head_chars' has this format (for AAL2 at least):
1434 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1435 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1436 Channel Identifier (8 bits) | ...
1439 /* Indicate that this is a reassembled PDU */
1440 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1442 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1443 TODO: Can we infer the correct value here?
1444 Meanwhile, just use the direction to make them distinguishable...
1446 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1448 /* Assume always AAL2 for FP */
1449 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1451 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1452 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1454 /* vpi is 8 bits (2nd & 3rd nibble) */
1455 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1456 hex_byte_from_chars(aal_header_chars+1);
1458 /* vci is next 16 bits */
1459 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1460 ((hex_from_char(aal_header_chars[3]) << 12) |
1461 (hex_from_char(aal_header_chars[4]) << 8) |
1462 (hex_from_char(aal_header_chars[5]) << 4) |
1463 hex_from_char(aal_header_chars[6]));
1465 /* 0 means we don't know how many cells the frame comprises. */
1466 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1468 /* cid is usually last byte. Unless last char is not hex digit, in which
1469 case cid is derived from last char in ascii */
1470 if (g_ascii_isalnum(aal_header_chars[11])) {
1471 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1472 hex_byte_from_chars(aal_header_chars+10);
1475 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1476 (int)aal_header_chars[11] - '0';
1481 /**********************************************/
1482 /* Fill in isdn pseudo-header with known info */
1483 /**********************************************/
1485 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1486 packet_direction_t direction)
1488 /* This field is used to set the 'Source' and 'Destination' columns to
1489 'User' or 'Network'. If we assume that we're simulating the network,
1490 treat Received messages as being destined for the network.
1492 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1494 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1495 everything else would be treated as a B-channel
1497 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1501 /*********************************************/
1502 /* Fill in ppp pseudo-header with known info */
1503 /*********************************************/
1505 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1506 packet_direction_t direction)
1508 /* Set direction. */
1509 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1513 /********************************************************/
1514 /* Return hex nibble equivalent of hex string character */
1515 /********************************************************/
1517 hex_from_char(gchar c)
1519 if ((c >= '0') && (c <= '9')) {
1523 if ((c >= 'a') && (c <= 'f')) {
1524 return 0x0a + (c - 'a');
1527 /* Not a valid hex string character */
1533 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1534 static guint8 s_tableValues[256][256];
1536 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1537 static void prepare_hex_byte_from_chars_table(void)
1539 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1540 'a', 'b', 'c', 'd', 'e', 'f' };
1543 for (i=0; i < 16; i++) {
1544 for (j=0; j < 16; j++) {
1545 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1550 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1551 static guint8 hex_byte_from_chars(gchar *c)
1553 /* Return value from quick table lookup */
1554 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1559 /********************************************************/
1560 /* Return character corresponding to hex nibble value */
1561 /********************************************************/
1563 char_from_hex(guint8 hex)
1565 static const char hex_lookup[16] =
1566 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1572 return hex_lookup[hex];
1575 /***********************************************/
1576 /* Equality test for packet prefix hash tables */
1577 /***********************************************/
1579 packet_offset_equal(gconstpointer v, gconstpointer v2)
1581 /* Dereferenced pointers must have same gint64 offset value */
1582 return (*(const gint64*)v == *(const gint64*)v2);
1586 /********************************************/
1587 /* Hash function for packet-prefix hash table */
1588 /********************************************/
1590 packet_offset_hash_func(gconstpointer v)
1592 /* Use low-order bits of gint64 offset value */
1593 return (guint)(*(const gint64*)v);
1597 /************************************************************************/
1598 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1599 /* Set secs and usecs as output */
1600 /* Return FALSE if no valid time can be read */
1601 /************************************************************************/
1603 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1606 #define MAX_MONTH_LETTERS 9
1607 char month[MAX_MONTH_LETTERS+1];
1609 int day, year, hour, minute, second;
1612 /* If line longer than expected, file is probably not correctly formatted */
1613 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1617 /********************************************************/
1618 /* Scan for all fields */
1619 scan_found = sscanf(linebuff, "%9s %2d, %4d %2d:%2d:%2d.%4u",
1620 month, &day, &year, &hour, &minute, &second, usecs);
1621 if (scan_found != 7) {
1622 /* Give up if not all found */
1626 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1627 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1628 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1629 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1630 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1631 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1632 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1633 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1634 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1635 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1636 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1637 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1639 /* Give up if not found a properly-formatted date */
1643 /******************************************************/
1644 /* Fill in remaining fields and return it in a time_t */
1645 tm.tm_year = year - 1900;
1650 tm.tm_isdst = -1; /* daylight saving time info not known */
1652 /* Get seconds from this time */
1653 *secs = mktime(&tm);
1655 /* Multiply 4 digits given to get micro-seconds */
1656 *usecs = *usecs * 100;
1661 /* Free the data allocated inside a line_prefix_info_t */
1663 free_line_prefix_info(gpointer key, gpointer value,
1664 gpointer user_data _U_)
1666 line_prefix_info_t *info = (line_prefix_info_t*)value;
1668 /* Free the 64-bit key value */
1671 /* Free the strings inside */
1672 g_free(info->before_time);
1673 if (info->after_time) {
1674 g_free(info->after_time);
1677 /* And the structure itself */
1680 /* Item will always be removed from table */
1685 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1690 * indent-tabs-mode: nil
1693 * vi: set shiftwidth=4 tabstop=8 expandtab:
1694 * :indentSize=4:tabSize=8:noTabs=true: