2 * Routines for handling file sets
4 * Wireshark - Network traffic analyzer
5 * By Gerald Combs <gerald@wireshark.org>
6 * Copyright 1998 Gerald Combs
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #ifdef HAVE_SYS_WAIT_H
14 # include <sys/wait.h>
22 #include <wsutil/file_util.h>
23 #include <wsutil/filesystem.h>
24 #include <wsutil/ws_assert.h>
26 #include <wiretap/wtap.h>
28 #include <epan/strutil.h>
32 typedef struct _fileset
{
38 * This is the fileset's global data.
40 * XXX This should probably be per-main-window instead of global.
45 * Given a stat structure, get the creation time of the file if available,
49 /* Microsoft's documentation says this is the creation time */
50 #define ST_CREATE_TIME(statb) ((statb).st_ctime)
52 /* UN*X - do we have a creation time? */
53 #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIME)
54 #define ST_CREATE_TIME(statb) ((statb).st_birthtime)
55 #elif defined(HAVE_STRUCT_STAT___ST_BIRTHTIME)
56 #define ST_CREATE_TIME(statb) ((statb).__st_birthtime)
58 #define ST_CREATE_TIME(statb) (0)
59 #endif /* creation time on UN*X */
62 /* is this a probable file of a file set (does the naming pattern match)? */
64 fileset_filename_match_pattern(const char *fname
, char **prefix
, char **suffix
, char **time
)
68 fileset_match_t ret
= FILESET_NO_MATCH
;
69 static char *pattern
= "(?P<prefix>.*)_\\d{5}_(?P<time>\\d{14})$";
70 static char *pattern2
= "(?P<prefix>.*)_(?P<time>\\d{14})_\\d{5}$";
71 static GRegex
*regex
= NULL
;
72 static GRegex
*regex2
= NULL
;
76 regex
= g_regex_new(pattern
,
77 (GRegexCompileFlags
)(G_REGEX_OPTIMIZE
| G_REGEX_ANCHORED
),
78 G_REGEX_MATCH_ANCHORED
, &gerr
);
80 ws_warning("failed to compile regex: %s", gerr
->message
);
89 regex2
= g_regex_new(pattern2
,
90 (GRegexCompileFlags
)(G_REGEX_OPTIMIZE
| G_REGEX_ANCHORED
),
91 G_REGEX_MATCH_ANCHORED
, &gerr
);
93 ws_warning("failed to compile regex: %s", gerr
->message
);
100 /* d:\dir1\test_00001_20050418010750.cap */
101 filename
= g_path_get_basename(fname
);
103 /* test_00001_20050418010750.cap */
104 sfx
= strrchr(filename
, '.');
107 GSList
*compression_type_extensions
= wtap_get_all_compression_type_extensions_list();
108 char *ext
= g_ascii_strdown(sfx
+ 1, -1);
109 for (GSList
*compression_extension
= compression_type_extensions
;
110 compression_extension
!= NULL
;
111 compression_extension
= g_slist_next(compression_extension
)) {
112 if (g_strcmp0(ext
, (const char*)compression_extension
->data
) == 0) {
113 sfx
= strrchr(filename
, '.');
121 g_slist_free(compression_type_extensions
);
122 } else { /* suffix is optional */
123 sfx
= filename
+ strlen(filename
);
126 /* test_00001_20050418010750 */
128 GMatchInfo
*match_info
;
129 g_regex_match(regex
, filename
, 0, &match_info
);
130 if (g_match_info_matches(match_info
)) {
132 *prefix
= g_match_info_fetch_named(match_info
, "prefix");
135 *time
= g_match_info_fetch_named(match_info
, "time");
138 *suffix
= g_strdup(sfx
);
140 ret
= FILESET_NUM_TIME
;
142 g_match_info_free(match_info
);
144 if (ret
== FILESET_NO_MATCH
) {
145 g_regex_match(regex2
, filename
, 0, &match_info
);
146 if (g_match_info_matches(match_info
)) {
148 *prefix
= g_match_info_fetch_named(match_info
, "prefix");
151 *time
= g_match_info_fetch_named(match_info
, "time");
154 *suffix
= g_strdup(sfx
);
156 ret
= FILESET_TIME_NUM
;
158 g_match_info_free(match_info
);
167 /* test if both files could be in the same file set */
168 /* (fname2 must already be in correct shape) */
170 fileset_is_file_in_set(const char *fname1
, const char *fname2
)
176 fileset_match_t match1
;
177 fileset_match_t match2
;
180 match1
= fileset_filename_match_pattern(fname1
, &pfx1
, &sfx1
, NULL
);
181 if (match1
== FILESET_NO_MATCH
) {
185 match2
= fileset_filename_match_pattern(fname2
, &pfx2
, &sfx2
, NULL
);
186 /* just to be sure ... */
187 ws_assert(match2
!= FILESET_NO_MATCH
);
188 if (match1
== match2
&& g_strcmp0(pfx1
, pfx2
) == 0 && g_strcmp0(sfx1
, sfx2
) == 0) {
200 /* GCompareFunc helper for g_list_find_custom() */
202 fileset_find_by_path(const void *a
, const void *b
)
204 const fileset_entry
*entry
;
207 entry
= (const fileset_entry
*) a
;
208 path
= (const char *) b
;
210 return g_strcmp0(entry
->fullname
, path
);
213 /* update the time and size of this file in the list */
215 fileset_update_file(const char *path
)
219 fileset_entry
*entry
= NULL
;
222 fh
= ws_open( path
, O_RDONLY
, 0000 /* no creation so don't matter */);
226 result
= ws_fstat64( fh
, &buf
);
228 /* Show statistics if they are valid */
230 entry_list
= g_list_find_custom(set
.entries
, path
,
231 fileset_find_by_path
);
234 entry
= (fileset_entry
*) entry_list
->data
;
235 entry
->ctime
= ST_CREATE_TIME(buf
);
236 entry
->mtime
= buf
.st_mtime
;
237 entry
->size
= buf
.st_size
;
245 /* we know this file is part of the set, so add it */
246 static fileset_entry
*
247 fileset_add_file(const char *dirname
, const char *fname
, bool current
)
252 fileset_entry
*entry
= NULL
;
255 path
= ws_strdup_printf("%s%s", dirname
, fname
);
257 fh
= ws_open( path
, O_RDONLY
, 0000 /* no creation so don't matter */);
261 result
= ws_fstat64( fh
, &buf
);
263 /* Show statistics if they are valid */
265 entry
= g_new(fileset_entry
, 1);
267 entry
->fullname
= g_strdup(path
);
268 entry
->name
= g_strdup(fname
);
269 entry
->ctime
= ST_CREATE_TIME(buf
);
270 entry
->mtime
= buf
.st_mtime
;
271 entry
->size
= buf
.st_size
;
272 entry
->current
= current
;
274 set
.entries
= g_list_append(set
.entries
, entry
);
286 /* compare two list entries by creation date/time (through filename) */
288 fileset_sort_compare(const void *a
, const void *b
)
290 const fileset_entry
*entry_a
= (const fileset_entry
*)a
;
291 const fileset_entry
*entry_b
= (const fileset_entry
*)b
;
293 return strcmp(entry_a
->name
, entry_b
->name
);
297 /* add all file set entries to the dialog */
298 void fileset_update_dlg(void *window
)
302 /* Add all entries to the dialog. */
303 fileset_dlg_begin_add_file(window
);
304 le
= g_list_first(set
.entries
);
306 fileset_dlg_add_file((fileset_entry
*)le
->data
, window
);
307 le
= g_list_next(le
);
309 fileset_dlg_end_add_file(window
);
313 /* walk through the directory of the loaded file and add every file matching the current file */
315 fileset_add_dir(const char *fname
, void *window
)
317 WS_DIR
*dir
; /* scanned directory */
318 WS_DIRENT
*file
; /* current file */
324 /* get (convert) directory name, but don't touch the given string */
325 fname_dup
= g_strdup(fname
);
326 dirname
= g_string_new(get_dirname(fname_dup
));
329 set
.dirname
= g_strdup(dirname
->str
);
331 dirname
= g_string_append_c(dirname
, G_DIR_SEPARATOR
);
333 /* is the current file probably a part of any fileset? */
334 if(fileset_filename_match_pattern(fname
, NULL
, NULL
, NULL
)) {
335 /* yes, go through the files in the directory and check if the file in question is part of the current file set */
336 if ((dir
= ws_dir_open(dirname
->str
, 0, NULL
)) != NULL
) {
337 while ((file
= ws_dir_read_name(dir
)) != NULL
) {
338 name
= ws_dir_get_name(file
);
339 if(fileset_is_file_in_set(name
, get_basename(fname
))) {
340 fileset_add_file(dirname
->str
, name
, strcmp(name
, get_basename(fname
))== 0 /* current */);
347 /* no, this is a "standalone file", just add this one */
348 fileset_add_file(dirname
->str
, get_basename(fname
), true /* current */);
349 /* don't add the file to the dialog here, this will be done in fileset_update_dlg() below */
352 g_string_free(dirname
, TRUE
/* free_segment */);
354 /* sort entries by creation time */
355 set
.entries
= g_list_sort(set
.entries
, fileset_sort_compare
);
357 fileset_update_dlg(window
);
361 /* get current directory name */
363 fileset_get_dirname(void)
369 /* get the current list entry, or NULL */
371 fileset_get_current(void)
374 fileset_entry
*entry
;
377 /* add all entries to the dialog */
378 le
= g_list_first(set
.entries
);
380 entry
= (fileset_entry
*)le
->data
;
384 le
= g_list_next(le
);
391 /* get the file set entry after the current one, or NULL */
393 fileset_get_next(void)
398 le
= fileset_get_current();
403 le
= g_list_next(le
);
408 return (fileset_entry
*)le
->data
;
412 /* get the file set entry before the current one, or NULL */
414 fileset_get_previous(void)
419 le
= fileset_get_current();
424 le
= g_list_previous(le
);
429 return (fileset_entry
*)le
->data
;
433 /* delete a single entry */
434 static void fileset_entry_delete(void *data
, void *user_data _U_
)
436 fileset_entry
*entry
= (fileset_entry
*)data
;
438 g_free( (void *) entry
->fullname
);
439 entry
->fullname
= NULL
;
440 g_free( (void *) entry
->name
);
446 /* delete the whole file set */
447 void fileset_delete(void)
449 /* free the entry list */
451 g_list_foreach(set
.entries
, fileset_entry_delete
, NULL
);
452 g_list_free(set
.entries
);
458 g_free( (void *) set
.dirname
);
464 * Editor modelines - https://www.wireshark.org/tools/modelines.html
469 * indent-tabs-mode: nil
472 * vi: set shiftwidth=4 tabstop=8 expandtab:
473 * :indentSize=4:tabSize=8:noTabs=true: