More P64 changes.
[metze/wireshark/wip.git] / wiretap / dbs-etherwatch.c
1 /* dbs-etherwatch.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 2001 by Marc Milgram <ethereal@mmilgram.NOSPAMmail.net>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include "wtap-int.h"
27 #include "buffer.h"
28 #include "dbs-etherwatch.h"
29 #include "file_wrappers.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <ctype.h>
35
36 /* This module reads the text output of the 'DBS-ETHERTRACE' command in VMS
37  * It was initially based on vms.c.
38  */
39
40 /*
41    Example 'ETHERWATCH' output data (with "printable" characters in the
42    "printable characters" section of the output replaced by "." if they have
43    the 8th bit set, so as not to upset compilers that are expecting text
44    in comments to be in a particular character encoding that can't handle
45    those values):
46 ETHERWATCH  X5-008
47 42 names and addresses were loaded
48 Reading recorded data from PERSISTENCE
49 ------------------------------------------------------------------------------
50 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
51 Protocol 08-00 00 00-00-00-00-00,   60 byte buffer at 10-OCT-2001 10:20:45.16
52   [E..<8...........]-    0-[45 00 00 3C 38 93 00 00 1D 06 D2 12 80 93 11 1A]
53   [.........(......]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 90 00 00 00 00]
54   [................]-   32-[A0 02 FF FF 95 BD 00 00 02 04 05 B4 03 03 04 01]
55   [............    ]-   48-[01 01 08 0A 90 90 E5 14 00 00 00 00]
56 ------------------------------------------------------------------------------
57 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
58 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
59   [E..(8......%....]-    0-[45 00 00 28 38 94 00 00 1D 06 D2 25 80 93 11 1A]
60   [.........(..Z.4w]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 91 5A 1C 34 77]
61   [P.#(.s..........]-   32-[50 10 23 28 C1 73 00 00 02 04 05 B4 03 03 00 00]
62   [..              ]-   48-[02 04]
63
64
65 Alternative HEX only output, slightly more efficient and all wireshark needs:
66 ------------------------------------------------------------------------------
67 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
68 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
69      0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6]
70     20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00]
71     40-[03 30 30 30 30 30 00 00 03 30]
72  */
73
74 /* Magic text to check for DBS-ETHERWATCH-ness of file */
75 static const char dbs_etherwatch_hdr_magic[]  =
76 { 'E', 'T', 'H', 'E', 'R', 'W', 'A', 'T', 'C', 'H', ' ', ' '};
77 #define DBS_ETHERWATCH_HDR_MAGIC_SIZE  \
78         (sizeof dbs_etherwatch_hdr_magic  / sizeof dbs_etherwatch_hdr_magic[0])
79
80 /* Magic text for start of packet */
81 static const char dbs_etherwatch_rec_magic[]  =
82 {'F', 'r', 'o', 'm', ' '};
83 #define DBS_ETHERWATCH_REC_MAGIC_SIZE \
84         (sizeof dbs_etherwatch_rec_magic  / sizeof dbs_etherwatch_rec_magic[0])
85
86 /*
87  * XXX - is this the biggest packet we can get?
88  */
89 #define DBS_ETHERWATCH_MAX_PACKET_LEN   16384
90
91 static gboolean dbs_etherwatch_read(wtap *wth, int *err, gchar **err_info,
92         gint64 *data_offset);
93 static gboolean dbs_etherwatch_seek_read(wtap *wth, gint64 seek_off,
94         union wtap_pseudo_header *pseudo_header, guint8 *pd, int len,
95         int *err, gchar **err_info);
96 static int parse_dbs_etherwatch_packet(wtap *wth, FILE_T fh, guint8* buf,
97         int *err, gchar **err_info);
98 static guint parse_single_hex_dump_line(char* rec, guint8 *buf,
99         int byte_offset);
100 static guint parse_hex_dump(char* dump, guint8 *buf, char seperator, char end);
101
102 /* Seeks to the beginning of the next packet, and returns the
103    byte offset.  Returns -1 on failure, and sets "*err" to the error. */
104 static gint64 dbs_etherwatch_seek_next_packet(wtap *wth, int *err)
105 {
106   int byte;
107   unsigned int level = 0;
108   gint64 cur_off;
109
110   while ((byte = file_getc(wth->fh)) != EOF) {
111     if (byte == dbs_etherwatch_rec_magic[level]) {
112       level++;
113       if (level >= DBS_ETHERWATCH_REC_MAGIC_SIZE) {
114         /* note: we're leaving file pointer right after the magic characters */
115         cur_off = file_tell(wth->fh);
116         if (cur_off == -1) {
117           /* Error. */
118           *err = file_error(wth->fh);
119           return -1;
120         }
121         return cur_off + 1;
122       }
123     } else {
124       level = 0;
125     }
126   }
127   if (file_eof(wth->fh)) {
128     /* We got an EOF. */
129     *err = 0;
130   } else {
131     /* We (presumably) got an error (there's no equivalent to "ferror()"
132        in zlib, alas, so we don't have a wrapper to check for an error). */
133     *err = file_error(wth->fh);
134   }
135   return -1;
136 }
137
138 #define DBS_ETHERWATCH_HEADER_LINES_TO_CHECK    200
139 #define DBS_ETHERWATCH_LINE_LENGTH              240
140
141 /* Look through the first part of a file to see if this is
142  * a DBS Ethertrace text trace file.
143  *
144  * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error;
145  * if we get an I/O error, "*err" will be set to a non-zero value.
146  */
147 static gboolean dbs_etherwatch_check_file_type(wtap *wth, int *err)
148 {
149         char    buf[DBS_ETHERWATCH_LINE_LENGTH];
150         int     line, byte;
151         gsize   reclen;
152         unsigned int i, level;
153
154         buf[DBS_ETHERWATCH_LINE_LENGTH-1] = 0;
155
156         for (line = 0; line < DBS_ETHERWATCH_HEADER_LINES_TO_CHECK; line++) {
157                 if (file_gets(buf, DBS_ETHERWATCH_LINE_LENGTH, wth->fh)!=NULL){
158
159                         reclen = strlen(buf);
160                         if (reclen < DBS_ETHERWATCH_HDR_MAGIC_SIZE)
161                                 continue;
162
163                         level = 0;
164                         for (i = 0; i < reclen; i++) {
165                                 byte = buf[i];
166                                 if (byte == dbs_etherwatch_hdr_magic[level]) {
167                                         level++;
168                                         if (level >=
169                                               DBS_ETHERWATCH_HDR_MAGIC_SIZE) {
170                                                 return TRUE;
171                                         }
172                                 }
173                                 else
174                                         level = 0;
175                         }
176                 }
177                 else {
178                         /* EOF or error. */
179                         if (file_eof(wth->fh))
180                                 *err = 0;
181                         else
182                                 *err = file_error(wth->fh);
183                         return FALSE;
184                 }
185         }
186         *err = 0;
187         return FALSE;
188 }
189
190
191 int dbs_etherwatch_open(wtap *wth, int *err, gchar **err_info _U_)
192 {
193         /* Look for DBS ETHERWATCH header */
194         if (!dbs_etherwatch_check_file_type(wth, err)) {
195                 if (*err == 0)
196                         return 0;
197                 else
198                         return -1;
199         }
200
201         wth->data_offset = 0;
202         wth->file_encap = WTAP_ENCAP_ETHERNET;
203         wth->file_type = WTAP_FILE_DBS_ETHERWATCH;
204         wth->snapshot_length = 0;       /* not known */
205         wth->subtype_read = dbs_etherwatch_read;
206         wth->subtype_seek_read = dbs_etherwatch_seek_read;
207         wth->tsprecision = WTAP_FILE_TSPREC_CSEC;
208
209         return 1;
210 }
211
212 /* Find the next packet and parse it; called from wtap_read(). */
213 static gboolean dbs_etherwatch_read(wtap *wth, int *err, gchar **err_info,
214     gint64 *data_offset)
215 {
216         gint64  offset;
217         guint8  *buf;
218         int     pkt_len;
219
220         /* Find the next packet */
221         offset = dbs_etherwatch_seek_next_packet(wth, err);
222         if (offset < 1)
223                 return FALSE;
224
225         /* Make sure we have enough room for the packet */
226         buffer_assure_space(wth->frame_buffer, DBS_ETHERWATCH_MAX_PACKET_LEN);
227         buf = buffer_start_ptr(wth->frame_buffer);
228
229         /* Parse the packet */
230         pkt_len = parse_dbs_etherwatch_packet(wth, wth->fh, buf, err, err_info);
231         if (pkt_len == -1)
232                 return FALSE;
233
234         /*
235          * We don't have an FCS in this frame.
236          */
237         wth->pseudo_header.eth.fcs_len = 0;
238
239         wth->data_offset = offset;
240         *data_offset = offset;
241         return TRUE;
242 }
243
244 /* Used to read packets in random-access fashion */
245 static gboolean
246 dbs_etherwatch_seek_read (wtap *wth, gint64 seek_off,
247         union wtap_pseudo_header *pseudo_header _U_,
248         guint8 *pd, int len, int *err, gchar **err_info)
249 {
250         int     pkt_len;
251
252         if (file_seek(wth->random_fh, seek_off - 1, SEEK_SET, err) == -1)
253                 return FALSE;
254
255         pkt_len = parse_dbs_etherwatch_packet(NULL, wth->random_fh, pd, err,
256             err_info);
257
258         if (pkt_len != len) {
259                 if (pkt_len != -1) {
260                         *err = WTAP_ERR_BAD_RECORD;
261                         *err_info = g_strdup_printf("dbs_etherwatch: packet length %d doesn't match requested length %d",
262                             pkt_len, len);
263                 }
264                 return FALSE;
265         }
266
267         /*
268          * We don't have an FCS in this frame.
269          */
270         pseudo_header->eth.fcs_len = 0;
271
272         return TRUE;
273 }
274
275 /* Parse a packet */
276 /*
277 Packet header:
278           1         2         3         4
279 0123456789012345678901234567890123456789012345
280 From 00-D0-C0-D2-4D-60 [MF1] to AA-00-04-00-FC-94 [PSERVB]
281 Protocol 08-00 00 00-00-00-00-00,   50 byte buffer at 10-OCT-2001 10:20:45.17
282 */
283 #define MAC_ADDR_LENGTH         6                       /* Length MAC address */
284 #define DEST_MAC_PREFIX         "] to "         /* Prefix to the dest. MAC address */
285 #define PROTOCOL_LENGTH         2                       /* Length protocol */
286 #define PROTOCOL_POS            9                       /* Position protocol */
287 #define SAP_LENGTH                      2                       /* Length DSAP+SSAP */
288 #define SAP_POS                         9                       /* Position DSAP+SSAP */
289 #define CTL_UNNUMB_LENGTH       1                       /* Length unnumbered control field */
290 #define CTL_NUMB_LENGTH         2                       /* Length numbered control field */
291 #define CTL_POS                         15                      /* Position control field */
292 #define PID_LENGTH                      5                       /* Length PID */
293 #define PID_POS                         18                      /* Position PID */
294 #define LENGTH_POS                      33                      /* Position length */
295 #define HEX_HDR_SPR                     '-'                     /* Seperator char header hex values */
296 #define HEX_HDR_END                     ' '                     /* End char hdr. hex val. except PID */
297 #define HEX_PID_END                     ','                     /* End char PID hex value */
298 #define IEEE802_LEN_LEN         2                       /* Length of the IEEE 802 len. field */
299 /*
300 To check whether it is Ethernet II or IEEE 802 we check the values of the
301 control field and PID, when they are all 0's we assume it is Ethernet II
302 else IEEE 802. In IEEE 802 the DSAP and SSAP are behind protocol, the
303 length in the IEEE data we have to construct.
304 */
305 #define ETH_II_CHECK_POS    15
306 #define ETH_II_CHECK_STR    "00 00-00-00-00-00,"
307 /*
308 To check whether it IEEE 802.3 with SNAP we check that both the DSAP & SSAP
309 values are 0xAA and the control field 0x03.
310 */
311 #define SNAP_CHECK_POS          9
312 #define SNAP_CHECK_STR          "AA-AA 03"
313 /*
314 To check whether the control field is 1 or two octets we check if it is
315 unnumbered. Unnumbered has length 1, numbered 2.
316 */
317 #define CTL_UNNUMB_MASK         0x03
318 #define CTL_UNNUMB_VALUE        0x03
319 static int
320 parse_dbs_etherwatch_packet(wtap *wth, FILE_T fh, guint8* buf, int *err,
321     gchar **err_info)
322 {
323         char    line[DBS_ETHERWATCH_LINE_LENGTH];
324         int     num_items_scanned;
325         int     eth_hdr_len, pkt_len, csec;
326         int length_pos, length_from, length;
327         struct tm tm;
328         char mon[4];
329         gchar *p;
330         static gchar months[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
331         int     count, line_count;
332
333         eth_hdr_len = 0;
334         /* Our file pointer should be on the first line containing the
335          * summary information for a packet. Read in that line and
336          * extract the useful information
337          */
338         if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
339                 *err = file_error(fh);
340                 if (*err == 0) {
341                         *err = WTAP_ERR_SHORT_READ;
342                 }
343                 return -1;
344         }
345
346         /* Get the destination address */
347         p = strstr(line, DEST_MAC_PREFIX);
348         if(!p) {
349                 *err = WTAP_ERR_BAD_RECORD;
350                 *err_info = g_strdup("dbs_etherwatch: destination address not found");
351                 return -1;
352         }
353         p += strlen(DEST_MAC_PREFIX);
354         if(parse_hex_dump(p, &buf[eth_hdr_len], HEX_HDR_SPR, HEX_HDR_END)
355                                 != MAC_ADDR_LENGTH) {
356                 *err = WTAP_ERR_BAD_RECORD;
357                 *err_info = g_strdup("dbs_etherwatch: destination address not valid");
358                 return -1;
359         }
360         eth_hdr_len += MAC_ADDR_LENGTH;
361
362         /* Get the source address */
363         /*
364          * Since the first part of the line is already skipped in order to find
365          * the start of the record we cannot index, just look for the first
366          * 'HEX' character
367          */
368         p = line;
369         while(!isxdigit((guchar)*p)) {
370                 p++;
371         }
372         if(parse_hex_dump(p, &buf[eth_hdr_len], HEX_HDR_SPR,
373                 HEX_HDR_END) != MAC_ADDR_LENGTH) {
374                 *err = WTAP_ERR_BAD_RECORD;
375                 *err_info = g_strdup("dbs_etherwatch: source address not valid");
376                 return -1;
377         }
378         eth_hdr_len += MAC_ADDR_LENGTH;
379
380         /* Read the next line of the record header */
381         if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
382                 *err = file_error(fh);
383                 if (*err == 0) {
384                         *err = WTAP_ERR_SHORT_READ;
385                 }
386                 return -1;
387         }
388
389         /* Check the lines is as least as long as the length position */
390         if(strlen(line) < LENGTH_POS) {
391                 *err = WTAP_ERR_BAD_RECORD;
392                 *err_info = g_strdup("dbs_etherwatch: line too short");
393                 return -1;
394         }
395
396         num_items_scanned = sscanf(line + LENGTH_POS,
397                                 "%d byte buffer at %d-%3s-%d %d:%d:%d.%d",
398                                 &pkt_len,
399                                 &tm.tm_mday, mon,
400                                 &tm.tm_year, &tm.tm_hour, &tm.tm_min,
401                                 &tm.tm_sec, &csec);
402
403         if (num_items_scanned != 8) {
404                 *err = WTAP_ERR_BAD_RECORD;
405                 *err_info = g_strdup("dbs_etherwatch: header line not valid");
406                 return -1;
407         }
408         
409         /* Determine whether it is Ethernet II or IEEE 802 */
410         if(strncmp(&line[ETH_II_CHECK_POS], ETH_II_CHECK_STR,
411                 strlen(ETH_II_CHECK_STR)) == 0) {
412                 /* Ethernet II */
413                 /* Get the Protocol */
414                 if(parse_hex_dump(&line[PROTOCOL_POS], &buf[eth_hdr_len], HEX_HDR_SPR,
415                                         HEX_HDR_END) != PROTOCOL_LENGTH) {
416                         *err = WTAP_ERR_BAD_RECORD;
417                         *err_info = g_strdup("dbs_etherwatch: Ethernet II protocol value not valid");
418                         return -1;
419                 }
420                 eth_hdr_len += PROTOCOL_LENGTH;
421         } else {
422                 /* IEEE 802 */
423                 /* Remember where to put the length in the header */
424                 length_pos = eth_hdr_len;
425                 /* Leave room in the header for the length */
426                 eth_hdr_len += IEEE802_LEN_LEN;
427                 /* Remember how much of the header should not be added to the length */
428                 length_from = eth_hdr_len;
429                 /* Get the DSAP + SSAP */
430                 if(parse_hex_dump(&line[SAP_POS], &buf[eth_hdr_len], HEX_HDR_SPR,
431                                         HEX_HDR_END) != SAP_LENGTH) {
432                         *err = WTAP_ERR_BAD_RECORD;
433                         *err_info = g_strdup("dbs_etherwatch: 802.2 DSAP+SSAP value not valid");
434                         return -1;
435                 }
436                 eth_hdr_len += SAP_LENGTH;
437                 /* Get the (first part of the) control field */
438                 if(parse_hex_dump(&line[CTL_POS], &buf[eth_hdr_len], HEX_HDR_SPR,
439                                         HEX_HDR_END) != CTL_UNNUMB_LENGTH) {
440                         *err = WTAP_ERR_BAD_RECORD;
441                         *err_info = g_strdup("dbs_etherwatch: 802.2 control field first part not valid");
442                         return -1;
443                 }
444                 /* Determine whether the control is numbered, and thus longer */
445                 if((buf[eth_hdr_len] & CTL_UNNUMB_MASK) != CTL_UNNUMB_VALUE) {
446                         /* Get the rest of the control field, the first octet in the PID */
447                         if(parse_hex_dump(&line[PID_POS],
448                                                 &buf[eth_hdr_len + CTL_UNNUMB_LENGTH], HEX_HDR_END,
449                                                 HEX_HDR_SPR) != CTL_NUMB_LENGTH - CTL_UNNUMB_LENGTH) {
450                                 *err = WTAP_ERR_BAD_RECORD;
451                                 *err_info = g_strdup("dbs_etherwatch: 802.2 control field second part value not valid");
452                                 return -1;
453                         }
454                         eth_hdr_len += CTL_NUMB_LENGTH;
455                 } else {
456                         eth_hdr_len += CTL_UNNUMB_LENGTH;
457                 }
458                 /* Determine whether it is SNAP */
459                 if(strncmp(&line[SNAP_CHECK_POS], SNAP_CHECK_STR,
460                                 strlen(SNAP_CHECK_STR)) == 0) {
461                         /* Get the PID */
462                         if(parse_hex_dump(&line[PID_POS], &buf[eth_hdr_len], HEX_HDR_SPR,
463                                                 HEX_PID_END) != PID_LENGTH) {
464                                 *err = WTAP_ERR_BAD_RECORD;
465                                 *err_info = g_strdup("dbs_etherwatch: 802.2 PID value not valid");
466                                 return -1;
467                         }
468                         eth_hdr_len += PID_LENGTH;
469                 }
470                 /* Write the length in the header */
471                 length = eth_hdr_len - length_from + pkt_len;
472                 buf[length_pos] = (length) >> 8;
473                 buf[length_pos+1] = (length) & 0xFF;
474         }
475
476         if (wth) {
477                 p = strstr(months, mon);
478                 if (p)
479                         tm.tm_mon = (int)(p - months) / 3;
480                 tm.tm_year -= 1900;
481
482                 tm.tm_isdst = -1;
483                 wth->phdr.ts.secs = mktime(&tm);
484                 wth->phdr.ts.nsecs = csec * 10000000;
485                 wth->phdr.caplen = eth_hdr_len + pkt_len;
486                 wth->phdr.len = eth_hdr_len + pkt_len;
487         }
488
489         /* Parse the hex dump */
490         count = 0;
491         while (count < pkt_len) {
492                 if (file_gets(line, DBS_ETHERWATCH_LINE_LENGTH, fh) == NULL) {
493                         *err = file_error(fh);
494                         if (*err == 0) {
495                                 *err = WTAP_ERR_SHORT_READ;
496                         }
497                         return -1;
498                 }
499                 if (!(line_count = parse_single_hex_dump_line(line,
500                                 &buf[eth_hdr_len + count], count))) {
501                         *err = WTAP_ERR_BAD_RECORD;
502                         *err_info = g_strdup("dbs_etherwatch: packet data value not valid");
503                         return -1;
504                 }
505                 count += line_count;
506                 if (count > pkt_len) {
507                         *err = WTAP_ERR_BAD_RECORD;
508                         *err_info = g_strdup("dbs_etherwatch: packet data value has too many bytes");
509                         return -1;
510                 }
511         }
512         return eth_hdr_len + pkt_len;
513 }
514
515 /* Parse a hex dump line */
516 /*
517 /DISPLAY=BOTH output:
518
519           1         2         3         4
520 0123456789012345678901234567890123456789012345
521   [E..(8...........]-    0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A]
522   [.........(..Z.4y]-   16-[80 93 80 D6 02 D2 02 03 00 28 A4 BF 5A 1C 34 79]
523   [P.#(.C...00000..]-   32-[50 10 23 28 C1 43 00 00 03 30 30 30 30 30 00 00]
524   [.0              ]-   48-[03 30]
525
526 /DISPLAY=HEXADECIMAL output:
527
528           1         2         3         4
529 0123456789012345678901234567890123456789012345
530      0-[45 00 00 28 38 9B 00 00 1D 06 D2 1E 80 93 11 1A 80 93 80 D6]
531     20-[02 D2 02 03 00 28 A4 BF 5A 1C 34 79 50 10 23 28 C1 43 00 00]
532     40-[03 30 30 30 30 30 00 00 03 30]
533
534 */
535
536 #define TYPE_CHECK_POS          2       /* Position to check the type of hex dump */
537 #define TYPE_CHECK_BOTH         '['     /* Value at pos. that indicates BOTH type */
538 #define COUNT_POS_BOTH          21      /* Count position BOTH type */
539 #define COUNT_POS_HEX           1       /* Count position HEX type */
540 #define COUNT_SIZE              5       /* Length counter */
541 #define HEX_DUMP_START          '['     /* Start char */
542 #define HEX_DUMP_SPR            ' ' /* Seperator char */
543 #define HEX_DUMP_END            ']' /* End char */
544
545 /* Take a string representing one line from a hex dump and converts the
546  * text to binary data. We check the printed offset with the offset
547  * we are passed to validate the record. We place the bytes in the buffer
548  * at the specified offset.
549  *
550  * Returns length parsed if a good hex dump, 0 if bad.
551  */
552 static guint
553 parse_single_hex_dump_line(char* rec, guint8 *buf, int byte_offset) {
554
555         int             pos, i;
556         int             value;
557
558
559         /* Check that the record is as least as long as the check offset */
560         for(i = 0; i < TYPE_CHECK_POS; i++)
561         {
562                 if(rec[i] == '\0') {
563                         return 0;
564                 }
565         }
566         /* determine the format and thus the counter offset and hex dump length */
567         if(rec[TYPE_CHECK_POS] == TYPE_CHECK_BOTH)
568         {
569                 pos = COUNT_POS_BOTH;
570         }
571         else
572         {
573                 pos = COUNT_POS_HEX;
574         }       
575
576         /* Check that the record is as least as long as the start position */
577         while(i < pos)
578         {
579                 if(rec[i] == '\0') {
580                         return 0;
581                 }
582                 i++;
583         }
584
585         /* Get the byte_offset directly from the record */
586         value = 0;
587         for(i = 0; i < COUNT_SIZE; i++) {
588                 if(!isspace((guchar)rec[pos])) {
589                         if(isdigit((guchar)rec[pos])) {
590                                 value *= 10;
591                                 value += rec[pos] - '0';
592                         } else {
593                                 return 0;
594                         }
595                 }
596                 pos++;
597         }
598
599         if (value != byte_offset) {
600                 return 0;
601         }
602
603         /* find the start of the hex dump */
604         while(rec[pos] != HEX_DUMP_START) {
605                 if(rec[pos] == '\0') {
606                         return 0;
607                 }
608                 pos++;
609         }
610         pos++;
611         return parse_hex_dump(&rec[pos], buf, HEX_DUMP_SPR, HEX_DUMP_END);
612 }
613
614 /* Parse a hex dump */
615 static guint
616 parse_hex_dump(char* dump, guint8 *buf, char seperator, char end) {
617         int             pos, count;
618
619         /* Parse the hex dump */
620         pos = 0;
621         count = 0;
622         while(dump[pos] != end) {
623                 /* Check the hex value */
624                 if(!(isxdigit((guchar)dump[pos]) &&
625                     isxdigit((guchar)dump[pos + 1]))) {
626                         return 0;
627                 }
628                 /* Get the hex value value */
629                 if(isdigit((guchar)dump[pos])) {
630                         buf[count] = (dump[pos] - '0') << 4;
631                 } else {
632                         buf[count] = (toupper(dump[pos]) - 'A' + 10) << 4;
633                 }
634                 pos++;
635                 if(isdigit((guchar)dump[pos])) {
636                         buf[count] += dump[pos] - '0';
637                 } else {
638                         buf[count] += toupper(dump[pos]) - 'A' + 10;
639                 }
640                 pos++;
641                 count++;
642                 /* Skip the seperator characters */
643                 while(dump[pos] == seperator) {
644                         pos++;
645                 }
646         }
647         return count;
648 }