Remove forward declarations of registration functions.
[metze/wireshark/wip.git] / fileset.c
1 /* fileset.c
2  * Routines for handling file sets
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34
35 #ifdef HAVE_DIRENT_H
36 #include <dirent.h>
37 #endif
38
39 #ifdef HAVE_SYS_STAT_H
40 # include <sys/stat.h>
41 #endif
42
43 #ifdef HAVE_SYS_WAIT_H
44 # include <sys/wait.h>
45 #endif
46
47 #include <stdio.h>
48 #include <string.h>
49 #include <ctype.h>
50
51 #include <glib.h>
52
53 #include <wsutil/file_util.h>
54 #include "globals.h"
55
56 #include <epan/filesystem.h>
57 #include <epan/strutil.h>
58
59 #include "fileset.h"
60
61
62
63 typedef struct _fileset {
64     GList   *entries;
65     char    *dirname;
66 } fileset;
67
68 /* this is the fileset's global data */
69 static fileset set = { NULL, NULL};
70
71
72 /* is this a probable file of a file set (does the naming pattern match)? */
73 gboolean
74 fileset_filename_match_pattern(const char *fname)
75 {
76     char        *pfx;
77     size_t       baselen;
78     size_t      minlen = strlen("_00001_20050418010750");
79     char        *filename;
80
81
82     /* d:\dir1\test_00001_20050418010750.cap */
83     filename = g_strdup(get_basename(fname));
84
85     /* test_00001_20050418010750.cap */
86     pfx = strrchr(filename, '.');
87     if(pfx == NULL) {  /* suffix is optional */
88         pfx = filename + strlen(filename);
89     }
90     /* test_00001_20050418010750 */
91     *pfx = '\0';
92
93     /* filename long enough? */
94     baselen = strlen(filename);
95     if(baselen < minlen) {
96         g_free(filename);
97         return FALSE;
98     }
99
100     /* there must be two underscores at special places */
101     if(filename[baselen-minlen] != '_' || filename[baselen-minlen+6] != '_') {
102         g_free(filename);
103         return FALSE;
104     }
105
106     /* replace the two underscores by digits */
107     filename[baselen-minlen] = '0';
108     filename[baselen-minlen+6] = '0';
109
110     /* we should have only digits now */
111     while(minlen--) {
112         baselen--;
113
114         if(!isdigit( (guchar) filename[baselen])) {
115             g_free(filename);
116             return FALSE;
117         }
118     }
119
120     g_free(filename);
121
122     /* ok, seems to be good */
123     return TRUE;
124 }
125
126
127 /* test, if both files could be in the same file set */
128 /* (the filenames must already be in correct shape) */
129 static gboolean
130 fileset_is_file_in_set(const char *fname1, const char *fname2)
131 {
132     char        *pfx1;
133     char        *pfx2;
134     char        *dup_f1;
135     char        *dup_f2;
136     size_t       minlen = strlen("_00001_20050418010750");
137
138
139     /* just to be sure ... */
140     g_assert(fileset_filename_match_pattern(fname1));
141     g_assert(fileset_filename_match_pattern(fname2));
142
143     dup_f1 = g_strdup(fname1);
144     dup_f2 = g_strdup(fname2);
145
146     pfx1 = strrchr(dup_f1, '.');
147     pfx2 = strrchr(dup_f2, '.');
148     /* suffix is optional */
149     if (!pfx1) pfx1 = dup_f1 + strlen(dup_f1);
150     if (!pfx2) pfx2 = dup_f2 + strlen(dup_f2);
151
152     /* the optional suffix (file extension) must be equal */
153     if(strcmp(pfx1, pfx2) != 0) {
154         g_free(dup_f1);
155         g_free(dup_f2);
156         return FALSE;
157     }
158
159     *(pfx1-minlen) = '\0';
160     *(pfx2-minlen) = '\0';
161
162     if(strcmp(dup_f1, dup_f2) != 0) {
163         g_free(dup_f1);
164         g_free(dup_f2);
165         return FALSE;
166     }
167
168     g_free(dup_f1);
169     g_free(dup_f2);
170     return TRUE;
171 }
172
173 /* GCompareFunc helper for g_list_find_custom() */
174 static gint
175 fileset_find_by_path(gconstpointer a, gconstpointer b)
176 {
177     const fileset_entry *entry;
178     const char *path;
179
180     entry = (const fileset_entry *) a;
181     path  = (const char *) b;
182
183     return g_strcmp0(entry->fullname, path);
184 }
185
186 /* update the time and size of this file in the list */
187 void
188 fileset_update_file(const char *path)
189 {
190     int fh, result;
191     ws_statb64 buf;
192     fileset_entry *entry = NULL;
193     GList *entry_list;
194
195     fh = ws_open( path, O_RDONLY, 0000 /* no creation so don't matter */);
196     if(fh !=  -1) {
197
198         /* Get statistics */
199         result = ws_fstat64( fh, &buf );
200
201         /* Show statistics if they are valid */
202         if( result == 0 ) {
203             entry_list = g_list_find_custom(set.entries, path,
204                                             fileset_find_by_path);
205
206             if (entry_list) {
207                 entry = (fileset_entry *) entry_list->data;
208                 entry->ctime    = buf.st_ctime;
209                 entry->mtime    = buf.st_mtime;
210                 entry->size     = buf.st_size;
211             }
212         }
213
214         ws_close(fh);
215     }
216 }
217
218 /* we know this file is part of the set, so add it */
219 static fileset_entry *
220 fileset_add_file(const char *dirname, const char *fname, gboolean current)
221 {
222     int fh, result;
223     ws_statb64 buf;
224     char *path;
225     fileset_entry *entry = NULL;
226
227
228     path = g_strdup_printf("%s%s", dirname, fname);
229
230     fh = ws_open( path, O_RDONLY, 0000 /* no creation so don't matter */);
231     if(fh !=  -1) {
232
233         /* Get statistics */
234         result = ws_fstat64( fh, &buf );
235
236         /* Show statistics if they are valid */
237         if( result == 0 ) {
238             entry = (fileset_entry *)g_malloc(sizeof(fileset_entry));
239
240             entry->fullname = g_strdup(path);
241             entry->name     = g_strdup(fname);
242             entry->ctime    = buf.st_ctime;
243             entry->mtime    = buf.st_mtime;
244             entry->size     = buf.st_size;
245             entry->current  = current;
246
247             set.entries = g_list_append(set.entries, entry);
248         }
249
250         ws_close(fh);
251     }
252
253     g_free(path);
254
255     return entry;
256 }
257
258
259 /* compare two list entries by creation date/time (through filename) */
260 static gint
261 fileset_sort_compare(gconstpointer a, gconstpointer b)
262 {
263     const fileset_entry *entry_a = (const fileset_entry *)a;
264     const fileset_entry *entry_b = (const fileset_entry *)b;
265
266     return strcmp(entry_a->name, entry_b->name);
267 }
268
269
270 /* add all file set entries to the dialog */
271 void fileset_update_dlg(void *window)
272 {
273     GList         *le;
274
275
276     /* add all entires to the dialog */
277     le = g_list_first(set.entries);
278     while(le) {
279         fileset_dlg_add_file((fileset_entry *)le->data, window);
280         le = g_list_next(le);
281     }
282 }
283
284
285 /* walk through the directory of the loaded file and add every file matching the current file */
286 void
287 fileset_add_dir(const char *fname, void *window)
288 {
289     WS_DIR        *dir;             /* scanned directory */
290     WS_DIRENT     *file;            /* current file */
291     const char    *name;
292     GString       *dirname;
293     gchar         *fname_dup;
294
295
296     /* get (convert) directory name, but don't touch the given string */
297     fname_dup = get_dirname(g_strdup(fname));
298     dirname = g_string_new(fname_dup);
299     g_free(fname_dup);
300
301     set.dirname = g_strdup(dirname->str);
302
303     dirname = g_string_append_c(dirname, G_DIR_SEPARATOR);
304
305     /* is the current file probably a part of any fileset? */
306     if(fileset_filename_match_pattern(fname)) {
307         /* yes, go through the files in the directory and check if the file in question is part of the current file set */
308         if ((dir = ws_dir_open(dirname->str, 0, NULL)) != NULL) {
309             while ((file = ws_dir_read_name(dir)) != NULL) {
310                 name = ws_dir_get_name(file);
311                 if(fileset_filename_match_pattern(name) && fileset_is_file_in_set(name, get_basename(fname))) {
312                     fileset_add_file(dirname->str, name, strcmp(name, get_basename(fname))== 0 /* current */);
313                 }
314             } /* while */
315
316             ws_dir_close(dir);
317         } /* if */
318     } else {
319         /* no, this is a "standalone file", just add this one */
320         fileset_add_file(dirname->str, get_basename(fname), TRUE /* current */);
321         /* don't add the file to the dialog here, this will be done in fileset_update_dlg() below */
322     }
323
324     g_string_free(dirname, TRUE /* free_segment */);
325
326     /* sort entries by creation time */
327     set.entries = g_list_sort(set.entries, fileset_sort_compare);
328
329     fileset_update_dlg(window);
330 }
331
332
333 /* get current directory name */
334 const char *
335 fileset_get_dirname(void)
336 {
337     return set.dirname;
338 }
339
340
341 /* get the current list entry, or NULL */
342 static GList *
343 fileset_get_current(void)
344 {
345     GList         *le;
346     fileset_entry *entry;
347
348
349     /* add all entires to the dialog */
350     le = g_list_first(set.entries);
351     while(le) {
352         entry = (fileset_entry *)le->data;
353         if(entry->current) {
354             return le;
355         }
356         le = g_list_next(le);
357     }
358
359     return NULL;
360 }
361
362
363 /* get the file set entry after the current one, or NULL */
364 fileset_entry *
365 fileset_get_next(void)
366 {
367     GList         *le;
368
369
370     le = fileset_get_current();
371     if(le == NULL) {
372         return NULL;
373     }
374
375     le = g_list_next(le);
376     if(le == NULL) {
377         return NULL;
378     }
379
380     return (fileset_entry *)le->data;
381 }
382
383
384 /* get the file set entry before the current one, or NULL */
385 fileset_entry *
386 fileset_get_previous(void)
387 {
388     GList         *le;
389
390
391     le = fileset_get_current();
392     if(le == NULL) {
393         return NULL;
394     }
395
396     le = g_list_previous(le);
397     if(le == NULL) {
398         return NULL;
399     }
400
401     return (fileset_entry *)le->data;
402 }
403
404
405 /* delete a single entry */
406 static void fileset_entry_delete(gpointer data, gpointer user_data _U_)
407 {
408     fileset_entry *entry = (fileset_entry *)data;
409
410     g_free( (gpointer) entry->fullname);
411     entry->fullname = NULL;
412     g_free( (gpointer) entry->name);
413     entry->name = NULL;
414     g_free(entry);
415 }
416
417
418 /* delete the whole file set */
419 void fileset_delete(void)
420 {
421     /* free the entry list */
422     if(set.entries) {
423         g_list_foreach(set.entries, fileset_entry_delete, NULL);
424         g_list_free(set.entries);
425         set.entries = NULL;
426     }
427
428     /* free the rest */
429     if(set.dirname) {
430         g_free( (gpointer) set.dirname);
431         set.dirname = NULL;
432     }
433 }