More Ethereal -> Wireshark renaming
[obnox/wireshark/wip.git] / editcap.c
1 /* Edit capture files.  We can delete packets, adjust timestamps, or
2  * simply convert from one format to another format.
3  *
4  * $Id$
5  *
6  * Originally written by Richard Sharpe.
7  * Improved by Guy Harris.
8  * Further improved by Richard Sharpe.
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <glib.h>
19
20 #ifdef HAVE_UNISTD_H
21 #include <unistd.h>
22 #endif
23
24
25 /*
26  * Just make sure we include the prototype for strptime as well
27  * (needed for glibc 2.2)
28  */
29 #define __USE_XOPEN
30
31 #include <time.h>
32 #ifdef HAVE_SYS_TIME_H
33 #include <sys/time.h>
34 #endif
35
36 #include "wtap.h"
37
38 #ifdef NEED_GETOPT_H
39 #include "getopt.h"
40 #endif
41
42 #ifdef _WIN32
43 #include <process.h>    /* getpid */
44 #endif
45
46 #ifdef NEED_STRPTIME_H
47 # include "strptime.h"
48 #endif
49
50 #include "epan/crypt-md5.h"
51
52 #include "svnversion.h"
53
54 /*
55  * Some globals so we can pass things to various routines
56  */
57
58 struct select_item {
59
60   int inclusive;
61   int first, second;
62
63 };
64
65
66 /*
67  * Duplicate frame detection
68  */
69 typedef struct _fd_hash_t {
70   md5_byte_t digest[16];
71   guint32 len;
72 } fd_hash_t;
73
74 #define DUP_DEPTH 5
75 fd_hash_t fd_hash[DUP_DEPTH];
76 int cur_dup = 0;
77
78 #define ONE_MILLION 1000000
79
80 /* Weights of different errors we can introduce */
81 /* We should probably make these command-line arguments */
82 /* XXX - Should we add a bit-level error? */
83 #define ERR_WT_BIT   5  /* Flip a random bit */
84 #define ERR_WT_BYTE  5  /* Substitute a random byte */
85 #define ERR_WT_ALNUM 5  /* Substitute a random character in [A-Za-z0-9] */
86 #define ERR_WT_FMT   2  /* Substitute "%s" */
87 #define ERR_WT_AA    1  /* Fill the remainder of the buffer with 0xAA */
88 #define ERR_WT_TOTAL (ERR_WT_BIT + ERR_WT_BYTE + ERR_WT_ALNUM + ERR_WT_FMT + ERR_WT_AA)
89
90 #define ALNUM_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
91 #define ALNUM_LEN (sizeof(ALNUM_CHARS) - 1)
92
93
94 struct time_adjustment {
95   struct timeval tv;
96   int is_negative;
97 };
98
99 static struct select_item selectfrm[100];
100 static int max_selected = -1;
101 static int keep_em = 0;
102 static int out_file_type = WTAP_FILE_PCAP;   /* default to "libpcap"   */
103 static int out_frame_type = -2;              /* Leave frame type alone */
104 static int verbose = 0;                      /* Not so verbose         */
105 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
106 static double err_prob = 0.0;
107 static time_t starttime = 0;
108 static time_t stoptime = 0;
109 static gboolean check_startstop = FALSE;
110 static gboolean dup_detect = FALSE;
111
112 /* Add a selection item, a simple parser for now */
113
114 static void add_selection(char *sel)
115 {
116   char *locn;
117   char *next;
118
119   if (max_selected == (sizeof(selectfrm)/sizeof(struct select_item)) - 1)
120     return;
121
122   printf("Add_Selected: %s\n", sel);
123
124   if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
125
126     printf("Not inclusive ...");
127
128     max_selected++;
129     selectfrm[max_selected].inclusive = 0;
130     selectfrm[max_selected].first = atoi(sel);
131
132     printf(" %i\n", selectfrm[max_selected].first);
133
134   }
135   else {
136
137     printf("Inclusive ...");
138
139     next = locn + 1;
140     max_selected++;
141     selectfrm[max_selected].inclusive = 1;
142     selectfrm[max_selected].first = atoi(sel);
143     selectfrm[max_selected].second = atoi(next);
144
145     printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
146
147   }
148
149
150 }
151
152 /* Was the packet selected? */
153
154 static int selected(int recno)
155 {
156   int i = 0;
157
158   for (i = 0; i<= max_selected; i++) {
159
160     if (selectfrm[i].inclusive) {
161       if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
162         return 1;
163     }
164     else {
165       if (recno == selectfrm[i].first)
166         return 1;
167     }
168   }
169
170   return 0;
171
172 }
173
174 /* is the packet in the selected timeframe */
175 static gboolean check_timestamp(wtap *wth) {
176         struct wtap_pkthdr* pkthdr = wtap_phdr(wth);
177         return ( (time_t) pkthdr->ts.secs >= starttime ) && ( (time_t) pkthdr->ts.secs <= stoptime );
178 }
179
180 static void
181 set_time_adjustment(char *optarg)
182 {
183   char *frac, *end;
184   long val;
185   int frac_digits;
186
187   if (!optarg)
188     return;
189
190   /* skip leading whitespace */
191   while (*optarg == ' ' || *optarg == '\t') {
192       optarg++;
193   }
194
195   /* check for a negative adjustment */
196   if (*optarg == '-') {
197       time_adj.is_negative = 1;
198       optarg++;
199   }
200
201   /* collect whole number of seconds, if any */
202   if (*optarg == '.') {         /* only fractional (i.e., .5 is ok) */
203       val  = 0;
204       frac = optarg;
205   } else {
206       val = strtol(optarg, &frac, 10);
207       if (frac == NULL || frac == optarg || val == LONG_MIN || val == LONG_MAX) {
208           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
209                   optarg);
210           exit(1);
211       }
212       if (val < 0) {            /* implies '--' since we caught '-' above  */
213           fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
214                   optarg);
215           exit(1);
216       }
217   }
218   time_adj.tv.tv_sec = val;
219
220   /* now collect the partial seconds, if any */
221   if (*frac != '\0') {             /* chars left, so get fractional part */
222     val = strtol(&(frac[1]), &end, 10);
223     if (*frac != '.' || end == NULL || end == frac
224         || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
225       fprintf(stderr, "editcap: \"%s\" isn't a valid time adjustment\n",
226               optarg);
227       exit(1);
228     }
229   }
230   else {
231     return;                     /* no fractional digits */
232   }
233
234   /* adjust fractional portion from fractional to numerator
235    * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
236   if (frac && end) {            /* both are valid */
237     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
238     while(frac_digits < 6) {    /* this is frac of 10^6 */
239       val *= 10;
240       frac_digits++;
241     }
242   }
243   time_adj.tv.tv_usec = val;
244 }
245
246 static gboolean
247 is_duplicate(guint8* fd, guint32 len) {
248   int i;
249   md5_state_t ms;
250
251   cur_dup++;
252   if (cur_dup >= DUP_DEPTH)
253     cur_dup = 0;
254
255   /* Calculate our digest */
256   md5_init(&ms);
257   md5_append(&ms, fd, len);
258   md5_finish(&ms, fd_hash[cur_dup].digest);
259
260   fd_hash[cur_dup].len = len;
261
262   /* Look for duplicates */
263   for (i = 0; i < DUP_DEPTH; i++) {
264     if (i == cur_dup)
265       continue;
266
267     if (fd_hash[i].len == fd_hash[cur_dup].len &&
268         memcmp(fd_hash[i].digest, fd_hash[cur_dup].digest, 16) == 0) {
269       return TRUE;
270     }
271   }
272
273   return FALSE;
274 }
275
276 static void usage(void)
277 {
278   fprintf(stderr, "Editcap %s"
279 #ifdef SVNVERSION
280           " (" SVNVERSION ")"
281 #endif
282           "\n", VERSION);
283   fprintf(stderr, "Edit and/or translate the format of capture files.\n");
284   fprintf(stderr, "See http://www.wireshark.org for more information.\n");
285   fprintf(stderr, "\n");
286   fprintf(stderr, "Usage: editcap [options] ... <infile> <outfile> [ <packet#>[-<packet#>] ... ]\n");
287   fprintf(stderr, "\n");
288   fprintf(stderr, "A single packet or a range of packets can be selected.\n");
289   fprintf(stderr, "\n");
290   fprintf(stderr, "Packets:\n");
291   fprintf(stderr, "  -C <choplen>           chop each packet at the end by <choplen> bytes\n");
292   fprintf(stderr, "  -d                     remove duplicate packets\n");
293   fprintf(stderr, "  -E <error probability> set the probability (between 0.0 and 1.0 incl.)\n");
294   fprintf(stderr, "                         that a particular packet byte will be randomly changed\n");
295   fprintf(stderr, "  -r                     keep the selected packets, default is to delete them\n");
296   fprintf(stderr, "  -s <snaplen>           truncate packets to max. <snaplen> bytes of data\n");
297   fprintf(stderr, "  -t <time adjustment>   adjust the timestamp of selected packets,\n");
298   fprintf(stderr, "                         <time adjustment> is in relative seconds (e.g. -0.5)\n");
299   fprintf(stderr, "  -A <start time>        don't output packets whose timestamp is before the\n");
300   fprintf(stderr, "                         given time (format as YYYY-MM-DD hh:mm:ss)\n");
301   fprintf(stderr, "  -B <stop time>         don't output packets whose timestamp is after the\n");
302   fprintf(stderr, "                         given time (format as YYYY-MM-DD hh:mm:ss)\n");
303   fprintf(stderr, "\n");
304   fprintf(stderr, "Output File(s):\n");
305   fprintf(stderr, "  -c <packets per file>  split the packet output to different files,\n");
306   fprintf(stderr, "                         with a maximum of <packets per file> each\n");
307   fprintf(stderr, "  -F <capture type>      set the output file type, default is libpcap\n");
308   fprintf(stderr, "                         an empty \"-F\" option will list the file types\n");
309   fprintf(stderr, "  -T <encap type>        set the output file encapsulation type,\n");
310   fprintf(stderr, "                         default is the same as the input file\n");
311   fprintf(stderr, "                         an empty \"-T\" option will list the encapsulation types\n");
312   fprintf(stderr, "\n");
313   fprintf(stderr, "Miscellaneous:\n");
314   fprintf(stderr, "  -h                     display this help and exit\n");
315   fprintf(stderr, "  -v                     verbose output\n");
316   fprintf(stderr, "\n");
317 }
318
319 static void list_capture_types(void) {
320     int i;
321
322     fprintf(stderr, "editcap: The available capture file types for \"F\":\n");
323     for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
324       if (wtap_dump_can_open(i))
325         fprintf(stderr, "    %s - %s\n",
326           wtap_file_type_short_string(i), wtap_file_type_string(i));
327     }
328 }
329
330 static void list_encap_types(void) {
331     int i;
332     const char *string;
333
334     fprintf(stderr, "editcap: The available encapsulation types for \"T\":\n");
335     for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
336         string = wtap_encap_short_string(i);
337         if (string != NULL)
338           fprintf(stderr, "    %s - %s\n",
339             string, wtap_encap_string(i));
340     }
341 }
342
343 int main(int argc, char *argv[])
344
345 {
346   wtap *wth;
347   int i, j, err;
348   gchar *err_info;
349   extern char *optarg;
350   extern int optind;
351   int opt;
352   char *p;
353   unsigned int snaplen = 0;             /* No limit               */
354   unsigned int choplen = 0;             /* No chop                */
355   wtap_dumper *pdh;
356   int count = 1;
357   gint64 data_offset;
358   struct wtap_pkthdr snap_phdr;
359   const struct wtap_pkthdr *phdr;
360   int err_type;
361   guint8 *buf;
362   int split_packet_count = 0;
363   int written_count = 0;
364   char *filename;
365
366   /* Process the options first */
367
368   while ((opt = getopt(argc, argv, "A:B:c:C:dE:F:hrs:t:T:v")) !=-1) {
369
370     switch (opt) {
371
372     case 'E':
373       err_prob = strtod(optarg, &p);
374       if (p == optarg || err_prob < 0.0 || err_prob > 1.0) {
375         fprintf(stderr, "editcap: probability \"%s\" must be between 0.0 and 1.0\n",
376             optarg);
377         exit(1);
378       }
379       srand(time(NULL) + getpid());
380       break;
381
382     case 'F':
383       out_file_type = wtap_short_string_to_file_type(optarg);
384       if (out_file_type < 0) {
385         fprintf(stderr, "editcap: \"%s\" isn't a valid capture file type\n\n",
386             optarg);
387         list_capture_types();
388         exit(1);
389       }
390       break;
391
392     case 'c':
393       split_packet_count = strtol(optarg, &p, 10);
394       if (p == optarg || *p != '\0') {
395         fprintf(stderr, "editcap: \"%s\" isn't a valid packet count\n",
396             optarg);
397         exit(1);
398       }
399       if (split_packet_count <= 0) {
400         fprintf(stderr, "editcap: \"%d\" packet count must be larger than zero\n",
401                 split_packet_count);
402         exit(1);
403       }
404       break;
405
406     case 'C':
407       choplen = strtol(optarg, &p, 10);
408       if (p == optarg || *p != '\0') {
409         fprintf(stderr, "editcap: \"%s\" isn't a valid chop length\n",
410             optarg);
411         exit(1);
412       }
413       break;
414
415     case 'd':
416       dup_detect = TRUE;
417       for (i = 0; i < DUP_DEPTH; i++) {
418         memset(&fd_hash[i].digest, 0, 16);
419         fd_hash[i].len = 0;
420       }
421       break;
422
423     case '?':              /* Bad options if GNU getopt */
424       switch(optopt) {
425       case'F':
426         list_capture_types();
427         break;
428       case'T':
429         list_encap_types();
430         break;
431       default:
432         usage();
433       }
434       exit(1);
435       break;
436
437     case 'h':
438       usage();
439       exit(1);
440       break;
441
442     case 'r':
443       keep_em = !keep_em;  /* Just invert */
444       break;
445
446     case 's':
447       snaplen = strtol(optarg, &p, 10);
448       if (p == optarg || *p != '\0') {
449         fprintf(stderr, "editcap: \"%s\" isn't a valid snapshot length\n",
450             optarg);
451         exit(1);
452       }
453       break;
454
455     case 't':
456       set_time_adjustment(optarg);
457       break;
458
459     case 'T':
460       out_frame_type = wtap_short_string_to_encap(optarg);
461       if (out_frame_type < 0) {
462         fprintf(stderr, "editcap: \"%s\" isn't a valid encapsulation type\n\n",
463             optarg);
464         list_encap_types();
465         exit(1);
466       }
467       break;
468
469     case 'v':
470       verbose = !verbose;  /* Just invert */
471       break;
472
473         case 'A':
474         {
475                 struct tm starttm;
476
477                 memset(&starttm,0,sizeof(struct tm));
478                 
479                 if(!strptime(optarg,"%F %T",&starttm)) {
480                         fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n",
481                                         optarg);
482                         exit(1);
483                 }
484
485                 check_startstop = TRUE;
486                 starttm.tm_isdst = -1;
487                 
488                 starttime = mktime(&starttm);
489                 break;
490         }
491         case 'B':
492         {
493                 struct tm stoptm;
494
495                 memset(&stoptm,0,sizeof(struct tm));
496
497                 if(!strptime(optarg,"%F %T",&stoptm)) {
498                         fprintf(stderr, "editcap: \"%s\" isn't a valid time format\n\n",
499                                         optarg);
500                         exit(1);
501                 }
502                 check_startstop = TRUE;
503                 stoptm.tm_isdst = -1;
504                 stoptime = mktime(&stoptm);
505                 break;
506         }
507     }
508
509   }
510
511 #ifdef DEBUG
512   printf("Optind = %i, argc = %i\n", optind, argc);
513 #endif
514
515   if ((argc - optind) < 1) {
516
517     usage();
518     exit(1);
519
520   }
521
522   if (check_startstop && !stoptime) {
523           struct tm stoptm;
524           /* XXX: will work until 2035 */
525           memset(&stoptm,0,sizeof(struct tm));
526           stoptm.tm_year = 135;
527           stoptm.tm_mday = 31;
528           stoptm.tm_mon = 11;
529
530           stoptime = mktime(&stoptm);
531   }
532
533   if (starttime > stoptime) {
534           fprintf(stderr, "editcap: start time is after the stop time\n");
535           exit(1);
536   }
537
538   wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
539
540   if (!wth) {
541     fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
542         wtap_strerror(err));
543     switch (err) {
544
545     case WTAP_ERR_UNSUPPORTED:
546     case WTAP_ERR_UNSUPPORTED_ENCAP:
547     case WTAP_ERR_BAD_RECORD:
548       fprintf(stderr, "(%s)\n", err_info);
549       g_free(err_info);
550       break;
551     }
552     exit(1);
553
554   }
555
556   if (verbose) {
557
558     fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
559             wtap_file_type_string(wtap_file_type(wth)));
560
561   }
562
563   /*
564    * Now, process the rest, if any ... we only write if there is an extra
565    * argument or so ...
566    */
567
568   if ((argc - optind) >= 2) {
569
570     if (out_frame_type == -2)
571       out_frame_type = wtap_file_encap(wth);
572
573     if (split_packet_count > 0) {
574       filename = (char *) malloc(strlen(argv[optind+1]) + 20);
575       if (!filename) {
576         exit(5);
577       }
578       sprintf(filename, "%s-%05d", argv[optind+1], 0);
579     } else {
580       filename = argv[optind+1];
581     }
582
583     pdh = wtap_dump_open(filename, out_file_type,
584                          out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
585     if (pdh == NULL) {
586
587       fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
588               wtap_strerror(err));
589       exit(1);
590
591     }
592
593     for (i = optind + 2; i < argc; i++)
594       add_selection(argv[i]);
595
596     while (wtap_read(wth, &err, &err_info, &data_offset)) {
597
598       if (split_packet_count > 0 && (written_count % split_packet_count == 0)) {
599         if (!wtap_dump_close(pdh, &err)) {
600
601           fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
602                   wtap_strerror(err));
603           exit(1);
604         }
605
606         sprintf(filename, "%s-%05d",argv[optind+1], count / split_packet_count);
607
608         if (verbose) {
609           fprintf(stderr, "Continuing writing in file %s\n", filename);
610         }
611
612         pdh = wtap_dump_open(filename, out_file_type,
613                              out_frame_type, wtap_snapshot_length(wth), FALSE /* compressed */, &err);
614         if (pdh == NULL) {
615
616           fprintf(stderr, "editcap: Can't open or create %s: %s\n", filename,
617                   wtap_strerror(err));
618           exit(1);
619
620         }
621       }
622
623       if ( ((check_startstop && check_timestamp(wth)) || (!check_startstop && !check_timestamp(wth))) && ((!selected(count) && !keep_em) ||
624           (selected(count) && keep_em)) ) {
625
626         if (verbose)
627           printf("Packet: %u\n", count);
628
629         /* We simply write it, perhaps after truncating it; we could do other
630            things, like modify it. */
631
632         phdr = wtap_phdr(wth);
633
634         if (choplen != 0 && phdr->caplen > choplen) {
635           snap_phdr = *phdr;
636           snap_phdr.caplen -= choplen;
637           phdr = &snap_phdr;
638         }
639
640         if (snaplen != 0 && phdr->caplen > snaplen) {
641           snap_phdr = *phdr;
642           snap_phdr.caplen = snaplen;
643           phdr = &snap_phdr;
644         }
645
646         /* assume that if the frame's tv_sec is 0, then
647          * the timestamp isn't supported */
648         if (phdr->ts.secs > 0 && time_adj.tv.tv_sec != 0) {
649           snap_phdr = *phdr;
650           if (time_adj.is_negative)
651             snap_phdr.ts.secs -= time_adj.tv.tv_sec;
652           else
653             snap_phdr.ts.secs += time_adj.tv.tv_sec;
654           phdr = &snap_phdr;
655         }
656
657         /* assume that if the frame's tv_sec is 0, then
658          * the timestamp isn't supported */
659         if (phdr->ts.secs > 0 && time_adj.tv.tv_usec != 0) {
660           snap_phdr = *phdr;
661           if (time_adj.is_negative) { /* subtract */
662             if (snap_phdr.ts.nsecs/1000 < time_adj.tv.tv_usec) { /* borrow */
663               snap_phdr.ts.secs--;
664               snap_phdr.ts.nsecs += ONE_MILLION * 1000;
665             }
666             snap_phdr.ts.nsecs -= time_adj.tv.tv_usec * 1000;
667           } else {                  /* add */
668             if (snap_phdr.ts.nsecs + time_adj.tv.tv_usec * 1000 > ONE_MILLION * 1000) {
669               /* carry */
670               snap_phdr.ts.secs++;
671               snap_phdr.ts.nsecs += (time_adj.tv.tv_usec - ONE_MILLION) * 1000;
672             } else {
673               snap_phdr.ts.nsecs += time_adj.tv.tv_usec * 1000;
674             }
675           }
676           phdr = &snap_phdr;
677         }
678
679         if (dup_detect) {
680           buf = wtap_buf_ptr(wth);
681           if (is_duplicate(buf, phdr->caplen)) {
682             if (verbose)
683               printf("Skipping duplicate: %u\n", count);
684             count++;
685             continue;
686           }
687         }
688
689         if (err_prob > 0.0) {
690           buf = wtap_buf_ptr(wth);
691           for (i = 0; i < (int) phdr->caplen; i++) {
692             if (rand() <= err_prob * RAND_MAX) {
693               err_type = rand() / (RAND_MAX / ERR_WT_TOTAL + 1);
694
695               if (err_type < ERR_WT_BIT) {
696                 buf[i] ^= 1 << (rand() / (RAND_MAX / 8 + 1));
697                 err_type = ERR_WT_TOTAL;
698               } else {
699                 err_type -= ERR_WT_BYTE;
700               }
701
702               if (err_type < ERR_WT_BYTE) {
703                 buf[i] = rand() / (RAND_MAX / 255 + 1);
704                 err_type = ERR_WT_TOTAL;
705               } else {
706                 err_type -= ERR_WT_BYTE;
707               }
708
709               if (err_type < ERR_WT_ALNUM) {
710                 buf[i] = ALNUM_CHARS[rand() / (RAND_MAX / ALNUM_LEN + 1)];
711                 err_type = ERR_WT_TOTAL;
712               } else {
713                 err_type -= ERR_WT_ALNUM;
714               }
715
716               if (err_type < ERR_WT_FMT) {
717                 if ((unsigned int)i < phdr->caplen - 2)
718                   strcpy((char*) &buf[i],  "%s");
719                 err_type = ERR_WT_TOTAL;
720               } else {
721                 err_type -= ERR_WT_FMT;
722               }
723
724               if (err_type < ERR_WT_AA) {
725                 for (j = i; j < (int) phdr->caplen; j++) {
726                   buf[j] = 0xAA;
727                 }
728                 i = phdr->caplen;
729               }
730             }
731           }
732         }
733
734         if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth),
735                        &err)) {
736
737           fprintf(stderr, "editcap: Error writing to %s: %s\n",
738                   filename, wtap_strerror(err));
739           exit(1);
740
741         }
742
743         written_count++;
744
745       }
746
747       count++;
748
749     }
750
751     if (err != 0) {
752       /* Print a message noting that the read failed somewhere along the line. */
753       fprintf(stderr,
754               "editcap: An error occurred while reading \"%s\": %s.\n",
755               argv[optind], wtap_strerror(err));
756       switch (err) {
757
758       case WTAP_ERR_UNSUPPORTED:
759       case WTAP_ERR_UNSUPPORTED_ENCAP:
760       case WTAP_ERR_BAD_RECORD:
761         fprintf(stderr, "(%s)\n", err_info);
762         break;
763       }
764     }
765
766     if (!wtap_dump_close(pdh, &err)) {
767
768       fprintf(stderr, "editcap: Error writing to %s: %s\n", filename,
769               wtap_strerror(err));
770       exit(1);
771
772     }
773   }
774
775   return 0;
776 }
777