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"
29 #include "catapult_dct2000.h"
31 #define MAX_FIRST_LINE_LENGTH 200
32 #define MAX_TIMESTAMP_LINE_LENGTH 100
33 #define MAX_LINE_LENGTH 65536
34 #define MAX_TIMESTAMP_LEN 32
35 #define MAX_SECONDS_CHARS 16
36 #define MAX_SUBSECOND_DECIMALS 4
37 #define MAX_CONTEXT_NAME 64
38 #define MAX_PROTOCOL_NAME 64
39 #define MAX_PORT_DIGITS 2
40 #define MAX_VARIANT_DIGITS 32
41 #define MAX_OUTHDR_NAME 256
42 #define AAL_HEADER_CHARS 12
45 - support for FP over AAL0
46 - support for IuR interface FP
50 /* 's' or 'r' of a packet as read from .out file */
51 typedef enum packet_direction_t
58 /***********************************************************************/
59 /* For each line, store (in case we need to dump): */
60 /* - String before time field */
61 /* - String beween time field and data (if NULL assume " l ") */
69 /*******************************************************************/
70 /* Information stored external to a file (wtap) needed for reading and dumping */
71 typedef struct dct2000_file_externals
73 /* Remember the time at the start of capture */
78 * The following information is needed only for dumping.
80 * XXX - Wiretap is not *supposed* to require that a packet being
81 * dumped come from a file of the same type that you currently have
82 * open; this should be fixed.
85 /* Buffer to hold first line, including magic and format number */
86 gchar firstline[MAX_FIRST_LINE_LENGTH];
87 gint firstline_length;
89 /* Buffer to hold second line with formatted file creation data/time */
90 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
91 gint secondline_length;
93 /* Hash table to store text prefix data part of displayed packets.
94 Records (file offset -> line_prefix_info_t)
96 GHashTable *packet_prefix_table;
97 } dct2000_file_externals_t;
99 /* 'Magic number' at start of Catapult DCT2000 .out files. */
100 static const gchar catapult_dct2000_magic[] = "Session Transcript";
102 /************************************************************/
103 /* Functions called from wiretap core */
104 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
105 gint64 *data_offset);
106 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
107 struct wtap_pkthdr *phdr,
108 Buffer *buf, int *err,
110 static void catapult_dct2000_close(wtap *wth);
112 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
113 const guint8 *pd, int *err, gchar **err_info);
116 /************************************************************/
117 /* Private helper functions */
118 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
119 gchar *buf, size_t bufsize, int *err,
121 static gboolean parse_line(char *linebuff, gint line_length,
122 gint *seconds, gint *useconds,
123 long *before_time_offset, long *after_time_offset,
126 packet_direction_t *direction,
127 int *encap, int *is_comment, int *is_sprint,
128 gchar *aal_header_chars,
129 gchar *context_name, guint8 *context_portp,
130 gchar *protocol_name, gchar *variant_name,
132 static void process_parsed_line(wtap *wth,
133 dct2000_file_externals_t *file_externals,
134 struct wtap_pkthdr *phdr,
135 Buffer *buf, gint64 file_offset,
136 char *linebuff, long dollar_offset,
137 int seconds, int useconds, gchar *timestamp_string,
138 packet_direction_t direction, int encap,
139 gchar *context_name, guint8 context_port,
140 gchar *protocol_name, gchar *variant_name,
141 gchar *outhdr_name, gchar *aal_header_chars,
142 gboolean is_comment, int data_chars);
143 static guint8 hex_from_char(gchar c);
144 static void prepare_hex_byte_from_chars_table(void);
145 static guint8 hex_byte_from_chars(gchar *c);
146 static gchar char_from_hex(guint8 hex);
148 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
149 packet_direction_t direction,
150 gchar *aal_header_chars);
151 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
152 packet_direction_t direction);
153 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
154 packet_direction_t direction);
156 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
157 static guint packet_offset_hash_func(gconstpointer v);
159 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
160 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
164 /********************************************/
165 /* Open file (for reading) */
166 /********************************************/
168 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
173 gint firstline_length = 0;
174 dct2000_file_externals_t *file_externals;
175 static gchar linebuff[MAX_LINE_LENGTH];
176 static gboolean hex_byte_table_values_set = FALSE;
178 /* Clear errno before reading from the file */
182 /********************************************************************/
183 /* First line needs to contain at least as many characters as magic */
185 if (!read_new_line(wth->fh, &offset, &firstline_length, linebuff,
186 sizeof linebuff, err, err_info)) {
187 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
188 return WTAP_OPEN_ERROR;
189 return WTAP_OPEN_NOT_MINE;
191 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
192 firstline_length >= MAX_FIRST_LINE_LENGTH) {
194 return WTAP_OPEN_NOT_MINE;
197 /* This file is not for us if it doesn't match our signature */
198 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
199 return WTAP_OPEN_NOT_MINE;
202 /* Make sure table is ready for use */
203 if (!hex_byte_table_values_set) {
204 prepare_hex_byte_from_chars_table();
205 hex_byte_table_values_set = TRUE;
208 /*********************************************************************/
209 /* Need entry in file_externals table */
211 /* Allocate a new file_externals structure for this file */
212 file_externals = g_new(dct2000_file_externals_t,1);
213 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
215 /* Copy this first line into buffer so could write out later */
216 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
217 file_externals->firstline_length = firstline_length;
220 /***********************************************************/
221 /* Second line contains file timestamp */
222 /* Store this offset in in file_externals */
224 if (!read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
225 linebuff, sizeof linebuff, err, err_info)) {
226 g_free(file_externals);
227 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
228 return WTAP_OPEN_ERROR;
229 return WTAP_OPEN_NOT_MINE;
231 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
232 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
234 /* Give up if file time line wasn't valid */
235 g_free(file_externals);
236 return WTAP_OPEN_NOT_MINE;
239 /* Fill in timestamp */
240 file_externals->start_secs = timestamp;
241 file_externals->start_usecs = usecs;
243 /* Copy this second line into buffer so could write out later */
244 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
247 /************************************************************/
248 /* File is for us. Fill in details so packets can be read */
250 /* Set our file type */
251 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000;
253 /* Use our own encapsulation to send all packets to our stub dissector */
254 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
256 /* Callbacks for reading operations */
257 wth->subtype_read = catapult_dct2000_read;
258 wth->subtype_seek_read = catapult_dct2000_seek_read;
259 wth->subtype_close = catapult_dct2000_close;
261 /* Choose microseconds (have 4 decimal places...) */
262 wth->file_tsprec = WTAP_TSPREC_USEC;
265 /***************************************************************/
266 /* Initialise packet_prefix_table (index is offset into file) */
267 file_externals->packet_prefix_table =
268 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
270 /* Set this wtap to point to the file_externals */
271 wth->priv = (void*)file_externals;
274 return WTAP_OPEN_MINE;
277 /* Ugly, but much faster than using g_snprintf! */
278 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
284 timestamp_string[idx++] = ((secs % 10)) + '0';
286 else if (secs < 100) {
287 timestamp_string[idx++] = ( secs / 10) + '0';
288 timestamp_string[idx++] = ((secs % 10)) + '0';
290 else if (secs < 1000) {
291 timestamp_string[idx++] = ((secs) / 100) + '0';
292 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
293 timestamp_string[idx++] = ((secs % 10)) + '0';
295 else if (secs < 10000) {
296 timestamp_string[idx++] = ((secs) / 1000) + '0';
297 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
298 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
299 timestamp_string[idx++] = ((secs % 10)) + '0';
301 else if (secs < 100000) {
302 timestamp_string[idx++] = ((secs) / 10000) + '0';
303 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
304 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
305 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
306 timestamp_string[idx++] = ((secs % 10)) + '0';
308 else if (secs < 1000000) {
309 timestamp_string[idx++] = ((secs) / 100000) + '0';
310 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
311 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
312 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
313 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
314 timestamp_string[idx++] = ((secs % 10)) + '0';
317 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
321 timestamp_string[idx++] = '.';
322 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
323 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
324 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
325 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
326 timestamp_string[idx++] = '\0';
329 /**************************************************/
330 /* Read packet function. */
331 /* Look for and read the next usable packet */
332 /* - return TRUE and details if found */
333 /**************************************************/
335 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
338 gint64 offset = file_tell(wth->fh);
339 long dollar_offset, before_time_offset, after_time_offset;
340 packet_direction_t direction;
343 /* Get wtap external structure for this wtap */
344 dct2000_file_externals_t *file_externals =
345 (dct2000_file_externals_t*)wth->priv;
347 /* Search for a line containing a usable packet */
349 int line_length, seconds, useconds, data_chars;
350 int is_comment = FALSE;
351 int is_sprint = FALSE;
352 gint64 this_offset = offset;
353 static gchar linebuff[MAX_LINE_LENGTH+1];
354 gchar aal_header_chars[AAL_HEADER_CHARS];
355 gchar context_name[MAX_CONTEXT_NAME];
356 guint8 context_port = 0;
357 gchar protocol_name[MAX_PROTOCOL_NAME+1];
358 gchar variant_name[MAX_VARIANT_DIGITS+1];
359 gchar outhdr_name[MAX_OUTHDR_NAME+1];
361 /* Are looking for first packet after 2nd line */
362 if (file_tell(wth->fh) == 0) {
363 this_offset += (file_externals->firstline_length+1+
364 file_externals->secondline_length+1);
367 /* Read a new line from file into linebuff */
368 if (!read_new_line(wth->fh, &offset, &line_length, linebuff,
369 sizeof linebuff, err, err_info)) {
371 return FALSE; /* error */
372 /* No more lines can be read, so quit. */
376 /* Try to parse the line as a frame record */
377 if (parse_line(linebuff, line_length, &seconds, &useconds,
378 &before_time_offset, &after_time_offset,
380 &data_chars, &direction, &encap, &is_comment, &is_sprint,
382 context_name, &context_port,
383 protocol_name, variant_name, outhdr_name)) {
384 line_prefix_info_t *line_prefix_info;
385 char timestamp_string[MAX_TIMESTAMP_LEN+1];
388 write_timestamp_string(timestamp_string, seconds, useconds/100);
390 /* Set data_offset to the beginning of the line we're returning.
391 This will be the seek_off parameter when this frame is re-read.
393 *data_offset = this_offset;
395 process_parsed_line(wth, file_externals,
397 wth->frame_buffer, this_offset,
398 linebuff, dollar_offset,
399 seconds, useconds, timestamp_string,
401 context_name, context_port,
402 protocol_name, variant_name,
403 outhdr_name, aal_header_chars,
404 is_comment, data_chars);
406 /* Store the packet prefix in the hash table */
407 line_prefix_info = g_new(line_prefix_info_t,1);
409 /* Create and use buffer for contents before time */
410 line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
411 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
412 line_prefix_info->before_time[before_time_offset] = '\0';
414 /* Create and use buffer for contents before time.
415 Do this only if it doesn't correspond to " l ", which is by far the most
417 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
418 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
420 line_prefix_info->after_time = NULL;
423 /* Allocate & write buffer for line between timestamp and data */
424 line_prefix_info->after_time = (gchar *)g_malloc(dollar_offset - after_time_offset);
425 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
426 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
429 /* Add packet entry into table */
430 pkey = (gint64 *)g_malloc(sizeof(*pkey));
432 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
434 /* OK, we have packet details to return */
439 /* No packet details to return... */
444 /**************************************************/
445 /* Read & seek function. */
446 /**************************************************/
448 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
449 struct wtap_pkthdr *phdr, Buffer *buf,
450 int *err, gchar **err_info)
454 long dollar_offset, before_time_offset, after_time_offset;
455 static gchar linebuff[MAX_LINE_LENGTH+1];
456 gchar aal_header_chars[AAL_HEADER_CHARS];
457 gchar context_name[MAX_CONTEXT_NAME];
458 guint8 context_port = 0;
459 gchar protocol_name[MAX_PROTOCOL_NAME+1];
460 gchar variant_name[MAX_VARIANT_DIGITS+1];
461 gchar outhdr_name[MAX_OUTHDR_NAME+1];
462 int is_comment = FALSE;
463 int is_sprint = FALSE;
464 packet_direction_t direction;
466 int seconds, useconds, data_chars;
468 /* Get wtap external structure for this wtap */
469 dct2000_file_externals_t *file_externals =
470 (dct2000_file_externals_t*)wth->priv;
475 /* Seek to beginning of packet */
476 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
480 /* Re-read whole line (this really should succeed) */
481 if (!read_new_line(wth->random_fh, &offset, &length, linebuff,
482 sizeof linebuff, err, err_info)) {
486 /* Try to parse this line again (should succeed as re-reading...) */
487 if (parse_line(linebuff, length, &seconds, &useconds,
488 &before_time_offset, &after_time_offset,
490 &data_chars, &direction, &encap, &is_comment, &is_sprint,
492 context_name, &context_port,
493 protocol_name, variant_name, outhdr_name)) {
494 char timestamp_string[MAX_TIMESTAMP_LEN+1];
496 write_timestamp_string(timestamp_string, seconds, useconds/100);
498 process_parsed_line(wth, file_externals,
500 linebuff, dollar_offset,
501 seconds, useconds, timestamp_string,
503 context_name, context_port,
504 protocol_name, variant_name,
505 outhdr_name, aal_header_chars,
506 is_comment, data_chars);
512 /* If get here, must have failed */
514 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
515 "line at position %" G_GINT64_MODIFIER "d",
521 /***************************************************************************/
522 /* Free dct2000-specific capture info from file that was open for reading */
523 /***************************************************************************/
525 catapult_dct2000_close(wtap *wth)
527 /* Get externals for this file */
528 dct2000_file_externals_t *file_externals =
529 (dct2000_file_externals_t*)wth->priv;
531 /* Free up its line prefix values */
532 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
533 free_line_prefix_info, NULL);
534 /* Free up its line prefix table */
535 g_hash_table_destroy(file_externals->packet_prefix_table);
541 /***************************/
543 /***************************/
546 gboolean first_packet_written;
550 /*****************************************************/
551 /* The file that we are writing to has been opened. */
552 /* Set other dump callbacks. */
553 /*****************************************************/
555 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
557 /* Fill in other dump callbacks */
558 wdh->subtype_write = catapult_dct2000_dump;
563 /*********************************************************/
564 /* Respond to queries about which encap types we support */
566 /*********************************************************/
568 catapult_dct2000_dump_can_write_encap(int encap)
571 case WTAP_ENCAP_CATAPULT_DCT2000:
572 /* We support this */
576 /* But don't write to any other formats... */
577 return WTAP_ERR_UNWRITABLE_ENCAP;
582 /*****************************************/
583 /* Write a single packet out to the file */
584 /*****************************************/
587 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
588 const guint8 *pd, int *err, gchar **err_info _U_)
590 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
592 line_prefix_info_t *prefix = NULL;
593 gchar time_string[16];
595 gboolean is_sprint = FALSE;
596 dct2000_dump_t *dct2000;
597 int consecutive_slashes=0;
600 /******************************************************/
601 /* Get the file_externals structure for this file */
602 /* Find wtap external structure for this wtap */
603 dct2000_file_externals_t *file_externals =
604 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
606 /* We can only write packet records. */
607 if (phdr->rec_type != REC_TYPE_PACKET) {
608 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
612 dct2000 = (dct2000_dump_t *)wdh->priv;
613 if (dct2000 == NULL) {
615 /* Write out saved first line */
616 if (!wtap_dump_file_write(wdh, file_externals->firstline,
617 file_externals->firstline_length, err)) {
620 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
624 /* Also write out saved second line with timestamp corresponding to the
625 opening time of the log.
627 if (!wtap_dump_file_write(wdh, file_externals->secondline,
628 file_externals->secondline_length, err)) {
631 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
635 /* Allocate the dct2000-specific dump structure */
636 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
637 wdh->priv = (void *)dct2000;
639 /* Copy time of beginning of file */
640 dct2000->start_time.secs = file_externals->start_secs;
641 dct2000->start_time.nsecs =
642 (file_externals->start_usecs * 1000);
644 /* Set flag so don't write header out again */
645 dct2000->first_packet_written = TRUE;
649 /******************************************************************/
650 /* Write out this packet's prefix, including calculated timestamp */
652 /* Look up line data prefix using stored offset */
653 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
654 (const void*)&(pseudo_header->dct2000.seek_off));
656 /* Write out text before timestamp */
657 if (!wtap_dump_file_write(wdh, prefix->before_time,
658 strlen(prefix->before_time), err)) {
662 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
663 /* This is much faster than strstr() for "/////" */
664 p_c = prefix->before_time;
665 while (p_c && (*p_c != '/')) {
668 while (p_c && (*p_c == '/')) {
669 consecutive_slashes++;
672 is_comment = (consecutive_slashes == 5);
674 /* Calculate time of this packet to write, relative to start of dump */
675 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
676 write_timestamp_string(time_string,
677 (int)(phdr->ts.secs - dct2000->start_time.secs),
678 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
681 write_timestamp_string(time_string,
682 (int)(phdr->ts.secs - dct2000->start_time.secs-1),
683 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
686 /* Write out the calculated timestamp */
687 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
691 /* Write out text between timestamp and start of hex data */
692 if (prefix->after_time == NULL) {
693 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
698 if (!wtap_dump_file_write(wdh, prefix->after_time,
699 strlen(prefix->after_time), err)) {
705 /****************************************************************/
706 /* Need to skip stub header at start of pd before we reach data */
709 for (n=0; pd[n] != '\0'; n++);
712 /* Context port number */
716 for (; pd[n] != '\0'; n++);
721 is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
723 for (; pd[n] != '\0'; n++);
726 /* Variant number (as string) */
727 for (; pd[n] != '\0'; n++);
730 /* Outhdr (as string) */
731 for (; pd[n] != '\0'; n++);
734 /* Direction & encap */
738 /**************************************/
739 /* Remainder is encapsulated protocol */
740 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
745 /* Each binary byte is written out as 2 hex string chars */
746 for (; n < phdr->len; n++) {
748 c[0] = char_from_hex((guint8)(pd[n] >> 4));
749 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
751 /* Write both hex chars of byte together */
752 if (!wtap_dump_file_write(wdh, c, 2, err)) {
758 for (; n < phdr->len; n++) {
762 /* Write both hex chars of byte together */
763 if (!wtap_dump_file_write(wdh, c, 1, err)) {
770 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
778 /****************************/
779 /* Private helper functions */
780 /****************************/
782 /**********************************************************************/
783 /* Read a new line from the file, starting at offset. */
784 /* - writes data to its argument linebuff */
785 /* - on return 'offset' will point to the next position to read from */
786 /* - return TRUE if this read is successful */
787 /**********************************************************************/
789 read_new_line(FILE_T fh, gint64 *offset, gint *length,
790 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
793 gint64 pos_before = file_tell(fh);
795 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
796 /* No characters found, or error */
797 *err = file_error(fh, err_info);
801 /* Set length (avoiding strlen()) and offset.. */
802 *length = (gint)(file_tell(fh) - pos_before);
803 *offset = *offset + *length;
805 /* ...but don't want to include newline in line length */
806 if (*length > 0 && linebuff[*length-1] == '\n') {
807 linebuff[*length-1] = '\0';
808 *length = *length - 1;
810 /* Nor do we want '\r' (as will be written when log is created on windows) */
811 if (*length > 0 && linebuff[*length-1] == '\r') {
812 linebuff[*length-1] = '\0';
813 *length = *length - 1;
820 /**********************************************************************/
821 /* Parse a line from buffer, by identifying: */
822 /* - context, port and direction of packet */
824 /* - data position and length */
825 /* Return TRUE if this packet looks valid and can be displayed */
826 /**********************************************************************/
828 parse_line(gchar *linebuff, gint line_length,
829 gint *seconds, gint *useconds,
830 long *before_time_offset, long *after_time_offset,
831 long *data_offset, gint *data_chars,
832 packet_direction_t *direction,
833 int *encap, int *is_comment, int *is_sprint,
834 gchar *aal_header_chars,
835 gchar *context_name, guint8 *context_portp,
836 gchar *protocol_name, gchar *variant_name,
841 char port_number_string[MAX_PORT_DIGITS+1];
842 int variant_digits = 0;
844 int protocol_chars = 0;
845 int outhdr_chars = 0;
847 char seconds_buff[MAX_SECONDS_CHARS+1];
849 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
850 int subsecond_decimals_chars;
851 int skip_first_byte = FALSE;
852 gboolean atm_header_present = FALSE;
857 /* Read context name until find '.' */
858 for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
859 if (linebuff[n] == '/') {
860 context_name[n] = '\0';
862 /* If not a comment (/////), not a valid line */
863 if (strncmp(linebuff+n, "/////", 5) != 0) {
867 /* There is no variant, outhdr, etc. Set protocol to be a comment */
868 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
872 if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
875 context_name[n] = linebuff[n];
877 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
881 /* Reset strings (that won't be set by comments) */
882 variant_name[0] = '\0';
883 outhdr_name[0] = '\0';
884 port_number_string[0] = '\0';
886 if (!(*is_comment)) {
887 /* '.' must follow context name */
888 if (linebuff[n] != '.') {
891 context_name[n] = '\0';
895 /* Now read port number */
896 for (port_digits = 0;
897 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
898 n++, port_digits++) {
900 if (!g_ascii_isdigit(linebuff[n])) {
903 port_number_string[port_digits] = linebuff[n];
905 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
909 /* Slash char must follow port number */
910 if (linebuff[n] != '/')
914 port_number_string[port_digits] = '\0';
915 if (port_digits == 1) {
916 *context_portp = port_number_string[0] - '0';
919 *context_portp = atoi(port_number_string);
924 /* Now for the protocol name */
925 for (protocol_chars = 0;
926 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
927 n++, protocol_chars++) {
929 if (!g_ascii_isalnum(linebuff[n]) && linebuff[n] != '_') {
932 protocol_name[protocol_chars] = linebuff[n];
934 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
935 /* If doesn't fit, fail rather than truncate */
938 protocol_name[protocol_chars] = '\0';
940 /* Slash char must follow protocol name */
941 if (linebuff[n] != '/') {
948 /* Following the / is the variant number. No digits indicate 1 */
949 for (variant_digits = 0;
950 (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
951 n++, variant_digits++) {
953 if (!g_ascii_isdigit(linebuff[n])) {
956 variant_name[variant_digits] = linebuff[n];
958 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
962 if (variant_digits > 0) {
963 variant_name[variant_digits] = '\0';
964 if (variant_digits == 1) {
965 variant = variant_name[0] - '0';
968 variant = atoi(variant_name);
972 variant_name[0] = '1';
973 variant_name[1] = '\0';
977 /* Outheader values may follow */
978 outhdr_name[0] = '\0';
979 if (linebuff[n] == ',') {
983 for (outhdr_chars = 0;
984 (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
985 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
986 n++, outhdr_chars++) {
988 if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
991 outhdr_name[outhdr_chars] = linebuff[n];
993 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
996 /* Terminate (possibly empty) string */
997 outhdr_name[outhdr_chars] = '\0';
1002 /******************************************************************/
1003 /* Now check whether we know how to use a packet of this protocol */
1005 if ((strcmp(protocol_name, "ip") == 0) ||
1006 (strcmp(protocol_name, "sctp") == 0) ||
1007 (strcmp(protocol_name, "gre") == 0) ||
1008 (strcmp(protocol_name, "mipv6") == 0) ||
1009 (strcmp(protocol_name, "igmp") == 0)) {
1011 *encap = WTAP_ENCAP_RAW_IP;
1015 /* FP may be carried over ATM, which has separate atm header to parse */
1016 if ((strcmp(protocol_name, "fp") == 0) ||
1017 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1019 if ((variant > 256) && (variant % 256 == 3)) {
1020 /* FP over udp is contained in IPPrim... */
1024 /* FP over AAL0 or AAL2 */
1025 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1026 atm_header_present = TRUE;
1029 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1030 /* FP (IuR) over AAL2 */
1031 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1032 atm_header_present = TRUE;
1036 if (strcmp(protocol_name, "ppp") == 0) {
1037 *encap = WTAP_ENCAP_PPP;
1040 if (strcmp(protocol_name, "isdn_l3") == 0) {
1041 /* TODO: find out what this byte means... */
1042 skip_first_byte = TRUE;
1043 *encap = WTAP_ENCAP_ISDN;
1046 if (strcmp(protocol_name, "isdn_l2") == 0) {
1047 *encap = WTAP_ENCAP_ISDN;
1050 if (strcmp(protocol_name, "ethernet") == 0) {
1051 *encap = WTAP_ENCAP_ETHERNET;
1054 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1055 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1057 *encap = DCT2000_ENCAP_SSCOP;
1060 if (strcmp(protocol_name, "frelay_l2") == 0) {
1061 *encap = WTAP_ENCAP_FRELAY;
1064 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1065 *encap = DCT2000_ENCAP_MTP2;
1068 if ((strcmp(protocol_name, "nbap") == 0) ||
1069 (strcmp(protocol_name, "nbap_r4") == 0) ||
1070 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1072 /* The entire message in these cases is nbap, so use an encap value. */
1073 *encap = DCT2000_ENCAP_NBAP;
1076 /* Not a supported board port protocol/encap, but can show as raw data or
1077 in some cases find protocol embedded inside primitive */
1078 *encap = DCT2000_ENCAP_UNHANDLED;
1082 /* Find separate ATM header if necessary */
1083 if (atm_header_present) {
1084 int header_chars_seen = 0;
1086 /* Scan ahead to the next $ */
1087 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1090 if (n+1 >= line_length) {
1094 /* Read consecutive hex chars into atm header buffer */
1096 ((n < line_length) &&
1097 (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1098 (header_chars_seen < AAL_HEADER_CHARS));
1099 n++, header_chars_seen++) {
1101 aal_header_chars[header_chars_seen] = linebuff[n];
1102 /* Next 6 characters after '9' are mapped to a->f */
1103 if (!g_ascii_isdigit(linebuff[n])) {
1104 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1108 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1116 /* If there is a number, skip all info to next '/'.
1117 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1118 and display dct2000 dissector... */
1119 if (g_ascii_isdigit(linebuff[n])) {
1120 while ((n+1 < line_length) && linebuff[n] != '/') {
1126 while ((n+1 < line_length) && linebuff[n] == '/') {
1130 /* Skip a space that may happen here */
1131 if ((n+1 < line_length) && linebuff[n] == ' ') {
1135 /* Next character gives direction of message (must be 's' or 'r') */
1136 if (!(*is_comment)) {
1137 if (linebuff[n] == 's') {
1141 if (linebuff[n] == 'r') {
1142 *direction = received;
1155 /*********************************************************************/
1156 /* Find and read the timestamp */
1158 /* Now scan to the next digit, which should be the start of the timestamp */
1159 /* This will involve skipping " tm " */
1161 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1162 if (n >= line_length) {
1166 for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
1167 if (n >= line_length) {
1171 *before_time_offset = n;
1174 for (seconds_chars = 0;
1175 (linebuff[n] != '.') &&
1176 (seconds_chars <= MAX_SECONDS_CHARS) &&
1178 n++, seconds_chars++) {
1180 if (!g_ascii_isdigit(linebuff[n])) {
1181 /* Found a non-digit before decimal point. Fail */
1184 seconds_buff[seconds_chars] = linebuff[n];
1186 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1187 /* Didn't fit in buffer. Fail rather than use truncated */
1191 /* Convert found value into number */
1192 seconds_buff[seconds_chars] = '\0';
1193 *seconds = atoi(seconds_buff);
1195 /* The decimal point must follow the last of the seconds digits */
1196 if (linebuff[n] != '.') {
1202 /* Subsecond decimal digits (expect 4-digit accuracy) */
1203 for (subsecond_decimals_chars = 0;
1204 (linebuff[n] != ' ') &&
1205 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1207 n++, subsecond_decimals_chars++) {
1209 if (!g_ascii_isdigit(linebuff[n])) {
1212 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1214 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1215 /* More numbers than expected - give up */
1218 /* Convert found value into microseconds */
1219 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1220 *useconds = atoi(subsecond_decimals_buff) * 100;
1222 /* Space character must follow end of timestamp */
1223 if (linebuff[n] != ' ') {
1227 *after_time_offset = n++;
1229 /* If we have a string message, it could either be a comment (with '$') or
1230 a sprint line (no '$') */
1232 if (strncmp(linebuff+n, "l $", 3) != 0) {
1234 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1238 if (!(*is_sprint)) {
1239 /* Now skip ahead to find start of data (marked by '$') */
1240 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1241 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1248 /* Set offset to data start within line */
1251 /* Set number of chars that comprise the hex string protocol data */
1252 *data_chars = line_length - n;
1254 /* May need to skip first byte (2 hex string chars) */
1255 if (skip_first_byte) {
1263 /***********************************/
1264 /* Process results of parse_line() */
1265 /***********************************/
1267 process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
1268 struct wtap_pkthdr *phdr,
1269 Buffer *buf, gint64 file_offset,
1270 char *linebuff, long dollar_offset,
1271 int seconds, int useconds, gchar *timestamp_string,
1272 packet_direction_t direction, int encap,
1273 gchar *context_name, guint8 context_port,
1274 gchar *protocol_name, gchar *variant_name,
1275 gchar *outhdr_name, gchar *aal_header_chars,
1276 gboolean is_comment, int data_chars)
1279 int stub_offset = 0;
1281 guint8 *frame_buffer;
1283 phdr->rec_type = REC_TYPE_PACKET;
1284 phdr->presence_flags = WTAP_HAS_TS;
1286 /* Make sure all packets go to Catapult DCT2000 dissector */
1287 phdr->pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1289 /* Fill in timestamp (capture base + packet offset) */
1290 phdr->ts.secs = file_externals->start_secs + seconds;
1291 if ((file_externals->start_usecs + useconds) >= 1000000) {
1295 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1297 /*****************************/
1298 /* Get the data buffer ready */
1299 ws_buffer_assure_space(buf,
1300 strlen(context_name)+1 + /* Context name */
1302 strlen(timestamp_string)+1 + /* timestamp */
1303 strlen(variant_name)+1 + /* variant */
1304 strlen(outhdr_name)+1 + /* outhdr */
1305 strlen(protocol_name)+1 + /* Protocol name */
1308 (is_comment ? data_chars : (data_chars/2)));
1309 frame_buffer = ws_buffer_start_ptr(buf);
1311 /******************************************/
1312 /* Write the stub info to the data buffer */
1315 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1316 stub_offset += (int)(length + 1);
1318 /* Context port number */
1319 frame_buffer[stub_offset] = context_port;
1322 /* Timestamp within file */
1323 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1324 stub_offset += (int)(length + 1);
1327 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1328 stub_offset += (int)(length + 1);
1330 /* Protocol variant number (as string) */
1331 length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1332 stub_offset += (int)(length + 1);
1335 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1336 stub_offset += (int)(length + 1);
1339 frame_buffer[stub_offset] = direction;
1343 frame_buffer[stub_offset] = (guint8)encap;
1346 /* Binary data length is half bytestring length + stub header */
1347 phdr->len = stub_offset + (is_comment ? data_chars : (data_chars/2));
1348 phdr->caplen = stub_offset + (is_comment ? data_chars : (data_chars/2));
1351 /***********************************************************/
1352 /* Copy packet data into buffer, converting from ascii hex */
1353 for (n=0; n < data_chars; n+=2) {
1354 frame_buffer[stub_offset + n/2] =
1355 hex_byte_from_chars(linebuff+dollar_offset+n);
1359 /***********************************************************/
1360 /* Copy packet data into buffer, just copying ascii chars */
1361 for (n=0; n < data_chars; n++) {
1362 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1366 /*****************************************/
1367 /* Set packet pseudo-header if necessary */
1368 phdr->pseudo_header.dct2000.seek_off = file_offset;
1369 phdr->pseudo_header.dct2000.wth = wth;
1372 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1373 set_aal_info(&phdr->pseudo_header, direction, aal_header_chars);
1375 case WTAP_ENCAP_ISDN:
1376 set_isdn_info(&phdr->pseudo_header, direction);
1378 case WTAP_ENCAP_PPP:
1379 set_ppp_info(&phdr->pseudo_header, direction);
1383 /* Other supported types don't need to set anything here... */
1388 /*********************************************/
1389 /* Fill in atm pseudo-header with known info */
1390 /*********************************************/
1392 set_aal_info(union wtap_pseudo_header *pseudo_header,
1393 packet_direction_t direction,
1394 gchar *aal_header_chars)
1396 /* 'aal_head_chars' has this format (for AAL2 at least):
1397 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1398 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1399 Channel Identifier (8 bits) | ...
1402 /* Indicate that this is a reassembled PDU */
1403 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1405 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1406 TODO: Can we infer the correct value here?
1407 Meanwhile, just use the direction to make them distinguishable...
1409 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1411 /* Assume always AAL2 for FP */
1412 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1414 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1415 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1417 /* vpi is 8 bits (2nd & 3rd nibble) */
1418 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1419 hex_byte_from_chars(aal_header_chars+1);
1421 /* vci is next 16 bits */
1422 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1423 ((hex_from_char(aal_header_chars[3]) << 12) |
1424 (hex_from_char(aal_header_chars[4]) << 8) |
1425 (hex_from_char(aal_header_chars[5]) << 4) |
1426 hex_from_char(aal_header_chars[6]));
1428 /* 0 means we don't know how many cells the frame comprises. */
1429 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1431 /* cid is usually last byte. Unless last char is not hex digit, in which
1432 case cid is derived from last char in ascii */
1433 if (g_ascii_isalnum(aal_header_chars[11])) {
1434 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1435 hex_byte_from_chars(aal_header_chars+10);
1438 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1439 (int)aal_header_chars[11] - '0';
1444 /**********************************************/
1445 /* Fill in isdn pseudo-header with known info */
1446 /**********************************************/
1448 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1449 packet_direction_t direction)
1451 /* This field is used to set the 'Source' and 'Destination' columns to
1452 'User' or 'Network'. If we assume that we're simulating the network,
1453 treat Received messages as being destined for the network.
1455 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1457 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1458 everything else would be treated as a B-channel
1460 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1464 /*********************************************/
1465 /* Fill in ppp pseudo-header with known info */
1466 /*********************************************/
1468 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1469 packet_direction_t direction)
1471 /* Set direction. */
1472 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1476 /********************************************************/
1477 /* Return hex nibble equivalent of hex string character */
1478 /********************************************************/
1480 hex_from_char(gchar c)
1482 if ((c >= '0') && (c <= '9')) {
1486 if ((c >= 'a') && (c <= 'f')) {
1487 return 0x0a + (c - 'a');
1490 /* Not a valid hex string character */
1496 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1497 static guint8 s_tableValues[255][255];
1499 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1500 static void prepare_hex_byte_from_chars_table(void)
1502 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1503 'a', 'b', 'c', 'd', 'e', 'f' };
1506 for (i=0; i < 16; i++) {
1507 for (j=0; j < 16; j++) {
1508 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1513 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1514 static guint8 hex_byte_from_chars(gchar *c)
1516 /* Return value from quick table lookup */
1517 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1522 /********************************************************/
1523 /* Return character corresponding to hex nibble value */
1524 /********************************************************/
1526 char_from_hex(guint8 hex)
1528 static const char hex_lookup[16] =
1529 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1535 return hex_lookup[hex];
1538 /***********************************************/
1539 /* Equality test for packet prefix hash tables */
1540 /***********************************************/
1542 packet_offset_equal(gconstpointer v, gconstpointer v2)
1544 /* Dereferenced pointers must have same gint64 offset value */
1545 return (*(const gint64*)v == *(const gint64*)v2);
1549 /********************************************/
1550 /* Hash function for packet-prefix hash table */
1551 /********************************************/
1553 packet_offset_hash_func(gconstpointer v)
1555 /* Use low-order bits of gint64 offset value */
1556 return (guint)(*(const gint64*)v);
1560 /************************************************************************/
1561 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1562 /* Set secs and usecs as output */
1563 /* Return FALSE if no valid time can be read */
1564 /************************************************************************/
1566 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1569 #define MAX_MONTH_LETTERS 9
1570 char month[MAX_MONTH_LETTERS+1];
1572 int day, year, hour, minute, second;
1575 /* If line longer than expected, file is probably not correctly formatted */
1576 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1580 /********************************************************/
1581 /* Scan for all fields */
1582 scan_found = sscanf(linebuff, "%10s %2d, %4d %2d:%2d:%2d.%4u",
1583 month, &day, &year, &hour, &minute, &second, usecs);
1584 if (scan_found != 7) {
1585 /* Give up if not all found */
1589 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1590 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1591 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1592 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1593 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1594 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1595 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1596 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1597 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1598 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1599 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1600 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1602 /* Give up if not found a properly-formatted date */
1606 /******************************************************/
1607 /* Fill in remaining fields and return it in a time_t */
1608 tm.tm_year = year - 1900;
1613 tm.tm_isdst = -1; /* daylight saving time info not known */
1615 /* Get seconds from this time */
1616 *secs = mktime(&tm);
1618 /* Multiply 4 digits given to get micro-seconds */
1619 *usecs = *usecs * 100;
1624 /* Free the data allocated inside a line_prefix_info_t */
1626 free_line_prefix_info(gpointer key, gpointer value,
1627 gpointer user_data _U_)
1629 line_prefix_info_t *info = (line_prefix_info_t*)value;
1631 /* Free the 64-bit key value */
1634 /* Free the strings inside */
1635 g_free(info->before_time);
1636 if (info->after_time) {
1637 g_free(info->after_time);
1640 /* And the structure itself */
1643 /* Item will always be removed from table */
1648 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1653 * indent-tabs-mode: nil
1656 * vi: set shiftwidth=4 tabstop=8 expandtab:
1657 * :indentSize=4:tabSize=8:noTabs=true: