4 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "file_wrappers.h"
29 #include "catapult_dct2000.h"
31 #define MAX_FIRST_LINE_LENGTH 200
32 #define MAX_TIMESTAMP_LINE_LENGTH 100
33 #define MAX_LINE_LENGTH 65536
34 #define MAX_TIMESTAMP_LEN 32
35 #define MAX_SECONDS_CHARS 16
36 #define MAX_SUBSECOND_DECIMALS 4
37 #define MAX_CONTEXT_NAME 64
38 #define MAX_PROTOCOL_NAME 64
39 #define MAX_PORT_DIGITS 2
40 #define MAX_VARIANT_DIGITS 32
41 #define MAX_OUTHDR_NAME 256
42 #define AAL_HEADER_CHARS 12
45 - support for FP over AAL0
46 - support for IuR interface FP
50 /* 's' or 'r' of a packet as read from .out file */
51 typedef enum packet_direction_t
58 /***********************************************************************/
59 /* For each line, store (in case we need to dump): */
60 /* - String before time field */
61 /* - String beween time field and data (if NULL assume " l ") */
69 /*******************************************************************/
70 /* Information stored external to a file (wtap) needed for reading and dumping */
71 typedef struct dct2000_file_externals
73 /* Remember the time at the start of capture */
78 * The following information is needed only for dumping.
80 * XXX - Wiretap is not *supposed* to require that a packet being
81 * dumped come from a file of the same type that you currently have
82 * open; this should be fixed.
85 /* Buffer to hold first line, including magic and format number */
86 gchar firstline[MAX_FIRST_LINE_LENGTH];
87 gint firstline_length;
89 /* Buffer to hold second line with formatted file creation data/time */
90 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
91 gint secondline_length;
93 /* Hash table to store text prefix data part of displayed packets.
94 Records (file offset -> line_prefix_info_t)
96 GHashTable *packet_prefix_table;
97 } dct2000_file_externals_t;
99 /* 'Magic number' at start of Catapult DCT2000 .out files. */
100 static const gchar catapult_dct2000_magic[] = "Session Transcript";
102 /************************************************************/
103 /* Functions called from wiretap core */
104 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
105 gint64 *data_offset);
106 static gboolean catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
107 struct wtap_pkthdr *phdr,
108 Buffer *buf, int *err,
110 static void catapult_dct2000_close(wtap *wth);
112 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
113 const guint8 *pd, int *err, gchar **err_info);
116 /************************************************************/
117 /* Private helper functions */
118 static gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length,
119 gchar *buf, size_t bufsize, int *err,
121 static gboolean parse_line(char *linebuff, gint line_length,
122 gint *seconds, gint *useconds,
123 long *before_time_offset, long *after_time_offset,
126 packet_direction_t *direction,
127 int *encap, int *is_comment, int *is_sprint,
128 gchar *aal_header_chars,
129 gchar *context_name, guint8 *context_portp,
130 gchar *protocol_name, gchar *variant_name,
132 static gboolean process_parsed_line(wtap *wth,
133 dct2000_file_externals_t *file_externals,
134 struct wtap_pkthdr *phdr,
135 Buffer *buf, gint64 file_offset,
136 char *linebuff, long dollar_offset,
137 int seconds, int useconds,
138 gchar *timestamp_string,
139 packet_direction_t direction, int encap,
140 gchar *context_name, guint8 context_port,
141 gchar *protocol_name, gchar *variant_name,
142 gchar *outhdr_name, gchar *aal_header_chars,
143 gboolean is_comment, int data_chars,
144 int *err, gchar **err_info);
145 static guint8 hex_from_char(gchar c);
146 static void prepare_hex_byte_from_chars_table(void);
147 static guint8 hex_byte_from_chars(gchar *c);
148 static gchar char_from_hex(guint8 hex);
150 static void set_aal_info(union wtap_pseudo_header *pseudo_header,
151 packet_direction_t direction,
152 gchar *aal_header_chars);
153 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
154 packet_direction_t direction);
155 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
156 packet_direction_t direction);
158 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
159 static guint packet_offset_hash_func(gconstpointer v);
161 static gboolean get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs);
162 static gboolean free_line_prefix_info(gpointer key, gpointer value, gpointer user_data);
166 /********************************************/
167 /* Open file (for reading) */
168 /********************************************/
170 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
175 gint firstline_length = 0;
176 dct2000_file_externals_t *file_externals;
177 static gchar linebuff[MAX_LINE_LENGTH];
178 static gboolean hex_byte_table_values_set = FALSE;
180 /* Clear errno before reading from the file */
184 /********************************************************************/
185 /* First line needs to contain at least as many characters as magic */
187 if (!read_new_line(wth->fh, &offset, &firstline_length, linebuff,
188 sizeof linebuff, err, err_info)) {
189 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
190 return WTAP_OPEN_ERROR;
191 return WTAP_OPEN_NOT_MINE;
193 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
194 firstline_length >= MAX_FIRST_LINE_LENGTH) {
196 return WTAP_OPEN_NOT_MINE;
199 /* This file is not for us if it doesn't match our signature */
200 if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0) {
201 return WTAP_OPEN_NOT_MINE;
204 /* Make sure table is ready for use */
205 if (!hex_byte_table_values_set) {
206 prepare_hex_byte_from_chars_table();
207 hex_byte_table_values_set = TRUE;
210 /*********************************************************************/
211 /* Need entry in file_externals table */
213 /* Allocate a new file_externals structure for this file */
214 file_externals = g_new(dct2000_file_externals_t,1);
215 memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
217 /* Copy this first line into buffer so could write out later */
218 g_strlcpy(file_externals->firstline, linebuff, firstline_length+1);
219 file_externals->firstline_length = firstline_length;
222 /***********************************************************/
223 /* Second line contains file timestamp */
224 /* Store this offset in in file_externals */
226 if (!read_new_line(wth->fh, &offset, &(file_externals->secondline_length),
227 linebuff, sizeof linebuff, err, err_info)) {
228 g_free(file_externals);
229 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
230 return WTAP_OPEN_ERROR;
231 return WTAP_OPEN_NOT_MINE;
233 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
234 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
236 /* Give up if file time line wasn't valid */
237 g_free(file_externals);
238 return WTAP_OPEN_NOT_MINE;
241 /* Fill in timestamp */
242 file_externals->start_secs = timestamp;
243 file_externals->start_usecs = usecs;
245 /* Copy this second line into buffer so could write out later */
246 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
249 /************************************************************/
250 /* File is for us. Fill in details so packets can be read */
252 /* Set our file type */
253 wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_CATAPULT_DCT2000;
255 /* Use our own encapsulation to send all packets to our stub dissector */
256 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
258 /* Callbacks for reading operations */
259 wth->subtype_read = catapult_dct2000_read;
260 wth->subtype_seek_read = catapult_dct2000_seek_read;
261 wth->subtype_close = catapult_dct2000_close;
263 /* Choose microseconds (have 4 decimal places...) */
264 wth->file_tsprec = WTAP_TSPREC_USEC;
267 /***************************************************************/
268 /* Initialise packet_prefix_table (index is offset into file) */
269 file_externals->packet_prefix_table =
270 g_hash_table_new(packet_offset_hash_func, packet_offset_equal);
272 /* Set this wtap to point to the file_externals */
273 wth->priv = (void*)file_externals;
276 return WTAP_OPEN_MINE;
279 /* Ugly, but much faster than using g_snprintf! */
280 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
286 timestamp_string[idx++] = ((secs % 10)) + '0';
288 else if (secs < 100) {
289 timestamp_string[idx++] = ( secs / 10) + '0';
290 timestamp_string[idx++] = ((secs % 10)) + '0';
292 else if (secs < 1000) {
293 timestamp_string[idx++] = ((secs) / 100) + '0';
294 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
295 timestamp_string[idx++] = ((secs % 10)) + '0';
297 else if (secs < 10000) {
298 timestamp_string[idx++] = ((secs) / 1000) + '0';
299 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
300 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
301 timestamp_string[idx++] = ((secs % 10)) + '0';
303 else if (secs < 100000) {
304 timestamp_string[idx++] = ((secs) / 10000) + '0';
305 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
306 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
307 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
308 timestamp_string[idx++] = ((secs % 10)) + '0';
310 else if (secs < 1000000) {
311 timestamp_string[idx++] = ((secs) / 100000) + '0';
312 timestamp_string[idx++] = ((secs % 100000)) / 10000 + '0';
313 timestamp_string[idx++] = ((secs % 10000)) / 1000 + '0';
314 timestamp_string[idx++] = ((secs % 1000)) / 100 + '0';
315 timestamp_string[idx++] = ((secs % 100)) / 10 + '0';
316 timestamp_string[idx++] = ((secs % 10)) + '0';
319 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
323 timestamp_string[idx++] = '.';
324 timestamp_string[idx++] = ( tenthousandths / 1000) + '0';
325 timestamp_string[idx++] = ((tenthousandths % 1000) / 100) + '0';
326 timestamp_string[idx++] = ((tenthousandths % 100) / 10) + '0';
327 timestamp_string[idx++] = ((tenthousandths % 10)) + '0';
328 timestamp_string[idx++] = '\0';
331 /**************************************************/
332 /* Read packet function. */
333 /* Look for and read the next usable packet */
334 /* - return TRUE and details if found */
335 /**************************************************/
337 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
340 gint64 offset = file_tell(wth->fh);
341 long dollar_offset, before_time_offset, after_time_offset;
342 packet_direction_t direction;
345 /* Get wtap external structure for this wtap */
346 dct2000_file_externals_t *file_externals =
347 (dct2000_file_externals_t*)wth->priv;
349 /* Search for a line containing a usable packet */
351 int line_length, seconds, useconds, data_chars;
352 int is_comment = FALSE;
353 int is_sprint = FALSE;
354 gint64 this_offset = offset;
355 static gchar linebuff[MAX_LINE_LENGTH+1];
356 gchar aal_header_chars[AAL_HEADER_CHARS];
357 gchar context_name[MAX_CONTEXT_NAME];
358 guint8 context_port = 0;
359 gchar protocol_name[MAX_PROTOCOL_NAME+1];
360 gchar variant_name[MAX_VARIANT_DIGITS+1];
361 gchar outhdr_name[MAX_OUTHDR_NAME+1];
363 /* Are looking for first packet after 2nd line */
364 if (file_tell(wth->fh) == 0) {
365 this_offset += (file_externals->firstline_length+1+
366 file_externals->secondline_length+1);
369 /* Read a new line from file into linebuff */
370 if (!read_new_line(wth->fh, &offset, &line_length, linebuff,
371 sizeof linebuff, err, err_info)) {
373 return FALSE; /* error */
374 /* No more lines can be read, so quit. */
378 /* Try to parse the line as a frame record */
379 if (parse_line(linebuff, line_length, &seconds, &useconds,
380 &before_time_offset, &after_time_offset,
382 &data_chars, &direction, &encap, &is_comment, &is_sprint,
384 context_name, &context_port,
385 protocol_name, variant_name, outhdr_name)) {
386 line_prefix_info_t *line_prefix_info;
387 char timestamp_string[MAX_TIMESTAMP_LEN+1];
390 write_timestamp_string(timestamp_string, seconds, useconds/100);
392 /* Set data_offset to the beginning of the line we're returning.
393 This will be the seek_off parameter when this frame is re-read.
395 *data_offset = this_offset;
397 if (!process_parsed_line(wth, file_externals,
399 wth->frame_buffer, this_offset,
400 linebuff, dollar_offset,
404 context_name, context_port,
405 protocol_name, variant_name,
406 outhdr_name, aal_header_chars,
407 is_comment, data_chars,
411 /* Store the packet prefix in the hash table */
412 line_prefix_info = g_new(line_prefix_info_t,1);
414 /* Create and use buffer for contents before time */
415 line_prefix_info->before_time = (gchar *)g_malloc(before_time_offset+1);
416 memcpy(line_prefix_info->before_time, linebuff, before_time_offset);
417 line_prefix_info->before_time[before_time_offset] = '\0';
419 /* Create and use buffer for contents before time.
420 Do this only if it doesn't correspond to " l ", which is by far the most
422 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
423 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
425 line_prefix_info->after_time = NULL;
428 /* Allocate & write buffer for line between timestamp and data */
429 line_prefix_info->after_time = (gchar *)g_malloc(dollar_offset - after_time_offset);
430 memcpy(line_prefix_info->after_time, linebuff+after_time_offset, dollar_offset - after_time_offset);
431 line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
434 /* Add packet entry into table */
435 pkey = (gint64 *)g_malloc(sizeof(*pkey));
437 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
439 /* OK, we have packet details to return */
444 /* No packet details to return... */
449 /**************************************************/
450 /* Read & seek function. */
451 /**************************************************/
453 catapult_dct2000_seek_read(wtap *wth, gint64 seek_off,
454 struct wtap_pkthdr *phdr, Buffer *buf,
455 int *err, gchar **err_info)
459 long dollar_offset, before_time_offset, after_time_offset;
460 static gchar linebuff[MAX_LINE_LENGTH+1];
461 gchar aal_header_chars[AAL_HEADER_CHARS];
462 gchar context_name[MAX_CONTEXT_NAME];
463 guint8 context_port = 0;
464 gchar protocol_name[MAX_PROTOCOL_NAME+1];
465 gchar variant_name[MAX_VARIANT_DIGITS+1];
466 gchar outhdr_name[MAX_OUTHDR_NAME+1];
467 int is_comment = FALSE;
468 int is_sprint = FALSE;
469 packet_direction_t direction;
471 int seconds, useconds, data_chars;
473 /* Get wtap external structure for this wtap */
474 dct2000_file_externals_t *file_externals =
475 (dct2000_file_externals_t*)wth->priv;
480 /* Seek to beginning of packet */
481 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
485 /* Re-read whole line (this really should succeed) */
486 if (!read_new_line(wth->random_fh, &offset, &length, linebuff,
487 sizeof linebuff, err, err_info)) {
491 /* Try to parse this line again (should succeed as re-reading...) */
492 if (parse_line(linebuff, length, &seconds, &useconds,
493 &before_time_offset, &after_time_offset,
495 &data_chars, &direction, &encap, &is_comment, &is_sprint,
497 context_name, &context_port,
498 protocol_name, variant_name, outhdr_name)) {
499 char timestamp_string[MAX_TIMESTAMP_LEN+1];
501 write_timestamp_string(timestamp_string, seconds, useconds/100);
503 if (!process_parsed_line(wth, file_externals,
505 linebuff, dollar_offset,
509 context_name, context_port,
510 protocol_name, variant_name,
511 outhdr_name, aal_header_chars,
512 is_comment, data_chars,
520 /* If get here, must have failed */
522 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
523 "line at position %" G_GINT64_MODIFIER "d",
529 /***************************************************************************/
530 /* Free dct2000-specific capture info from file that was open for reading */
531 /***************************************************************************/
533 catapult_dct2000_close(wtap *wth)
535 /* Get externals for this file */
536 dct2000_file_externals_t *file_externals =
537 (dct2000_file_externals_t*)wth->priv;
539 /* Free up its line prefix values */
540 g_hash_table_foreach_remove(file_externals->packet_prefix_table,
541 free_line_prefix_info, NULL);
542 /* Free up its line prefix table */
543 g_hash_table_destroy(file_externals->packet_prefix_table);
549 /***************************/
551 /***************************/
554 gboolean first_packet_written;
558 /*****************************************************/
559 /* The file that we are writing to has been opened. */
560 /* Set other dump callbacks. */
561 /*****************************************************/
563 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
565 /* Fill in other dump callbacks */
566 wdh->subtype_write = catapult_dct2000_dump;
571 /*********************************************************/
572 /* Respond to queries about which encap types we support */
574 /*********************************************************/
576 catapult_dct2000_dump_can_write_encap(int encap)
579 case WTAP_ENCAP_CATAPULT_DCT2000:
580 /* We support this */
584 /* But don't write to any other formats... */
585 return WTAP_ERR_UNWRITABLE_ENCAP;
590 /*****************************************/
591 /* Write a single packet out to the file */
592 /*****************************************/
595 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
596 const guint8 *pd, int *err, gchar **err_info _U_)
598 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
600 line_prefix_info_t *prefix = NULL;
601 gchar time_string[16];
603 gboolean is_sprint = FALSE;
604 dct2000_dump_t *dct2000;
605 int consecutive_slashes=0;
608 /******************************************************/
609 /* Get the file_externals structure for this file */
610 /* Find wtap external structure for this wtap */
611 dct2000_file_externals_t *file_externals =
612 (dct2000_file_externals_t*)pseudo_header->dct2000.wth->priv;
614 /* We can only write packet records. */
615 if (phdr->rec_type != REC_TYPE_PACKET) {
616 *err = WTAP_ERR_UNWRITABLE_REC_TYPE;
620 dct2000 = (dct2000_dump_t *)wdh->priv;
621 if (dct2000 == NULL) {
623 /* Write out saved first line */
624 if (!wtap_dump_file_write(wdh, file_externals->firstline,
625 file_externals->firstline_length, err)) {
628 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
632 /* Also write out saved second line with timestamp corresponding to the
633 opening time of the log.
635 if (!wtap_dump_file_write(wdh, file_externals->secondline,
636 file_externals->secondline_length, err)) {
639 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
643 /* Allocate the dct2000-specific dump structure */
644 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
645 wdh->priv = (void *)dct2000;
647 /* Copy time of beginning of file */
648 dct2000->start_time.secs = file_externals->start_secs;
649 dct2000->start_time.nsecs =
650 (file_externals->start_usecs * 1000);
652 /* Set flag so don't write header out again */
653 dct2000->first_packet_written = TRUE;
657 /******************************************************************/
658 /* Write out this packet's prefix, including calculated timestamp */
660 /* Look up line data prefix using stored offset */
661 prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
662 (const void*)&(pseudo_header->dct2000.seek_off));
664 /* Write out text before timestamp */
665 if (!wtap_dump_file_write(wdh, prefix->before_time,
666 strlen(prefix->before_time), err)) {
670 /* Can infer from prefix if this is a comment (whose payload is displayed differently) */
671 /* This is much faster than strstr() for "/////" */
672 p_c = prefix->before_time;
673 while (p_c && (*p_c != '/')) {
676 while (p_c && (*p_c == '/')) {
677 consecutive_slashes++;
680 is_comment = (consecutive_slashes == 5);
682 /* Calculate time of this packet to write, relative to start of dump */
683 if (phdr->ts.nsecs >= dct2000->start_time.nsecs) {
684 write_timestamp_string(time_string,
685 (int)(phdr->ts.secs - dct2000->start_time.secs),
686 (phdr->ts.nsecs - dct2000->start_time.nsecs) / 100000);
689 write_timestamp_string(time_string,
690 (int)(phdr->ts.secs - dct2000->start_time.secs-1),
691 ((1000000000 + (phdr->ts.nsecs / 100000)) - (dct2000->start_time.nsecs / 100000)) % 10000);
694 /* Write out the calculated timestamp */
695 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
699 /* Write out text between timestamp and start of hex data */
700 if (prefix->after_time == NULL) {
701 if (!wtap_dump_file_write(wdh, " l ", strlen(" l "), err)) {
706 if (!wtap_dump_file_write(wdh, prefix->after_time,
707 strlen(prefix->after_time), err)) {
713 /****************************************************************/
714 /* Need to skip stub header at start of pd before we reach data */
717 for (n=0; pd[n] != '\0'; n++);
720 /* Context port number */
724 for (; pd[n] != '\0'; n++);
729 is_sprint = (strcmp((const char *)pd+n, "sprint") == 0);
731 for (; pd[n] != '\0'; n++);
734 /* Variant number (as string) */
735 for (; pd[n] != '\0'; n++);
738 /* Outhdr (as string) */
739 for (; pd[n] != '\0'; n++);
742 /* Direction & encap */
746 /**************************************/
747 /* Remainder is encapsulated protocol */
748 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
753 /* Each binary byte is written out as 2 hex string chars */
754 for (; n < phdr->len; n++) {
756 c[0] = char_from_hex((guint8)(pd[n] >> 4));
757 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
759 /* Write both hex chars of byte together */
760 if (!wtap_dump_file_write(wdh, c, 2, err)) {
766 for (; n < phdr->len; n++) {
770 /* Write both hex chars of byte together */
771 if (!wtap_dump_file_write(wdh, c, 1, err)) {
778 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
786 /****************************/
787 /* Private helper functions */
788 /****************************/
790 /**********************************************************************/
791 /* Read a new line from the file, starting at offset. */
792 /* - writes data to its argument linebuff */
793 /* - on return 'offset' will point to the next position to read from */
794 /* - return TRUE if this read is successful */
795 /**********************************************************************/
797 read_new_line(FILE_T fh, gint64 *offset, gint *length,
798 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
801 gint64 pos_before = file_tell(fh);
803 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
804 /* No characters found, or error */
805 *err = file_error(fh, err_info);
809 /* Set length (avoiding strlen()) and offset.. */
810 *length = (gint)(file_tell(fh) - pos_before);
811 *offset = *offset + *length;
813 /* ...but don't want to include newline in line length */
814 if (*length > 0 && linebuff[*length-1] == '\n') {
815 linebuff[*length-1] = '\0';
816 *length = *length - 1;
818 /* Nor do we want '\r' (as will be written when log is created on windows) */
819 if (*length > 0 && linebuff[*length-1] == '\r') {
820 linebuff[*length-1] = '\0';
821 *length = *length - 1;
828 /**********************************************************************/
829 /* Parse a line from buffer, by identifying: */
830 /* - context, port and direction of packet */
832 /* - data position and length */
833 /* Return TRUE if this packet looks valid and can be displayed */
834 /**********************************************************************/
836 parse_line(gchar *linebuff, gint line_length,
837 gint *seconds, gint *useconds,
838 long *before_time_offset, long *after_time_offset,
839 long *data_offset, gint *data_chars,
840 packet_direction_t *direction,
841 int *encap, int *is_comment, int *is_sprint,
842 gchar *aal_header_chars,
843 gchar *context_name, guint8 *context_portp,
844 gchar *protocol_name, gchar *variant_name,
849 char port_number_string[MAX_PORT_DIGITS+1];
850 int variant_digits = 0;
852 int protocol_chars = 0;
853 int outhdr_chars = 0;
855 char seconds_buff[MAX_SECONDS_CHARS+1];
857 char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
858 int subsecond_decimals_chars;
859 int skip_first_byte = FALSE;
860 gboolean atm_header_present = FALSE;
865 /* Read context name until find '.' */
866 for (n=0; (n < MAX_CONTEXT_NAME) && (n+1 < line_length) && (linebuff[n] != '.'); n++) {
867 if (linebuff[n] == '/') {
868 context_name[n] = '\0';
870 /* If not a comment (/////), not a valid line */
871 if (strncmp(linebuff+n, "/////", 5) != 0) {
875 /* There is no variant, outhdr, etc. Set protocol to be a comment */
876 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
880 if (!g_ascii_isalnum(linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
883 context_name[n] = linebuff[n];
885 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
889 /* Reset strings (that won't be set by comments) */
890 variant_name[0] = '\0';
891 outhdr_name[0] = '\0';
892 port_number_string[0] = '\0';
894 if (!(*is_comment)) {
895 /* '.' must follow context name */
896 if (linebuff[n] != '.') {
899 context_name[n] = '\0';
903 /* Now read port number */
904 for (port_digits = 0;
905 (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
906 n++, port_digits++) {
908 if (!g_ascii_isdigit(linebuff[n])) {
911 port_number_string[port_digits] = linebuff[n];
913 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
917 /* Slash char must follow port number */
918 if (linebuff[n] != '/')
922 port_number_string[port_digits] = '\0';
923 if (port_digits == 1) {
924 *context_portp = port_number_string[0] - '0';
927 *context_portp = atoi(port_number_string);
932 /* Now for the protocol name */
933 for (protocol_chars = 0;
934 (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
935 n++, protocol_chars++) {
937 if (!g_ascii_isalnum(linebuff[n]) && linebuff[n] != '_') {
940 protocol_name[protocol_chars] = linebuff[n];
942 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
943 /* If doesn't fit, fail rather than truncate */
946 protocol_name[protocol_chars] = '\0';
948 /* Slash char must follow protocol name */
949 if (linebuff[n] != '/') {
956 /* Following the / is the variant number. No digits indicate 1 */
957 for (variant_digits = 0;
958 (g_ascii_isdigit(linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
959 n++, variant_digits++) {
961 if (!g_ascii_isdigit(linebuff[n])) {
964 variant_name[variant_digits] = linebuff[n];
966 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
970 if (variant_digits > 0) {
971 variant_name[variant_digits] = '\0';
972 if (variant_digits == 1) {
973 variant = variant_name[0] - '0';
976 variant = atoi(variant_name);
980 variant_name[0] = '1';
981 variant_name[1] = '\0';
985 /* Outheader values may follow */
986 outhdr_name[0] = '\0';
987 if (linebuff[n] == ',') {
991 for (outhdr_chars = 0;
992 (g_ascii_isdigit(linebuff[n]) || linebuff[n] == ',') &&
993 (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
994 n++, outhdr_chars++) {
996 if (!g_ascii_isdigit(linebuff[n]) && (linebuff[n] != ',')) {
999 outhdr_name[outhdr_chars] = linebuff[n];
1001 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
1004 /* Terminate (possibly empty) string */
1005 outhdr_name[outhdr_chars] = '\0';
1010 /******************************************************************/
1011 /* Now check whether we know how to use a packet of this protocol */
1013 if ((strcmp(protocol_name, "ip") == 0) ||
1014 (strcmp(protocol_name, "sctp") == 0) ||
1015 (strcmp(protocol_name, "gre") == 0) ||
1016 (strcmp(protocol_name, "mipv6") == 0) ||
1017 (strcmp(protocol_name, "igmp") == 0)) {
1019 *encap = WTAP_ENCAP_RAW_IP;
1023 /* FP may be carried over ATM, which has separate atm header to parse */
1024 if ((strcmp(protocol_name, "fp") == 0) ||
1025 (strncmp(protocol_name, "fp_r", 4) == 0)) {
1027 if ((variant > 256) && (variant % 256 == 3)) {
1028 /* FP over udp is contained in IPPrim... */
1032 /* FP over AAL0 or AAL2 */
1033 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1034 atm_header_present = TRUE;
1037 else if (strcmp(protocol_name, "fpiur_r5") == 0) {
1038 /* FP (IuR) over AAL2 */
1039 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1040 atm_header_present = TRUE;
1044 if (strcmp(protocol_name, "ppp") == 0) {
1045 *encap = WTAP_ENCAP_PPP;
1048 if (strcmp(protocol_name, "isdn_l3") == 0) {
1049 /* TODO: find out what this byte means... */
1050 skip_first_byte = TRUE;
1051 *encap = WTAP_ENCAP_ISDN;
1054 if (strcmp(protocol_name, "isdn_l2") == 0) {
1055 *encap = WTAP_ENCAP_ISDN;
1058 if (strcmp(protocol_name, "ethernet") == 0) {
1059 *encap = WTAP_ENCAP_ETHERNET;
1062 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1063 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1065 *encap = DCT2000_ENCAP_SSCOP;
1068 if (strcmp(protocol_name, "frelay_l2") == 0) {
1069 *encap = WTAP_ENCAP_FRELAY;
1072 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1073 *encap = DCT2000_ENCAP_MTP2;
1076 if ((strcmp(protocol_name, "nbap") == 0) ||
1077 (strcmp(protocol_name, "nbap_r4") == 0) ||
1078 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1080 /* The entire message in these cases is nbap, so use an encap value. */
1081 *encap = DCT2000_ENCAP_NBAP;
1084 /* Not a supported board port protocol/encap, but can show as raw data or
1085 in some cases find protocol embedded inside primitive */
1086 *encap = DCT2000_ENCAP_UNHANDLED;
1090 /* Find separate ATM header if necessary */
1091 if (atm_header_present) {
1092 int header_chars_seen = 0;
1094 /* Scan ahead to the next $ */
1095 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1098 if (n+1 >= line_length) {
1102 /* Read consecutive hex chars into atm header buffer */
1104 ((n < line_length) &&
1105 (linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1106 (header_chars_seen < AAL_HEADER_CHARS));
1107 n++, header_chars_seen++) {
1109 aal_header_chars[header_chars_seen] = linebuff[n];
1110 /* Next 6 characters after '9' are mapped to a->f */
1111 if (!g_ascii_isdigit(linebuff[n])) {
1112 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1116 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
1124 /* If there is a number, skip all info to next '/'.
1125 TODO: for IP encapsulation, should store PDCP ueid, drb in pseudo info
1126 and display dct2000 dissector... */
1127 if (g_ascii_isdigit(linebuff[n])) {
1128 while ((n+1 < line_length) && linebuff[n] != '/') {
1134 while ((n+1 < line_length) && linebuff[n] == '/') {
1138 /* Skip a space that may happen here */
1139 if ((n+1 < line_length) && linebuff[n] == ' ') {
1143 /* Next character gives direction of message (must be 's' or 'r') */
1144 if (!(*is_comment)) {
1145 if (linebuff[n] == 's') {
1149 if (linebuff[n] == 'r') {
1150 *direction = received;
1163 /*********************************************************************/
1164 /* Find and read the timestamp */
1166 /* Now scan to the next digit, which should be the start of the timestamp */
1167 /* This will involve skipping " tm " */
1169 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1170 if (n >= line_length) {
1174 for (; (n < line_length) && !g_ascii_isdigit(linebuff[n]); n++);
1175 if (n >= line_length) {
1179 *before_time_offset = n;
1182 for (seconds_chars = 0;
1183 (linebuff[n] != '.') &&
1184 (seconds_chars <= MAX_SECONDS_CHARS) &&
1186 n++, seconds_chars++) {
1188 if (!g_ascii_isdigit(linebuff[n])) {
1189 /* Found a non-digit before decimal point. Fail */
1192 seconds_buff[seconds_chars] = linebuff[n];
1194 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1195 /* Didn't fit in buffer. Fail rather than use truncated */
1199 /* Convert found value into number */
1200 seconds_buff[seconds_chars] = '\0';
1201 *seconds = atoi(seconds_buff);
1203 /* The decimal point must follow the last of the seconds digits */
1204 if (linebuff[n] != '.') {
1210 /* Subsecond decimal digits (expect 4-digit accuracy) */
1211 for (subsecond_decimals_chars = 0;
1212 (linebuff[n] != ' ') &&
1213 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1215 n++, subsecond_decimals_chars++) {
1217 if (!g_ascii_isdigit(linebuff[n])) {
1220 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1222 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1223 /* More numbers than expected - give up */
1226 /* Convert found value into microseconds */
1227 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1228 *useconds = atoi(subsecond_decimals_buff) * 100;
1230 /* Space character must follow end of timestamp */
1231 if (linebuff[n] != ' ') {
1235 *after_time_offset = n++;
1237 /* If we have a string message, it could either be a comment (with '$') or
1238 a sprint line (no '$') */
1240 if (strncmp(linebuff+n, "l $", 3) != 0) {
1242 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
1246 if (!(*is_sprint)) {
1247 /* Now skip ahead to find start of data (marked by '$') */
1248 for (; (linebuff[n] != '$') && (linebuff[n] != '\'') && (n+1 < line_length); n++);
1249 if ((linebuff[n] == '\'') || (n+1 >= line_length)) {
1256 /* Set offset to data start within line */
1259 /* Set number of chars that comprise the hex string protocol data */
1260 *data_chars = line_length - n;
1262 /* May need to skip first byte (2 hex string chars) */
1263 if (skip_first_byte) {
1271 /***********************************/
1272 /* Process results of parse_line() */
1273 /***********************************/
1275 process_parsed_line(wtap *wth, dct2000_file_externals_t *file_externals,
1276 struct wtap_pkthdr *phdr,
1277 Buffer *buf, gint64 file_offset,
1278 char *linebuff, long dollar_offset,
1279 int seconds, int useconds, gchar *timestamp_string,
1280 packet_direction_t direction, int encap,
1281 gchar *context_name, guint8 context_port,
1282 gchar *protocol_name, gchar *variant_name,
1283 gchar *outhdr_name, gchar *aal_header_chars,
1284 gboolean is_comment, int data_chars,
1285 int *err, gchar **err_info)
1288 int stub_offset = 0;
1290 guint8 *frame_buffer;
1292 phdr->rec_type = REC_TYPE_PACKET;
1293 phdr->presence_flags = WTAP_HAS_TS;
1295 /* Make sure all packets go to Catapult DCT2000 dissector */
1296 phdr->pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
1298 /* Fill in timestamp (capture base + packet offset) */
1299 phdr->ts.secs = file_externals->start_secs + seconds;
1300 if ((file_externals->start_usecs + useconds) >= 1000000) {
1304 ((file_externals->start_usecs + useconds) % 1000000) *1000;
1307 * Calculate the length of the stub info and the packet data.
1308 * The packet data length is half bytestring length.
1310 phdr->caplen = (guint)strlen(context_name)+1 + /* Context name */
1312 (guint)strlen(timestamp_string)+1 + /* timestamp */
1313 (guint)strlen(variant_name)+1 + /* variant */
1314 (guint)strlen(outhdr_name)+1 + /* outhdr */
1315 (guint)strlen(protocol_name)+1 + /* Protocol name */
1318 (is_comment ? data_chars : (data_chars/2));
1319 if (phdr->caplen > WTAP_MAX_PACKET_SIZE) {
1321 * Probably a corrupt capture file; return an error,
1322 * so that our caller doesn't blow up trying to allocate
1323 * space for an immensely-large packet.
1325 *err = WTAP_ERR_BAD_FILE;
1326 *err_info = g_strdup_printf("catapult dct2000: File has %u-byte packet, bigger than maximum of %u",
1327 phdr->caplen, WTAP_MAX_PACKET_SIZE);
1330 phdr->len = phdr->caplen;
1332 /*****************************/
1333 /* Get the data buffer ready */
1334 ws_buffer_assure_space(buf, phdr->caplen);
1335 frame_buffer = ws_buffer_start_ptr(buf);
1337 /******************************************/
1338 /* Write the stub info to the data buffer */
1341 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1342 stub_offset += (int)(length + 1);
1344 /* Context port number */
1345 frame_buffer[stub_offset] = context_port;
1348 /* Timestamp within file */
1349 length = g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1);
1350 stub_offset += (int)(length + 1);
1353 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1354 stub_offset += (int)(length + 1);
1356 /* Protocol variant number (as string) */
1357 length = g_strlcpy((gchar*)&frame_buffer[stub_offset], variant_name, MAX_VARIANT_DIGITS+1);
1358 stub_offset += (int)(length + 1);
1361 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1362 stub_offset += (int)(length + 1);
1365 frame_buffer[stub_offset] = direction;
1369 frame_buffer[stub_offset] = (guint8)encap;
1373 /***********************************************************/
1374 /* Copy packet data into buffer, converting from ascii hex */
1375 for (n=0; n < data_chars; n+=2) {
1376 frame_buffer[stub_offset + n/2] =
1377 hex_byte_from_chars(linebuff+dollar_offset+n);
1381 /***********************************************************/
1382 /* Copy packet data into buffer, just copying ascii chars */
1383 for (n=0; n < data_chars; n++) {
1384 frame_buffer[stub_offset + n] = linebuff[dollar_offset+n];
1388 /*****************************************/
1389 /* Set packet pseudo-header if necessary */
1390 phdr->pseudo_header.dct2000.seek_off = file_offset;
1391 phdr->pseudo_header.dct2000.wth = wth;
1394 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1395 set_aal_info(&phdr->pseudo_header, direction, aal_header_chars);
1397 case WTAP_ENCAP_ISDN:
1398 set_isdn_info(&phdr->pseudo_header, direction);
1400 case WTAP_ENCAP_PPP:
1401 set_ppp_info(&phdr->pseudo_header, direction);
1405 /* Other supported types don't need to set anything here... */
1412 /*********************************************/
1413 /* Fill in atm pseudo-header with known info */
1414 /*********************************************/
1416 set_aal_info(union wtap_pseudo_header *pseudo_header,
1417 packet_direction_t direction,
1418 gchar *aal_header_chars)
1420 /* 'aal_head_chars' has this format (for AAL2 at least):
1421 Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1422 Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1423 Channel Identifier (8 bits) | ...
1426 /* Indicate that this is a reassembled PDU */
1427 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1429 /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1430 TODO: Can we infer the correct value here?
1431 Meanwhile, just use the direction to make them distinguishable...
1433 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1435 /* Assume always AAL2 for FP */
1436 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1438 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1439 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1441 /* vpi is 8 bits (2nd & 3rd nibble) */
1442 pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1443 hex_byte_from_chars(aal_header_chars+1);
1445 /* vci is next 16 bits */
1446 pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1447 ((hex_from_char(aal_header_chars[3]) << 12) |
1448 (hex_from_char(aal_header_chars[4]) << 8) |
1449 (hex_from_char(aal_header_chars[5]) << 4) |
1450 hex_from_char(aal_header_chars[6]));
1452 /* 0 means we don't know how many cells the frame comprises. */
1453 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1455 /* cid is usually last byte. Unless last char is not hex digit, in which
1456 case cid is derived from last char in ascii */
1457 if (g_ascii_isalnum(aal_header_chars[11])) {
1458 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1459 hex_byte_from_chars(aal_header_chars+10);
1462 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1463 (int)aal_header_chars[11] - '0';
1468 /**********************************************/
1469 /* Fill in isdn pseudo-header with known info */
1470 /**********************************************/
1472 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1473 packet_direction_t direction)
1475 /* This field is used to set the 'Source' and 'Destination' columns to
1476 'User' or 'Network'. If we assume that we're simulating the network,
1477 treat Received messages as being destined for the network.
1479 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1481 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1482 everything else would be treated as a B-channel
1484 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1488 /*********************************************/
1489 /* Fill in ppp pseudo-header with known info */
1490 /*********************************************/
1492 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1493 packet_direction_t direction)
1495 /* Set direction. */
1496 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1500 /********************************************************/
1501 /* Return hex nibble equivalent of hex string character */
1502 /********************************************************/
1504 hex_from_char(gchar c)
1506 if ((c >= '0') && (c <= '9')) {
1510 if ((c >= 'a') && (c <= 'f')) {
1511 return 0x0a + (c - 'a');
1514 /* Not a valid hex string character */
1520 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1521 static guint8 s_tableValues[256][256];
1523 /* Prepare table values so ready so don't need to check inside hex_byte_from_chars() */
1524 static void prepare_hex_byte_from_chars_table(void)
1526 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1527 'a', 'b', 'c', 'd', 'e', 'f' };
1530 for (i=0; i < 16; i++) {
1531 for (j=0; j < 16; j++) {
1532 s_tableValues[hex_char_array[i]][hex_char_array[j]] = i*16 + j;
1537 /* Extract and return a byte value from 2 ascii hex chars, starting from the given pointer */
1538 static guint8 hex_byte_from_chars(gchar *c)
1540 /* Return value from quick table lookup */
1541 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1546 /********************************************************/
1547 /* Return character corresponding to hex nibble value */
1548 /********************************************************/
1550 char_from_hex(guint8 hex)
1552 static const char hex_lookup[16] =
1553 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1559 return hex_lookup[hex];
1562 /***********************************************/
1563 /* Equality test for packet prefix hash tables */
1564 /***********************************************/
1566 packet_offset_equal(gconstpointer v, gconstpointer v2)
1568 /* Dereferenced pointers must have same gint64 offset value */
1569 return (*(const gint64*)v == *(const gint64*)v2);
1573 /********************************************/
1574 /* Hash function for packet-prefix hash table */
1575 /********************************************/
1577 packet_offset_hash_func(gconstpointer v)
1579 /* Use low-order bits of gint64 offset value */
1580 return (guint)(*(const gint64*)v);
1584 /************************************************************************/
1585 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1586 /* Set secs and usecs as output */
1587 /* Return FALSE if no valid time can be read */
1588 /************************************************************************/
1590 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1593 #define MAX_MONTH_LETTERS 9
1594 char month[MAX_MONTH_LETTERS+1];
1596 int day, year, hour, minute, second;
1599 /* If line longer than expected, file is probably not correctly formatted */
1600 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
1604 /********************************************************/
1605 /* Scan for all fields */
1606 scan_found = sscanf(linebuff, "%9s %2d, %4d %2d:%2d:%2d.%4u",
1607 month, &day, &year, &hour, &minute, &second, usecs);
1608 if (scan_found != 7) {
1609 /* Give up if not all found */
1613 if (strcmp(month, "January" ) == 0) tm.tm_mon = 0;
1614 else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1;
1615 else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2;
1616 else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3;
1617 else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4;
1618 else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5;
1619 else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6;
1620 else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7;
1621 else if (strcmp(month, "September") == 0) tm.tm_mon = 8;
1622 else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9;
1623 else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10;
1624 else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11;
1626 /* Give up if not found a properly-formatted date */
1630 /******************************************************/
1631 /* Fill in remaining fields and return it in a time_t */
1632 tm.tm_year = year - 1900;
1637 tm.tm_isdst = -1; /* daylight saving time info not known */
1639 /* Get seconds from this time */
1640 *secs = mktime(&tm);
1642 /* Multiply 4 digits given to get micro-seconds */
1643 *usecs = *usecs * 100;
1648 /* Free the data allocated inside a line_prefix_info_t */
1650 free_line_prefix_info(gpointer key, gpointer value,
1651 gpointer user_data _U_)
1653 line_prefix_info_t *info = (line_prefix_info_t*)value;
1655 /* Free the 64-bit key value */
1658 /* Free the strings inside */
1659 g_free(info->before_time);
1660 if (info->after_time) {
1661 g_free(info->after_time);
1664 /* And the structure itself */
1667 /* Item will always be removed from table */
1672 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1677 * indent-tabs-mode: nil
1680 * vi: set shiftwidth=4 tabstop=8 expandtab:
1681 * :indentSize=4:tabSize=8:noTabs=true: