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.
28 #include "file_wrappers.h"
29 #include <wsutil/buffer.h>
31 #include "catapult_dct2000.h"
33 #define MAX_FIRST_LINE_LENGTH 200
34 #define MAX_TIMESTAMP_LINE_LENGTH 100
35 #define MAX_LINE_LENGTH 65536
36 #define MAX_TIMESTAMP_LEN 32
37 #define MAX_SECONDS_CHARS 16
38 #define MAX_SUBSECOND_DECIMALS 4
39 #define MAX_CONTEXT_NAME 64
40 #define MAX_PROTOCOL_NAME 64
41 #define MAX_PORT_DIGITS 2
42 #define MAX_VARIANT_DIGITS 32
43 #define MAX_OUTHDR_NAME 256
44 #define AAL_HEADER_CHARS 12
47 - support for FP over AAL0
48 - support for IuR interface FP
52 /* 's' or 'r' of a packet as read from .out file */
53 typedef enum packet_direction_t
60 /***********************************************************************/
61 /* For each line, store (in case we need to dump): */
62 /* - String before time field */
63 /* - String beween time field and data (if NULL assume " l ") */
71 /*******************************************************************/
72 /* Information stored external to a file (wtap) needed for reading and dumping */
73 typedef struct dct2000_file_externals
75 /* Remember the time at the start of capture */
80 * The following information is needed only for dumping.
82 * XXX - Wiretap is not *supposed* to require that a packet being
83 * dumped come from a file of the same type that you currently have
84 * open; this should be fixed.
87 /* Buffer to hold first line, including magic and format number */
88 gchar firstline[MAX_FIRST_LINE_LENGTH];
89 gint firstline_length;
91 /* Buffer to hold second line with formatted file creation data/time */
92 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
93 gint secondline_length;
95 /* Hash table to store text prefix data part of displayed packets.
96 Records (file offset -> line_prefix_info_t)
98 GHashTable *packet_prefix_table;
99 } dct2000_file_externals_t;
101 /* 'Magic number' at start of Catapult DCT2000 .out files. */
102 static const gchar catapult_dct2000_magic[] = "Session Transcript";
104 /************************************************************/
105 /* Functions called from wiretap core */
106 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
107 gint64 *data_offset);
108 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
109 struct wtap_pkthdr *phdr,
110 Buffer *buf, int *err,
112 static void catapult_dct2000_close(wtap *wth);
114 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
115 const guint8 *pd, int *err);
118 /************************************************************/
119 /* Private helper functions */
120 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
121 gchar *buf, size_t bufsize, int *err,
123 static gboolean parse_line(char *linebuff, gint line_length,
124 gint *seconds, gint *useconds,
125 long *before_time_offset, long *after_time_offset,
128 packet_direction_t *direction,
129 int *encap, int *is_comment, int *is_sprint,
130 gchar *aal_header_chars,
131 gchar *context_name, guint8 *context_portp,
132 gchar *protocol_name, gchar *variant_name,
134 static void process_parsed_line(wtap *wth,
135 dct2000_file_externals_t *file_externals,
136 struct wtap_pkthdr *phdr,
137 Buffer *buf, gint64 file_offset,
138 char *linebuff, long dollar_offset,
139 int seconds, int useconds, 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 static guint8 hex_from_char(gchar c);
146 static void prepare_hex_byte_from_chars_table(void);
147 static guint8 hex_byte_from_chars(gchar *c);
148 static gchar char_from_hex(guint8 hex);
150 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
151 packet_direction_t direction,
152 gchar *aal_header_chars);
153 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
154 packet_direction_t direction);
155 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
156 packet_direction_t direction);
158 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
159 static guint packet_offset_hash_func(gconstpointer v);
161 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
162 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
166 /********************************************/
167 /* Open file (for reading) */
168 /********************************************/
170 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
175 gint firstline_length = 0;
176 dct2000_file_externals_t *file_externals;
177 static gchar linebuff[MAX_LINE_LENGTH];
178 static gboolean hex_byte_table_values_set = FALSE;
180 /* Clear errno before reading from the file */
184 /********************************************************************/
185 /* First line needs to contain at least as many characters as magic */
187 if (!read_new_line(wth->fh, &offset, &firstline_length, linebuff,
188 sizeof linebuff, err, err_info)) {
189 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
193 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
194 firstline_length >= MAX_FIRST_LINE_LENGTH) {
199 /* This file is not for us if it doesn't match our signature */
200 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
204 /* Make sure table is ready for use */
205 if (!hex_byte_table_values_set) {
206 prepare_hex_byte_from_chars_table();
207 hex_byte_table_values_set = TRUE;
210 /*********************************************************************/
211 /* Need entry in file_externals table */
213 /* Allocate a new file_externals structure for this file */
214 file_externals = g_new(dct2000_file_externals_t,1);
215 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
217 /* Copy this first line into buffer so could write out later */
218 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
219 file_externals->firstline_length = firstline_length;
222 /***********************************************************/
223 /* Second line contains file timestamp */
224 /* Store this offset in in file_externals */
226 if (!read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
227 linebuff, sizeof linebuff, err, err_info)) {
228 g_free(file_externals);
229 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
233 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
234 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
236 /* Give up if file time line wasn't valid */
237 g_free(file_externals);
241 /* Fill in timestamp */
242 file_externals->start_secs = timestamp;
243 file_externals->start_usecs = usecs;
245 /* Copy this second line into buffer so could write out later */
246 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
249 /************************************************************/
250 /* File is for us. Fill in details so packets can be read */
252 /* Set our file type */
253 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000;
255 /* Use our own encapsulation to send all packets to our stub dissector */
256 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
258 /* Callbacks for reading operations */
259 wth->subtype_read = catapult_dct2000_read;
260 wth->subtype_seek_read = catapult_dct2000_seek_read;
261 wth->subtype_close = catapult_dct2000_close;
263 /* Choose microseconds (have 4 decimal places...) */
264 wth->file_tsprec = WTAP_TSPREC_USEC;
267 /***************************************************************/
268 /* Initialise packet_prefix_table (index is offset into file) */
269 file_externals->packet_prefix_table =
270 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
272 /* Set this wtap to point to the file_externals */
273 wth->priv = (void*)file_externals;
279 /* Ugly, but much faster than using g_snprintf! */
280 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
286 timestamp_string[idx++] = ((secs % 10)) + '0';
288 else if (secs < 100) {
289 timestamp_string[idx++] = ( secs / 10) + '0';
290 timestamp_string[idx++] = ((secs % 10)) + '0';
292 else if (secs < 1000) {
293 timestamp_string[idx++] = ((secs) / 100) + '0';
294 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
295 timestamp_string[idx++] = ((secs % 10)) + '0';
297 else if (secs < 10000) {
298 timestamp_string[idx++] = ((secs) / 1000) + '0';
299 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
300 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
301 timestamp_string[idx++] = ((secs % 10)) + '0';
303 else if (secs < 100000) {
304 timestamp_string[idx++] = ((secs) / 10000) + '0';
305 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
306 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
307 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
308 timestamp_string[idx++] = ((secs % 10)) + '0';
310 else if (secs < 1000000) {
311 timestamp_string[idx++] = ((secs) / 100000) + '0';
312 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
313 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
314 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
315 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
316 timestamp_string[idx++] = ((secs % 10)) + '0';
319 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
323 timestamp_string[idx++] = '.';
324 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
325 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
326 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
327 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
328 timestamp_string[idx++] = '\0';
331 /**************************************************/
332 /* Read packet function. */
333 /* Look for and read the next usable packet */
334 /* - return TRUE and details if found */
335 /**************************************************/
337 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
340 gint64 offset = file_tell(wth->fh);
341 long dollar_offset, before_time_offset, after_time_offset;
342 packet_direction_t direction;
345 /* Get wtap external structure for this wtap */
346 dct2000_file_externals_t *file_externals =
347 (dct2000_file_externals_t*)wth->priv;
349 /* Search for a line containing a usable packet */
351 int line_length, seconds, useconds, data_chars;
352 int is_comment = FALSE;
353 int is_sprint = FALSE;
354 gint64 this_offset = offset;
355 static gchar linebuff[MAX_LINE_LENGTH+1];
356 gchar aal_header_chars[AAL_HEADER_CHARS];
357 gchar context_name[MAX_CONTEXT_NAME];
358 guint8 context_port = 0;
359 gchar protocol_name[MAX_PROTOCOL_NAME+1];
360 gchar variant_name[MAX_VARIANT_DIGITS+1];
361 gchar outhdr_name[MAX_OUTHDR_NAME+1];
363 /* Are looking for first packet after 2nd line */
364 if (file_tell(wth->fh) == 0) {
365 this_offset += (file_externals->firstline_length+1+
366 file_externals->secondline_length+1);
369 /* Read a new line from file into linebuff */
370 if (!read_new_line(wth->fh, &offset, &line_length, linebuff,
371 sizeof linebuff, err, err_info)) {
373 return FALSE; /* error */
374 /* No more lines can be read, so quit. */
378 /* Try to parse the line as a frame record */
379 if (parse_line(linebuff, line_length, &seconds, &useconds,
380 &before_time_offset, &after_time_offset,
382 &data_chars, &direction, &encap, &is_comment, &is_sprint,
384 context_name, &context_port,
385 protocol_name, variant_name, outhdr_name)) {
386 line_prefix_info_t *line_prefix_info;
387 char timestamp_string[MAX_TIMESTAMP_LEN+1];
390 write_timestamp_string(timestamp_string, seconds, useconds/100);
392 /* Set data_offset to the beginning of the line we're returning.
393 This will be the seek_off parameter when this frame is re-read.
395 *data_offset = this_offset;
397 process_parsed_line(wth, file_externals,
399 wth->frame_buffer, this_offset,
400 linebuff, dollar_offset,
401 seconds, useconds, timestamp_string,
403 context_name, context_port,
404 protocol_name, variant_name,
405 outhdr_name, aal_header_chars,
406 is_comment, data_chars);
408 /* Store the packet prefix in the hash table */
409 line_prefix_info = g_new(line_prefix_info_t,1);
411 /* Create and use buffer for contents before time */
412 line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
413 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
414 line_prefix_info->before_time[before_time_offset] = '\0';
416 /* Create and use buffer for contents before time.
417 Do this only if it doesn't correspond to " l ", which is by far the most
419 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
420 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
422 line_prefix_info->after_time = NULL;
425 /* Allocate & write buffer for line between timestamp and data */
426 line_prefix_info->after_time = (gchar *)g_malloc(dollar_offset - after_time_offset);
427 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
428 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
431 /* Add packet entry into table */
432 pkey = (gint64 *)g_malloc(sizeof(*pkey));
434 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
436 /* OK, we have packet details to return */
441 /* No packet details to return... */
446 /**************************************************/
447 /* Read & seek function. */
448 /**************************************************/
450 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
451 struct wtap_pkthdr *phdr, Buffer *buf,
452 int *err, gchar **err_info)
456 long dollar_offset, before_time_offset, after_time_offset;
457 static gchar linebuff[MAX_LINE_LENGTH+1];
458 gchar aal_header_chars[AAL_HEADER_CHARS];
459 gchar context_name[MAX_CONTEXT_NAME];
460 guint8 context_port = 0;
461 gchar protocol_name[MAX_PROTOCOL_NAME+1];
462 gchar variant_name[MAX_VARIANT_DIGITS+1];
463 gchar outhdr_name[MAX_OUTHDR_NAME+1];
464 int is_comment = FALSE;
465 int is_sprint = FALSE;
466 packet_direction_t direction;
468 int seconds, useconds, data_chars;
470 /* Get wtap external structure for this wtap */
471 dct2000_file_externals_t *file_externals =
472 (dct2000_file_externals_t*)wth->priv;
477 /* Seek to beginning of packet */
478 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
482 /* Re-read whole line (this really should succeed) */
483 if (!read_new_line(wth->random_fh, &offset, &length, linebuff,
484 sizeof linebuff, err, err_info)) {
488 /* Try to parse this line again (should succeed as re-reading...) */
489 if (parse_line(linebuff, length, &seconds, &useconds,
490 &before_time_offset, &after_time_offset,
492 &data_chars, &direction, &encap, &is_comment, &is_sprint,
494 context_name, &context_port,
495 protocol_name, variant_name, outhdr_name)) {
496 char timestamp_string[MAX_TIMESTAMP_LEN+1];
498 write_timestamp_string(timestamp_string, seconds, useconds/100);
500 process_parsed_line(wth, file_externals,
502 linebuff, dollar_offset,
503 seconds, useconds, timestamp_string,
505 context_name, context_port,
506 protocol_name, variant_name,
507 outhdr_name, aal_header_chars,
508 is_comment, data_chars);
514 /* If get here, must have failed */
516 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
517 "line at position %" G_GINT64_MODIFIER "d",
523 /***************************************************************************/
524 /* Free dct2000-specific capture info from file that was open for reading */
525 /***************************************************************************/
527 catapult_dct2000_close(wtap *wth)
529 /* Get externals for this file */
530 dct2000_file_externals_t *file_externals =
531 (dct2000_file_externals_t*)wth->priv;
533 /* Free up its line prefix values */
534 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
535 free_line_prefix_info, NULL);
536 /* Free up its line prefix table */
537 g_hash_table_destroy(file_externals->packet_prefix_table);
543 /***************************/
545 /***************************/
548 gboolean first_packet_written;
552 /*****************************************************/
553 /* The file that we are writing to has been opened. */
554 /* Set other dump callbacks. */
555 /*****************************************************/
557 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
559 /* Fill in other dump callbacks */
560 wdh->subtype_write = catapult_dct2000_dump;
565 /*********************************************************/
566 /* Respond to queries about which encap types we support */
568 /*********************************************************/
570 catapult_dct2000_dump_can_write_encap(int encap)
573 case WTAP_ENCAP_CATAPULT_DCT2000:
574 /* We support this */
578 /* But don't write to any other formats... */
579 return WTAP_ERR_UNSUPPORTED_ENCAP;
584 /*****************************************/
585 /* Write a single packet out to the file */
586 /*****************************************/
589 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
590 const guint8 *pd, int *err)
592 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
594 line_prefix_info_t *prefix = NULL;
595 gchar time_string[16];
597 gboolean is_sprint = FALSE;
598 dct2000_dump_t *dct2000;
599 int consecutive_slashes=0;
602 /******************************************************/
603 /* Get the file_externals structure for this file */
604 /* Find wtap external structure for this wtap */
605 dct2000_file_externals_t *file_externals =
606 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
608 /* We can only write packet records. */
609 if (phdr->rec_type != REC_TYPE_PACKET) {
610 *err = WTAP_ERR_REC_TYPE_UNSUPPORTED;
614 dct2000 = (dct2000_dump_t *)wdh->priv;
615 if (dct2000 == NULL) {
617 /* Write out saved first line */
618 if (!wtap_dump_file_write(wdh, file_externals->firstline,
619 file_externals->firstline_length, err)) {
622 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
626 /* Also write out saved second line with timestamp corresponding to the
627 opening time of the log.
629 if (!wtap_dump_file_write(wdh, file_externals->secondline,
630 file_externals->secondline_length, err)) {
633 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
637 /* Allocate the dct2000-specific dump structure */
638 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
639 wdh->priv = (void *)dct2000;
641 /* Copy time of beginning of file */
642 dct2000->start_time.secs = file_externals->start_secs;
643 dct2000->start_time.nsecs =
644 (file_externals->start_usecs * 1000);
646 /* Set flag so don't write header out again */
647 dct2000->first_packet_written = TRUE;
651 /******************************************************************/
652 /* Write out this packet's prefix, including calculated timestamp */
654 /* Look up line data prefix using stored offset */
655 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
656 (const void*)&(pseudo_header->dct2000.seek_off));
658 /* Write out text before timestamp */
659 if (!wtap_dump_file_write(wdh, prefix->before_time,
660 strlen(prefix->before_time), err)) {
664 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
665 /* This is much faster than strstr() for "/////" */
666 p_c = prefix->before_time;
667 while (p_c && (*p_c != '/')) {
670 while (p_c && (*p_c == '/')) {
671 consecutive_slashes++;
674 is_comment = (consecutive_slashes == 5);
676 /* Calculate time of this packet to write, relative to start of dump */
677 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
678 write_timestamp_string(time_string,
679 (int)(phdr->ts.secs - dct2000->start_time.secs),
680 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
683 write_timestamp_string(time_string,
684 (int)(phdr->ts.secs - dct2000->start_time.secs-1),
685 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
688 /* Write out the calculated timestamp */
689 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
693 /* Write out text between timestamp and start of hex data */
694 if (prefix->after_time == NULL) {
695 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
700 if (!wtap_dump_file_write(wdh, prefix->after_time,
701 strlen(prefix->after_time), err)) {
707 /****************************************************************/
708 /* Need to skip stub header at start of pd before we reach data */
711 for (n=0; pd[n] != '\0'; n++);
714 /* Context port number */
718 for (; pd[n] != '\0'; n++);
723 is_sprint = strcmp((const char *)pd+n, "sprint") == 0;
725 for (; pd[n] != '\0'; n++);
728 /* Variant number (as string) */
729 for (; pd[n] != '\0'; n++);
732 /* Outhdr (as string) */
733 for (; pd[n] != '\0'; n++);
736 /* Direction & encap */
740 /**************************************/
741 /* Remainder is encapsulated protocol */
742 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
747 /* Each binary byte is written out as 2 hex string chars */
748 for (; n < phdr->len; n++) {
750 c[0] = char_from_hex((guint8)(pd[n] >> 4));
751 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
753 /* Write both hex chars of byte together */
754 if (!wtap_dump_file_write(wdh, c, 2, err)) {
760 for (; n < phdr->len; n++) {
764 /* Write both hex chars of byte together */
765 if (!wtap_dump_file_write(wdh, c, 1, err)) {
772 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
780 /****************************/
781 /* Private helper functions */
782 /****************************/
784 /**********************************************************************/
785 /* Read a new line from the file, starting at offset. */
786 /* - writes data to its argument linebuff */
787 /* - on return 'offset' will point to the next position to read from */
788 /* - return TRUE if this read is successful */
789 /**********************************************************************/
791 read_new_line(FILE_T fh, gint64 *offset, gint *length,
792 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
795 gint64 pos_before = file_tell(fh);
797 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
798 /* No characters found, or error */
799 *err = file_error(fh, err_info);
803 /* Set length (avoiding strlen()) and offset.. */
804 *length = (gint)(file_tell(fh) - pos_before);
805 *offset = *offset + *length;
807 /* ...but don't want to include newline in line length */
808 if (*length > 0 && linebuff[*length-1] == '\n') {
809 linebuff[*length-1] = '\0';
810 *length = *length - 1;
812 /* Nor do we want '\r' (as will be written when log is created on windows) */
813 if (*length > 0 && linebuff[*length-1] == '\r') {
814 linebuff[*length-1] = '\0';
815 *length = *length - 1;
822 /**********************************************************************/
823 /* Parse a line from buffer, by identifying: */
824 /* - context, port and direction of packet */
826 /* - data position and length */
827 /* Return TRUE if this packet looks valid and can be displayed */
828 /**********************************************************************/
830 parse_line(gchar *linebuff, gint line_length,
831 gint *seconds, gint *useconds,
832 long *before_time_offset, long *after_time_offset,
833 long *data_offset, gint *data_chars,
834 packet_direction_t *direction,
835 int *encap, int *is_comment, int *is_sprint,
836 gchar *aal_header_chars,
837 gchar *context_name, guint8 *context_portp,
838 gchar *protocol_name, gchar *variant_name,
843 char port_number_string[MAX_PORT_DIGITS+1];
844 int variant_digits = 0;
846 int protocol_chars = 0;
847 int outhdr_chars = 0;
849 char seconds_buff[MAX_SECONDS_CHARS+1];
851 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
852 int subsecond_decimals_chars;
853 int skip_first_byte = FALSE;
854 gboolean atm_header_present = FALSE;
859 /* Read context name until find '.' */
860 for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
861 if (linebuff[n] == '/') {
862 context_name[n] = '\0';
864 /* If not a comment (/////), not a valid line */
865 if (strncmp(linebuff+n, "/////", 5) != 0) {
869 /* There is no variant, outhdr, etc. Set protocol to be a comment */
870 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
874 if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
877 context_name[n] = linebuff[n];
879 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
883 /* Reset strings (that won't be set by comments) */
884 variant_name[0] = '\0';
885 outhdr_name[0] = '\0';
886 port_number_string[0] = '\0';
888 if (!(*is_comment)) {
889 /* '.' must follow context name */
890 if (linebuff[n] != '.') {
893 context_name[n] = '\0';
897 /* Now read port number */
898 for (port_digits = 0;
899 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
900 n++, port_digits++) {
902 if (!isdigit((guchar)linebuff[n])) {
905 port_number_string[port_digits] = linebuff[n];
907 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
911 /* Slash char must follow port number */
912 if (linebuff[n] != '/')
916 port_number_string[port_digits] = '\0';
917 if (port_digits == 1) {
918 *context_portp = port_number_string[0] - '0';
921 *context_portp = atoi(port_number_string);
926 /* Now for the protocol name */
927 for (protocol_chars = 0;
928 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
929 n++, protocol_chars++) {
931 if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_') {
934 protocol_name[protocol_chars] = linebuff[n];
936 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
937 /* If doesn't fit, fail rather than truncate */
940 protocol_name[protocol_chars] = '\0';
942 /* Slash char must follow protocol name */
943 if (linebuff[n] != '/') {
950 /* Following the / is the variant number. No digits indicate 1 */
951 for (variant_digits = 0;
952 (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
953 n++, variant_digits++) {
955 if (!isdigit((guchar)linebuff[n])) {
958 variant_name[variant_digits] = linebuff[n];
960 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
964 if (variant_digits > 0) {
965 variant_name[variant_digits] = '\0';
966 if (variant_digits == 1) {
967 variant = variant_name[0] - '0';
970 variant = atoi(variant_name);
974 variant_name[0] = '1';
975 variant_name[1] = '\0';
979 /* Outheader values may follow */
980 outhdr_name[0] = '\0';
981 if (linebuff[n] == ',') {
985 for (outhdr_chars = 0;
986 (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
987 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
988 n++, outhdr_chars++) {
990 if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ',')) {
993 outhdr_name[outhdr_chars] = linebuff[n];
995 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
998 /* Terminate (possibly empty) string */
999 outhdr_name[outhdr_chars] = '\0';
1004 /******************************************************************/
1005 /* Now check whether we know how to use a packet of this protocol */
1007 if ((strcmp(protocol_name, "ip") == 0) ||
1008 (strcmp(protocol_name, "sctp") == 0) ||
1009 (strcmp(protocol_name, "gre") == 0) ||
1010 (strcmp(protocol_name, "mipv6") == 0) ||
1011 (strcmp(protocol_name, "igmp") == 0)) {
1013 *encap = WTAP_ENCAP_RAW_IP;
1017 /* FP may be carried over ATM, which has separate atm header to parse */
1018 if ((strcmp(protocol_name, "fp") == 0) ||
1019 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1021 if ((variant > 256) && (variant % 256 == 3)) {
1022 /* FP over udp is contained in IPPrim... */
1026 /* FP over AAL0 or AAL2 */
1027 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1028 atm_header_present = TRUE;
1031 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1032 /* FP (IuR) over AAL2 */
1033 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1034 atm_header_present = TRUE;
1038 if (strcmp(protocol_name, "ppp") == 0) {
1039 *encap = WTAP_ENCAP_PPP;
1042 if (strcmp(protocol_name, "isdn_l3") == 0) {
1043 /* TODO: find out what this byte means... */
1044 skip_first_byte = TRUE;
1045 *encap = WTAP_ENCAP_ISDN;
1048 if (strcmp(protocol_name, "isdn_l2") == 0) {
1049 *encap = WTAP_ENCAP_ISDN;
1052 if (strcmp(protocol_name, "ethernet") == 0) {
1053 *encap = WTAP_ENCAP_ETHERNET;
1056 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1057 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1059 *encap = DCT2000_ENCAP_SSCOP;
1062 if (strcmp(protocol_name, "frelay_l2") == 0) {
1063 *encap = WTAP_ENCAP_FRELAY;
1066 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1067 *encap = DCT2000_ENCAP_MTP2;
1070 if ((strcmp(protocol_name, "nbap") == 0) ||
1071 (strcmp(protocol_name, "nbap_r4") == 0) ||
1072 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1074 /* The entire message in these cases is nbap, so use an encap value. */
1075 *encap = DCT2000_ENCAP_NBAP;
1078 /* Not a supported board port protocol/encap, but can show as raw data or
1079 in some cases find protocol embedded inside primitive */
1080 *encap = DCT2000_ENCAP_UNHANDLED;
1084 /* Find separate ATM header if necessary */
1085 if (atm_header_present) {
1086 int header_chars_seen = 0;
1088 /* Scan ahead to the next $ */
1089 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1092 if (n+1 >= line_length) {
1096 /* Read consecutive hex chars into atm header buffer */
1098 ((n < line_length) &&
1099 (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1100 (header_chars_seen < AAL_HEADER_CHARS));
1101 n++, header_chars_seen++) {
1103 aal_header_chars[header_chars_seen] = linebuff[n];
1104 /* Next 6 characters after '9' are mapped to a->f */
1105 if (!isdigit((guchar)linebuff[n])) {
1106 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1110 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1118 /* If there is a number, skip all info to next '/'.
1119 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1120 and display dct2000 dissector... */
1121 if (isdigit((guchar)linebuff[n])) {
1122 while ((n+1 < line_length) && linebuff[n] != '/') {
1128 while ((n+1 < line_length) && linebuff[n] == '/') {
1132 /* Skip a space that may happen here */
1133 if ((n+1 < line_length) && linebuff[n] == ' ') {
1137 /* Next character gives direction of message (must be 's' or 'r') */
1138 if (!(*is_comment)) {
1139 if (linebuff[n] == 's') {
1143 if (linebuff[n] == 'r') {
1144 *direction = received;
1157 /*********************************************************************/
1158 /* Find and read the timestamp */
1160 /* Now scan to the next digit, which should be the start of the timestamp */
1161 /* This will involve skipping " tm " */
1163 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1164 if (n >= line_length) {
1168 for (; (n < line_length) && !isdigit((guchar)linebuff[n]); n++);
1169 if (n >= line_length) {
1173 *before_time_offset = n;
1176 for (seconds_chars = 0;
1177 (linebuff[n] != '.') &&
1178 (seconds_chars <= MAX_SECONDS_CHARS) &&
1180 n++, seconds_chars++) {
1182 if (!isdigit((guchar)linebuff[n])) {
1183 /* Found a non-digit before decimal point. Fail */
1186 seconds_buff[seconds_chars] = linebuff[n];
1188 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1189 /* Didn't fit in buffer. Fail rather than use truncated */
1193 /* Convert found value into number */
1194 seconds_buff[seconds_chars] = '\0';
1195 *seconds = atoi(seconds_buff);
1197 /* The decimal point must follow the last of the seconds digits */
1198 if (linebuff[n] != '.') {
1204 /* Subsecond decimal digits (expect 4-digit accuracy) */
1205 for (subsecond_decimals_chars = 0;
1206 (linebuff[n] != ' ') &&
1207 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1209 n++, subsecond_decimals_chars++) {
1211 if (!isdigit((guchar)linebuff[n])) {
1214 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1216 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1217 /* More numbers than expected - give up */
1220 /* Convert found value into microseconds */
1221 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1222 *useconds = atoi(subsecond_decimals_buff) * 100;
1224 /* Space character must follow end of timestamp */
1225 if (linebuff[n] != ' ') {
1229 *after_time_offset = n++;
1231 /* If we have a string message, it could either be a comment (with '$') or
1232 a sprint line (no '$') */
1234 if (strncmp(linebuff+n, "l $", 3) != 0) {
1236 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1240 if (!(*is_sprint)) {
1241 /* Now skip ahead to find start of data (marked by '$') */
1242 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1243 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1250 /* Set offset to data start within line */
1253 /* Set number of chars that comprise the hex string protocol data */
1254 *data_chars = line_length - n;
1256 /* May need to skip first byte (2 hex string chars) */
1257 if (skip_first_byte) {
1265 /***********************************/
1266 /* Process results of parse_line() */
1267 /***********************************/
1269 process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
1270 struct wtap_pkthdr *phdr,
1271 Buffer *buf, gint64 file_offset,
1272 char *linebuff, long dollar_offset,
1273 int seconds, int useconds, gchar *timestamp_string,
1274 packet_direction_t direction, int encap,
1275 gchar *context_name, guint8 context_port,
1276 gchar *protocol_name, gchar *variant_name,
1277 gchar *outhdr_name, gchar *aal_header_chars,
1278 gboolean is_comment, int data_chars)
1281 int stub_offset = 0;
1283 guint8 *frame_buffer;
1285 phdr->rec_type = REC_TYPE_PACKET;
1286 phdr->presence_flags = WTAP_HAS_TS;
1288 /* Make sure all packets go to Catapult DCT2000 dissector */
1289 phdr->pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1291 /* Fill in timestamp (capture base + packet offset) */
1292 phdr->ts.secs = file_externals->start_secs + seconds;
1293 if ((file_externals->start_usecs + useconds) >= 1000000) {
1297 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1299 /*****************************/
1300 /* Get the data buffer ready */
1301 ws_buffer_assure_space(buf,
1302 strlen(context_name)+1 + /* Context name */
1304 strlen(timestamp_string)+1 + /* timestamp */
1305 strlen(variant_name)+1 + /* variant */
1306 strlen(outhdr_name)+1 + /* outhdr */
1307 strlen(protocol_name)+1 + /* Protocol name */
1310 (is_comment ? data_chars : (data_chars/2)));
1311 frame_buffer = ws_buffer_start_ptr(buf);
1313 /******************************************/
1314 /* Write the stub info to the data buffer */
1317 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1318 stub_offset += (int)(length + 1);
1320 /* Context port number */
1321 frame_buffer[stub_offset] = context_port;
1324 /* Timestamp within file */
1325 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1326 stub_offset += (int)(length + 1);
1329 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1330 stub_offset += (int)(length + 1);
1332 /* Protocol variant number (as string) */
1333 length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1334 stub_offset += (int)(length + 1);
1337 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1338 stub_offset += (int)(length + 1);
1341 frame_buffer[stub_offset] = direction;
1345 frame_buffer[stub_offset] = (guint8)encap;
1348 /* Binary data length is half bytestring length + stub header */
1349 phdr->len = stub_offset + (is_comment ? data_chars : (data_chars/2));
1350 phdr->caplen = stub_offset + (is_comment ? data_chars : (data_chars/2));
1353 /***********************************************************/
1354 /* Copy packet data into buffer, converting from ascii hex */
1355 for (n=0; n < data_chars; n+=2) {
1356 frame_buffer[stub_offset + n/2] =
1357 hex_byte_from_chars(linebuff+dollar_offset+n);
1361 /***********************************************************/
1362 /* Copy packet data into buffer, just copying ascii chars */
1363 for (n=0; n < data_chars; n++) {
1364 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1368 /*****************************************/
1369 /* Set packet pseudo-header if necessary */
1370 phdr->pseudo_header.dct2000.seek_off = file_offset;
1371 phdr->pseudo_header.dct2000.wth = wth;
1374 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1375 set_aal_info(&phdr->pseudo_header, direction, aal_header_chars);
1377 case WTAP_ENCAP_ISDN:
1378 set_isdn_info(&phdr->pseudo_header, direction);
1380 case WTAP_ENCAP_PPP:
1381 set_ppp_info(&phdr->pseudo_header, direction);
1385 /* Other supported types don't need to set anything here... */
1390 /*********************************************/
1391 /* Fill in atm pseudo-header with known info */
1392 /*********************************************/
1394 set_aal_info(union wtap_pseudo_header *pseudo_header,
1395 packet_direction_t direction,
1396 gchar *aal_header_chars)
1398 /* 'aal_head_chars' has this format (for AAL2 at least):
1399 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1400 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1401 Channel Identifier (8 bits) | ...
1404 /* Indicate that this is a reassembled PDU */
1405 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1407 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1408 TODO: Can we infer the correct value here?
1409 Meanwhile, just use the direction to make them distinguishable...
1411 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1413 /* Assume always AAL2 for FP */
1414 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1416 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1417 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1419 /* vpi is 8 bits (2nd & 3rd nibble) */
1420 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1421 hex_byte_from_chars(aal_header_chars+1);
1423 /* vci is next 16 bits */
1424 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1425 ((hex_from_char(aal_header_chars[3]) << 12) |
1426 (hex_from_char(aal_header_chars[4]) << 8) |
1427 (hex_from_char(aal_header_chars[5]) << 4) |
1428 hex_from_char(aal_header_chars[6]));
1430 /* 0 means we don't know how many cells the frame comprises. */
1431 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1433 /* cid is usually last byte. Unless last char is not hex digit, in which
1434 case cid is derived from last char in ascii */
1435 if (isalnum((guchar)aal_header_chars[11])) {
1436 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1437 hex_byte_from_chars(aal_header_chars+10);
1440 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1441 (int)aal_header_chars[11] - '0';
1446 /**********************************************/
1447 /* Fill in isdn pseudo-header with known info */
1448 /**********************************************/
1450 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1451 packet_direction_t direction)
1453 /* This field is used to set the 'Source' and 'Destination' columns to
1454 'User' or 'Network'. If we assume that we're simulating the network,
1455 treat Received messages as being destined for the network.
1457 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1459 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1460 everything else would be treated as a B-channel
1462 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1466 /*********************************************/
1467 /* Fill in ppp pseudo-header with known info */
1468 /*********************************************/
1470 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1471 packet_direction_t direction)
1473 /* Set direction. */
1474 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1478 /********************************************************/
1479 /* Return hex nibble equivalent of hex string character */
1480 /********************************************************/
1482 hex_from_char(gchar c)
1484 if ((c >= '0') && (c <= '9')) {
1488 if ((c >= 'a') && (c <= 'f')) {
1489 return 0x0a + (c - 'a');
1492 /* Not a valid hex string character */
1498 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1499 static guint8 s_tableValues[255][255];
1501 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1502 static void prepare_hex_byte_from_chars_table(void)
1504 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1505 'a', 'b', 'c', 'd', 'e', 'f' };
1508 for (i=0; i < 16; i++) {
1509 for (j=0; j < 16; j++) {
1510 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1515 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1516 static guint8 hex_byte_from_chars(gchar *c)
1518 /* Return value from quick table lookup */
1519 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1524 /********************************************************/
1525 /* Return character corresponding to hex nibble value */
1526 /********************************************************/
1528 char_from_hex(guint8 hex)
1530 static const char hex_lookup[16] =
1531 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1537 return hex_lookup[hex];
1540 /***********************************************/
1541 /* Equality test for packet prefix hash tables */
1542 /***********************************************/
1544 packet_offset_equal(gconstpointer v, gconstpointer v2)
1546 /* Dereferenced pointers must have same gint64 offset value */
1547 return (*(const gint64*)v == *(const gint64*)v2);
1551 /********************************************/
1552 /* Hash function for packet-prefix hash table */
1553 /********************************************/
1555 packet_offset_hash_func(gconstpointer v)
1557 /* Use low-order bits of gint64 offset value */
1558 return (guint)(*(const gint64*)v);
1562 /************************************************************************/
1563 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1564 /* Set secs and usecs as output */
1565 /* Return FALSE if no valid time can be read */
1566 /************************************************************************/
1568 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1572 #define MAX_MONTH_LETTERS 9
1573 char month[MAX_MONTH_LETTERS+1];
1575 int day, year, hour, minute, second;
1578 /* If line longer than expected, file is probably not correctly formatted */
1579 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1583 /**************************************************************/
1584 /* First is month. Read until get a space following the month */
1585 for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++) {
1586 month[n] = linebuff[n];
1590 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1591 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1592 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1593 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1594 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1595 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1596 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1597 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1598 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1599 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1600 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1601 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1603 /* Give up if not found a properly-formatted date */
1606 /* Skip space char */
1609 /********************************************************/
1610 /* Scan for remaining numerical fields */
1611 scan_found = sscanf(linebuff+n, "%2d, %4d %2d:%2d:%2d.%4u",
1612 &day, &year, &hour, &minute, &second, usecs);
1613 if (scan_found != 6) {
1614 /* Give up if not all found */
1618 /******************************************************/
1619 /* Fill in remaining fields and return it in a time_t */
1620 tm.tm_year = year - 1900;
1625 tm.tm_isdst = -1; /* daylight saving time info not known */
1627 /* Get seconds from this time */
1628 *secs = mktime(&tm);
1630 /* Multiply 4 digits given to get micro-seconds */
1631 *usecs = *usecs * 100;
1636 /* Free the data allocated inside a line_prefix_info_t */
1638 free_line_prefix_info(gpointer key, gpointer value,
1639 gpointer user_data _U_)
1641 line_prefix_info_t *info = (line_prefix_info_t*)value;
1643 /* Free the 64-bit key value */
1646 /* Free the strings inside */
1647 g_free(info->before_time);
1648 if (info->after_time) {
1649 g_free(info->after_time);
1652 /* And the structure itself */
1655 /* Item will always be removed from table */