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