Fix (hopefully) a compiler varning
[obnox/wireshark/wip.git] / editcap.c
1 /* Edit capture files.  We can delete records, 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 <glib.h>
18
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
22
23 #ifdef HAVE_SYS_TIME_H
24 #include <sys/time.h>
25 #endif
26
27 #include <string.h>
28 #include "wtap.h"
29
30 #ifdef NEED_GETOPT_H
31 #include "getopt.h"
32 #endif
33
34 /*
35  * Some globals so we can pass things to various routines
36  */
37
38 struct select_item {
39
40   int inclusive;
41   int first, second;
42
43 };
44
45 #define ONE_MILLION 1000000
46
47 struct time_adjustment {
48   struct timeval tv;
49   int is_negative;
50 };
51
52 static struct select_item selectfrm[100];
53 static int max_selected = -1;
54 static int count = 1;
55 static int keep_em = 0;
56 static int out_file_type = WTAP_FILE_PCAP;   /* default to "libpcap"   */
57 static int out_frame_type = -2;              /* Leave frame type alone */
58 static int verbose = 0;                      /* Not so verbose         */
59 static unsigned int snaplen = 0;             /* No limit               */
60 static struct time_adjustment time_adj = {{0, 0}, 0}; /* no adjustment */
61
62 /* Add a selection item, a simple parser for now */
63
64 static void add_selection(char *sel)
65 {
66   char *locn;
67   char *next;
68
69   if (max_selected == (sizeof(selectfrm)/sizeof(struct select_item)) - 1)
70     return;
71
72   printf("Add_Selected: %s\n", sel);
73
74   if ((locn = strchr(sel, '-')) == NULL) { /* No dash, so a single number? */
75
76     printf("Not inclusive ...");
77
78     max_selected++;
79     selectfrm[max_selected].inclusive = 0;
80     selectfrm[max_selected].first = atoi(sel);
81
82     printf(" %i\n", selectfrm[max_selected].first);
83
84   }
85   else {
86
87     printf("Inclusive ...");
88
89     next = locn + 1;
90     max_selected++;
91     selectfrm[max_selected].inclusive = 1;
92     selectfrm[max_selected].first = atoi(sel);
93     selectfrm[max_selected].second = atoi(next);
94
95     printf(" %i, %i\n", selectfrm[max_selected].first, selectfrm[max_selected].second);
96
97   }
98
99
100 }
101
102 /* Was the record selected? */
103
104 static int selected(int recno)
105 {
106   int i = 0;
107
108   for (i = 0; i<= max_selected; i++) {
109
110     if (selectfrm[i].inclusive) {
111       if (selectfrm[i].first <= recno && selectfrm[i].second >= recno)
112         return 1;
113     }
114     else {
115       if (recno == selectfrm[i].first)
116         return 1;
117     }
118   }
119
120   return 0;
121
122 }
123
124 /* An argument to the callback routine */
125
126 typedef struct {
127         char    *filename;
128         wtap_dumper *pdh;
129 } callback_arg;
130
131 /*
132  *The callback routine that is called for each frame in the input file
133  */
134
135 static void
136 edit_callback(guchar *user, const struct wtap_pkthdr *phdr, long offset _U_,
137     union wtap_pseudo_header *pseudo_header, const guchar *buf)
138 {
139   callback_arg *argp = (callback_arg *)user;
140   int err;
141   struct wtap_pkthdr snap_phdr;
142
143   if ((!selected(count) && !keep_em) ||
144       (selected(count) && keep_em)) {
145
146     if (verbose)
147       printf("Record: %u\n", count);
148
149     /* We simply write it, perhaps after truncating it; we could do other
150        things, like modify it. */
151
152     if (snaplen != 0 && phdr->caplen > snaplen) {
153       snap_phdr = *phdr;
154       snap_phdr.caplen = snaplen;
155       phdr = &snap_phdr;
156     }
157
158     /* assume that if the frame's tv_sec is 0, then
159      * the timestamp isn't supported */
160     if (phdr->ts.tv_sec > 0 && time_adj.tv.tv_sec != 0) {
161       snap_phdr = *phdr;
162       if (time_adj.is_negative)
163         snap_phdr.ts.tv_sec -= time_adj.tv.tv_sec;
164       else
165         snap_phdr.ts.tv_sec += time_adj.tv.tv_sec;
166       phdr = &snap_phdr;
167     }
168
169     /* assume that if the frame's tv_sec is 0, then
170      * the timestamp isn't supported */
171     if (phdr->ts.tv_sec > 0 && time_adj.tv.tv_usec != 0) {
172       snap_phdr = *phdr;
173       if (time_adj.is_negative) { /* subtract */
174         if (snap_phdr.ts.tv_usec < time_adj.tv.tv_usec) { /* borrow */
175           snap_phdr.ts.tv_sec--;
176           snap_phdr.ts.tv_usec += ONE_MILLION;
177         }
178         snap_phdr.ts.tv_usec -= time_adj.tv.tv_usec;
179       } else {                  /* add */
180         if (snap_phdr.ts.tv_usec + time_adj.tv.tv_usec > ONE_MILLION) {
181           /* carry */
182           snap_phdr.ts.tv_sec++;
183           snap_phdr.ts.tv_usec += time_adj.tv.tv_usec - ONE_MILLION;
184         } else {
185           snap_phdr.ts.tv_usec += time_adj.tv.tv_usec;
186         }
187       }
188       phdr = &snap_phdr;
189     }
190
191     if (!wtap_dump(argp->pdh, phdr, pseudo_header, buf, &err)) {
192
193       fprintf(stderr, "editcap: Error writing to %s: %s\n", argp->filename,
194         wtap_strerror(err));
195       exit(1);
196
197     }
198
199   }
200
201   count++;
202
203 }
204
205 static void
206 set_time_adjustment(char *optarg)
207 {
208   char *frac, *end;
209   long val;
210   int frac_digits;
211
212   if (!optarg)
213     return;
214
215   /* skip leading whitespace */
216   while (*optarg == ' ' || *optarg == '\t') {
217       optarg++;
218   }
219
220   /* check for a negative adjustment */
221   if (*optarg == '-') {
222       time_adj.is_negative = 1;
223       optarg++;
224   }
225
226   /* collect whole number of seconds, if any */
227   if (*optarg == '.') {         /* only fractional (i.e., .5 is ok) */
228       val  = 0;
229       frac = optarg;
230   } else {
231       val = strtol(optarg, &frac, 10);
232       if (frac == NULL || frac == optarg || val == LONG_MIN || val == LONG_MAX) {
233           fprintf(stderr, "editcap: \"%s\" is not a valid ime adjustment\n",
234                   optarg);
235           exit(1);
236       }
237       if (val < 0) {            /* implies '--' since we caught '-' above  */
238           fprintf(stderr, "editcap: \"%s\" is not a valid time adjustment\n",
239                   optarg);
240           exit(1);
241       }
242   }
243   time_adj.tv.tv_sec = val;
244
245   /* now collect the partial seconds, if any */
246   if (*frac != '\0') {             /* chars left, so get fractional part */
247     val = strtol(&(frac[1]), &end, 10);
248     if (*frac != '.' || end == NULL || end == frac
249         || val < 0 || val > ONE_MILLION || val == LONG_MIN || val == LONG_MAX) {
250       fprintf(stderr, "editcap: \"%s\" is not a valid time adjustment\n",
251               optarg);
252       exit(1);
253     }
254   }
255   else {
256     return;                     /* no fractional digits */
257   }
258
259   /* adjust fractional portion from fractional to numerator
260    * e.g., in "1.5" from 5 to 500000 since .5*10^6 = 500000 */
261   if (frac && end) {            /* both are valid */
262     frac_digits = end - frac - 1;   /* fractional digit count (remember '.') */
263     while(frac_digits < 6) {    /* this is frac of 10^6 */
264       val *= 10;
265       frac_digits++;
266     }
267   }
268   time_adj.tv.tv_usec = val;
269 }
270
271 static void usage(void)
272 {
273   int i;
274   const char *string;
275
276   fprintf(stderr, "Usage: editcap [-r] [-h] [-v] [-T <encap type>] [-F <capture type>]\n");
277   fprintf(stderr, "               [-s <snaplen>] [-t <time adjustment>]\n");
278   fprintf(stderr, "               <infile> <outfile> [ <record#>[-<record#>] ... ]\n");
279   fprintf(stderr, "  where\t-r specifies that the records specified should be kept, not deleted, \n");
280   fprintf(stderr, "                           default is to delete\n");
281   fprintf(stderr, "       \t-v specifies verbose operation, default is silent\n");
282   fprintf(stderr, "       \t-h produces this help listing.\n");
283   fprintf(stderr, "       \t-T <encap type> specifies the encapsulation type to use:\n");
284   for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
285       string = wtap_encap_short_string(i);
286       if (string != NULL)
287         fprintf(stderr, "       \t    %s - %s\n",
288           string, wtap_encap_string(i));
289   }
290   fprintf(stderr, "       \t    default is the same as the input file\n");
291   fprintf(stderr, "       \t-F <capture type> specifies the capture file type to write:\n");
292   for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
293     if (wtap_dump_can_open(i))
294       fprintf(stderr, "       \t    %s - %s\n",
295         wtap_file_type_short_string(i), wtap_file_type_string(i));
296   }
297   fprintf(stderr, "       \t    default is libpcap\n");
298   fprintf(stderr, "       \t-s <snaplen> specifies that packets should be truncated to\n");
299   fprintf(stderr, "       \t   <snaplen> bytes of data\n");
300   fprintf(stderr, "       \t-t <time adjustment> specifies the time adjustment\n");
301   fprintf(stderr, "       \t   to be applied to selected packets\n");
302   fprintf(stderr, "\n      \t    A range of records can be specified as well\n");
303 }
304
305 int main(int argc, char *argv[])
306
307 {
308   wtap *wth;
309   int i, err;
310   gchar *err_info;
311   callback_arg args;
312   extern char *optarg;
313   extern int optind;
314   int opt;
315   char *p;
316   int snapshot_length;
317
318   /* Process the options first */
319
320   while ((opt = getopt(argc, argv, "T:F:rvs:t:h")) !=-1) {
321
322     switch (opt) {
323
324     case 'T':
325       out_frame_type = wtap_short_string_to_encap(optarg);
326       if (out_frame_type < 0) {
327         fprintf(stderr, "editcap: \"%s\" is not a valid encapsulation type\n",
328             optarg);
329         exit(1);
330       }
331       break;
332
333     case 'F':
334       out_file_type = wtap_short_string_to_file_type(optarg);
335       if (out_file_type < 0) {
336         fprintf(stderr, "editcap: \"%s\" is not a valid capture file type\n",
337             optarg);
338         exit(1);
339       }
340       break;
341
342     case 'v':
343       verbose = !verbose;  /* Just invert */
344       break;
345
346     case 'r':
347       keep_em = !keep_em;  /* Just invert */
348       break;
349
350     case 's':
351       snaplen = strtol(optarg, &p, 10);
352       if (p == optarg || *p != '\0') {
353         fprintf(stderr, "editcap: \"%s\" is not a valid snapshot length\n",
354             optarg);
355         exit(1);
356       }
357       break;
358
359     case 't':
360       set_time_adjustment(optarg);
361       break;
362
363     case 'h':
364       usage();
365       exit(1);
366       break;
367
368     case '?':              /* Bad options if GNU getopt */
369       usage();
370       exit(1);
371       break;
372
373     }
374
375   }
376
377 #ifdef DEBUG
378   printf("Optind = %i, argc = %i\n", optind, argc);
379 #endif
380
381   if ((argc - optind) < 1) {
382
383     usage();
384     exit(1);
385
386   }
387
388   wth = wtap_open_offline(argv[optind], &err, &err_info, FALSE);
389
390   if (!wth) {
391     fprintf(stderr, "editcap: Can't open %s: %s\n", argv[optind],
392         wtap_strerror(err));
393     switch (err) {
394
395     case WTAP_ERR_UNSUPPORTED:
396     case WTAP_ERR_UNSUPPORTED_ENCAP:
397     case WTAP_ERR_BAD_RECORD:
398       fprintf(stderr, "(%s)\n", err_info);
399       g_free(err_info);
400       break;
401     }
402     exit(1);
403
404   }
405
406   if (verbose) {
407
408     fprintf(stderr, "File %s is a %s capture file.\n", argv[optind],
409             wtap_file_type_string(wtap_file_type(wth)));
410
411   }
412
413   /*
414    * Now, process the rest, if any ... we only write if there is an extra
415    * argument or so ...
416    */
417
418   if ((argc - optind) >= 2) {
419
420     args.filename = argv[optind + 1];
421     if (out_frame_type == -2)
422       out_frame_type = wtap_file_encap(wth);
423
424     snapshot_length = wtap_snapshot_length(wth);
425     if (snapshot_length == 0) {
426       /* Snapshot length of input file not known. */
427       snapshot_length = WTAP_MAX_PACKET_SIZE;
428     }
429     args.pdh = wtap_dump_open(argv[optind + 1], out_file_type,
430                               out_frame_type, wtap_snapshot_length(wth), &err);
431     if (args.pdh == NULL) {
432
433       fprintf(stderr, "editcap: Can't open or create %s: %s\n", argv[optind+1],
434               wtap_strerror(err));
435       exit(1);
436
437     }
438
439     for (i = optind + 2; i < argc; i++)
440       add_selection(argv[i]);
441
442     if (!wtap_loop(wth, 0, edit_callback, (char *)&args, &err, &err_info)) {
443       /* Print a message noting that the read failed somewhere along the line. */
444       fprintf(stderr,
445               "editcap: An error occurred while reading \"%s\": %s.\n",
446               argv[optind], wtap_strerror(err));
447       switch (err) {
448
449       case WTAP_ERR_UNSUPPORTED:
450       case WTAP_ERR_UNSUPPORTED_ENCAP:
451       case WTAP_ERR_BAD_RECORD:
452         fprintf(stderr, "(%s)\n", err_info);
453         break;
454       }
455     }
456
457     if (!wtap_dump_close(args.pdh, &err)) {
458
459       fprintf(stderr, "editcap: Error writing to %s: %s\n", argv[2],
460               wtap_strerror(err));
461       exit(1);
462
463     }
464   }
465
466   return 0;
467 }
468