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