Removed trailing whitespaces from .h and .c files using the
[obnox/wireshark/wip.git] / mergecap.c
1 /* Combine two dump files, either by appending or by merging by timestamp
2  *
3  * $Id: mergecap.c,v 1.10 2002/08/02 23:35:46 jmayer Exp $
4  *
5  * Written by Scott Renfro <scott@renfro.org> based on
6  * editcap by Richard Sharpe and Guy Harris
7  *
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <glib.h>
17
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
21
22 #ifdef HAVE_SYS_TIME_H
23 #include <sys/time.h>
24 #endif
25
26 #include <string.h>
27 #include "wtap.h"
28
29 #ifdef NEED_GETOPT_H
30 #include "getopt.h"
31 #endif
32
33
34 /*
35  * Global variables
36  */
37 static int verbose = 0;                      /* Not so verbose         */
38
39 /* 
40  * Structures to manage our files
41  */
42 typedef struct in_file_t {
43   const char *filename;
44   wtap       *wth;
45   int         err;
46   long        data_offset;
47   gboolean    ok;
48 } in_file_t;
49
50 typedef struct out_file_t {
51   const char  *filename;
52   wtap_dumper *pdh;
53   int          file_type;
54   int          frame_type;
55   unsigned int snaplen;
56   int          count;
57 } out_file_t;
58 static out_file_t out_file;
59
60 /*
61  * Routine to write frame to output file
62  */
63 static void
64 write_frame(guchar *user, const struct wtap_pkthdr *phdr, long offset _U_,
65             union wtap_pseudo_header *pseudo_header, const guchar *buf) 
66 {
67   wtap_dumper *pdh = (wtap_dumper*)user;
68   int err;
69   struct wtap_pkthdr snap_phdr;
70   
71   if (verbose)
72     printf("Record: %u\n", out_file.count++);
73
74   /* We simply write it, perhaps after truncating it; we could do other
75    * things, like modify it. */
76   if (out_file.snaplen != 0 && phdr->caplen > out_file.snaplen) {
77     snap_phdr = *phdr;
78     snap_phdr.caplen = out_file.snaplen;
79     phdr = &snap_phdr;
80   }
81
82   if (!wtap_dump(pdh, phdr, pseudo_header, buf, &err)) {
83     fprintf(stderr, "mergecap: Error writing to %s: %s\n",
84             out_file.filename, wtap_strerror(err));
85     exit(1);
86   }
87 }
88
89
90 /*
91  * routine to concatenate files
92  */
93 static void
94 append(int count, in_file_t in_files[], out_file_t *out_file)
95 {
96   int i;
97   int err;
98
99   for (i = 0; i < count; i++) {
100     if (!wtap_loop(in_files[i].wth, 0, write_frame,
101                    (guchar*)out_file->pdh, &err)) {
102     fprintf(stderr, "mergecap: Error appending from %s to %s: %s\n",
103             in_files[i].filename, out_file->filename, wtap_strerror(err));
104     }
105   }
106 }
107
108
109 /*
110  * returns TRUE if first argument is earlier than second
111  */
112 static gboolean
113 is_earlier(struct timeval *l, struct timeval *r) {     
114   if (l->tv_sec > r->tv_sec) {  /* left is later */
115     return FALSE;
116   } else if (l->tv_sec < r->tv_sec) { /* left is earlier */
117     return TRUE;
118   } else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
119     return FALSE;
120   }
121   /* either one < two or one == two
122    * either way, return one
123    */
124   return TRUE;    
125 }
126
127
128 /*
129  * returns index of earliest timestamp in set of input files
130  * or -1 if no valid files remain
131  */
132 static int
133 earliest(int count, in_file_t in_files[]) {
134   int i;
135   int ei = -1;
136   struct timeval tv = {LONG_MAX, LONG_MAX};
137
138   for (i = 0; i < count; i++) {
139     struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
140
141     if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
142       tv = phdr->ts;
143       ei = i;
144     }
145   }
146   return ei;
147 }
148  
149 /*
150  * actually merge the files
151  */
152 static void
153 merge(int count, in_file_t in_files[], out_file_t *out_file)
154 {
155   int i;
156
157   /* prime the pump (read in first frame from each file) */
158   for (i = 0; i < count; i++) {
159     in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
160                                &(in_files[i].data_offset));
161   }
162
163   /* now keep writing the earliest frame until we're out of frames */
164   while ( -1 != (i = earliest(count, in_files))) {
165     
166     /* write out earliest frame, and fetch another from its
167      * input file
168      */
169     write_frame((guchar*)out_file->pdh,
170                 wtap_phdr(in_files[i].wth),
171                 in_files[i].data_offset,
172                 wtap_pseudoheader(in_files[i].wth),
173                 wtap_buf_ptr(in_files[i].wth));
174     in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
175                                 &(in_files[i].data_offset));
176   }
177 }
178
179
180 /*
181  * Select an output frame type based on the input files
182  * From Guy: If all files have the same frame type, then use that.
183  *           Otherwise select WTAP_ENCAP_PER_PACKET.  If the selected
184  *           output file type doesn't support per packet frame types,
185  *           then the wtap_dump_open call will fail with a reasonable
186  *           error condition.
187  */
188 static int
189 select_frame_type(int count, in_file_t files[])
190 {
191   int i;
192   int selected_frame_type;
193   
194   selected_frame_type = wtap_file_encap(files[0].wth);
195   
196   for (i = 1; i < count; i++) {
197     int this_frame_type = wtap_file_encap(files[i].wth);
198     if (selected_frame_type != this_frame_type) {
199       selected_frame_type = WTAP_ENCAP_PER_PACKET;
200       if (verbose) {
201         fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
202         fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
203         fprintf(stderr, "          %s had type %s (%s)\n",
204                 files[0].filename,
205                 wtap_encap_string(selected_frame_type),
206                 wtap_encap_short_string(selected_frame_type));
207         fprintf(stderr, "          %s had type %s (%s)\n",
208                 files[i].filename,
209                 wtap_encap_string(this_frame_type),
210                 wtap_encap_short_string(this_frame_type));
211       }
212       break;
213     }
214   }
215   
216   if (verbose) {
217       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
218               wtap_encap_string(selected_frame_type),
219               wtap_encap_short_string(selected_frame_type));
220   }
221
222   return selected_frame_type;
223 }
224     
225
226 /*
227  * Close the output file
228  */
229 static void
230 close_outfile(out_file_t *out_file)
231 {
232   int err;
233   if (!wtap_dump_close(out_file->pdh, &err)) {
234     fprintf(stderr, "mergecap: Error closing file %s: %s\n",
235             out_file->filename, wtap_strerror(err));
236     exit(1);
237   }
238 }
239
240
241 /*
242  * Open the output file
243  *
244  * Return FALSE if file cannot be opened (so caller can clean up)
245  */
246 static gboolean
247 open_outfile(out_file_t *out_file, int snapshot_len)
248 {
249   int err;
250   if (!out_file) {
251     fprintf(stderr, "mergecap: internal error (null out_file)\n");
252     exit(1);
253   }
254
255   out_file->pdh = wtap_dump_open(out_file->filename, out_file->file_type,
256                                  out_file->frame_type, snapshot_len, &err);
257   if (!out_file->pdh) {
258     fprintf(stderr, "mergecap: Can't open/create %s:\n", out_file->filename);
259     fprintf(stderr, "          %s\n", wtap_strerror(err));
260     return FALSE;
261   }
262   return TRUE;
263 }
264
265
266 /*
267  * Scan through input files and find maximum snapshot length
268  */
269 static int
270 max_snapshot_length(int count, in_file_t in_files[])
271 {
272   int i;
273   int max_snapshot = 0;
274   int snapshot_length;
275
276   for (i = 0; i < count; i++) {
277     snapshot_length = wtap_snapshot_length(in_files[i].wth);
278     if (snapshot_length == 0) {
279       /* Snapshot length of input file not known. */
280       snapshot_length = WTAP_MAX_PACKET_SIZE;
281     }
282     if (snapshot_length > max_snapshot)
283       max_snapshot = snapshot_length;
284   }
285   return max_snapshot;
286 }
287
288
289 /*
290  * Scan through and close each input file
291  */
292 static void
293 close_in_files(int count, in_file_t in_files[])
294 {
295   int i;
296   for (i = 0; i < count; i++) {
297     wtap_close(in_files[i].wth);
298   }
299 }
300
301
302 /*
303  * Scan through the arguments and open the input files
304  */
305 static int
306 open_in_files(int argc, char *argv[], in_file_t *in_files[])
307 {
308   int i;
309   int count = 0;
310   int err;
311   in_file_t *files;
312   int files_size = argc * sizeof(in_file_t);
313   
314
315   files = malloc(files_size);
316   if (!files) {
317     fprintf(stderr, "mergecap: error allocating %d bytes of memory\n",
318             files_size);
319     exit(1);
320   }
321   *in_files = files;
322
323   for (i = 0; i < argc; i++) {
324     files[count].filename    = argv[i];
325     files[count].wth         = wtap_open_offline(argv[i], &err, FALSE);
326     files[count].err         = 0;
327     files[count].data_offset = 0;
328     files[count].ok          = TRUE;
329     if (!files[count].wth) {
330       fprintf(stderr, "mergecap: skipping %s: %s\n", argv[i],
331               wtap_strerror(err));
332     } else {
333       if (verbose) {
334         fprintf(stderr, "mergecap: %s is type %s.\n", argv[i],
335                 wtap_file_type_string(wtap_file_type(files[count].wth)));
336       }
337       count++;
338     }
339   }
340   if (verbose)
341     fprintf(stderr, "mergecap: opened %d of %d input files\n", count,
342     argc);
343
344   return count;
345 }
346
347
348 /*
349  * Show the usage
350  */  
351 static void
352 usage()
353 {
354   int i;
355   const char *string;
356
357   fprintf(stderr, "Usage: mergecap [-hva] [-s <snaplen>] [-T <encap type>]\n");
358   fprintf(stderr, "          [-F <capture type>] -w <outfile> <infile> [...]\n\n");
359   fprintf(stderr, "  where\t-h produces this help listing.\n");
360   fprintf(stderr, "       \t-v verbose operation, default is silent\n");
361   fprintf(stderr, "       \t-a files should be concatenated, not merged\n");
362   fprintf(stderr, "       \t     Default merges based on frame timestamps\n");
363   fprintf(stderr, "       \t-s <snaplen>: truncate packets to <snaplen> bytes of data\n");
364   fprintf(stderr, "       \t-w <outfile>: sets output filename to <outfile>\n");
365   fprintf(stderr, "       \t-T <encap type> encapsulation type to use:\n");
366   for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
367       string = wtap_encap_short_string(i);
368       if (string != NULL)
369         fprintf(stderr, "       \t     %s - %s\n",
370           string, wtap_encap_string(i));
371   }
372   fprintf(stderr, "       \t     default is the same as the first input file\n");
373   fprintf(stderr, "       \t-F <capture type> capture file type to write:\n");
374   for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
375     if (wtap_dump_can_open(i))
376       fprintf(stderr, "       \t     %s - %s\n",
377         wtap_file_type_short_string(i), wtap_file_type_string(i));
378   }
379   fprintf(stderr, "       \t     default is libpcap\n");
380 }
381
382
383
384 int
385 main(int argc, char *argv[])
386 {
387   extern char *optarg;
388   extern int   optind;
389   int          opt;
390   char        *p;
391   gboolean     do_append     = FALSE;
392   int          in_file_count = 0;
393   in_file_t   *in_files      = NULL;
394   
395   /* initialize out_file */
396   out_file.filename   = NULL;   
397   out_file.pdh        = NULL;              /* wiretap dumpfile */
398   out_file.file_type  = WTAP_FILE_PCAP;    /* default to "libpcap" */
399   out_file.frame_type = -2;                /* leave type alone */
400   out_file.snaplen    = 0;                 /* no limit */
401   out_file.count      = 1;                 /* frames output */
402
403   /* Process the options first */
404   while ((opt = getopt(argc, argv, "hvas:T:F:w:")) != -1) {
405
406     switch (opt) {
407     case 'w':
408       out_file.filename = optarg;
409       break;
410         
411     case 'a':
412       do_append = !do_append;
413       break;
414     
415     case 'T':
416       out_file.frame_type = wtap_short_string_to_encap(optarg);
417       if (out_file.frame_type < 0) {
418         fprintf(stderr, "mergecap: \"%s\" is not a valid encapsulation type\n",
419             optarg);
420         exit(1);
421       }
422       break;
423
424     case 'F':
425       out_file.file_type = wtap_short_string_to_file_type(optarg);
426       if (out_file.file_type < 0) {
427         fprintf(stderr, "mergecap: \"%s\" is not a valid capture file type\n",
428             optarg);
429         exit(1);
430       }
431       break;
432
433     case 'v':
434       verbose = !verbose;  /* Just invert */
435       break;
436
437     case 's':
438       out_file.snaplen = strtol(optarg, &p, 10);
439       if (p == optarg || *p != '\0') {
440         fprintf(stderr, "mergecap: \"%s\" is not a valid snapshot length\n",
441             optarg);
442         exit(1);
443       }
444       break;
445
446     case 'h':
447       fprintf(stderr, "mergecap version %s\n", VERSION);
448       usage();
449       exit(1);
450       break;
451
452     case '?':              /* Bad options if GNU getopt */
453       usage();
454       exit(1);
455       break;
456
457     }
458
459   }
460
461   /* check for proper args; at a minimum, must have an output
462    * filename and one input file
463    */
464   in_file_count = argc - optind;
465   if (!out_file.filename) {
466     fprintf(stderr, "mergecap: an output filename must be set with -w\n");
467     usage();
468     exit(1);
469   }
470
471   /* open the input files */
472   in_file_count = open_in_files(in_file_count, &argv[optind], &in_files);
473   if (in_file_count < 1) {
474     fprintf(stderr, "mergecap: No valid input files\n");
475     exit(1);
476   }
477
478   /* set the outfile frame type */
479   if (out_file.frame_type == -2)
480     out_file.frame_type = select_frame_type(in_file_count, in_files);
481   
482   /* open the outfile */
483   if (!open_outfile(&out_file, max_snapshot_length(in_file_count, in_files))) {
484     close_in_files(in_file_count, in_files);
485     exit(1);
486   }
487
488   /* do the merge (or append) */
489   if (do_append)
490     append(in_file_count, in_files, &out_file);
491   else
492     merge(in_file_count, in_files, &out_file);
493
494   close_in_files(in_file_count, in_files);
495   close_outfile(&out_file);
496
497   free(in_files);
498
499   return 0;
500 }