replace *a lot* of file related calls by their GLib counterparts. This is necessary...
[obnox/wireshark/wip.git] / mergecap.c
1 /* Combine two dump files, either by appending or by merging by timestamp
2  *
3  * $Id$
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 <errno.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 #include "svnversion.h"
35 #include "merge.h"
36 #include "file_util.h"
37
38 #ifdef HAVE_FCNTL_H
39 #include <fcntl.h>
40 #endif
41
42 static int
43 get_natural_int(const char *string, const char *name)
44 {
45   long number;
46   char *p;
47
48   number = strtol(string, &p, 10);
49   if (p == string || *p != '\0') {
50     fprintf(stderr, "mergecap: The specified %s \"%s\" isn't a decimal number\n",
51             name, string);
52     exit(1);
53   }
54   if (number < 0) {
55     fprintf(stderr, "mergecap: The specified %s is a negative number\n",
56             name);
57     exit(1);
58   }
59   if (number > INT_MAX) {
60     fprintf(stderr, "mergecap: The specified %s is too large (greater than %d)\n",
61             name, INT_MAX);
62     exit(1);
63   }
64   return number;
65 }
66
67 static int
68 get_positive_int(const char *string, const char *name)
69 {
70   long number;
71
72   number = get_natural_int(string, name);
73
74   if (number == 0) {
75     fprintf(stderr, "mergecap: The specified %s is zero\n",
76             name);
77     exit(1);
78   }
79
80   return number;
81 }
82
83 /*
84  * Show the usage
85  */
86 static void
87 usage(void)
88 {
89   int i;
90   const char *string;
91
92   printf("Usage: mergecap [-hva] [-s <snaplen>] [-T <encap type>]\n");
93   printf("          [-F <capture type>] -w <outfile> <infile> [...]\n\n");
94   printf("  where\t-h produces this help listing.\n");
95   printf("       \t-v verbose operation, default is silent\n");
96   printf("       \t-a files should be concatenated, not merged\n");
97   printf("       \t     Default merges based on frame timestamps\n");
98   printf("       \t-s <snaplen>: truncate packets to <snaplen> bytes of data\n");
99   printf("       \t-w <outfile>: sets output filename to <outfile>\n");
100   printf("       \t-T <encap type> encapsulation type to use:\n");
101   for (i = 0; i < WTAP_NUM_ENCAP_TYPES; i++) {
102       string = wtap_encap_short_string(i);
103       if (string != NULL)
104         printf("       \t     %s - %s\n",
105           string, wtap_encap_string(i));
106   }
107   printf("       \t     default is the same as the first input file\n");
108   printf("       \t-F <capture type> capture file type to write:\n");
109   for (i = 0; i < WTAP_NUM_FILE_TYPES; i++) {
110     if (wtap_dump_can_open(i))
111       printf("       \t     %s - %s\n",
112         wtap_file_type_short_string(i), wtap_file_type_string(i));
113   }
114   printf("       \t     default is libpcap\n");
115 }
116
117 int
118 main(int argc, char *argv[])
119 {
120   extern char *optarg;
121   extern int   optind;
122   int          opt;
123   gboolean     do_append     = FALSE;
124   gboolean     verbose       = FALSE;
125   int          in_file_count = 0;
126   guint        snaplen = 0;
127   int          file_type = WTAP_FILE_PCAP;      /* default to libpcap format */
128   int          frame_type = -2;
129   int          out_fd;
130   merge_in_file_t   *in_files      = NULL;
131   int          i;
132   wtap        *wth;
133   struct wtap_pkthdr *phdr, snap_phdr;
134   wtap_dumper *pdh;
135   int          open_err, read_err, write_err, close_err;
136   gchar       *err_info;
137   int          err_fileno;
138   char        *out_filename = NULL;
139   gboolean     got_read_error = FALSE, got_write_error = FALSE;
140   int          count;
141
142   /* Process the options first */
143   while ((opt = getopt(argc, argv, "hvas:T:F:w:")) != -1) {
144
145     switch (opt) {
146     case 'w':
147       out_filename = optarg;
148       break;
149
150     case 'a':
151       do_append = !do_append;
152       break;
153
154     case 'T':
155       frame_type = wtap_short_string_to_encap(optarg);
156       if (frame_type < 0) {
157         fprintf(stderr, "mergecap: \"%s\" isn't a valid encapsulation type\n",
158             optarg);
159         exit(1);
160       }
161       break;
162
163     case 'F':
164       file_type = wtap_short_string_to_file_type(optarg);
165       if (file_type < 0) {
166         fprintf(stderr, "mergecap: \"%s\" isn't a valid capture file type\n",
167             optarg);
168         exit(1);
169       }
170       break;
171
172     case 'v':
173       verbose = TRUE;
174       break;
175
176     case 's':
177       snaplen = get_positive_int(optarg, "snapshot length");
178       break;
179
180     case 'h':
181       printf("mergecap version %s"
182 #ifdef SVNVERSION
183           " (" SVNVERSION ")"
184 #endif
185           "\n", VERSION);
186       usage();
187       exit(0);
188       break;
189
190     case '?':              /* Bad options if GNU getopt */
191       usage();
192       return 1;
193       break;
194
195     }
196
197   }
198
199   /* check for proper args; at a minimum, must have an output
200    * filename and one input file
201    */
202   in_file_count = argc - optind;
203   if (!out_filename) {
204     fprintf(stderr, "mergecap: an output filename must be set with -w\n");
205     fprintf(stderr, "          run with -h for help\n");
206     return 1;
207   }
208   if (in_file_count < 1) {
209     fprintf(stderr, "mergecap: No input files were specified\n");
210     return 1;
211   }
212
213   /* open the input files */
214   if (!merge_open_in_files(in_file_count, &argv[optind], &in_files,
215                            &open_err, &err_info, &err_fileno)) {
216     fprintf(stderr, "mergecap: Can't open %s: %s\n", argv[optind + err_fileno],
217         wtap_strerror(open_err));
218     switch (open_err) {
219
220     case WTAP_ERR_UNSUPPORTED:
221     case WTAP_ERR_UNSUPPORTED_ENCAP:
222     case WTAP_ERR_BAD_RECORD:
223       fprintf(stderr, "(%s)\n", err_info);
224       g_free(err_info);
225       break;
226     }
227     return 2;
228   }
229
230   if (verbose) {
231     for (i = 0; i < in_file_count; i++)
232       fprintf(stderr, "mergecap: %s is type %s.\n", argv[optind + i],
233               wtap_file_type_string(wtap_file_type(in_files[i].wth)));
234   }
235
236   if (snaplen == 0) {
237     /*
238      * Snapshot length not specified - default to the maximum of the
239      * snapshot lengths of the input files.
240      */
241     snaplen = merge_max_snapshot_length(in_file_count, in_files);
242   }
243
244   /* set the outfile frame type */
245   if (frame_type == -2) {
246     /*
247      * Default to the appropriate frame type for the input files.
248      */
249     frame_type = merge_select_frame_type(in_file_count, in_files);
250     if (verbose) {
251       if (frame_type == WTAP_ENCAP_PER_PACKET) {
252         /*
253          * Find out why we had to choose WTAP_ENCAP_PER_PACKET.
254          */
255         int first_frame_type, this_frame_type;
256
257         first_frame_type = wtap_file_encap(in_files[0].wth);
258         for (i = 1; i < in_file_count; i++) {
259           this_frame_type = wtap_file_encap(in_files[i].wth);
260           if (first_frame_type != this_frame_type) {
261             fprintf(stderr, "mergecap: multiple frame encapsulation types detected\n");
262             fprintf(stderr, "          defaulting to WTAP_ENCAP_PER_PACKET\n");
263             fprintf(stderr, "          %s had type %s (%s)\n",
264                     in_files[0].filename,
265                     wtap_encap_string(first_frame_type),
266                     wtap_encap_short_string(first_frame_type));
267             fprintf(stderr, "          %s had type %s (%s)\n",
268                     in_files[i].filename,
269                     wtap_encap_string(this_frame_type),
270                     wtap_encap_short_string(this_frame_type));
271             break;
272           }
273         }
274       }
275       fprintf(stderr, "mergecap: selected frame_type %s (%s)\n",
276               wtap_encap_string(frame_type),
277               wtap_encap_short_string(frame_type));
278     }
279   }
280
281   /* open the outfile */
282   if (strncmp(out_filename, "-", 2) == 0) {  
283     /* use stdout as the outfile */
284     out_fd = 1 /*stdout*/;
285   } else {
286     /* open the outfile */
287     out_fd = eth_open(out_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
288     if (out_fd == -1) {
289       fprintf(stderr, "mergecap: Couldn't open output file %s: %s\n",
290               out_filename, strerror(errno));
291       exit(1);
292     }
293   }  
294     
295   /* prepare the outfile */
296   pdh = wtap_dump_fdopen(out_fd, file_type, frame_type, snaplen, FALSE /* compressed */, &open_err);
297   if (pdh == NULL) {
298     merge_close_in_files(in_file_count, in_files);
299     free(in_files);
300     fprintf(stderr, "mergecap: Can't open or create %s: %s\n", out_filename,
301             wtap_strerror(open_err));
302     exit(1);
303   }
304
305   /* do the merge (or append) */
306   count = 1;
307   for (;;) {
308     if (do_append)
309       wth = merge_append_read_packet(in_file_count, in_files, &read_err,
310                                      &err_info);
311     else
312       wth = merge_read_packet(in_file_count, in_files, &read_err,
313                               &err_info);
314     if (wth == NULL) {
315       if (read_err != 0)
316         got_read_error = TRUE;
317       break;
318     }
319
320     if (verbose)
321       fprintf(stderr, "Record: %u\n", count++);
322
323     /* We simply write it, perhaps after truncating it; we could do other
324      * things, like modify it. */
325     phdr = wtap_phdr(wth);
326     if (snaplen != 0 && phdr->caplen > snaplen) {
327       snap_phdr = *phdr;
328       snap_phdr.caplen = snaplen;
329       phdr = &snap_phdr;
330     }
331
332     if (!wtap_dump(pdh, phdr, wtap_pseudoheader(wth),
333          wtap_buf_ptr(wth), &write_err)) {
334       got_write_error = TRUE;
335       break;
336     }
337   }
338
339   merge_close_in_files(in_file_count, in_files);
340   if (!got_read_error && !got_write_error) {
341     if (!wtap_dump_close(pdh, &write_err))
342       got_write_error = TRUE;
343   } else
344     wtap_dump_close(pdh, &close_err);
345
346   if (got_read_error) {
347     /*
348      * Find the file on which we got the error, and report the error.
349      */
350     for (i = 0; i < in_file_count; i++) {
351       if (in_files[i].state == GOT_ERROR) {
352         fprintf(stderr, "mergecap: Error reading %s: %s\n",
353                 in_files[i].filename, wtap_strerror(read_err));
354         switch (read_err) {
355
356         case WTAP_ERR_UNSUPPORTED:
357         case WTAP_ERR_UNSUPPORTED_ENCAP:
358         case WTAP_ERR_BAD_RECORD:
359           fprintf(stderr, "(%s)\n", err_info);
360           g_free(err_info);
361           break;
362         }
363       }
364     }
365   }
366
367   if (got_write_error) {
368     fprintf(stderr, "mergecap: Error writing to outfile: %s\n",
369             wtap_strerror(write_err));
370   }
371
372   free(in_files);
373
374   return (!got_read_error && !got_write_error) ? 0 : 2;
375 }