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