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