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