Check in trivial changes before generating a big diff...
[metze/wireshark/wip.git] / wiretap / catapult_dct2000.c
1 /* catapult_dct2000.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 #include "wtap-int.h"
32 #include "file_wrappers.h"
33 #include "buffer.h"
34
35 #include "catapult_dct2000.h"
36
37 #define MAX_FIRST_LINE_LENGTH      200
38 #define MAX_TIMESTAMP_LINE_LENGTH  100
39 #define MAX_LINE_LENGTH            32000
40 #define MAX_SECONDS_CHARS          16
41 #define MAX_SUBSECOND_DECIMALS     4
42 #define MAX_CONTEXT_NAME           64
43 #define MAX_PROTOCOL_NAME          64
44 #define MAX_PORT_DIGITS            2
45 #define MAX_VARIANT_DIGITS         32
46 #define MAX_OUTHDR_NAME            64
47 #define AAL_HEADER_CHARS           12
48
49 /* TODO:
50    - support for FP over AAL0
51    - support for IuR interface FP
52    - support for x.25?
53 */
54
55 /* 's' or 'r' of a packet as read from .out file */
56 typedef enum packet_direction_t
57 {
58     sent,
59     received
60 } packet_direction_t;
61
62
63 typedef struct
64 {
65     gchar *before_time;
66     gchar *after_time; /* If NULL assume " l " */
67 } line_prefix_info_t;
68
69 /*******************************************************************/
70 /* Information stored external to a file (wtap) needed for dumping */
71 typedef struct dct2000_file_externals
72 {
73     /* Buffer to hold first line, including magic and format number */
74     gchar firstline[MAX_FIRST_LINE_LENGTH];
75     gint  firstline_length;
76
77     /* Buffer to hold second line with formatted file creation data/time */
78     gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
79     gint  secondline_length;
80
81     /* Hash table to store text prefix data part of displayed packets.
82        Records (file offset -> pre-data-prefix-string)
83        N.B. This is only needed for dumping
84     */
85     GHashTable *packet_prefix_table;
86 } dct2000_file_externals_t;
87
88 /* This global table maps wtap -> file_external structs */
89 static GHashTable *file_externals_table = NULL;
90
91
92 /***********************************************************/
93 /* Transient data used for parsing                         */
94
95 /* Buffer to hold a single text line read from the file */
96 static gchar linebuff[MAX_LINE_LENGTH];
97
98 /* Buffer for separate AAL header */
99 static gchar aal_header_chars[AAL_HEADER_CHARS];
100
101 /* 'Magic number' at start of Catapult DCT2000 .out files. */
102 static const gchar catapult_dct2000_magic[] = "Session Transcript";
103
104 /* Context name + port that the packet was captured at */
105 static gchar context_name[MAX_CONTEXT_NAME];
106 static guint8 context_port;
107
108 /* The DCT2000 protocol name of the packet, plus variant number */
109 static gchar protocol_name[MAX_PROTOCOL_NAME+1];
110 static gchar variant_name[MAX_VARIANT_DIGITS+1];
111 static gchar outhdr_name[MAX_OUTHDR_NAME+1];
112
113
114 /************************************************************/
115 /* Functions called from wiretap                            */
116 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
117                                       gint64 *data_offset);
118 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
119                                            union wtap_pseudo_header *pseudo_header,
120                                            guchar *pd, int length,
121                                            int *err, gchar **err_info);
122 static void catapult_dct2000_close(wtap *wth);
123
124 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
125                                       const union wtap_pseudo_header *pseudo_header,
126                                       const guchar *pd, int *err);
127 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh, int *err);
128
129
130 /************************************************************/
131 /* Private helper functions                                 */
132 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length);
133 static gboolean parse_line(gint line_length, gint *seconds, gint *useconds,
134                            long *before_time_offset, long *after_time_offset,
135                            long *data_offset,
136                            gint *data_chars,
137                            packet_direction_t *direction,
138                            int *encap);
139 static int write_stub_header(guchar *frame_buffer, char *timestamp_string,
140                       packet_direction_t direction, int encap);
141 static guchar hex_from_char(gchar c);
142 static gchar char_from_hex(guchar hex);
143
144 static void set_pseudo_header_info(wtap *wth,
145                                    int pkt_encap,
146                                    gint64 file_offset,
147                                    union wtap_pseudo_header *pseudo_header,
148                                    packet_direction_t direction);
149 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
150                          packet_direction_t direction);
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);
155
156 static gint wth_equal(gconstpointer v, gconstpointer v2);
157 static guint wth_hash_func(gconstpointer v);
158 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
159 static guint packet_offset_hash_func(gconstpointer v);
160
161 static gboolean get_file_time_stamp(time_t *secs, guint32 *usecs);
162 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
163
164
165
166 /********************************************/
167 /* Open file                                */
168 /********************************************/
169 int catapult_dct2000_open(wtap *wth, int *err, gchar **err_info _U_)
170 {
171     gint64  offset = 0;
172     time_t  timestamp;
173     guint32 usecs;
174     gint firstline_length;
175     dct2000_file_externals_t *file_externals;
176
177     /* Clear errno before reading from the file */
178     errno = 0;
179
180
181     /*********************************************************************/
182     /* Need entry in file_externals table                                */
183
184     /* Create file externals table if it doesn't yet exist */
185     if (file_externals_table == NULL)
186     {
187         file_externals_table = g_hash_table_new(wth_hash_func, wth_equal);
188     }
189
190
191     /********************************************************************/
192     /* First line needs to contain at least as many characters as magic */
193
194     read_new_line(wth->fh, &offset, &firstline_length);
195     if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
196         firstline_length >= MAX_FIRST_LINE_LENGTH)
197     {
198         return 0;
199     }
200
201     /* This file is not for us if it doesn't match our signature */
202     if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0)
203     {
204         return 0;
205     }
206
207
208     /* Allocate a new file_externals structure */
209     file_externals = g_malloc(sizeof(dct2000_file_externals_t));
210     memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
211
212     /* Copy this first line into buffer so could write out later */
213     strncpy(file_externals->firstline, linebuff, firstline_length);
214     file_externals->firstline_length = firstline_length;
215
216
217     /***********************************************************/
218     /* Second line contains file timestamp                     */
219     /* Store this offset in in wth->capture->catapult_dct2000  */
220
221     read_new_line(wth->fh, &offset, &(file_externals->secondline_length));
222     if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
223         (!get_file_time_stamp(&timestamp, &usecs)))
224     {
225         /* Give up if file time line wasn't valid */
226         g_free(file_externals);
227         return 0;
228     }
229
230     wth->capture.catapult_dct2000 = g_malloc(sizeof(catapult_dct2000_t));
231     wth->capture.catapult_dct2000->start_secs = timestamp;
232     wth->capture.catapult_dct2000->start_usecs = usecs;
233
234     /* Copy this second line into buffer so could write out later */
235     strncpy(file_externals->secondline, linebuff, file_externals->secondline_length);
236
237
238     /************************************************************/
239     /* File is for us. Fill in details so packets can be read   */
240
241     /* Set our file type */
242     wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
243
244     /* Use our own encapsulation to send all packets to our stub dissector */
245     wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
246
247     /* Callbacks for reading operations */
248     wth->subtype_read = catapult_dct2000_read;
249     wth->subtype_seek_read = catapult_dct2000_seek_read;
250     wth->subtype_close = catapult_dct2000_close;
251
252     /* Choose microseconds (have 4 decimal places...) */
253     wth->tsprecision = WTAP_FILE_TSPREC_USEC;
254
255
256     /***************************************************************/
257     /* Initialise packet_prefix_table (index is offset into file)  */
258     file_externals->packet_prefix_table =
259         g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
260
261     /* Add file_externals for this wtap into the global table */
262     g_hash_table_insert(file_externals_table,
263                         (void*)wth, (void*)file_externals);
264
265     *err = errno;
266     return 1;
267 }
268
269
270 /**************************************************/
271 /* Read function.                                 */
272 /* Look for and read the next usable packet       */
273 /* - return TRUE and details if found             */
274 /**************************************************/
275 gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info _U_,
276                                gint64 *data_offset)
277 {
278     gint64 offset = wth->data_offset;
279     long dollar_offset, before_time_offset, after_time_offset;
280     packet_direction_t direction;
281     int encap;
282
283     /* Find wtap external structure for this wtap */
284     dct2000_file_externals_t *file_externals =
285         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table, wth);
286
287     /* There *has* to be an entry for this wth */
288     if (!file_externals)
289     {
290         return FALSE;
291     }
292
293     /* Search for a line containing a usable message */
294     while (1)
295     {
296         int line_length, seconds, useconds, data_chars;
297         gint64 this_offset = offset;
298
299         /* Are looking for first packet after 2nd line */
300         if (wth->data_offset == 0)
301         {
302             this_offset += (file_externals->firstline_length+1+
303                             file_externals->secondline_length+1);
304         }
305
306         /* Clear errno before reading from the file */
307         errno = 0;
308
309         /* Read a new line from file into linebuff */
310         if (read_new_line(wth->fh, &offset, &line_length) == FALSE)
311         {
312             /* Get out when no more lines to be read */
313             break;
314         }
315
316         /* Try to parse the line as a message */
317         if (parse_line(line_length, &seconds, &useconds,
318                        &before_time_offset, &after_time_offset,
319                        &dollar_offset,
320                        &data_chars, &direction, &encap))
321         {
322             guchar *frame_buffer;
323             int n;
324             int stub_offset = 0;
325             line_prefix_info_t *line_prefix_info;
326             char timestamp_string[32];
327             gint64 *pkey = NULL;
328
329             sprintf(timestamp_string, "%d.%04d", seconds, useconds/100);
330
331             /* All packets go to Catapult DCT2000 stub dissector */
332             wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
333
334             /* Set data_offset to the beginning of the line we're returning.
335                This will be the seek_off parameter when this frame is re-read.
336             */
337             *data_offset = this_offset;
338
339             /* This is the position in the file where the next _read() will be called from */
340             wth->data_offset = this_offset + line_length + 1;
341
342             /* Fill in timestamp (capture base + packet offset) */
343             wth->phdr.ts.secs = wth->capture.catapult_dct2000->start_secs + seconds;
344             if ((wth->capture.catapult_dct2000->start_usecs + useconds) >= 1000000)
345             {
346                 wth->phdr.ts.secs++;
347             }
348             wth->phdr.ts.nsecs =
349                 ((wth->capture.catapult_dct2000->start_usecs + useconds) % 1000000) *1000;
350
351             /* Get buffer pointer ready */
352             buffer_assure_space(wth->frame_buffer,
353                                 strlen(context_name)+1 +     /* Context name */
354                                 1 +                          /* port */
355                                 strlen(timestamp_string)+1 + /* timestamp */
356                                 strlen(variant_name)+1 +     /* variant */
357                                 strlen(outhdr_name)+1 +      /* outhdr */
358                                 strlen(protocol_name)+1 +    /* Protocol name */
359                                 1 +                          /* direction */
360                                 1 +                          /* encap */
361                                 (data_chars/2));
362             frame_buffer = buffer_start_ptr(wth->frame_buffer);
363
364
365             /*********************/
366             /* Write stub header */
367             stub_offset = write_stub_header(frame_buffer, timestamp_string,
368                                             direction, encap);
369
370             /* Binary data length is half bytestring length + stub header */
371             wth->phdr.len = data_chars/2 + stub_offset;
372             wth->phdr.caplen = data_chars/2 + stub_offset;
373
374
375             /*************************/
376             /* Copy data into buffer */
377             for (n=0; n <= data_chars; n+=2)
378             {
379                 frame_buffer[stub_offset + n/2] =
380                     (hex_from_char(linebuff[dollar_offset+n]) << 4) |
381                      hex_from_char(linebuff[dollar_offset+n+1]);
382             }
383
384             /* Store the packet prefix in the hash table */
385             line_prefix_info = g_malloc(sizeof(line_prefix_info_t));
386
387             /* Create and use buffer for contents before time */
388             line_prefix_info->before_time = g_malloc(before_time_offset+1);
389             strncpy(line_prefix_info->before_time, linebuff, before_time_offset);
390             line_prefix_info->before_time[before_time_offset] = '\0';
391
392             /* Create and use buffer for contents before time.
393                Do this only if it doesn't correspond to " l ", which is by far the most
394                common case. */
395             if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
396                 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0))
397             {
398                 line_prefix_info->after_time = NULL;
399             }
400             else
401             {
402                 line_prefix_info->after_time = g_malloc(dollar_offset - after_time_offset);
403                 strncpy(line_prefix_info->after_time, linebuff+after_time_offset,
404                         dollar_offset - after_time_offset);
405                 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
406             }
407
408             /* Add packet entry into table */
409             pkey = g_malloc(sizeof(pkey));
410             *pkey = this_offset;
411             g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
412
413             /* Set pseudo-header if necessary */
414             set_pseudo_header_info(wth, encap, this_offset, &wth->pseudo_header,
415                                    direction);
416
417             /* OK, we have packet details to return */
418             *err = errno;
419             return TRUE;
420         }
421     }
422
423     /* No packet details to return... */
424     *err = errno;
425     return FALSE;
426 }
427
428
429 /**************************************************/
430 /* Read & seek function.                          */
431 /**************************************************/
432 static gboolean
433 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
434                            union wtap_pseudo_header *pseudo_header, guchar *pd,
435                            int length, int *err, gchar **err_info)
436 {
437     gint64 offset = wth->data_offset;
438     long dollar_offset, before_time_offset, after_time_offset;
439     packet_direction_t direction;
440     int encap;
441     int seconds, useconds, data_chars;
442
443     /* Reset errno */
444     *err = errno = 0;
445
446     /* Seek to beginning of packet */
447     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
448     {
449         return FALSE;
450     }
451
452     /* Re-read whole line (this should succeed) */
453     if (read_new_line(wth->random_fh, &offset, &length) == FALSE)
454     {
455         return FALSE;
456     }
457
458     /* Try to parse this line again (should succeed as re-reading...) */
459     if (parse_line(length, &seconds, &useconds,
460                    &before_time_offset, &after_time_offset,
461                    &dollar_offset,
462                    &data_chars, &direction, &encap))
463     {
464         int n;
465         int stub_offset = 0;
466         char timestamp_string[32];
467         sprintf(timestamp_string, "%d.%04d", seconds, useconds/100);
468
469         /* Make sure all packets go to catapult dct2000 dissector */
470         wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
471
472
473         /*********************/
474         /* Write stub header */
475         stub_offset = write_stub_header((guchar*)pd, timestamp_string,
476                                         direction, encap);
477
478
479         /********************************/
480         /* Copy packet data into buffer */
481         for (n=0; n <= data_chars; n+=2)
482         {
483             pd[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
484                                      hex_from_char(linebuff[dollar_offset+n+1]);
485         }
486
487         /* Set packet pseudo-header if necessary */
488         set_pseudo_header_info(wth, encap, seek_off, pseudo_header, direction);
489
490         *err = errno = 0;
491         return TRUE;
492     }
493
494     /* If get here, must have failed */
495     *err = errno;
496     *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
497                                 "line at position %lld", seek_off);
498     return FALSE;
499 }
500
501
502 /******************************************/
503 /* Free dct2000-specific capture info     */
504 /******************************************/
505 void catapult_dct2000_close(wtap *wth)
506 {
507     /* Look up externals for this file */
508     dct2000_file_externals_t *file_externals =
509         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table, wth);
510
511     /* The entry *has* to be found */
512     if (!file_externals)
513     {
514         return;
515     }
516
517     /* Free up its line prefix values */
518     g_hash_table_foreach_remove(file_externals->packet_prefix_table,
519                                 free_line_prefix_info, NULL);
520     /* Free up its line prefix table */
521     g_hash_table_destroy(file_externals->packet_prefix_table);
522
523     /* And remove the externals entry from the global table */
524     g_hash_table_remove(file_externals_table, (void*)wth);
525
526     /* And free up file_externals itself */
527     g_free(file_externals);
528
529     /* Also free this capture info */
530     g_free(wth->capture.catapult_dct2000);
531 }
532
533
534
535
536 /***************************/
537 /* Dump functions          */
538 /***************************/
539
540 /*****************************************************/
541 /* The file that we are writing to has been opened.  */
542 /* Set other dump callbacks.                         */
543 /*****************************************************/
544 gboolean catapult_dct2000_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err _U_)
545 {
546     /* Fill in other dump callbacks */
547     wdh->subtype_write = catapult_dct2000_dump;
548     wdh->subtype_close = catapult_dct2000_dump_close;
549
550     return TRUE;
551 }
552
553 /*********************************************************/
554 /* Respond to queries about which encap types we support */
555 /* writing to.                                           */
556 /*********************************************************/
557 int catapult_dct2000_dump_can_write_encap(int encap)
558 {
559     switch (encap)
560     {
561         case WTAP_ENCAP_CATAPULT_DCT2000:
562             /* We support this */
563             return 0;
564         default:
565             return WTAP_ERR_UNSUPPORTED_ENCAP;
566     }
567 }
568
569
570 /*****************************************/
571 /* Write a single packet out to the file */
572 /*****************************************/
573 gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
574                                const union wtap_pseudo_header *pseudo_header,
575                                const guchar *pd, int *err _U_)
576 {
577     guint32 n;
578     line_prefix_info_t *prefix = NULL;
579     gchar time_string[16];
580
581     /******************************************************/
582     /* Look up the file_externals structure for this file */
583     /* Find wtap external structure for this wtap */
584     dct2000_file_externals_t *file_externals =
585         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table,
586                                                        pseudo_header->dct2000.wth);
587
588     if (wdh->dump.dct2000 == NULL)
589     {
590         /* Allocate the dct2000-specific dump structure */
591         wdh->dump.dct2000 = g_malloc(sizeof(catapult_dct2000_t));
592
593         /* Write out saved first line */
594         fwrite(file_externals->firstline, 1, file_externals->firstline_length, wdh->fh);
595         fwrite("\n", 1, 1, wdh->fh);
596
597         /* Also write out saved second line with timestamp corresponding to the
598            opening time of the log.
599         */
600         fwrite(file_externals->secondline, 1, file_externals->secondline_length, wdh->fh);
601         fwrite("\n", 1, 1, wdh->fh);
602
603         /* Allocate the dct2000-specific dump structure */
604         wdh->dump.dct2000 = g_malloc(sizeof(catapult_dct2000_t));
605
606         /* Copy time of beginning of file */
607         wdh->dump.dct2000->start_time.secs =
608             pseudo_header->dct2000.wth->capture.catapult_dct2000->start_secs;
609         wdh->dump.dct2000->start_time.nsecs =
610             (pseudo_header->dct2000.wth->capture.catapult_dct2000->start_usecs * 1000);
611
612         /* Set flag do don't write header out again */
613         wdh->dump.dct2000->first_packet_written = TRUE;
614     }
615
616
617     /******************************************************************/
618     /* Write out this packet's prefix, including calculated timestamp */
619
620     /* Look up line data prefix using stored offset */
621     prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
622                                                       (const void*)&(pseudo_header->dct2000.seek_off));
623
624     /* Write out text before timestamp */
625     fwrite(prefix->before_time, 1, strlen(prefix->before_time), wdh->fh);
626
627     /* Calculate time of this packet to write, relative to start of dump */
628     if (phdr->ts.nsecs >= wdh->dump.dct2000->start_time.nsecs)
629     {
630         g_snprintf(time_string, 16, "%ld.%04d",
631                  (long)(phdr->ts.secs - wdh->dump.dct2000->start_time.secs),
632                  (phdr->ts.nsecs - wdh->dump.dct2000->start_time.nsecs) / 100000);
633     }
634     else
635     {
636         g_snprintf(time_string, 16, "%ld.%04u",
637                  (long)(phdr->ts.secs - wdh->dump.dct2000->start_time.secs-1),
638                  ((1000000000 + (phdr->ts.nsecs / 100000)) - (wdh->dump.dct2000->start_time.nsecs / 100000)) % 10000);
639     }
640
641     /* Write out the calculated timestamp */
642     fwrite(time_string, 1, strlen(time_string), wdh->fh);
643
644     /* Write out text between timestamp and start of hex data */
645     if (prefix->after_time == NULL)
646     {
647         fwrite(" l ", 1, strlen(" l "), wdh->fh);
648     }
649     else
650     {
651         fwrite(prefix->after_time, 1, strlen(prefix->after_time), wdh->fh);
652     }
653
654
655     /****************************************************************/
656     /* Need to skip stub header at start of pd before we reach data */
657
658     /* Context name */
659     for (n=0; pd[n] != '\0'; n++);
660     n++;
661
662     /* Context port number */
663     n++;
664
665     /* Timestamp */
666     for (; pd[n] != '\0'; n++);
667     n++;
668
669     /* Protocol name */
670     for (; pd[n] != '\0'; n++);
671     n++;
672
673     /* Variant number (as string) */
674     for (; pd[n] != '\0'; n++);
675     n++;
676
677     /* Outhdr (as string) */
678     for (; pd[n] != '\0'; n++);
679     n++;
680
681     /* Direction & encap */
682     n += 2;
683
684
685     /**************************************/
686     /* Remainder is encapsulated protocol */
687     fwrite("$", 1, 1, wdh->fh);
688
689     /* Each binary byte is written out as 2 hex string chars */ 
690     for (; n < phdr->len; n++)
691     {
692         gchar c[2];
693         c[0] = char_from_hex((guchar)(pd[n] >> 4));
694         c[1] = char_from_hex((guchar)(pd[n] & 0x0f));
695
696         /* Write both hex chars of byte together */
697         fwrite(c, 1, 2, wdh->fh);
698     }
699
700     /* End the line */
701     fwrite("\n", 1, 1, wdh->fh);
702
703     return TRUE;
704 }
705
706
707 /******************************************************/
708 /* Close a file we've been writing to.                */
709 /******************************************************/
710 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh _U_, int *err _U_)
711 {
712     return TRUE;
713 }
714
715
716
717
718 /****************************/
719 /* Private helper functions */
720 /****************************/
721
722 /**********************************************************************/
723 /* Read a new line from the file, starting at offset.                 */
724 /* - writes data to static var linebuff                               */
725 /* - on return 'offset' will point to the next position to read from  */
726 /* - return TRUE if this read is successful                           */
727 /**********************************************************************/
728 gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length)
729 {
730     char *result;
731
732     /* Read in a line */
733     result = file_gets(linebuff, MAX_LINE_LENGTH, fh);
734     if (result == NULL)
735     {
736         /* No characters found */
737         return FALSE;
738     }
739
740     /* Set length and offset.. */
741     *length = strlen(linebuff);
742     *offset = *offset + *length;
743
744     /* ...but don't want to include newline in line length */
745     if (linebuff[*length-1] == '\n')
746     {
747         linebuff[*length-1] = '\0';
748         *length = *length - 1;
749     }
750
751     return TRUE;
752 }
753
754
755 /**********************************************************************/
756 /* Parse a line from buffer, by identifying:                          */
757 /* - context, port and direction of packet                            */
758 /* - timestamp                                                        */
759 /* - data position and length                                         */
760 /* Return TRUE if this packet looks valid and can be displayed        */
761 /**********************************************************************/
762 gboolean parse_line(gint line_length, gint *seconds, gint *useconds,
763                     long *before_time_offset, long *after_time_offset,
764                     long *data_offset, gint *data_chars,
765                     packet_direction_t *direction,
766                     int *encap)
767 {
768     int  n = 0;
769     int  port_digits = 0;
770     char port_number_string[MAX_PORT_DIGITS+1];
771     int  variant_digits = 0;
772     int  variant = 1;
773     int  protocol_chars = 0;
774     int  outhdr_chars = 0;
775
776     char seconds_buff[MAX_SECONDS_CHARS+1];
777     int  seconds_chars;
778     char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
779     int  subsecond_decimals_chars;
780
781     gboolean atm_header_present = FALSE;
782
783     /* Read context name until find '.' */
784     for (n=0; linebuff[n] != '.' && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++)
785     {
786         if (!isalnum(linebuff[n]) && (linebuff[n] != '_'))
787         {
788             return FALSE;
789         }
790         context_name[n] = linebuff[n];
791     }
792     if (n == MAX_CONTEXT_NAME || (n+1 >= line_length))
793     {
794         return FALSE;
795     }
796
797     /* '.' must follow context name */
798     if (linebuff[n] != '.')
799     {
800         return FALSE;
801     }
802     context_name[n] = '\0';
803     /* Skip it */
804     n++;
805
806
807     /* Now read port number */
808     for (port_digits = 0;
809          (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
810          n++, port_digits++)
811     {
812         if (!isdigit(linebuff[n]))
813         {
814             return FALSE;
815         }
816         port_number_string[port_digits] = linebuff[n];
817     }
818     if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length))
819     {
820         return FALSE;
821     }
822
823     /* Slash char must follow port number */
824     if (linebuff[n] != '/')
825     {
826         return FALSE;
827     }
828     port_number_string[port_digits] = '\0';
829     context_port = atoi(port_number_string);
830     /* Skip it */
831     n++;
832
833
834     /* Now for the protocol name */
835     for (protocol_chars = 0;
836          (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
837          n++, protocol_chars++)
838     {
839         if (!isalnum(linebuff[n]) && linebuff[n] != '_')
840         {
841             return FALSE;
842         }
843         protocol_name[protocol_chars] = linebuff[n];
844     }
845     if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length)
846     {
847         /* If doesn't fit, fail rather than truncate */
848         return FALSE;
849     }
850     protocol_name[protocol_chars] = '\0';
851
852     /* Slash char must follow protocol name */
853     if (linebuff[n] != '/')
854     {
855         return FALSE;
856     }
857     /* Skip it */
858     n++;
859
860
861     /* Following the / is the variant number.  No digits indicate 1 */
862     for (variant_digits = 0;
863          (isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
864          n++, variant_digits++)
865     {
866         if (!isdigit(linebuff[n]))
867         {
868             return FALSE;
869         }
870         variant_name[variant_digits] = linebuff[n];
871     }
872     if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length))
873     {
874         return FALSE;
875     }
876     if (variant_digits > 0)
877     {
878         variant_name[variant_digits] = '\0';
879         variant = atoi(variant_name);
880     }
881     else
882     {
883         strcpy(variant_name, "1");
884     }
885
886
887     /* Outheader values may follow */
888     outhdr_name[0] = '\0';
889     if (linebuff[n] == ',')
890     {
891         /* Skip , */
892         n++;
893
894         for (outhdr_chars = 0;
895              (isdigit(linebuff[n]) || linebuff[n] == ',') &&
896              (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
897              n++, outhdr_chars++)
898         {
899             if (!isdigit(linebuff[n]) && (linebuff[n] != ','))
900             {
901                 return FALSE;
902             }
903             outhdr_name[outhdr_chars] = linebuff[n];
904         }
905         if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length))
906         {
907             return FALSE;
908         }
909         /* Terminate (possibly empty) string */
910         outhdr_name[outhdr_chars] = '\0';
911     }
912
913
914
915
916     /******************************************************************/
917     /* Now check whether we know how to use a packet of this protocol */
918
919     if ((strcmp(protocol_name, "ip") == 0)  || (strcmp(protocol_name, "sctp") == 0))
920     {
921         *encap = WTAP_ENCAP_RAW_IP;
922     }
923     else
924
925     /* FP may be carried over ATM, which has separate atm header to parse */
926     if ((strcmp(protocol_name, "fp") == 0) ||
927         (strcmp(protocol_name, "fp_r4") == 0) ||
928         (strcmp(protocol_name, "fp_r5") == 0) ||
929         (strcmp(protocol_name, "fp_r6") == 0))
930     {
931         if ((variant > 256) && (variant % 256 == 3))
932         {
933             /* FP over udp is contained in IPPrim... */
934             *encap = 0;
935         }
936         else
937         {
938             /* FP over AAL0 or AAL2 */
939             *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
940             atm_header_present = TRUE;
941         }
942     }
943
944     else
945     if (strcmp(protocol_name, "ppp") == 0)
946     {
947         *encap = WTAP_ENCAP_PPP;
948     }
949     else
950     if (strcmp(protocol_name, "isdn_l3") == 0)
951     {
952         /* Despite the name, this does seem to correspond to L2... */
953         *encap = WTAP_ENCAP_ISDN;
954     }
955     else
956     if (strcmp(protocol_name, "ethernet") == 0)
957     {
958         *encap = WTAP_ENCAP_ETHERNET;
959     }
960     else
961     if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
962         (strcmp(protocol_name, "saaluni_sscop") == 0))
963     {
964         *encap = DCT2000_ENCAP_SSCOP;
965     }
966     else
967     if (strcmp(protocol_name, "frelay_l2") == 0)
968     {
969         *encap = WTAP_ENCAP_FRELAY;
970     }
971     else
972     if (strcmp(protocol_name, "ss7_mtp2") == 0)
973     {
974         *encap = DCT2000_ENCAP_MTP2;
975     }
976     else
977     if ((strcmp(protocol_name, "nbap") == 0) ||
978         (strcmp(protocol_name, "nbap_r4") == 0) ||
979         (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0))
980     {
981         /* The entire message in these cases is nbap, so use an encap value. */
982         *encap = DCT2000_ENCAP_NBAP;
983     }
984     else
985     {
986         /* Not a supported board port protocol/encap, but can show as raw data or
987            in some cases find protocol embedded inside primitive */
988         *encap = DCT2000_ENCAP_UNHANDLED;
989     }
990
991
992     /* Find separate ATM header if necessary */
993     if (atm_header_present)
994     {
995         int header_chars_seen = 0;
996
997         /* Scan ahead to the next $ */
998         for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
999         /* Skip it */
1000         n++;
1001         if (n+1 >= line_length)
1002         {
1003             return FALSE;
1004         }
1005
1006         /* Read consecutive hex chars into atm header buffer */
1007         for (;
1008              (isalnum(linebuff[n]) &&
1009               (n < line_length) &&
1010               (header_chars_seen < AAL_HEADER_CHARS));
1011              n++, header_chars_seen++)
1012         {
1013             aal_header_chars[header_chars_seen] = linebuff[n];
1014         }
1015
1016         /* Sometimes see strange encoding of cid in last (non-digit) character */
1017         if (header_chars_seen == (AAL_HEADER_CHARS-1))
1018         {
1019             aal_header_chars[AAL_HEADER_CHARS-1] = linebuff[n];
1020             header_chars_seen++;
1021         }
1022
1023         if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length)
1024         {
1025             return FALSE;
1026         }
1027     }
1028
1029
1030     /* Scan ahead to the next space */
1031     for (; (linebuff[n] != ' ') && (n+1 < line_length); n++);
1032     if (n+1 >= line_length)
1033     {
1034         return FALSE;
1035     }
1036     /* Skip it */
1037     n++;
1038
1039     /* Next character gives direction of message (must be 's' or 'r') */
1040     if (linebuff[n] == 's')
1041     {
1042         *direction = sent;
1043     }
1044     else
1045     if (linebuff[n] == 'r')
1046     {
1047         *direction = received;
1048     }
1049     else
1050     {
1051         return FALSE;
1052     }
1053
1054
1055     /*********************************************************************/
1056     /* Find and read the timestamp                                       */
1057
1058     /* Now scan to the next digit, which should be the start of the timestamp */
1059     for (; !isdigit(linebuff[n]) && (n < line_length); n++);
1060     if (n >= line_length)
1061     {
1062         return FALSE;
1063     }
1064
1065     *before_time_offset = n;
1066
1067     /* Seconds */
1068     for (seconds_chars = 0;
1069          (linebuff[n] != '.') &&
1070          (seconds_chars <= MAX_SECONDS_CHARS) &&
1071          (n < line_length);
1072          n++, seconds_chars++)
1073     {
1074         if (!isdigit(linebuff[n]))
1075         {
1076             /* Found a non-digit before decimal point. Fail */
1077             return FALSE;
1078         }
1079         seconds_buff[seconds_chars] = linebuff[n];
1080     }
1081     if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length)
1082     {
1083         /* Didn't fit in buffer.  Fail rather than use truncated */
1084         return FALSE;
1085     }
1086
1087     /* Convert found value into number */
1088     seconds_buff[seconds_chars] = '\0';
1089     *seconds = atoi(seconds_buff);
1090
1091     /* The decimal point must follow the last of the seconds digits */
1092     if (linebuff[n] != '.')
1093     {
1094         return FALSE;
1095     }
1096     /* Skip it */
1097     n++;
1098
1099     /* Subsecond decimal digits (expect 4-digit accuracy) */
1100     for (subsecond_decimals_chars = 0;
1101          (linebuff[n] != ' ') &&
1102          (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1103          (n < line_length);
1104          n++, subsecond_decimals_chars++)
1105     {
1106         if (!isdigit(linebuff[n]))
1107         {
1108             return FALSE;
1109         }
1110         subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1111     }
1112     if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length)
1113     {
1114         /* More numbers than expected - give up */
1115         return FALSE;
1116     }
1117     /* Convert found value into microseconds */
1118     subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1119     *useconds = atoi(subsecond_decimals_buff) * 100;
1120
1121     /* Space character must follow end of timestamp */
1122     if (linebuff[n] != ' ')
1123     {
1124         return FALSE;
1125     }
1126
1127     *after_time_offset = n;
1128
1129     /* Now skip ahead to find start of data (marked by '$') */
1130     for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1131     if (n+1 >= line_length)
1132     {
1133         return FALSE;
1134     }
1135     /* Skip it */
1136     n++;
1137
1138     /* Set offset to data start within line */
1139     *data_offset = n;
1140
1141     /* Set number of chars that comprise the hex string protocol data */
1142     *data_chars = line_length - n;
1143
1144     /* Need to skip first byte (2 hex string chars) from ISDN messages.
1145        TODO: find out what this byte means...
1146     */
1147     if (*encap == WTAP_ENCAP_ISDN)
1148     {
1149         *data_offset += 2;
1150         *data_chars -= 2;
1151     }
1152
1153     return TRUE;
1154 }
1155
1156 /*****************************************************************/
1157 /* Write the stub info to the data buffer while reading a packet */
1158 /*****************************************************************/
1159 int write_stub_header(guchar *frame_buffer, char *timestamp_string,
1160                       packet_direction_t direction, int encap)
1161 {
1162     int stub_offset = 0;
1163     
1164     strcpy((char*)frame_buffer, context_name);
1165     stub_offset += (strlen(context_name) + 1);
1166
1167     /* Context port number */
1168     frame_buffer[stub_offset] = context_port;
1169     stub_offset++;
1170
1171     /* Timestamp within file */
1172     strcpy((char*)&frame_buffer[stub_offset], timestamp_string);
1173     stub_offset += (strlen(timestamp_string) + 1);
1174
1175     /* Protocol name */
1176     strcpy((char*)&frame_buffer[stub_offset], protocol_name);
1177     stub_offset += (strlen(protocol_name) + 1);
1178
1179     /* Protocol variant number (as string) */
1180     strcpy((void*)&frame_buffer[stub_offset], variant_name);
1181     stub_offset += (strlen(variant_name) + 1);
1182
1183     /* Outhdr */
1184     strcpy((char*)&frame_buffer[stub_offset], outhdr_name);
1185     stub_offset += (strlen(outhdr_name) + 1);
1186
1187     /* Direction */
1188     frame_buffer[stub_offset] = direction;
1189     stub_offset++;
1190
1191     /* Encap */
1192     frame_buffer[stub_offset] = (guint8)encap;
1193     stub_offset++;
1194
1195     return stub_offset;
1196 }
1197
1198
1199 /**************************************************************/
1200 /* Set pseudo-header info depending upon packet encapsulation */
1201 /**************************************************************/
1202 void set_pseudo_header_info(wtap *wth,
1203                             int pkt_encap,
1204                             gint64 file_offset,
1205                             union wtap_pseudo_header *pseudo_header,
1206                             packet_direction_t direction)
1207 {
1208     pseudo_header->dct2000.seek_off = file_offset;
1209     pseudo_header->dct2000.wth = wth;
1210
1211     switch (pkt_encap)
1212     {
1213         case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1214             set_aal_info(pseudo_header, direction);
1215             break;
1216         case WTAP_ENCAP_ISDN:
1217             set_isdn_info(pseudo_header, direction);
1218             break;
1219         case WTAP_ENCAP_PPP:
1220             set_ppp_info(pseudo_header, direction);
1221             break;
1222
1223         default:
1224             /* Other supported types don't need to set anything here... */
1225             break;
1226     }
1227 }
1228
1229
1230 /*********************************************/
1231 /* Fill in atm pseudo-header with known info */
1232 /*********************************************/
1233 void set_aal_info(union wtap_pseudo_header *pseudo_header, packet_direction_t direction)
1234 {
1235     /* 'aal_head_chars' has this format (for AAL2 at least):
1236        Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1237        Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1238        Channel Identifier (8 bits) | ...
1239     */
1240
1241     /* Indicate that this is a reassembled PDU */
1242     pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1243
1244     /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1245        TODO: Can we infer the correct value here?
1246        Meanwhile, just use the direction to make them distinguishable...
1247     */
1248     pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1249
1250     /* Assume always AAL2 for FP */
1251     pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1252
1253     pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1254     pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1255
1256     /* vpi is 8 bits (2nd & 3rd nibble) */
1257     pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1258         ((hex_from_char(aal_header_chars[1]) << 4) |
1259           hex_from_char(aal_header_chars[2]));
1260
1261     /* vci is next 16 bits */
1262     pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1263         ((hex_from_char(aal_header_chars[3]) << 12) |
1264          (hex_from_char(aal_header_chars[4]) << 8) |
1265          (hex_from_char(aal_header_chars[5]) << 4) |
1266          hex_from_char(aal_header_chars[6]));
1267
1268     /* 0 means we don't know how many cells the frame comprises. */
1269     pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1270
1271     /* cid is usually last byte.  Unless last char is not hex digit, in which
1272        case cid is derived from last char in ascii */
1273     if (isalnum(aal_header_chars[11]))
1274     {
1275         pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1276             ((hex_from_char(aal_header_chars[10]) << 4) |
1277               hex_from_char(aal_header_chars[11]));
1278     }
1279     else
1280     {
1281         pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1282             (int)aal_header_chars[11] - 48;
1283     }
1284
1285 }
1286
1287
1288 /**********************************************/
1289 /* Fill in isdn pseudo-header with known info */
1290 /**********************************************/
1291 void set_isdn_info(union wtap_pseudo_header *pseudo_header,
1292                    packet_direction_t direction)
1293 {
1294     /* This field is used to set the 'Source' and 'Destination' columns to
1295        'User' or 'Network'. If we assume that we're simulating the network,
1296        treat Received messages as being destined for the network.
1297     */
1298     pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1299
1300     /* This corresponds to the circuit ID.  0 is treated as LAPD,
1301        everything else would be treated as a B-channel
1302     */
1303     pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1304 }
1305
1306
1307 /*********************************************/
1308 /* Fill in ppp pseudo-header with known info */
1309 /*********************************************/
1310 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
1311                          packet_direction_t direction)
1312 {
1313     /* Set direction. */
1314     pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1315 }
1316
1317
1318 /********************************************************/
1319 /* Return hex nibble equivalent of hex string character */
1320 /********************************************************/
1321 guchar hex_from_char(gchar c)
1322 {
1323     if ((c >= '0') && (c <= '9'))
1324     {
1325         return c - '0';
1326     }
1327
1328     if ((c >= 'a') && (c <= 'f'))
1329     {
1330         return 0x0a + (c - 'a');
1331     }
1332
1333     /* Not a valid hex string character */
1334     return 0xff;
1335 }
1336
1337
1338 /********************************************************/
1339 /* Return character corresponding to hex nibble value   */
1340 /********************************************************/
1341 gchar char_from_hex(guchar hex)
1342 {
1343     static char hex_lookup[16] =
1344     { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1345
1346     if (hex > 15)
1347     {
1348         return '?';
1349     }
1350
1351     return hex_lookup[hex];
1352 }
1353
1354 /***************************************************/
1355 /* Equality function for file_externals hash table */
1356 /***************************************************/
1357 gint wth_equal(gconstpointer v, gconstpointer v2)
1358 {
1359     return (v == v2);
1360 }
1361
1362 /***********************************************/
1363 /* Hash function for file_externals hash table */
1364 /***********************************************/
1365 guint wth_hash_func(gconstpointer v)
1366 {
1367     return (guint)v;
1368 }
1369
1370
1371 /***********************************************/
1372 /* Equality test for packet prefix hash tables */
1373 /***********************************************/
1374 gint packet_offset_equal(gconstpointer v, gconstpointer v2)
1375 {
1376     /* Dereferenced pointers must have same gint64 offset value */
1377     return (*(const gint64*)v == *(const gint64*)v2);
1378 }
1379
1380
1381 /********************************************/
1382 /* Hash function for packet-prefix hash table */
1383 /********************************************/
1384 guint packet_offset_hash_func(gconstpointer v)
1385 {
1386     /* Use low-order bits of git64 offset value */
1387     return (guint)(*(const gint64*)v);
1388 }
1389
1390
1391 /************************************************************************/
1392 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1393 /* Set secs and usecs as output                                         */
1394 /* Return FALSE if no valid time can be read                            */
1395 /************************************************************************/
1396 gboolean get_file_time_stamp(time_t *secs, guint32 *usecs)
1397 {
1398     int n;
1399     struct tm tm;
1400     #define MAX_MONTH_LETTERS 9
1401     char month[MAX_MONTH_LETTERS+1];
1402
1403     int day, year, hour, minute, second;
1404     int scan_found;
1405
1406     /* If line longer than expected, file is probably not correctly formatted */
1407     if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
1408     {
1409         return FALSE;
1410     }
1411
1412     /**************************************************************/
1413     /* First is month. Read until get a space following the month */
1414     for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++)
1415     {
1416         month[n] = linebuff[n];
1417     }
1418     month[n] = '\0';
1419
1420     if      (strcmp(month, "January"  ) == 0)  tm.tm_mon = 0;
1421     else if (strcmp(month, "February" ) == 0)  tm.tm_mon = 1;
1422     else if (strcmp(month, "March"    ) == 0)  tm.tm_mon = 2;
1423     else if (strcmp(month, "April"    ) == 0)  tm.tm_mon = 3;
1424     else if (strcmp(month, "May"      ) == 0)  tm.tm_mon = 4;
1425     else if (strcmp(month, "June"     ) == 0)  tm.tm_mon = 5;
1426     else if (strcmp(month, "July"     ) == 0)  tm.tm_mon = 6;
1427     else if (strcmp(month, "August"   ) == 0)  tm.tm_mon = 7;
1428     else if (strcmp(month, "September") == 0)  tm.tm_mon = 8;
1429     else if (strcmp(month, "October"  ) == 0)  tm.tm_mon = 9;
1430     else if (strcmp(month, "November" ) == 0)  tm.tm_mon = 10;
1431     else if (strcmp(month, "December" ) == 0)  tm.tm_mon = 11;
1432     else
1433     {
1434         /* Give up if not found a properly-formatted date */
1435         return FALSE;
1436     }
1437     /* Skip space char */
1438     n++;
1439
1440     /********************************************************/
1441     /* Scan for remaining numerical fields                  */
1442     scan_found = sscanf(linebuff+n, "%d, %d     %d:%d:%d.%u",
1443                         &day, &year, &hour, &minute, &second, usecs);
1444     if (scan_found != 6)
1445     {
1446         /* Give up if not all found */
1447         return FALSE;
1448     }
1449
1450     /******************************************************/
1451     /* Fill in remaining fields and return it in a time_t */
1452     tm.tm_year = year - 1900;
1453     tm.tm_mday = day;
1454     tm.tm_hour = hour;
1455     tm.tm_min = minute;
1456     tm.tm_sec = second;
1457     tm.tm_isdst = -1;    /* daylight saving time info not known */
1458
1459     /* Get seconds from this time */
1460     *secs = mktime(&tm);
1461
1462     /* Multiply 4 digits given to get micro-seconds */
1463     *usecs = *usecs * 100;
1464
1465     return TRUE;
1466 }
1467
1468 /* Free the data allocated inside a line_prefix_info_t */
1469 gboolean free_line_prefix_info(gpointer key, gpointer value,
1470                                gpointer user_data _U_)
1471 {
1472     line_prefix_info_t *info = (line_prefix_info_t*)value;
1473
1474     /* Free the 64-bit key value */
1475     g_free(key);
1476
1477     /* Free the strings inside */
1478     g_free(info->before_time);
1479     if (info->after_time)
1480     {
1481         g_free(info->after_time);
1482     }
1483
1484     /* And the structure itself */
1485     g_free(info);
1486
1487     /* Item will always be removed from table */
1488     return TRUE;
1489 }
1490