6 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
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.
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.
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.
30 #include "file_wrappers.h"
33 #include "catapult_dct2000.h"
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
49 - support for FP over AAL0
50 - support for IuR interface FP
54 /* 's' or 'r' of a packet as read from .out file */
55 typedef enum packet_direction_t
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 ") */
73 /*******************************************************************/
74 /* Information stored external to a file (wtap) needed for reading and dumping */
75 typedef struct dct2000_file_externals
77 /* Remember the time at the start of capture */
82 * The following information is needed only for dumping.
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.
89 /* Buffer to hold first line, including magic and format number */
90 gchar firstline[MAX_FIRST_LINE_LENGTH];
91 gint firstline_length;
93 /* Buffer to hold second line with formatted file creation data/time */
94 gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
95 gint secondline_length;
97 /* Hash table to store text prefix data part of displayed packets.
98 Records (file offset -> line_prefix_info_t)
100 GHashTable *packet_prefix_table;
101 } dct2000_file_externals_t;
103 /* 'Magic number' at start of Catapult DCT2000 .out files. */
104 static const gchar catapult_dct2000_magic[] = "Session Transcript";
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);
116 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
117 const guint8 *pd, int *err);
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,
125 static gboolean parse_line(char *linebuff, gint line_length,
126 gint *seconds, gint *useconds,
127 long *before_time_offset, long *after_time_offset,
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,
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,
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);
146 static void set_pseudo_header_info(wtap *wth,
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);
160 static gint packet_offset_equal(gconstpointer v, gconstpointer v2);
161 static guint packet_offset_hash_func(gconstpointer v);
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);
168 /********************************************/
169 /* Open file (for reading) */
170 /********************************************/
172 catapult_dct2000_open(wtap *wth, int *err, gchar **err_info)
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;
182 /* Clear errno before reading from the file */
186 /********************************************************************/
187 /* First line needs to contain at least as many characters as magic */
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)
195 if (((size_t)firstline_length < strlen(catapult_dct2000_magic)) ||
196 firstline_length >= MAX_FIRST_LINE_LENGTH) {
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) {
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;
212 /*********************************************************************/
213 /* Need entry in file_externals table */
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));
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;
224 /***********************************************************/
225 /* Second line contains file timestamp */
226 /* Store this offset in in file_externals */
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)
235 if ((file_externals->secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) ||
236 (!get_file_time_stamp(linebuff, ×tamp, &usecs))) {
238 /* Give up if file time line wasn't valid */
239 g_free(file_externals);
243 /* Fill in timestamp */
244 file_externals->start_secs = timestamp;
245 file_externals->start_usecs = usecs;
247 /* Copy this second line into buffer so could write out later */
248 g_strlcpy(file_externals->secondline, linebuff, file_externals->secondline_length+1);
251 /************************************************************/
252 /* File is for us. Fill in details so packets can be read */
254 /* Set our file type */
255 wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
257 /* Use our own encapsulation to send all packets to our stub dissector */
258 wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
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;
265 /* Choose microseconds (have 4 decimal places...) */
266 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
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);
274 /* Set this wtap to point to the file_externals */
275 wth->priv = (void*)file_externals;
281 /* Ugly, but much faster than using g_snprintf! */
282 static void write_timestamp_string(char *timestamp_string, int secs, int tenthousandths)
288 timestamp_string[idx++] = ((secs % 10)) + '0';
290 else if (secs < 100) {
291 timestamp_string[idx++] = ( secs / 10) + '0';
292 timestamp_string[idx++] = ((secs % 10)) + '0';
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';
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';
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';
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';
321 g_snprintf(timestamp_string, MAX_TIMESTAMP_LEN, "%d.%04d", secs, tenthousandths);
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';
333 /**************************************************/
334 /* Read packet function. */
335 /* Look for and read the next usable packet */
336 /* - return TRUE and details if found */
337 /**************************************************/
339 catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
342 gint64 offset = file_tell(wth->fh);
343 long dollar_offset, before_time_offset, after_time_offset;
344 packet_direction_t direction;
347 /* Get wtap external structure for this wtap */
348 dct2000_file_externals_t *file_externals =
349 (dct2000_file_externals_t*)wth->priv;
351 /* There *has* to be an entry for this wth */
352 if (!file_externals) {
356 /* Search for a line containing a usable packet */
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];
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);
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)) {
380 return FALSE; /* error */
381 /* No more lines can be read, so quit. */
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,
389 &data_chars, &direction, &encap, &is_comment, &is_sprint,
391 context_name, &context_port,
392 protocol_name, variant_name, outhdr_name)) {
393 guint8 *frame_buffer;
396 line_prefix_info_t *line_prefix_info;
397 char timestamp_string[MAX_TIMESTAMP_LEN+1];
400 write_timestamp_string(timestamp_string, seconds, useconds/100);
402 wth->phdr.presence_flags = WTAP_HAS_TS;
404 /* All packets go to Catapult DCT2000 stub dissector */
405 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
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.
410 *data_offset = this_offset;
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) {
418 ((file_externals->start_usecs + useconds) % 1000000) *1000;
420 /* Get buffer pointer ready */
421 buffer_assure_space(wth->frame_buffer,
422 strlen(context_name)+1 + /* Context name */
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 */
430 (is_comment ? data_chars : (data_chars/2)));
431 frame_buffer = buffer_start_ptr(wth->frame_buffer);
434 /*********************/
435 /* Write stub header */
436 stub_offset = write_stub_header(frame_buffer, timestamp_string,
437 direction, encap, context_name,
439 protocol_name, variant_name,
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));
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);
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];
463 /* Store the packet prefix in the hash table */
464 line_prefix_info = g_new(line_prefix_info_t,1);
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';
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
474 if (((size_t)(dollar_offset - after_time_offset -1) == strlen(" l ")) &&
475 (strncmp(linebuff+after_time_offset, " l ", strlen(" l ")) == 0)) {
477 line_prefix_info->after_time = NULL;
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';
486 /* Add packet entry into table */
487 pkey = g_malloc(sizeof(*pkey));
489 g_hash_table_insert(file_externals->packet_prefix_table, pkey, line_prefix_info);
491 /* Set pseudo-header if necessary */
492 set_pseudo_header_info(wth, encap, this_offset, &wth->phdr.pseudo_header,
493 direction, aal_header_chars);
495 /* OK, we have packet details to return */
500 /* No packet details to return... */
505 /**************************************************/
506 /* Read & seek function. */
507 /**************************************************/
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)
513 union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
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;
527 int seconds, useconds, data_chars;
529 /* Get wtap external structure for this wtap */
530 dct2000_file_externals_t *file_externals =
531 (dct2000_file_externals_t*)wth->priv;
533 /* There *has* to be an entry for this wth */
534 if (!file_externals) {
541 /* Seek to beginning of packet */
542 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) {
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)) {
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,
556 &data_chars, &direction, &encap, &is_comment, &is_sprint,
558 context_name, &context_port,
559 protocol_name, variant_name, outhdr_name)) {
562 char timestamp_string[MAX_TIMESTAMP_LEN+1];
564 write_timestamp_string(timestamp_string, seconds, useconds/100);
566 /* Make sure all packets go to catapult dct2000 dissector */
567 wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
570 /*********************/
571 /* Write stub header */
572 stub_offset = write_stub_header(pd, timestamp_string,
573 direction, encap, context_name,
575 protocol_name, variant_name,
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);
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];
594 /* Set packet pseudo-header if necessary */
595 set_pseudo_header_info(wth, encap, seek_off, pseudo_header, direction,
602 /* If get here, must have failed */
604 *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
605 "line at position %" G_GINT64_MODIFIER "d",
611 /***************************************************************************/
612 /* Free dct2000-specific capture info from file that was open for reading */
613 /***************************************************************************/
615 catapult_dct2000_close(wtap *wth)
617 /* Get externals for this file */
618 dct2000_file_externals_t *file_externals =
619 (dct2000_file_externals_t*)wth->priv;
621 /* The entry *has* to be found */
622 if (!file_externals) {
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);
636 /***************************/
638 /***************************/
641 gboolean first_packet_written;
642 struct wtap_nstime start_time;
645 /*****************************************************/
646 /* The file that we are writing to has been opened. */
647 /* Set other dump callbacks. */
648 /*****************************************************/
650 catapult_dct2000_dump_open(wtap_dumper *wdh, int *err _U_)
652 /* Fill in other dump callbacks */
653 wdh->subtype_write = catapult_dct2000_dump;
658 /*********************************************************/
659 /* Respond to queries about which encap types we support */
661 /*********************************************************/
663 catapult_dct2000_dump_can_write_encap(int encap)
666 case WTAP_ENCAP_CATAPULT_DCT2000:
667 /* We support this */
671 /* But don't write to any other formats... */
672 return WTAP_ERR_UNSUPPORTED_ENCAP;
677 /*****************************************/
678 /* Write a single packet out to the file */
679 /*****************************************/
682 catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
683 const guint8 *pd, int *err)
685 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
687 line_prefix_info_t *prefix = NULL;
688 gchar time_string[16];
690 gboolean is_sprint = FALSE;
691 dct2000_dump_t *dct2000;
692 int consecutive_slashes=0;
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;
701 dct2000 = (dct2000_dump_t *)wdh->priv;
702 if (dct2000 == NULL) {
704 /* Write out saved first line */
705 if (!wtap_dump_file_write(wdh, file_externals->firstline,
706 file_externals->firstline_length, err)) {
709 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
713 /* Also write out saved second line with timestamp corresponding to the
714 opening time of the log.
716 if (!wtap_dump_file_write(wdh, file_externals->secondline,
717 file_externals->secondline_length, err)) {
720 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
724 /* Allocate the dct2000-specific dump structure */
725 dct2000 = (dct2000_dump_t *)g_malloc(sizeof(dct2000_dump_t));
726 wdh->priv = (void *)dct2000;
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);
733 /* Set flag so don't write header out again */
734 dct2000->first_packet_written = TRUE;
738 /******************************************************************/
739 /* Write out this packet's prefix, including calculated timestamp */
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));
745 /* Write out text before timestamp */
746 if (!wtap_dump_file_write(wdh, prefix->before_time,
747 strlen(prefix->before_time), err)) {
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 != '/')) {
757 while (p_c && (*p_c == '/')) {
758 consecutive_slashes++;
761 is_comment = (consecutive_slashes == 5);
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);
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);
775 /* Write out the calculated timestamp */
776 if (!wtap_dump_file_write(wdh, time_string, strlen(time_string), err)) {
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)) {
787 if (!wtap_dump_file_write(wdh, prefix->after_time,
788 strlen(prefix->after_time), err)) {
794 /****************************************************************/
795 /* Need to skip stub header at start of pd before we reach data */
798 for (n=0; pd[n] != '\0'; n++);
801 /* Context port number */
805 for (; pd[n] != '\0'; n++);
810 is_sprint = strcmp(pd+n, "sprint") == 0;
812 for (; pd[n] != '\0'; n++);
815 /* Variant number (as string) */
816 for (; pd[n] != '\0'; n++);
819 /* Outhdr (as string) */
820 for (; pd[n] != '\0'; n++);
823 /* Direction & encap */
827 /**************************************/
828 /* Remainder is encapsulated protocol */
829 if (!wtap_dump_file_write(wdh, is_sprint ? " " : "$", 1, err)) {
834 /* Each binary byte is written out as 2 hex string chars */
835 for (; n < phdr->len; n++) {
837 c[0] = char_from_hex((guint8)(pd[n] >> 4));
838 c[1] = char_from_hex((guint8)(pd[n] & 0x0f));
840 /* Write both hex chars of byte together */
841 if (!wtap_dump_file_write(wdh, c, 2, err)) {
847 for (; n < phdr->len; n++) {
851 /* Write both hex chars of byte together */
852 if (!wtap_dump_file_write(wdh, c, 1, err)) {
859 if (!wtap_dump_file_write(wdh, "\n", 1, err)) {
867 /****************************/
868 /* Private helper functions */
869 /****************************/
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 /**********************************************************************/
878 read_new_line(FILE_T fh, gint64 *offset, gint *length,
879 gchar *linebuff, size_t linebuffsize, int *err, gchar **err_info)
882 gint64 pos_before = file_tell(fh);
884 if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) {
885 /* No characters found, or error */
886 *err = file_error(fh, err_info);
890 /* Set length (avoiding strlen()) and offset.. */
891 *length = (gint)(file_tell(fh) - pos_before);
892 *offset = *offset + *length;
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;
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;
909 /**********************************************************************/
910 /* Parse a line from buffer, by identifying: */
911 /* - context, port and direction of packet */
913 /* - data position and length */
914 /* Return TRUE if this packet looks valid and can be displayed */
915 /**********************************************************************/
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,
930 char port_number_string[MAX_PORT_DIGITS+1];
931 int variant_digits = 0;
933 int protocol_chars = 0;
934 int outhdr_chars = 0;
936 char seconds_buff[MAX_SECONDS_CHARS+1];
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;
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';
951 /* If not a comment (/////), not a valid line */
952 if (strncmp(linebuff+n, "/////", 5) != 0) {
956 /* There is no variant, outhdr, etc. Set protocol to be a comment */
957 g_strlcpy(protocol_name, "comment", MAX_PROTOCOL_NAME);
961 if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_') && (linebuff[n] != '-')) {
964 context_name[n] = linebuff[n];
966 if (n == MAX_CONTEXT_NAME || (n+1 >= line_length)) {
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';
975 if (!(*is_comment)) {
976 /* '.' must follow context name */
977 if (linebuff[n] != '.') {
980 context_name[n] = '\0';
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++) {
989 if (!isdigit((guchar)linebuff[n])) {
992 port_number_string[port_digits] = linebuff[n];
994 if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length)) {
998 /* Slash char must follow port number */
999 if (linebuff[n] != '/')
1003 port_number_string[port_digits] = '\0';
1004 if (port_digits == 1) {
1005 *context_portp = port_number_string[0] - '0';
1008 *context_portp = atoi(port_number_string);
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++) {
1018 if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_') {
1021 protocol_name[protocol_chars] = linebuff[n];
1023 if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) {
1024 /* If doesn't fit, fail rather than truncate */
1027 protocol_name[protocol_chars] = '\0';
1029 /* Slash char must follow protocol name */
1030 if (linebuff[n] != '/') {
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++) {
1042 if (!isdigit((guchar)linebuff[n])) {
1045 variant_name[variant_digits] = linebuff[n];
1047 if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length)) {
1051 if (variant_digits > 0) {
1052 variant_name[variant_digits] = '\0';
1053 if (variant_digits == 1) {
1054 variant = variant_name[0] - '0';
1057 variant = atoi(variant_name);
1061 variant_name[0] = '1';
1062 variant_name[1] = '\0';
1066 /* Outheader values may follow */
1067 outhdr_name[0] = '\0';
1068 if (linebuff[n] == ',') {
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++) {
1077 if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ',')) {
1080 outhdr_name[outhdr_chars] = linebuff[n];
1082 if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length)) {
1085 /* Terminate (possibly empty) string */
1086 outhdr_name[outhdr_chars] = '\0';
1091 /******************************************************************/
1092 /* Now check whether we know how to use a packet of this protocol */
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)) {
1100 *encap = WTAP_ENCAP_RAW_IP;
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)) {
1108 if ((variant > 256) && (variant % 256 == 3)) {
1109 /* FP over udp is contained in IPPrim... */
1113 /* FP over AAL0 or AAL2 */
1114 *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
1115 atm_header_present = TRUE;
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;
1125 if (strcmp(protocol_name, "ppp") == 0) {
1126 *encap = WTAP_ENCAP_PPP;
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;
1135 if (strcmp(protocol_name, "isdn_l2") == 0) {
1136 *encap = WTAP_ENCAP_ISDN;
1139 if (strcmp(protocol_name, "ethernet") == 0) {
1140 *encap = WTAP_ENCAP_ETHERNET;
1143 if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1144 (strcmp(protocol_name, "saaluni_sscop") == 0)) {
1146 *encap = DCT2000_ENCAP_SSCOP;
1149 if (strcmp(protocol_name, "frelay_l2") == 0) {
1150 *encap = WTAP_ENCAP_FRELAY;
1153 if (strcmp(protocol_name, "ss7_mtp2") == 0) {
1154 *encap = DCT2000_ENCAP_MTP2;
1157 if ((strcmp(protocol_name, "nbap") == 0) ||
1158 (strcmp(protocol_name, "nbap_r4") == 0) ||
1159 (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0)) {
1161 /* The entire message in these cases is nbap, so use an encap value. */
1162 *encap = DCT2000_ENCAP_NBAP;
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;
1171 /* Find separate ATM header if necessary */
1172 if (atm_header_present) {
1173 int header_chars_seen = 0;
1175 /* Scan ahead to the next $ */
1176 for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1179 if (n+1 >= line_length) {
1183 /* Read consecutive hex chars into atm header buffer */
1185 ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1186 (n < line_length) &&
1187 (header_chars_seen < AAL_HEADER_CHARS));
1188 n++, header_chars_seen++) {
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;
1197 if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length) {
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] != '/') {
1215 while ((n+1 < line_length) && linebuff[n] == '/') {
1219 /* Skip a space that may happen here */
1220 if ((n+1 < line_length) && linebuff[n] == ' ') {
1224 /* Next character gives direction of message (must be 's' or 'r') */
1225 if (!(*is_comment)) {
1226 if (linebuff[n] == 's') {
1230 if (linebuff[n] == 'r') {
1231 *direction = received;
1244 /*********************************************************************/
1245 /* Find and read the timestamp */
1247 /* Now scan to the next digit, which should be the start of the timestamp */
1248 /* This will involve skipping " tm " */
1250 for (; ((linebuff[n] != 't') || (linebuff[n+1] != 'm')) && (n+1 < line_length); n++);
1251 if (n >= line_length) {
1255 for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1256 if (n >= line_length) {
1260 *before_time_offset = n;
1263 for (seconds_chars = 0;
1264 (linebuff[n] != '.') &&
1265 (seconds_chars <= MAX_SECONDS_CHARS) &&
1267 n++, seconds_chars++) {
1269 if (!isdigit((guchar)linebuff[n])) {
1270 /* Found a non-digit before decimal point. Fail */
1273 seconds_buff[seconds_chars] = linebuff[n];
1275 if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) {
1276 /* Didn't fit in buffer. Fail rather than use truncated */
1280 /* Convert found value into number */
1281 seconds_buff[seconds_chars] = '\0';
1282 *seconds = atoi(seconds_buff);
1284 /* The decimal point must follow the last of the seconds digits */
1285 if (linebuff[n] != '.') {
1291 /* Subsecond decimal digits (expect 4-digit accuracy) */
1292 for (subsecond_decimals_chars = 0;
1293 (linebuff[n] != ' ') &&
1294 (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1296 n++, subsecond_decimals_chars++) {
1298 if (!isdigit((guchar)linebuff[n])) {
1301 subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1303 if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) {
1304 /* More numbers than expected - give up */
1307 /* Convert found value into microseconds */
1308 subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1309 *useconds = atoi(subsecond_decimals_buff) * 100;
1311 /* Space character must follow end of timestamp */
1312 if (linebuff[n] != ' ') {
1316 *after_time_offset = n++;
1318 /* If we have a string message, it could either be a comment (with '$') or
1319 a sprint line (no '$') */
1321 if (strncmp(linebuff+n, "l $", 3) != 0) {
1323 g_strlcpy(protocol_name, "sprint", MAX_PROTOCOL_NAME);
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)) {
1337 /* Set offset to data start within line */
1340 /* Set number of chars that comprise the hex string protocol data */
1341 *data_chars = line_length - n;
1343 /* May need to skip first byte (2 hex string chars) */
1344 if (skip_first_byte) {
1352 /*****************************************************************/
1353 /* Write the stub info to the data buffer while reading a packet */
1354 /*****************************************************************/
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,
1362 int stub_offset = 0;
1365 length = g_strlcpy((char*)frame_buffer, context_name, MAX_CONTEXT_NAME+1);
1366 stub_offset += (int)(length + 1);
1368 /* Context port number */
1369 frame_buffer[stub_offset] = context_port;
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);
1377 length = g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1);
1378 stub_offset += (int)(length + 1);
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);
1385 length = g_strlcpy((char*)&frame_buffer[stub_offset], outhdr_name, MAX_OUTHDR_NAME+1);
1386 stub_offset += (int)(length + 1);
1389 frame_buffer[stub_offset] = direction;
1393 frame_buffer[stub_offset] = (guint8)encap;
1400 /**************************************************************/
1401 /* Set pseudo-header info depending upon packet encapsulation */
1402 /**************************************************************/
1404 set_pseudo_header_info(wtap *wth,
1407 union wtap_pseudo_header *pseudo_header,
1408 packet_direction_t direction,
1409 gchar *aal_header_chars)
1411 pseudo_header->dct2000.seek_off = file_offset;
1412 pseudo_header->dct2000.wth = wth;
1414 switch (pkt_encap) {
1415 case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1416 set_aal_info(pseudo_header, direction, aal_header_chars);
1418 case WTAP_ENCAP_ISDN:
1419 set_isdn_info(pseudo_header, direction);
1421 case WTAP_ENCAP_PPP:
1422 set_ppp_info(pseudo_header, direction);
1426 /* Other supported types don't need to set anything here... */
1432 /*********************************************/
1433 /* Fill in atm pseudo-header with known info */
1434 /*********************************************/
1436 set_aal_info(union wtap_pseudo_header *pseudo_header,
1437 packet_direction_t direction,
1438 gchar *aal_header_chars)
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) | ...
1446 /* Indicate that this is a reassembled PDU */
1447 pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
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...
1453 pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1455 /* Assume always AAL2 for FP */
1456 pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1458 pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1459 pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
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);
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]));
1472 /* 0 means we don't know how many cells the frame comprises. */
1473 pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
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);
1482 pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1483 (int)aal_header_chars[11] - '0';
1488 /**********************************************/
1489 /* Fill in isdn pseudo-header with known info */
1490 /**********************************************/
1492 set_isdn_info(union wtap_pseudo_header *pseudo_header,
1493 packet_direction_t direction)
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.
1499 pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1501 /* This corresponds to the circuit ID. 0 is treated as LAPD,
1502 everything else would be treated as a B-channel
1504 pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1508 /*********************************************/
1509 /* Fill in ppp pseudo-header with known info */
1510 /*********************************************/
1512 set_ppp_info(union wtap_pseudo_header *pseudo_header,
1513 packet_direction_t direction)
1515 /* Set direction. */
1516 pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1520 /********************************************************/
1521 /* Return hex nibble equivalent of hex string character */
1522 /********************************************************/
1524 hex_from_char(gchar c)
1526 if ((c >= '0') && (c <= '9')) {
1530 if ((c >= 'a') && (c <= 'f')) {
1531 return 0x0a + (c - 'a');
1534 /* Not a valid hex string character */
1540 /* Table allowing fast lookup from a pair of ascii hex characters to a guint8 */
1541 static guint8 s_tableValues[255][255];
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)
1546 guchar hex_char_array[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
1547 'a', 'b', 'c', 'd', 'e', 'f' };
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;
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)
1560 /* Return value from quick table lookup */
1561 return s_tableValues[(unsigned char)c[0]][(unsigned char)c[1]];
1566 /********************************************************/
1567 /* Return character corresponding to hex nibble value */
1568 /********************************************************/
1570 char_from_hex(guint8 hex)
1572 static char hex_lookup[16] =
1573 { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1579 return hex_lookup[hex];
1582 /***********************************************/
1583 /* Equality test for packet prefix hash tables */
1584 /***********************************************/
1586 packet_offset_equal(gconstpointer v, gconstpointer v2)
1588 /* Dereferenced pointers must have same gint64 offset value */
1589 return (*(const gint64*)v == *(const gint64*)v2);
1593 /********************************************/
1594 /* Hash function for packet-prefix hash table */
1595 /********************************************/
1597 packet_offset_hash_func(gconstpointer v)
1599 /* Use low-order bits of gint64 offset value */
1600 return (guint)(*(const gint64*)v);
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 /************************************************************************/
1610 get_file_time_stamp(gchar *linebuff, time_t *secs, guint32 *usecs)
1614 #define MAX_MONTH_LETTERS 9
1615 char month[MAX_MONTH_LETTERS+1];
1617 int day, year, hour, minute, second;
1620 /* If line longer than expected, file is probably not correctly formatted */
1621 if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) {
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];
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;
1645 /* Give up if not found a properly-formatted date */
1648 /* Skip space char */
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 */
1660 /******************************************************/
1661 /* Fill in remaining fields and return it in a time_t */
1662 tm.tm_year = year - 1900;
1667 tm.tm_isdst = -1; /* daylight saving time info not known */
1669 /* Get seconds from this time */
1670 *secs = mktime(&tm);
1672 /* Multiply 4 digits given to get micro-seconds */
1673 *usecs = *usecs * 100;
1678 /* Free the data allocated inside a line_prefix_info_t */
1680 free_line_prefix_info(gpointer key, gpointer value,
1681 gpointer user_data _U_)
1683 line_prefix_info_t *info = (line_prefix_info_t*)value;
1685 /* Free the 64-bit key value */
1688 /* Free the strings inside */
1689 g_free(info->before_time);
1690 if (info->after_time) {
1691 g_free(info->after_time);
1694 /* And the structure itself */
1697 /* Item will always be removed from table */