Fix more ctype.h calls - casting to "int" suppresses the warning, but
[obnox/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 = 0;
175     dct2000_file_externals_t *file_externals;
176
177     /* Clear errno before reading from the file */
178     errno = 0;
179
180
181     /********************************************************************/
182     /* First line needs to contain at least as many characters as magic */
183
184     read_new_line(wth->fh, &offset, &firstline_length);
185     if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
186         firstline_length >= MAX_FIRST_LINE_LENGTH)
187     {
188         return 0;
189     }
190
191     /* This file is not for us if it doesn't match our signature */
192     if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0)
193     {
194         return 0;
195     }
196
197
198     /*********************************************************************/
199     /* Need entry in file_externals table                                */
200
201     /* Create file externals table if it doesn't yet exist */
202     if (file_externals_table == NULL)
203     {
204         file_externals_table = g_hash_table_new(wth_hash_func, wth_equal);
205     }
206
207     /* Allocate a new file_externals structure */
208     file_externals = g_malloc(sizeof(dct2000_file_externals_t));
209     memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
210
211     /* Copy this first line into buffer so could write out later */
212     strncpy(file_externals->firstline, linebuff, firstline_length);
213     file_externals->firstline_length = firstline_length;
214
215
216     /***********************************************************/
217     /* Second line contains file timestamp                     */
218     /* Store this offset in in wth->capture->catapult_dct2000  */
219
220     read_new_line(wth->fh, &offset, &(file_externals->secondline_length));
221     if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
222         (!get_file_time_stamp(&timestamp, &usecs)))
223     {
224         /* Give up if file time line wasn't valid */
225         g_free(file_externals);
226         return 0;
227     }
228
229     wth->capture.catapult_dct2000 = g_malloc(sizeof(catapult_dct2000_t));
230     wth->capture.catapult_dct2000->start_secs = timestamp;
231     wth->capture.catapult_dct2000->start_usecs = usecs;
232
233     /* Copy this second line into buffer so could write out later */
234     strncpy(file_externals->secondline, linebuff, file_externals->secondline_length);
235
236
237     /************************************************************/
238     /* File is for us. Fill in details so packets can be read   */
239
240     /* Set our file type */
241     wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
242
243     /* Use our own encapsulation to send all packets to our stub dissector */
244     wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
245
246     /* Callbacks for reading operations */
247     wth->subtype_read = catapult_dct2000_read;
248     wth->subtype_seek_read = catapult_dct2000_seek_read;
249     wth->subtype_close = catapult_dct2000_close;
250
251     /* Choose microseconds (have 4 decimal places...) */
252     wth->tsprecision = WTAP_FILE_TSPREC_USEC;
253
254
255     /***************************************************************/
256     /* Initialise packet_prefix_table (index is offset into file)  */
257     file_externals->packet_prefix_table =
258         g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
259
260     /* Add file_externals for this wtap into the global table */
261     g_hash_table_insert(file_externals_table,
262                         (void*)wth, (void*)file_externals);
263
264     *err = errno;
265     return 1;
266 }
267
268
269 /**************************************************/
270 /* Read function.                                 */
271 /* Look for and read the next usable packet       */
272 /* - return TRUE and details if found             */
273 /**************************************************/
274 gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info _U_,
275                                gint64 *data_offset)
276 {
277     gint64 offset = wth->data_offset;
278     long dollar_offset, before_time_offset, after_time_offset;
279     packet_direction_t direction;
280     int encap;
281
282     /* Find wtap external structure for this wtap */
283     dct2000_file_externals_t *file_externals =
284         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table, wth);
285
286     /* There *has* to be an entry for this wth */
287     if (!file_externals)
288     {
289         return FALSE;
290     }
291
292     /* Search for a line containing a usable message */
293     while (1)
294     {
295         int line_length, seconds, useconds, data_chars;
296         gint64 this_offset = offset;
297
298         /* Are looking for first packet after 2nd line */
299         if (wth->data_offset == 0)
300         {
301             this_offset += (file_externals->firstline_length+1+
302                             file_externals->secondline_length+1);
303         }
304
305         /* Clear errno before reading from the file */
306         errno = 0;
307
308         /* Read a new line from file into linebuff */
309         if (read_new_line(wth->fh, &offset, &line_length) == FALSE)
310         {
311             /* Get out when no more lines to be read */
312             break;
313         }
314
315         /* Try to parse the line as a frame record */
316         if (parse_line(line_length, &seconds, &useconds,
317                        &before_time_offset, &after_time_offset,
318                        &dollar_offset,
319                        &data_chars, &direction, &encap))
320         {
321             guchar *frame_buffer;
322             int n;
323             int stub_offset = 0;
324             line_prefix_info_t *line_prefix_info;
325             char timestamp_string[32];
326             gint64 *pkey = NULL;
327
328             sprintf(timestamp_string, "%d.%04d", seconds, useconds/100);
329
330             /* All packets go to Catapult DCT2000 stub dissector */
331             wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
332
333             /* Set data_offset to the beginning of the line we're returning.
334                This will be the seek_off parameter when this frame is re-read.
335             */
336             *data_offset = this_offset;
337
338             /* This is the position in the file where the next _read() will be called from */
339             wth->data_offset = this_offset + line_length + 1;
340
341             /* Fill in timestamp (capture base + packet offset) */
342             wth->phdr.ts.secs = wth->capture.catapult_dct2000->start_secs + seconds;
343             if ((wth->capture.catapult_dct2000->start_usecs + useconds) >= 1000000)
344             {
345                 wth->phdr.ts.secs++;
346             }
347             wth->phdr.ts.nsecs =
348                 ((wth->capture.catapult_dct2000->start_usecs + useconds) % 1000000) *1000;
349
350             /* Get buffer pointer ready */
351             buffer_assure_space(wth->frame_buffer,
352                                 strlen(context_name)+1 +     /* Context name */
353                                 1 +                          /* port */
354                                 strlen(timestamp_string)+1 + /* timestamp */
355                                 strlen(variant_name)+1 +     /* variant */
356                                 strlen(outhdr_name)+1 +      /* outhdr */
357                                 strlen(protocol_name)+1 +    /* Protocol name */
358                                 1 +                          /* direction */
359                                 1 +                          /* encap */
360                                 (data_chars/2));
361             frame_buffer = buffer_start_ptr(wth->frame_buffer);
362
363
364             /*********************/
365             /* Write stub header */
366             stub_offset = write_stub_header(frame_buffer, timestamp_string,
367                                             direction, encap);
368
369             /* Binary data length is half bytestring length + stub header */
370             wth->phdr.len = data_chars/2 + stub_offset;
371             wth->phdr.caplen = data_chars/2 + stub_offset;
372
373
374             /*************************/
375             /* Copy data into buffer */
376             for (n=0; n <= data_chars; n+=2)
377             {
378                 frame_buffer[stub_offset + n/2] =
379                     (hex_from_char(linebuff[dollar_offset+n]) << 4) |
380                      hex_from_char(linebuff[dollar_offset+n+1]);
381             }
382
383             /* Store the packet prefix in the hash table */
384             line_prefix_info = g_malloc(sizeof(line_prefix_info_t));
385
386             /* Create and use buffer for contents before time */
387             line_prefix_info->before_time = g_malloc(before_time_offset+1);
388             strncpy(line_prefix_info->before_time, linebuff, before_time_offset);
389             line_prefix_info->before_time[before_time_offset] = '\0';
390
391             /* Create and use buffer for contents before time.
392                Do this only if it doesn't correspond to " l ", which is by far the most
393                common case. */
394             if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
395                 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0))
396             {
397                 line_prefix_info->after_time = NULL;
398             }
399             else
400             {
401                 line_prefix_info->after_time = g_malloc(dollar_offset - after_time_offset);
402                 strncpy(line_prefix_info->after_time, linebuff+after_time_offset,
403                         dollar_offset - after_time_offset);
404                 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
405             }
406
407             /* Add packet entry into table */
408             pkey = g_malloc(sizeof(pkey));
409             *pkey = this_offset;
410             g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
411
412             /* Set pseudo-header if necessary */
413             set_pseudo_header_info(wth, encap, this_offset, &wth->pseudo_header,
414                                    direction);
415
416             /* OK, we have packet details to return */
417             *err = errno;
418             return TRUE;
419         }
420     }
421
422     /* No packet details to return... */
423     *err = errno;
424     return FALSE;
425 }
426
427
428 /**************************************************/
429 /* Read & seek function.                          */
430 /**************************************************/
431 static gboolean
432 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
433                            union wtap_pseudo_header *pseudo_header, guchar *pd,
434                            int length, int *err, gchar **err_info)
435 {
436     gint64 offset = wth->data_offset;
437     long dollar_offset, before_time_offset, after_time_offset;
438     packet_direction_t direction;
439     int encap;
440     int seconds, useconds, data_chars;
441
442     /* Reset errno */
443     *err = errno = 0;
444
445     /* Seek to beginning of packet */
446     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
447     {
448         return FALSE;
449     }
450
451     /* Re-read whole line (this should succeed) */
452     if (read_new_line(wth->random_fh, &offset, &length) == FALSE)
453     {
454         return FALSE;
455     }
456
457     /* Try to parse this line again (should succeed as re-reading...) */
458     if (parse_line(length, &seconds, &useconds,
459                    &before_time_offset, &after_time_offset,
460                    &dollar_offset,
461                    &data_chars, &direction, &encap))
462     {
463         int n;
464         int stub_offset = 0;
465         char timestamp_string[32];
466         sprintf(timestamp_string, "%d.%04d", seconds, useconds/100);
467
468         /* Make sure all packets go to catapult dct2000 dissector */
469         wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
470
471
472         /*********************/
473         /* Write stub header */
474         stub_offset = write_stub_header((guchar*)pd, timestamp_string,
475                                         direction, encap);
476
477
478         /********************************/
479         /* Copy packet data into buffer */
480         for (n=0; n <= data_chars; n+=2)
481         {
482             pd[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
483                                      hex_from_char(linebuff[dollar_offset+n+1]);
484         }
485
486         /* Set packet pseudo-header if necessary */
487         set_pseudo_header_info(wth, encap, seek_off, pseudo_header, direction);
488
489         *err = errno = 0;
490         return TRUE;
491     }
492
493     /* If get here, must have failed */
494     *err = errno;
495     *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
496                                 "line at position %" G_GINT64_MODIFIER "d",
497                                 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     int  skip_first_byte = FALSE;
781
782     gboolean atm_header_present = FALSE;
783
784     /* Read context name until find '.' */
785     for (n=0; linebuff[n] != '.' && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++)
786     {
787         if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_'))
788         {
789             return FALSE;
790         }
791         context_name[n] = linebuff[n];
792     }
793     if (n == MAX_CONTEXT_NAME || (n+1 >= line_length))
794     {
795         return FALSE;
796     }
797
798     /* '.' must follow context name */
799     if (linebuff[n] != '.')
800     {
801         return FALSE;
802     }
803     context_name[n] = '\0';
804     /* Skip it */
805     n++;
806
807
808     /* Now read port number */
809     for (port_digits = 0;
810          (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
811          n++, port_digits++)
812     {
813         if (!isdigit((guchar)linebuff[n]))
814         {
815             return FALSE;
816         }
817         port_number_string[port_digits] = linebuff[n];
818     }
819     if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length))
820     {
821         return FALSE;
822     }
823
824     /* Slash char must follow port number */
825     if (linebuff[n] != '/')
826     {
827         return FALSE;
828     }
829     port_number_string[port_digits] = '\0';
830     context_port = atoi(port_number_string);
831     /* Skip it */
832     n++;
833
834
835     /* Now for the protocol name */
836     for (protocol_chars = 0;
837          (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
838          n++, protocol_chars++)
839     {
840         if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_')
841         {
842             return FALSE;
843         }
844         protocol_name[protocol_chars] = linebuff[n];
845     }
846     if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length)
847     {
848         /* If doesn't fit, fail rather than truncate */
849         return FALSE;
850     }
851     protocol_name[protocol_chars] = '\0';
852
853     /* Slash char must follow protocol name */
854     if (linebuff[n] != '/')
855     {
856         return FALSE;
857     }
858     /* Skip it */
859     n++;
860
861
862     /* Following the / is the variant number.  No digits indicate 1 */
863     for (variant_digits = 0;
864          (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
865          n++, variant_digits++)
866     {
867         if (!isdigit((guchar)linebuff[n]))
868         {
869             return FALSE;
870         }
871         variant_name[variant_digits] = linebuff[n];
872     }
873     if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length))
874     {
875         return FALSE;
876     }
877     if (variant_digits > 0)
878     {
879         variant_name[variant_digits] = '\0';
880         variant = atoi(variant_name);
881     }
882     else
883     {
884         strcpy(variant_name, "1");
885     }
886
887
888     /* Outheader values may follow */
889     outhdr_name[0] = '\0';
890     if (linebuff[n] == ',')
891     {
892         /* Skip , */
893         n++;
894
895         for (outhdr_chars = 0;
896              (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
897              (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
898              n++, outhdr_chars++)
899         {
900             if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ','))
901             {
902                 return FALSE;
903             }
904             outhdr_name[outhdr_chars] = linebuff[n];
905         }
906         if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length))
907         {
908             return FALSE;
909         }
910         /* Terminate (possibly empty) string */
911         outhdr_name[outhdr_chars] = '\0';
912     }
913
914
915
916
917     /******************************************************************/
918     /* Now check whether we know how to use a packet of this protocol */
919
920     if ((strcmp(protocol_name, "ip") == 0) ||
921         (strcmp(protocol_name, "sctp") == 0) ||
922         (strcmp(protocol_name, "gre") == 0) ||
923         (strcmp(protocol_name, "mipv6") == 0))
924     {
925         *encap = WTAP_ENCAP_RAW_IP;
926     }
927     else
928
929     /* FP may be carried over ATM, which has separate atm header to parse */
930     if ((strcmp(protocol_name, "fp") == 0) ||
931         (strcmp(protocol_name, "fp_r4") == 0) ||
932         (strcmp(protocol_name, "fp_r5") == 0) ||
933         (strcmp(protocol_name, "fp_r6") == 0))
934     {
935         if ((variant > 256) && (variant % 256 == 3))
936         {
937             /* FP over udp is contained in IPPrim... */
938             *encap = 0;
939         }
940         else
941         {
942             /* FP over AAL0 or AAL2 */
943             *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
944             atm_header_present = TRUE;
945         }
946     }
947     else if (strcmp(protocol_name, "fpiur_r5") == 0)
948     {
949         /* FP (IuR) over AAL2 */
950         *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
951         atm_header_present = TRUE;
952     }
953
954
955     else
956     if (strcmp(protocol_name, "ppp") == 0)
957     {
958         *encap = WTAP_ENCAP_PPP;
959     }
960     else
961     if (strcmp(protocol_name, "isdn_l3") == 0)
962     {
963        /* TODO: find out what this byte means... */
964         skip_first_byte = TRUE;
965         *encap = WTAP_ENCAP_ISDN;
966     }
967     else
968     if (strcmp(protocol_name, "isdn_l2") == 0)
969     {
970         *encap = WTAP_ENCAP_ISDN;
971     }
972     else
973     if (strcmp(protocol_name, "ethernet") == 0)
974     {
975         *encap = WTAP_ENCAP_ETHERNET;
976     }
977     else
978     if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
979         (strcmp(protocol_name, "saaluni_sscop") == 0))
980     {
981         *encap = DCT2000_ENCAP_SSCOP;
982     }
983     else
984     if (strcmp(protocol_name, "frelay_l2") == 0)
985     {
986         *encap = WTAP_ENCAP_FRELAY;
987     }
988     else
989     if (strcmp(protocol_name, "ss7_mtp2") == 0)
990     {
991         *encap = DCT2000_ENCAP_MTP2;
992     }
993     else
994     if ((strcmp(protocol_name, "nbap") == 0) ||
995         (strcmp(protocol_name, "nbap_r4") == 0) ||
996         (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0))
997     {
998         /* The entire message in these cases is nbap, so use an encap value. */
999         *encap = DCT2000_ENCAP_NBAP;
1000     }
1001     else
1002     {
1003         /* Not a supported board port protocol/encap, but can show as raw data or
1004            in some cases find protocol embedded inside primitive */
1005         *encap = DCT2000_ENCAP_UNHANDLED;
1006     }
1007
1008
1009     /* Find separate ATM header if necessary */
1010     if (atm_header_present)
1011     {
1012         int header_chars_seen = 0;
1013
1014         /* Scan ahead to the next $ */
1015         for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1016         /* Skip it */
1017         n++;
1018         if (n+1 >= line_length)
1019         {
1020             return FALSE;
1021         }
1022
1023         /* Read consecutive hex chars into atm header buffer */
1024         for (;
1025              ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1026               (n < line_length) &&
1027               (header_chars_seen < AAL_HEADER_CHARS));
1028              n++, header_chars_seen++)
1029         {
1030             aal_header_chars[header_chars_seen] = linebuff[n];
1031             /* Next 6 characters after '9' are mapped to a->f */
1032             if (!isdigit((guchar)linebuff[n]))
1033             {
1034                 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1035             }
1036         }
1037
1038         if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length)
1039         {
1040             return FALSE;
1041         }
1042     }
1043
1044
1045     /* Scan ahead to the next space */
1046     for (; (linebuff[n] != ' ') && (n+1 < line_length); n++);
1047     if (n+1 >= line_length)
1048     {
1049         return FALSE;
1050     }
1051     /* Skip it */
1052     n++;
1053
1054     /* Next character gives direction of message (must be 's' or 'r') */
1055     if (linebuff[n] == 's')
1056     {
1057         *direction = sent;
1058     }
1059     else
1060     if (linebuff[n] == 'r')
1061     {
1062         *direction = received;
1063     }
1064     else
1065     {
1066         return FALSE;
1067     }
1068
1069
1070     /*********************************************************************/
1071     /* Find and read the timestamp                                       */
1072
1073     /* Now scan to the next digit, which should be the start of the timestamp */
1074     for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1075     if (n >= line_length)
1076     {
1077         return FALSE;
1078     }
1079
1080     *before_time_offset = n;
1081
1082     /* Seconds */
1083     for (seconds_chars = 0;
1084          (linebuff[n] != '.') &&
1085          (seconds_chars <= MAX_SECONDS_CHARS) &&
1086          (n < line_length);
1087          n++, seconds_chars++)
1088     {
1089         if (!isdigit((guchar)linebuff[n]))
1090         {
1091             /* Found a non-digit before decimal point. Fail */
1092             return FALSE;
1093         }
1094         seconds_buff[seconds_chars] = linebuff[n];
1095     }
1096     if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length)
1097     {
1098         /* Didn't fit in buffer.  Fail rather than use truncated */
1099         return FALSE;
1100     }
1101
1102     /* Convert found value into number */
1103     seconds_buff[seconds_chars] = '\0';
1104     *seconds = atoi(seconds_buff);
1105
1106     /* The decimal point must follow the last of the seconds digits */
1107     if (linebuff[n] != '.')
1108     {
1109         return FALSE;
1110     }
1111     /* Skip it */
1112     n++;
1113
1114     /* Subsecond decimal digits (expect 4-digit accuracy) */
1115     for (subsecond_decimals_chars = 0;
1116          (linebuff[n] != ' ') &&
1117          (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1118          (n < line_length);
1119          n++, subsecond_decimals_chars++)
1120     {
1121         if (!isdigit((guchar)linebuff[n]))
1122         {
1123             return FALSE;
1124         }
1125         subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1126     }
1127     if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length)
1128     {
1129         /* More numbers than expected - give up */
1130         return FALSE;
1131     }
1132     /* Convert found value into microseconds */
1133     subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1134     *useconds = atoi(subsecond_decimals_buff) * 100;
1135
1136     /* Space character must follow end of timestamp */
1137     if (linebuff[n] != ' ')
1138     {
1139         return FALSE;
1140     }
1141
1142     *after_time_offset = n;
1143
1144     /* Now skip ahead to find start of data (marked by '$') */
1145     for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1146     if (n+1 >= line_length)
1147     {
1148         return FALSE;
1149     }
1150     /* Skip it */
1151     n++;
1152
1153     /* Set offset to data start within line */
1154     *data_offset = n;
1155
1156     /* Set number of chars that comprise the hex string protocol data */
1157     *data_chars = line_length - n;
1158
1159     /* May need to skip first byte (2 hex string chars) */
1160     if (skip_first_byte)
1161     {
1162         *data_offset += 2;
1163         *data_chars -= 2;
1164     }
1165
1166
1167     return TRUE;
1168 }
1169
1170 /*****************************************************************/
1171 /* Write the stub info to the data buffer while reading a packet */
1172 /*****************************************************************/
1173 int write_stub_header(guchar *frame_buffer, char *timestamp_string,
1174                       packet_direction_t direction, int encap)
1175 {
1176     int stub_offset = 0;
1177     
1178     strcpy((char*)frame_buffer, context_name);
1179     stub_offset += (strlen(context_name) + 1);
1180
1181     /* Context port number */
1182     frame_buffer[stub_offset] = context_port;
1183     stub_offset++;
1184
1185     /* Timestamp within file */
1186     strcpy((char*)&frame_buffer[stub_offset], timestamp_string);
1187     stub_offset += (strlen(timestamp_string) + 1);
1188
1189     /* Protocol name */
1190     strcpy((char*)&frame_buffer[stub_offset], protocol_name);
1191     stub_offset += (strlen(protocol_name) + 1);
1192
1193     /* Protocol variant number (as string) */
1194     strcpy((void*)&frame_buffer[stub_offset], variant_name);
1195     stub_offset += (strlen(variant_name) + 1);
1196
1197     /* Outhdr */
1198     strcpy((char*)&frame_buffer[stub_offset], outhdr_name);
1199     stub_offset += (strlen(outhdr_name) + 1);
1200
1201     /* Direction */
1202     frame_buffer[stub_offset] = direction;
1203     stub_offset++;
1204
1205     /* Encap */
1206     frame_buffer[stub_offset] = (guint8)encap;
1207     stub_offset++;
1208
1209     return stub_offset;
1210 }
1211
1212
1213 /**************************************************************/
1214 /* Set pseudo-header info depending upon packet encapsulation */
1215 /**************************************************************/
1216 void set_pseudo_header_info(wtap *wth,
1217                             int pkt_encap,
1218                             gint64 file_offset,
1219                             union wtap_pseudo_header *pseudo_header,
1220                             packet_direction_t direction)
1221 {
1222     pseudo_header->dct2000.seek_off = file_offset;
1223     pseudo_header->dct2000.wth = wth;
1224
1225     switch (pkt_encap)
1226     {
1227         case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1228             set_aal_info(pseudo_header, direction);
1229             break;
1230         case WTAP_ENCAP_ISDN:
1231             set_isdn_info(pseudo_header, direction);
1232             break;
1233         case WTAP_ENCAP_PPP:
1234             set_ppp_info(pseudo_header, direction);
1235             break;
1236
1237         default:
1238             /* Other supported types don't need to set anything here... */
1239             break;
1240     }
1241 }
1242
1243
1244 /*********************************************/
1245 /* Fill in atm pseudo-header with known info */
1246 /*********************************************/
1247 void set_aal_info(union wtap_pseudo_header *pseudo_header, packet_direction_t direction)
1248 {
1249     /* 'aal_head_chars' has this format (for AAL2 at least):
1250        Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1251        Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1252        Channel Identifier (8 bits) | ...
1253     */
1254
1255     /* Indicate that this is a reassembled PDU */
1256     pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1257
1258     /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1259        TODO: Can we infer the correct value here?
1260        Meanwhile, just use the direction to make them distinguishable...
1261     */
1262     pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1263
1264     /* Assume always AAL2 for FP */
1265     pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1266
1267     pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1268     pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1269
1270     /* vpi is 8 bits (2nd & 3rd nibble) */
1271     pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1272         ((hex_from_char(aal_header_chars[1]) << 4) |
1273           hex_from_char(aal_header_chars[2]));
1274
1275     /* vci is next 16 bits */
1276     pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1277         ((hex_from_char(aal_header_chars[3]) << 12) |
1278          (hex_from_char(aal_header_chars[4]) << 8) |
1279          (hex_from_char(aal_header_chars[5]) << 4) |
1280          hex_from_char(aal_header_chars[6]));
1281
1282     /* 0 means we don't know how many cells the frame comprises. */
1283     pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1284
1285     /* cid is usually last byte.  Unless last char is not hex digit, in which
1286        case cid is derived from last char in ascii */
1287     if (isalnum((int)aal_header_chars[11]))
1288     {
1289         pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1290             ((hex_from_char(aal_header_chars[10]) << 4) |
1291               hex_from_char(aal_header_chars[11]));
1292     }
1293     else
1294     {
1295         pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1296             (int)aal_header_chars[11] - 48;
1297     }
1298
1299 }
1300
1301
1302 /**********************************************/
1303 /* Fill in isdn pseudo-header with known info */
1304 /**********************************************/
1305 void set_isdn_info(union wtap_pseudo_header *pseudo_header,
1306                    packet_direction_t direction)
1307 {
1308     /* This field is used to set the 'Source' and 'Destination' columns to
1309        'User' or 'Network'. If we assume that we're simulating the network,
1310        treat Received messages as being destined for the network.
1311     */
1312     pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1313
1314     /* This corresponds to the circuit ID.  0 is treated as LAPD,
1315        everything else would be treated as a B-channel
1316     */
1317     pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1318 }
1319
1320
1321 /*********************************************/
1322 /* Fill in ppp pseudo-header with known info */
1323 /*********************************************/
1324 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
1325                          packet_direction_t direction)
1326 {
1327     /* Set direction. */
1328     pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1329 }
1330
1331
1332 /********************************************************/
1333 /* Return hex nibble equivalent of hex string character */
1334 /********************************************************/
1335 guchar hex_from_char(gchar c)
1336 {
1337     if ((c >= '0') && (c <= '9'))
1338     {
1339         return c - '0';
1340     }
1341
1342     if ((c >= 'a') && (c <= 'f'))
1343     {
1344         return 0x0a + (c - 'a');
1345     }
1346
1347     /* Not a valid hex string character */
1348     return 0xff;
1349 }
1350
1351
1352 /********************************************************/
1353 /* Return character corresponding to hex nibble value   */
1354 /********************************************************/
1355 gchar char_from_hex(guchar hex)
1356 {
1357     static char hex_lookup[16] =
1358     { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1359
1360     if (hex > 15)
1361     {
1362         return '?';
1363     }
1364
1365     return hex_lookup[hex];
1366 }
1367
1368 /***************************************************/
1369 /* Equality function for file_externals hash table */
1370 /***************************************************/
1371 gint wth_equal(gconstpointer v, gconstpointer v2)
1372 {
1373     return (v == v2);
1374 }
1375
1376 /***********************************************/
1377 /* Hash function for file_externals hash table */
1378 /***********************************************/
1379 guint wth_hash_func(gconstpointer v)
1380 {
1381     return (guint)(unsigned long)v;
1382 }
1383
1384
1385 /***********************************************/
1386 /* Equality test for packet prefix hash tables */
1387 /***********************************************/
1388 gint packet_offset_equal(gconstpointer v, gconstpointer v2)
1389 {
1390     /* Dereferenced pointers must have same gint64 offset value */
1391     return (*(const gint64*)v == *(const gint64*)v2);
1392 }
1393
1394
1395 /********************************************/
1396 /* Hash function for packet-prefix hash table */
1397 /********************************************/
1398 guint packet_offset_hash_func(gconstpointer v)
1399 {
1400     /* Use low-order bits of git64 offset value */
1401     return (guint)(*(const gint64*)v);
1402 }
1403
1404
1405 /************************************************************************/
1406 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1407 /* Set secs and usecs as output                                         */
1408 /* Return FALSE if no valid time can be read                            */
1409 /************************************************************************/
1410 gboolean get_file_time_stamp(time_t *secs, guint32 *usecs)
1411 {
1412     int n;
1413     struct tm tm;
1414     #define MAX_MONTH_LETTERS 9
1415     char month[MAX_MONTH_LETTERS+1];
1416
1417     int day, year, hour, minute, second;
1418     int scan_found;
1419
1420     /* If line longer than expected, file is probably not correctly formatted */
1421     if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
1422     {
1423         return FALSE;
1424     }
1425
1426     /**************************************************************/
1427     /* First is month. Read until get a space following the month */
1428     for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++)
1429     {
1430         month[n] = linebuff[n];
1431     }
1432     month[n] = '\0';
1433
1434     if      (strcmp(month, "January"  ) == 0)  tm.tm_mon = 0;
1435     else if (strcmp(month, "February" ) == 0)  tm.tm_mon = 1;
1436     else if (strcmp(month, "March"    ) == 0)  tm.tm_mon = 2;
1437     else if (strcmp(month, "April"    ) == 0)  tm.tm_mon = 3;
1438     else if (strcmp(month, "May"      ) == 0)  tm.tm_mon = 4;
1439     else if (strcmp(month, "June"     ) == 0)  tm.tm_mon = 5;
1440     else if (strcmp(month, "July"     ) == 0)  tm.tm_mon = 6;
1441     else if (strcmp(month, "August"   ) == 0)  tm.tm_mon = 7;
1442     else if (strcmp(month, "September") == 0)  tm.tm_mon = 8;
1443     else if (strcmp(month, "October"  ) == 0)  tm.tm_mon = 9;
1444     else if (strcmp(month, "November" ) == 0)  tm.tm_mon = 10;
1445     else if (strcmp(month, "December" ) == 0)  tm.tm_mon = 11;
1446     else
1447     {
1448         /* Give up if not found a properly-formatted date */
1449         return FALSE;
1450     }
1451     /* Skip space char */
1452     n++;
1453
1454     /********************************************************/
1455     /* Scan for remaining numerical fields                  */
1456     scan_found = sscanf(linebuff+n, "%d, %d     %d:%d:%d.%u",
1457                         &day, &year, &hour, &minute, &second, usecs);
1458     if (scan_found != 6)
1459     {
1460         /* Give up if not all found */
1461         return FALSE;
1462     }
1463
1464     /******************************************************/
1465     /* Fill in remaining fields and return it in a time_t */
1466     tm.tm_year = year - 1900;
1467     tm.tm_mday = day;
1468     tm.tm_hour = hour;
1469     tm.tm_min = minute;
1470     tm.tm_sec = second;
1471     tm.tm_isdst = -1;    /* daylight saving time info not known */
1472
1473     /* Get seconds from this time */
1474     *secs = mktime(&tm);
1475
1476     /* Multiply 4 digits given to get micro-seconds */
1477     *usecs = *usecs * 100;
1478
1479     return TRUE;
1480 }
1481
1482 /* Free the data allocated inside a line_prefix_info_t */
1483 gboolean free_line_prefix_info(gpointer key, gpointer value,
1484                                gpointer user_data _U_)
1485 {
1486     line_prefix_info_t *info = (line_prefix_info_t*)value;
1487
1488     /* Free the 64-bit key value */
1489     g_free(key);
1490
1491     /* Free the strings inside */
1492     g_free(info->before_time);
1493     if (info->after_time)
1494     {
1495         g_free(info->after_time);
1496     }
1497
1498     /* And the structure itself */
1499     g_free(info);
1500
1501     /* Item will always be removed from table */
1502     return TRUE;
1503 }
1504