The common merge code merely needs to offer the abstraction of routines
[obnox/wireshark/wip.git] / merge.c
1 /* Combine multiple 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 <glib.h>
17 #include <errno.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 #ifdef HAVE_SYS_STAT_H
28 #include <sys/stat.h>
29 #endif
30
31 #include <string.h>
32 #include "wtap.h"
33 #include "merge.h"
34
35 /*
36  * Scan through the arguments and open the input files
37  */
38 gboolean
39 merge_open_in_files(int in_file_count, char *const *in_file_names,
40                     merge_in_file_t **in_files, int *err, gchar **err_info,
41                     int *err_fileno)
42 {
43   int i, j;
44   int files_size = in_file_count * sizeof(merge_in_file_t);
45   merge_in_file_t *files;
46   struct stat statb;
47
48   files = g_malloc(files_size);
49   *in_files = files;
50
51   for (i = 0; i < in_file_count; i++) {
52     files[i].filename    = in_file_names[i];
53     files[i].wth         = wtap_open_offline(in_file_names[i], err, err_info, FALSE);
54     files[i].data_offset = 0;
55     files[i].state       = PACKET_NOT_PRESENT;
56     if (!files[i].wth) {
57       /* Close the files we've already opened. */
58       for (j = 0; j < i; j++)
59         wtap_close(files[j].wth);
60       *err_fileno = i;
61       return FALSE;
62     }
63     if (fstat(wtap_fd(files[i].wth), &statb) < 0) {
64       *err = errno;
65       for (j = 0; j <= i; j++)
66         wtap_close(files[j].wth);
67       *err_fileno = i;
68       return FALSE;
69     }
70     files[i].size = statb.st_size;
71   }
72   return TRUE;
73 }
74
75 /*
76  * Scan through and close each input file
77  */
78 void
79 merge_close_in_files(int count, merge_in_file_t in_files[])
80 {
81   int i;
82   for (i = 0; i < count; i++) {
83     wtap_close(in_files[i].wth);
84   }
85 }
86
87 /*
88  * Select an output frame type based on the input files
89  * From Guy: If all files have the same frame type, then use that.
90  *           Otherwise select WTAP_ENCAP_PER_PACKET.  If the selected
91  *           output file type doesn't support per packet frame types,
92  *           then the wtap_dump_open call will fail with a reasonable
93  *           error condition.
94  */
95 int
96 merge_select_frame_type(int count, merge_in_file_t files[])
97 {
98   int i;
99   int selected_frame_type;
100
101   selected_frame_type = wtap_file_encap(files[0].wth);
102
103   for (i = 1; i < count; i++) {
104     int this_frame_type = wtap_file_encap(files[i].wth);
105     if (selected_frame_type != this_frame_type) {
106       selected_frame_type = WTAP_ENCAP_PER_PACKET;
107       break;
108     }
109   }
110
111   return selected_frame_type;
112 }
113
114 /*
115  * Scan through input files and find maximum snapshot length
116  */
117 int
118 merge_max_snapshot_length(int count, merge_in_file_t in_files[])
119 {
120   int i;
121   int max_snapshot = 0;
122   int snapshot_length;
123
124   for (i = 0; i < count; i++) {
125     snapshot_length = wtap_snapshot_length(in_files[i].wth);
126     if (snapshot_length == 0) {
127       /* Snapshot length of input file not known. */
128       snapshot_length = WTAP_MAX_PACKET_SIZE;
129     }
130     if (snapshot_length > max_snapshot)
131       max_snapshot = snapshot_length;
132   }
133   return max_snapshot;
134 }
135
136 /*
137  * returns TRUE if first argument is earlier than second
138  */
139 static gboolean
140 is_earlier(struct timeval *l, struct timeval *r) {
141   if (l->tv_sec > r->tv_sec) {  /* left is later */
142     return FALSE;
143   } else if (l->tv_sec < r->tv_sec) { /* left is earlier */
144     return TRUE;
145   } else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
146     return FALSE;
147   }
148   /* either one < two or one == two
149    * either way, return one
150    */
151   return TRUE;
152 }
153
154 /*
155  * Read the next packet, in chronological order, from the set of files
156  * to be merged.
157  */
158 wtap *
159 merge_read_packet(int in_file_count, merge_in_file_t in_files[], int *err,
160                   gchar **err_info)
161 {
162   int i;
163   int ei = -1;
164   struct timeval tv = {LONG_MAX, LONG_MAX};
165   struct wtap_pkthdr *phdr;
166
167   /*
168    * Make sure we have a packet available from each file, if there are any
169    * packets left in the file in question, and search for the packet
170    * with the earliest time stamp.
171    */
172   for (i = 0; i < in_file_count; i++) {
173     if (in_files[i].state == PACKET_NOT_PRESENT) {
174       /*
175        * No packet available, and we haven't seen an error or EOF yet,
176        * so try to read the next packet.
177        */
178       if (!wtap_read(in_files[i].wth, err, err_info, &in_files[i].data_offset)) {
179         if (*err != 0) {
180           in_files[i].state = GOT_ERROR;
181           return NULL;
182         }
183         in_files[i].state = AT_EOF;
184       } else
185         in_files[i].state = PACKET_PRESENT;
186     }
187     
188     if (in_files[i].state == PACKET_PRESENT) {
189       phdr = wtap_phdr(in_files[i].wth);
190       if (is_earlier(&phdr->ts, &tv)) {
191         tv = phdr->ts;
192         ei = i;
193       }
194     }
195   }
196
197   if (ei == -1) {
198     /* All the streams are at EOF.  Return an EOF indication. */
199     *err = 0;
200     return NULL;
201   }
202
203   /* We'll need to read another packet from this file. */
204   in_files[ei].state = PACKET_NOT_PRESENT;
205
206   /* Return a pointer to the wtap structure for the file with that frame. */
207   return in_files[ei].wth;
208 }
209
210 /*
211  * Read the next packet, in file sequence order, from the set of files
212  * to be merged.
213  */
214 wtap *
215 merge_append_read_packet(int in_file_count, merge_in_file_t in_files[],
216                          int *err, gchar **err_info)
217 {
218   int i;
219
220   /*
221    * Find the first file not at EOF, and read the next packet from it.
222    */
223   for (i = 0; i < in_file_count; i++) {
224     if (in_files[i].state == AT_EOF)
225       continue; /* This file is already at EOF */
226     if (wtap_read(in_files[i].wth, err, err_info, &in_files[i].data_offset))
227       break; /* We have a packet */
228     if (*err != 0) {
229       /* Read error - quit immediately. */
230       in_files[i].state = GOT_ERROR;
231       return NULL;
232     }
233     /* EOF - flag this file as being at EOF, and try the next one. */
234     in_files[i].state = AT_EOF;
235   }
236   if (i == in_file_count) {
237     /* All the streams are at EOF.  Return an EOF indication. */
238     *err = 0;
239     return NULL;
240   }
241
242   /* Return a pointer to the wtap structure for the file with that frame. */
243   return in_files[i].wth;
244 }