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