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