Make "merge_files()" and "merge_append_files()" return a tri-state
[obnox/wireshark/wip.git] / merge.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 <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 #include "merge.h"
29
30 /*
31  * Scan through the arguments and open the input files
32  */
33 gboolean
34 merge_open_in_files(int in_file_count, char *const *in_file_names,
35                     merge_in_file_t **in_files, int *err, gchar **err_info,
36                     int *err_fileno)
37 {
38   int i, j;
39   int files_size = in_file_count * sizeof(merge_in_file_t);
40   merge_in_file_t *files;
41
42   files = g_malloc(files_size);
43   *in_files = files;
44
45   for (i = 0; i < in_file_count; i++) {
46     files[i].filename    = in_file_names[i];
47     files[i].wth         = wtap_open_offline(in_file_names[i], err, err_info, FALSE);
48     files[i].err         = 0;
49     files[i].data_offset = 0;
50     files[i].ok          = TRUE;
51     if (!files[i].wth) {
52       /* Close the files we've already opened. */
53       for (j = 0; j < i; j++)
54         wtap_close(files[j].wth);
55       *err_fileno = i;
56       return FALSE;
57     }
58   }
59   return TRUE;
60 }
61
62 /*
63  * Scan through and close each input file
64  */
65 void
66 merge_close_in_files(int count, merge_in_file_t in_files[])
67 {
68   int i;
69   for (i = 0; i < count; i++) {
70     wtap_close(in_files[i].wth);
71   }
72 }
73
74 /*
75  * Open the output file
76  *
77  * Return FALSE if file cannot be opened (so caller can report an error
78  * and clean up)
79  */
80 gboolean
81 merge_open_outfile(merge_out_file_t *out_file, int fd, int file_type,
82                    int frame_type, int snapshot_len, int *err)
83 {
84   out_file->pdh = wtap_dump_fdopen(fd, file_type, frame_type, snapshot_len,
85                                    err);
86   if (!out_file->pdh)
87     return FALSE;
88
89   out_file->snaplen = snapshot_len;
90   out_file->count = 1;
91   return TRUE;
92 }
93
94 /*
95  * Close the output file
96  */
97 gboolean
98 merge_close_outfile(merge_out_file_t *out_file, int *err)
99 {
100   if (!wtap_dump_close(out_file->pdh, err))
101     return FALSE;
102   return TRUE;
103 }
104
105 /*
106  * Select an output frame type based on the input files
107  * From Guy: If all files have the same frame type, then use that.
108  *           Otherwise select WTAP_ENCAP_PER_PACKET.  If the selected
109  *           output file type doesn't support per packet frame types,
110  *           then the wtap_dump_open call will fail with a reasonable
111  *           error condition.
112  */
113 int
114 merge_select_frame_type(int count, merge_in_file_t files[])
115 {
116   int i;
117   int selected_frame_type;
118
119   selected_frame_type = wtap_file_encap(files[0].wth);
120
121   for (i = 1; i < count; i++) {
122     int this_frame_type = wtap_file_encap(files[i].wth);
123     if (selected_frame_type != this_frame_type) {
124       selected_frame_type = WTAP_ENCAP_PER_PACKET;
125       break;
126     }
127   }
128
129   return selected_frame_type;
130 }
131
132 /*
133  * Scan through input files and find maximum snapshot length
134  */
135 int
136 merge_max_snapshot_length(int count, merge_in_file_t in_files[])
137 {
138   int i;
139   int max_snapshot = 0;
140   int snapshot_length;
141
142   for (i = 0; i < count; i++) {
143     snapshot_length = wtap_snapshot_length(in_files[i].wth);
144     if (snapshot_length == 0) {
145       /* Snapshot length of input file not known. */
146       snapshot_length = WTAP_MAX_PACKET_SIZE;
147     }
148     if (snapshot_length > max_snapshot)
149       max_snapshot = snapshot_length;
150   }
151   return max_snapshot;
152 }
153
154 /*
155  * Routine to write frame to output file
156  */
157 static gboolean
158 write_frame(wtap *wth, merge_out_file_t *out_file, int *err)
159 {
160   const struct wtap_pkthdr *phdr = wtap_phdr(wth);
161   struct wtap_pkthdr snap_phdr;
162
163   /* We simply write it, perhaps after truncating it; we could do other
164    * things, like modify it. */
165   if (out_file->snaplen != 0 && phdr->caplen > out_file->snaplen) {
166     snap_phdr = *phdr;
167     snap_phdr.caplen = out_file->snaplen;
168     phdr = &snap_phdr;
169   }
170
171   if (!wtap_dump(out_file->pdh, phdr, wtap_pseudoheader(wth), wtap_buf_ptr(wth), err))
172     return FALSE;
173
174   return TRUE;
175 }
176
177 /*
178  * returns TRUE if first argument is earlier than second
179  */
180 static gboolean
181 is_earlier(struct timeval *l, struct timeval *r) {
182   if (l->tv_sec > r->tv_sec) {  /* left is later */
183     return FALSE;
184   } else if (l->tv_sec < r->tv_sec) { /* left is earlier */
185     return TRUE;
186   } else if (l->tv_usec > r->tv_usec) { /* tv_sec equal, l.usec later */
187     return FALSE;
188   }
189   /* either one < two or one == two
190    * either way, return one
191    */
192   return TRUE;
193 }
194
195
196 /*
197  * returns index of earliest timestamp in set of input files
198  * or -1 if no valid files remain
199  */
200 static int
201 earliest(int count, merge_in_file_t in_files[]) {
202   int i;
203   int ei = -1;
204   struct timeval tv = {LONG_MAX, LONG_MAX};
205
206   for (i = 0; i < count; i++) {
207     struct wtap_pkthdr *phdr = wtap_phdr(in_files[i].wth);
208
209     if (in_files[i].ok && is_earlier(&(phdr->ts), &tv)) {
210       tv = phdr->ts;
211       ei = i;
212     }
213   }
214   return ei;
215 }
216
217 /*
218  * actually merge the files
219  */
220 merge_status_e
221 merge_files(int count, merge_in_file_t in_files[], merge_out_file_t *out_file, int *err)
222 {
223   int i;
224
225   /* prime the pump (read in first frame from each file) */
226   for (i = 0; i < count; i++) {
227     in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
228                                &(in_files[i].err_info),
229                                &(in_files[i].data_offset));
230     if (!in_files[i].ok)
231       return MERGE_READ_ERROR;
232   }
233
234   /* now keep writing the earliest frame until we're out of frames */
235   while ( -1 != (i = earliest(count, in_files))) {
236
237     /* write out earliest frame, and fetch another from its
238      * input file
239      */
240     if(!write_frame(in_files[i].wth, out_file, err))
241         return MERGE_WRITE_ERROR;
242     in_files[i].ok = wtap_read(in_files[i].wth, &(in_files[i].err),
243                                &(in_files[i].err_info),
244                                &(in_files[i].data_offset));
245     if (!in_files[i].ok)
246       return MERGE_READ_ERROR;
247   }
248
249   return MERGE_SUCCESS;
250 }
251
252 static merge_status_e
253 append_loop(merge_in_file_t in_files[], int i, int count,
254             merge_out_file_t *out_file, int *err)
255 {
256   gchar        *err_info;
257   long          data_offset;
258   int           loop = 0;
259
260   /* Start by clearing error flag */
261   *err = 0;
262
263   while ( (wtap_read(in_files[i].wth, err, &err_info, &data_offset)) ) {
264     if(!write_frame(in_files[i].wth, out_file, err))
265       return MERGE_WRITE_ERROR;
266     if (count > 0 && ++loop >= count)
267       break;
268   }
269
270   if (*err != 0) {
271     in_files[i].ok = FALSE;
272     in_files[i].err = *err;
273     in_files[i].err_info = err_info;
274     return MERGE_READ_ERROR;
275   }
276   return MERGE_SUCCESS;
277 }
278
279 /*
280  * routine to concatenate files
281  */
282 merge_status_e
283 merge_append_files(int count, merge_in_file_t in_files[],
284                    merge_out_file_t *out_file, int *err)
285 {
286   int i;
287   merge_status_e status;
288
289   for (i = 0; i < count; i++) {
290     status = append_loop(in_files, i, 0, out_file, err);
291     if (status != MERGE_SUCCESS)
292       return status;
293   }
294
295   return MERGE_SUCCESS;
296 }