/src/wireshark/wsutil/filesystem.c
Line | Count | Source |
1 | | /* filesystem.c |
2 | | * Filesystem utility routines |
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 | 0 | #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL |
13 | | |
14 | | #include "filesystem.h" |
15 | | |
16 | | #include <stdio.h> |
17 | | #include <stdlib.h> |
18 | | #include <string.h> |
19 | | #include <errno.h> |
20 | | |
21 | | #ifdef _WIN32 |
22 | | #include <windows.h> |
23 | | #include <tchar.h> |
24 | | #include <shlobj.h> |
25 | | #include <wsutil/unicode-utils.h> |
26 | | #else /* _WIN32 */ |
27 | | #ifdef ENABLE_APPLICATION_BUNDLE |
28 | | #include <mach-o/dyld.h> |
29 | | #endif |
30 | | #ifdef __FreeBSD__ |
31 | | #include <sys/types.h> |
32 | | #include <sys/sysctl.h> |
33 | | #endif |
34 | | #ifdef HAVE_DLGET |
35 | | #include <dlfcn.h> |
36 | | #endif |
37 | | #include <pwd.h> |
38 | | #endif /* _WIN32 */ |
39 | | |
40 | | #include <wsutil/file_util.h> |
41 | | #include <wsutil/privileges.h> |
42 | | #include <wsutil/report_message.h> |
43 | | #include <wsutil/utf8_entities.h> |
44 | | |
45 | | #include "path_config.h" |
46 | | |
47 | | #define PROFILES_DIR "profiles" |
48 | | #define PLUGINS_DIR_NAME "plugins" |
49 | 0 | #define EXTCAP_DIR_NAME "extcap" |
50 | | #define PROFILES_INFO_NAME "profile_files.txt" |
51 | | |
52 | 14 | #define _S G_DIR_SEPARATOR_S |
53 | | |
54 | | char *persconffile_dir; |
55 | | char *datafile_dir; |
56 | | char *persdatafile_dir; |
57 | | char *persconfprofile; |
58 | | char *doc_dir; |
59 | | char *current_working_dir; |
60 | | |
61 | | /* Directory from which the executable came. */ |
62 | | static char *progfile_dir; |
63 | | static char *install_prefix; |
64 | | |
65 | | static bool do_store_persconffiles; |
66 | | static GHashTable *profile_files; |
67 | | |
68 | | /* |
69 | | * Given a pathname, return a pointer to the last pathname separator |
70 | | * character in the pathname, or NULL if the pathname contains no |
71 | | * separators. |
72 | | */ |
73 | | char * |
74 | | find_last_pathname_separator(const char *path) |
75 | 14 | { |
76 | 14 | char *separator; |
77 | | |
78 | | #ifdef _WIN32 |
79 | | char c; |
80 | | |
81 | | /* |
82 | | * We have to scan for '\' or '/'. |
83 | | * Get to the end of the string. |
84 | | */ |
85 | | separator = strchr(path, '\0'); /* points to ending '\0' */ |
86 | | while (separator > path) { |
87 | | c = *--separator; |
88 | | if (c == '\\' || c == '/') |
89 | | return separator; /* found it */ |
90 | | } |
91 | | |
92 | | /* |
93 | | * OK, we didn't find any, so no directories - but there might |
94 | | * be a drive letter.... |
95 | | */ |
96 | | return strchr(path, ':'); |
97 | | #else |
98 | 14 | separator = strrchr(path, '/'); |
99 | 14 | return separator; |
100 | 14 | #endif |
101 | 14 | } |
102 | | |
103 | | /* |
104 | | * Given a pathname, return the last component. |
105 | | */ |
106 | | const char * |
107 | | get_basename(const char *path) |
108 | 0 | { |
109 | 0 | const char *filename; |
110 | |
|
111 | 0 | ws_assert(path != NULL); |
112 | 0 | filename = find_last_pathname_separator(path); |
113 | 0 | if (filename == NULL) { |
114 | | /* |
115 | | * There're no directories, drive letters, etc. in the |
116 | | * name; the pathname *is* the file name. |
117 | | */ |
118 | 0 | filename = path; |
119 | 0 | } else { |
120 | | /* |
121 | | * Skip past the pathname or drive letter separator. |
122 | | */ |
123 | 0 | filename++; |
124 | 0 | } |
125 | 0 | return filename; |
126 | 0 | } |
127 | | |
128 | | /* |
129 | | * Given a pathname, return a string containing everything but the |
130 | | * last component. NOTE: this overwrites the pathname handed into |
131 | | * it.... |
132 | | */ |
133 | | char * |
134 | | get_dirname(char *path) |
135 | 0 | { |
136 | 0 | char *separator; |
137 | |
|
138 | 0 | ws_assert(path != NULL); |
139 | 0 | separator = find_last_pathname_separator(path); |
140 | 0 | if (separator == NULL) { |
141 | | /* |
142 | | * There're no directories, drive letters, etc. in the |
143 | | * name; there is no directory path to return. |
144 | | */ |
145 | 0 | return NULL; |
146 | 0 | } |
147 | | |
148 | | /* |
149 | | * Get rid of the last pathname separator and the final file |
150 | | * name following it. |
151 | | */ |
152 | 0 | *separator = '\0'; |
153 | | |
154 | | /* |
155 | | * "path" now contains the pathname of the directory containing |
156 | | * the file/directory to which it referred. |
157 | | */ |
158 | 0 | return path; |
159 | 0 | } |
160 | | |
161 | | /* |
162 | | * Given a pathname, return: |
163 | | * |
164 | | * the errno, if an attempt to "stat()" the file fails; |
165 | | * |
166 | | * EISDIR, if the attempt succeeded and the file turned out |
167 | | * to be a directory; |
168 | | * |
169 | | * 0, if the attempt succeeded and the file turned out not |
170 | | * to be a directory. |
171 | | */ |
172 | | |
173 | | int |
174 | | test_for_directory(const char *path) |
175 | 30 | { |
176 | 30 | ws_statb64 statb; |
177 | | |
178 | 30 | if (ws_stat64(path, &statb) < 0) |
179 | 30 | return errno; |
180 | | |
181 | 0 | if (S_ISDIR(statb.st_mode)) |
182 | 0 | return EISDIR; |
183 | 0 | else |
184 | 0 | return 0; |
185 | 0 | } |
186 | | |
187 | | int |
188 | | test_for_fifo(const char *path) |
189 | 0 | { |
190 | 0 | ws_statb64 statb; |
191 | |
|
192 | 0 | if (ws_stat64(path, &statb) < 0) |
193 | 0 | return errno; |
194 | | |
195 | 0 | if (S_ISFIFO(statb.st_mode)) |
196 | 0 | return ESPIPE; |
197 | 0 | else |
198 | 0 | return 0; |
199 | 0 | } |
200 | | |
201 | | bool |
202 | | test_for_regular_file(const char *path) |
203 | 0 | { |
204 | 0 | ws_statb64 statb; |
205 | |
|
206 | 0 | if (!path) { |
207 | 0 | return false; |
208 | 0 | } |
209 | | |
210 | 0 | if (ws_stat64(path, &statb) != 0) |
211 | 0 | return false; |
212 | | |
213 | 0 | return S_ISREG(statb.st_mode); |
214 | 0 | } |
215 | | |
216 | | #ifdef ENABLE_APPLICATION_BUNDLE |
217 | | /* |
218 | | * Directory of the application bundle in which we're contained, |
219 | | * if we're contained in an application bundle. Otherwise, NULL. |
220 | | * |
221 | | * Note: Table 2-5 "Subdirectories of the Contents directory" of |
222 | | * |
223 | | * https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW1 |
224 | | * |
225 | | * says that the "Frameworks" directory |
226 | | * |
227 | | * Contains any private shared libraries and frameworks used by the |
228 | | * executable. The frameworks in this directory are revision-locked |
229 | | * to the application and cannot be superseded by any other, even |
230 | | * newer, versions that may be available to the operating system. In |
231 | | * other words, the frameworks included in this directory take precedence |
232 | | * over any other similarly named frameworks found in other parts of |
233 | | * the operating system. For information on how to add private |
234 | | * frameworks to your application bundle, see Framework Programming Guide. |
235 | | * |
236 | | * so if we were to ship with any frameworks (e.g. Qt) we should |
237 | | * perhaps put them in a Frameworks directory rather than under |
238 | | * Resources. |
239 | | * |
240 | | * It also says that the "PlugIns" directory |
241 | | * |
242 | | * Contains loadable bundles that extend the basic features of your |
243 | | * application. You use this directory to include code modules that |
244 | | * must be loaded into your application's process space in order to |
245 | | * be used. You would not use this directory to store standalone |
246 | | * executables. |
247 | | * |
248 | | * Our plugins are just raw .so/.dylib files; I don't know whether by |
249 | | * "bundles" they mean application bundles (i.e., directory hierarchies) |
250 | | * or just "bundles" in the Mach-O sense (which are an image type that |
251 | | * can be loaded with dlopen() but not linked as libraries; our plugins |
252 | | * are, I think, built as dylibs and can be loaded either way). |
253 | | * |
254 | | * And it says that the "SharedSupport" directory |
255 | | * |
256 | | * Contains additional non-critical resources that do not impact the |
257 | | * ability of the application to run. You might use this directory to |
258 | | * include things like document templates, clip art, and tutorials |
259 | | * that your application expects to be present but that do not affect |
260 | | * the ability of your application to run. |
261 | | * |
262 | | * I don't think I'd put the files that currently go under Resources/share |
263 | | * into that category; they're not, for example, sample Lua scripts that |
264 | | * don't actually get run by Wireshark, they're configuration/data files |
265 | | * for Wireshark whose absence might not prevent Wireshark from running |
266 | | * but that would affect how it behaves when run. |
267 | | */ |
268 | | static char *appbundle_dir; |
269 | | #endif |
270 | | |
271 | | /* |
272 | | * true if we're running from the build directory and we aren't running |
273 | | * with special privileges. |
274 | | */ |
275 | | static bool running_in_build_directory_flag; |
276 | | |
277 | | #ifndef _WIN32 |
278 | | /* |
279 | | * Get the pathname of the executable using various platform- |
280 | | * dependent mechanisms for various UN*Xes. |
281 | | * |
282 | | * These calls all should return something independent of the argv[0] |
283 | | * passed to the program, so it shouldn't be fooled by an argv[0] |
284 | | * that doesn't match the executable path. |
285 | | * |
286 | | * We don't use dladdr() because: |
287 | | * |
288 | | * not all UN*Xes necessarily have dladdr(); |
289 | | * |
290 | | * those that do have it don't necessarily have dladdr(main) |
291 | | * return information about the executable image; |
292 | | * |
293 | | * those that do have a dladdr() where dladdr(main) returns |
294 | | * information about the executable image don't necessarily |
295 | | * have a mechanism by which the executable image can get |
296 | | * its own path from the kernel (either by a call or by it |
297 | | * being handed to it along with argv[] and the environment), |
298 | | * so they just fall back on getting it from argv[0], which we |
299 | | * already have code to do; |
300 | | * |
301 | | * those that do have such a mechanism don't necessarily use |
302 | | * it in dladdr(), and, instead, just fall back on getting it |
303 | | * from argv[0]; |
304 | | * |
305 | | * so the only places where it's worth bothering to use dladdr() |
306 | | * are platforms where dladdr(main) return information about the |
307 | | * executable image by getting it from the kernel rather than |
308 | | * by looking at argv[0], and where we can't get at that information |
309 | | * ourselves, and we haven't seen any indication that there are any |
310 | | * such platforms. |
311 | | * |
312 | | * In particular, some dynamic linkers supply a dladdr() such that |
313 | | * dladdr(main) just returns something derived from argv[0], so |
314 | | * just using dladdr(main) is the wrong thing to do if there's |
315 | | * another mechanism that can get you a more reliable version of |
316 | | * the executable path. |
317 | | * |
318 | | * So, on platforms where we know of a mechanism to get that path |
319 | | * (where getting that path doesn't involve argv[0], which is not |
320 | | * guaranteed to reflect the path to the binary), this routine |
321 | | * attempts to use that platform's mechanism. On other platforms, |
322 | | * it just returns NULL. |
323 | | * |
324 | | * This is not guaranteed to return an absolute path; if it doesn't, |
325 | | * our caller must prepend the current directory if it's a path. |
326 | | * |
327 | | * This is not guaranteed to return the "real path"; it might return |
328 | | * something with symbolic links in the path. Our caller must |
329 | | * use realpath() if they want the real thing, but that's also true of |
330 | | * something obtained by looking at argv[0]. |
331 | | */ |
332 | | #define xx_free free /* hack so checkAPIs doesn't complain */ |
333 | | static const char * |
334 | | get_current_executable_path(void) |
335 | 14 | { |
336 | | #if defined(ENABLE_APPLICATION_BUNDLE) |
337 | | static char *executable_path; |
338 | | uint32_t path_buf_size; |
339 | | |
340 | | if (executable_path) { |
341 | | return executable_path; |
342 | | } |
343 | | |
344 | | path_buf_size = PATH_MAX; |
345 | | executable_path = (char *)g_malloc(path_buf_size); |
346 | | if (_NSGetExecutablePath(executable_path, &path_buf_size) == -1) { |
347 | | executable_path = (char *)g_realloc(executable_path, path_buf_size); |
348 | | if (_NSGetExecutablePath(executable_path, &path_buf_size) == -1) |
349 | | return NULL; |
350 | | } |
351 | | /* |
352 | | * Resolve our path so that it's possible to symlink the executables |
353 | | * in our application bundle. |
354 | | */ |
355 | | char *rp_execpath = realpath(executable_path, NULL); |
356 | | if (rp_execpath) { |
357 | | g_free(executable_path); |
358 | | executable_path = g_strdup(rp_execpath); |
359 | | xx_free(rp_execpath); |
360 | | } |
361 | | return executable_path; |
362 | | #elif defined(__linux__) |
363 | | /* |
364 | | * In older versions of GNU libc's dynamic linker, as used on Linux, |
365 | | * dladdr(main) supplies a path based on argv[0], so we use |
366 | | * /proc/self/exe instead; there are Linux distributions with |
367 | | * kernels that support /proc/self/exe and those older versions |
368 | | * of the dynamic linker, and this will get a better answer on |
369 | | * those versions. |
370 | | * |
371 | | * XXX - are there OS versions that support "exe" but not "self"? |
372 | | */ |
373 | 14 | static char executable_path[PATH_MAX + 1]; |
374 | 14 | ssize_t r; |
375 | | |
376 | 14 | if ((r = readlink("/proc/self/exe", executable_path, PATH_MAX)) == -1) |
377 | 0 | return NULL; |
378 | 14 | executable_path[r] = '\0'; |
379 | 14 | return executable_path; |
380 | | #elif defined(__FreeBSD__) && defined(KERN_PROC_PATHNAME) |
381 | | /* |
382 | | * In older versions of FreeBSD's dynamic linker, dladdr(main) |
383 | | * supplies a path based on argv[0], so we use the KERN_PROC_PATHNAME |
384 | | * sysctl instead; there are, I think, versions of FreeBSD |
385 | | * that support the sysctl that have and those older versions |
386 | | * of the dynamic linker, and this will get a better answer on |
387 | | * those versions. |
388 | | */ |
389 | | int mib[4]; |
390 | | char *executable_path; |
391 | | size_t path_buf_size; |
392 | | |
393 | | mib[0] = CTL_KERN; |
394 | | mib[1] = KERN_PROC; |
395 | | mib[2] = KERN_PROC_PATHNAME; |
396 | | mib[3] = -1; |
397 | | path_buf_size = PATH_MAX; |
398 | | executable_path = (char *)g_malloc(path_buf_size); |
399 | | if (sysctl(mib, 4, executable_path, &path_buf_size, NULL, 0) == -1) { |
400 | | if (errno != ENOMEM) |
401 | | return NULL; |
402 | | executable_path = (char *)g_realloc(executable_path, path_buf_size); |
403 | | if (sysctl(mib, 4, executable_path, &path_buf_size, NULL, 0) == -1) |
404 | | return NULL; |
405 | | } |
406 | | return executable_path; |
407 | | #elif defined(__NetBSD__) |
408 | | /* |
409 | | * In all versions of NetBSD's dynamic linker as of 2013-08-12, |
410 | | * dladdr(main) supplies a path based on argv[0], so we use |
411 | | * /proc/curproc/exe instead. |
412 | | * |
413 | | * XXX - are there OS versions that support "exe" but not "curproc" |
414 | | * or "self"? Are there any that support "self" but not "curproc"? |
415 | | */ |
416 | | static char executable_path[PATH_MAX + 1]; |
417 | | ssize_t r; |
418 | | |
419 | | if ((r = readlink("/proc/curproc/exe", executable_path, PATH_MAX)) == -1) |
420 | | return NULL; |
421 | | executable_path[r] = '\0'; |
422 | | return executable_path; |
423 | | #elif defined(__DragonFly__) |
424 | | /* |
425 | | * In older versions of DragonFly BSD's dynamic linker, dladdr(main) |
426 | | * supplies a path based on argv[0], so we use /proc/curproc/file |
427 | | * instead; it appears to be supported by all versions of DragonFly |
428 | | * BSD. |
429 | | */ |
430 | | static char executable_path[PATH_MAX + 1]; |
431 | | ssize_t r; |
432 | | |
433 | | if ((r = readlink("/proc/curproc/file", executable_path, PATH_MAX)) == -1) |
434 | | return NULL; |
435 | | executable_path[r] = '\0'; |
436 | | return executable_path; |
437 | | #elif defined(HAVE_GETEXECNAME) |
438 | | /* |
439 | | * Solaris, with getexecname(). |
440 | | * It appears that getexecname() dates back to at least Solaris 8, |
441 | | * but /proc/{pid}/path is first documented in the Solaris 10 documentation, |
442 | | * so we use getexecname() if available, rather than /proc/self/path/a.out |
443 | | * (which isn't documented, but appears to be a symlink to the |
444 | | * executable image file). |
445 | | */ |
446 | | return getexecname(); |
447 | | #elif defined(HAVE_DLGET) |
448 | | /* |
449 | | * HP-UX 11, with dlget(); use dlget() and dlgetname(). |
450 | | * See |
451 | | * |
452 | | * https://web.archive.org/web/20081025174755/http://h21007.www2.hp.com/portal/site/dspp/menuitem.863c3e4cbcdc3f3515b49c108973a801?ciid=88086d6e1de021106d6e1de02110275d6e10RCRD#two |
453 | | */ |
454 | | struct load_module_desc desc; |
455 | | |
456 | | if (dlget(-2, &desc, sizeof(desc)) != NULL) |
457 | | return dlgetname(&desc, sizeof(desc), NULL, NULL, NULL); |
458 | | else |
459 | | return NULL; |
460 | | #else |
461 | | /* Fill in your favorite UN*X's code here, if there is something */ |
462 | | return NULL; |
463 | | #endif |
464 | 14 | } |
465 | | #endif /* _WIN32 */ |
466 | | |
467 | | /* Extcap executables are in their own subdirectory. This trims that off and |
468 | | * reduces progfile_dir to the common program file directory. */ |
469 | | static void trim_progfile_dir(const char* app_flavor_lower _U_) |
470 | 14 | { |
471 | 14 | char *progfile_last_dir = find_last_pathname_separator(progfile_dir); |
472 | | |
473 | | #ifdef _WIN32 |
474 | | /* |
475 | | * Check the flavor of our extcap subdirectory. |
476 | | * XXX - Do we only need to do this on Windows, or on other platforms too? |
477 | | */ |
478 | | if (progfile_last_dir && strncmp(progfile_last_dir + 1, app_flavor_lower, strlen(app_flavor_lower)) == 0) { |
479 | | char* flavor_last_dir = find_last_pathname_separator(progfile_dir); |
480 | | char flavor_sep = *flavor_last_dir; |
481 | | *flavor_last_dir = '\0'; |
482 | | |
483 | | progfile_last_dir = find_last_pathname_separator(progfile_dir); |
484 | | |
485 | | if (!(progfile_last_dir && strncmp(progfile_last_dir + 1, "extcap", sizeof("extcap")) == 0)) { |
486 | | /* |
487 | | * Not an extcap, restore the flavor separator (it might have been |
488 | | * some other "wireshark" directory, especially on case insensitive |
489 | | * filesystems.) |
490 | | */ |
491 | | *flavor_last_dir = flavor_sep; |
492 | | return; |
493 | | } |
494 | | } else |
495 | | #endif |
496 | 14 | if (! (progfile_last_dir && strncmp(progfile_last_dir + 1, "extcap", sizeof("extcap")) == 0)) { |
497 | | /* Check for an unflavored extcap directory. */ |
498 | 14 | return; |
499 | 14 | } |
500 | | |
501 | 0 | *progfile_last_dir = '\0'; |
502 | 0 | char *extcap_progfile_dir = progfile_dir; |
503 | 0 | progfile_dir = g_strdup(extcap_progfile_dir); |
504 | 0 | g_free(extcap_progfile_dir); |
505 | 0 | } |
506 | | |
507 | | #if !defined(_WIN32) || defined(HAVE_MSYSTEM) |
508 | | static char * |
509 | | trim_last_dir_from_path(const char *_path) |
510 | 0 | { |
511 | 0 | char *path = ws_strdup(_path); |
512 | 0 | char *last_dir = find_last_pathname_separator(path); |
513 | 0 | if (last_dir) { |
514 | 0 | *last_dir = '\0'; |
515 | 0 | } |
516 | 0 | return path; |
517 | 0 | } |
518 | | #endif |
519 | | |
520 | | /* |
521 | | * Construct the path name of a non-extcap Wireshark executable file, |
522 | | * given the program name. The executable name doesn't include ".exe"; |
523 | | * append it on Windows, so that callers don't have to worry about that. |
524 | | * |
525 | | * This presumes that all non-extcap executables are in the same directory. |
526 | | * |
527 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
528 | | * caller is done with it. |
529 | | */ |
530 | | char * |
531 | | get_executable_path(const char *program_name) |
532 | 0 | { |
533 | | /* |
534 | | * Fail if we don't know what directory contains the executables. |
535 | | */ |
536 | 0 | if (progfile_dir == NULL) |
537 | 0 | return NULL; |
538 | | |
539 | | #ifdef _WIN32 |
540 | | return ws_strdup_printf("%s\\%s.exe", progfile_dir, program_name); |
541 | | #else |
542 | 0 | return ws_strdup_printf("%s/%s", progfile_dir, program_name); |
543 | 0 | #endif |
544 | 0 | } |
545 | | |
546 | | /* |
547 | | * Get the pathname of the directory from which the executable came, |
548 | | * and save it for future use. Returns NULL on success, and a |
549 | | * g_mallocated string containing an error on failure. |
550 | | */ |
551 | | #ifdef _WIN32 |
552 | | static char * |
553 | | configuration_init_w32(const char* app_flavor, const char* arg0 _U_) |
554 | | { |
555 | | TCHAR prog_pathname_w[_MAX_PATH+2]; |
556 | | char *prog_pathname; |
557 | | DWORD error; |
558 | | TCHAR *msg_w; |
559 | | char *msg; |
560 | | size_t msglen; |
561 | | |
562 | | /* |
563 | | * Attempt to get the full pathname of the currently running |
564 | | * program. |
565 | | */ |
566 | | if (GetModuleFileName(NULL, prog_pathname_w, G_N_ELEMENTS(prog_pathname_w)) != 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { |
567 | | /* |
568 | | * XXX - Should we use g_utf16_to_utf8()? |
569 | | */ |
570 | | prog_pathname = utf_16to8(prog_pathname_w); |
571 | | /* |
572 | | * We got it; strip off the last component, which would be |
573 | | * the file name of the executable, giving us the pathname |
574 | | * of the directory where the executable resides. |
575 | | */ |
576 | | progfile_dir = g_path_get_dirname(prog_pathname); |
577 | | if (progfile_dir != NULL) { |
578 | | /* We succeeded. */ |
579 | | trim_progfile_dir(app_flavor); |
580 | | /* Now try to figure out if we're running in a build directory. */ |
581 | | char *wsutil_lib = g_build_filename(progfile_dir, "wsutil.lib", (char *)NULL); |
582 | | if (file_exists(wsutil_lib)) { |
583 | | running_in_build_directory_flag = true; |
584 | | } |
585 | | g_free(wsutil_lib); |
586 | | } else { |
587 | | /* |
588 | | * OK, no. What do we do now? |
589 | | */ |
590 | | return ws_strdup_printf("No \\ in executable pathname \"%s\"", |
591 | | prog_pathname); |
592 | | } |
593 | | } else { |
594 | | /* |
595 | | * Oh, well. Return an indication of the error. |
596 | | */ |
597 | | error = GetLastError(); |
598 | | if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, |
599 | | NULL, error, 0, (LPTSTR) &msg_w, 0, NULL) == 0) { |
600 | | /* |
601 | | * Gak. We can't format the message. |
602 | | */ |
603 | | return ws_strdup_printf("GetModuleFileName failed: %lu (FormatMessage failed: %lu)", |
604 | | error, GetLastError()); |
605 | | } |
606 | | msg = utf_16to8(msg_w); |
607 | | LocalFree(msg_w); |
608 | | /* |
609 | | * "FormatMessage()" "helpfully" sticks CR/LF at the |
610 | | * end of the message. Get rid of it. |
611 | | */ |
612 | | msglen = strlen(msg); |
613 | | if (msglen >= 2) { |
614 | | msg[msglen - 1] = '\0'; |
615 | | msg[msglen - 2] = '\0'; |
616 | | } |
617 | | return ws_strdup_printf("GetModuleFileName failed: %s (%lu)", |
618 | | msg, error); |
619 | | } |
620 | | |
621 | | #ifdef HAVE_MSYSTEM |
622 | | /* |
623 | | * We already have the program_dir. Find the installation prefix. |
624 | | * This is one level up from the bin_dir. If the program_dir does |
625 | | * not end with "bin" then assume we are running in the build directory |
626 | | * and the "installation prefix" (staging directory) is the same as |
627 | | * the program_dir. |
628 | | */ |
629 | | if (g_str_has_suffix(progfile_dir, _S"bin")) { |
630 | | install_prefix = trim_last_dir_from_path(progfile_dir); |
631 | | } |
632 | | else { |
633 | | install_prefix = g_strdup(progfile_dir); |
634 | | running_in_build_directory_flag = true; |
635 | | } |
636 | | #endif /* HAVE_MSYSTEM */ |
637 | | |
638 | | return NULL; |
639 | | } |
640 | | |
641 | | #else /* !_WIN32 */ |
642 | | |
643 | | static char * |
644 | | configuration_init_posix(const char* app_flavor, const char* arg0) |
645 | 14 | { |
646 | 14 | const char *execname; |
647 | 14 | char *prog_pathname; |
648 | 14 | char *curdir; |
649 | 14 | long path_max; |
650 | 14 | const char *pathstr; |
651 | 14 | const char *path_start, *path_end; |
652 | 14 | size_t path_component_len, path_len; |
653 | 14 | char *retstr; |
654 | 14 | char *path; |
655 | 14 | char *dir_end; |
656 | | |
657 | | /* Hard-coded value used if we cannot obtain the path of the running executable. */ |
658 | 14 | install_prefix = g_strdup(INSTALL_PREFIX); |
659 | | |
660 | | /* |
661 | | * Check whether XXX_RUN_FROM_BUILD_DIRECTORY is set in the |
662 | | * environment; if so, set running_in_build_directory_flag if we |
663 | | * weren't started with special privileges. (If we were started |
664 | | * with special privileges, it's not safe to allow the user to point |
665 | | * us to some other directory; running_in_build_directory_flag, when |
666 | | * set, causes us to look for plugins and the like in the build |
667 | | * directory.) |
668 | | */ |
669 | 14 | char* upper_app_flavor = g_ascii_strup(app_flavor, -1); |
670 | 14 | char *run_from_envar = g_strdup_printf("%s_RUN_FROM_BUILD_DIRECTORY", upper_app_flavor); |
671 | 14 | if (g_getenv(run_from_envar) != NULL && !started_with_special_privs()) { |
672 | 0 | running_in_build_directory_flag = true; |
673 | 0 | } |
674 | 14 | g_free(run_from_envar); |
675 | 14 | g_free(upper_app_flavor); |
676 | | |
677 | 14 | execname = get_current_executable_path(); |
678 | 14 | if (execname == NULL) { |
679 | | /* |
680 | | * OK, guess based on argv[0]. |
681 | | */ |
682 | 0 | execname = arg0; |
683 | 0 | } |
684 | | |
685 | | /* |
686 | | * Try to figure out the directory in which the currently running |
687 | | * program resides, given something purporting to be the executable |
688 | | * name (from an OS mechanism or from the argv[0] it was started with). |
689 | | * That might be the absolute path of the program, or a path relative |
690 | | * to the current directory of the process that started it, or |
691 | | * just a name for the program if it was started from the command |
692 | | * line and was searched for in $PATH. It's not guaranteed to be |
693 | | * any of those, however, so there are no guarantees.... |
694 | | */ |
695 | 14 | if (execname[0] == '/') { |
696 | | /* |
697 | | * It's an absolute path. |
698 | | */ |
699 | 14 | prog_pathname = g_strdup(execname); |
700 | 14 | } else if (strchr(execname, '/') != NULL) { |
701 | | /* |
702 | | * It's a relative path, with a directory in it. |
703 | | * Get the current directory, and combine it |
704 | | * with that directory. |
705 | | */ |
706 | 0 | path_max = pathconf(".", _PC_PATH_MAX); |
707 | 0 | if (path_max == -1) { |
708 | | /* |
709 | | * We have no idea how big a buffer to |
710 | | * allocate for the current directory. |
711 | | */ |
712 | 0 | return ws_strdup_printf("pathconf failed: %s\n", |
713 | 0 | g_strerror(errno)); |
714 | 0 | } |
715 | 0 | curdir = (char *)g_malloc((size_t)path_max); |
716 | 0 | if (getcwd(curdir, (size_t)path_max) == NULL) { |
717 | | /* |
718 | | * It failed - give up, and just stick |
719 | | * with DATA_DIR. |
720 | | */ |
721 | 0 | g_free(curdir); |
722 | 0 | return ws_strdup_printf("getcwd failed: %s\n", |
723 | 0 | g_strerror(errno)); |
724 | 0 | } |
725 | 0 | path = ws_strdup_printf("%s/%s", curdir, execname); |
726 | 0 | g_free(curdir); |
727 | 0 | prog_pathname = path; |
728 | 0 | } else { |
729 | | /* |
730 | | * It's just a file name. |
731 | | * Search the path for a file with that name |
732 | | * that's executable. |
733 | | */ |
734 | 0 | prog_pathname = NULL; /* haven't found it yet */ |
735 | 0 | pathstr = g_getenv("PATH"); |
736 | 0 | path_start = pathstr; |
737 | 0 | if (path_start != NULL) { |
738 | 0 | while (*path_start != '\0') { |
739 | 0 | path_end = strchr(path_start, ':'); |
740 | 0 | if (path_end == NULL) |
741 | 0 | path_end = path_start + strlen(path_start); |
742 | 0 | path_component_len = (size_t)(path_end - path_start); |
743 | 0 | path_len = path_component_len + 1 |
744 | 0 | + strlen(execname) + 1; |
745 | 0 | path = (char *)g_malloc(path_len); |
746 | 0 | memcpy(path, path_start, path_component_len); |
747 | 0 | path[path_component_len] = '\0'; |
748 | 0 | (void) g_strlcat(path, "/", path_len); |
749 | 0 | (void) g_strlcat(path, execname, path_len); |
750 | 0 | if (access(path, X_OK) == 0) { |
751 | | /* |
752 | | * Found it! |
753 | | */ |
754 | 0 | prog_pathname = path; |
755 | 0 | break; |
756 | 0 | } |
757 | | |
758 | | /* |
759 | | * That's not it. If there are more |
760 | | * path components to test, try them. |
761 | | */ |
762 | 0 | if (*path_end == ':') |
763 | 0 | path_end++; |
764 | 0 | path_start = path_end; |
765 | 0 | g_free(path); |
766 | 0 | } |
767 | 0 | if (prog_pathname == NULL) { |
768 | | /* |
769 | | * Program not found in path. |
770 | | */ |
771 | 0 | return ws_strdup_printf("\"%s\" not found in \"%s\"", |
772 | 0 | execname, pathstr); |
773 | 0 | } |
774 | 0 | } else { |
775 | | /* |
776 | | * PATH isn't set. |
777 | | * XXX - should we pick a default? |
778 | | */ |
779 | 0 | return g_strdup("PATH isn't set"); |
780 | 0 | } |
781 | 0 | } |
782 | | |
783 | | /* |
784 | | * OK, we have what we think is the pathname |
785 | | * of the program. |
786 | | * |
787 | | * First, find the last "/" in the directory, |
788 | | * as that marks the end of the directory pathname. |
789 | | */ |
790 | 14 | dir_end = strrchr(prog_pathname, '/'); |
791 | 14 | if (dir_end != NULL) { |
792 | | /* |
793 | | * Found it. Strip off the last component, |
794 | | * as that's the path of the program. |
795 | | */ |
796 | 14 | *dir_end = '\0'; |
797 | | |
798 | | /* |
799 | | * Is there a "/run" at the end? |
800 | | */ |
801 | 14 | dir_end = strrchr(prog_pathname, '/'); |
802 | 14 | if (dir_end != NULL) { |
803 | 14 | if (!started_with_special_privs()) { |
804 | | /* |
805 | | * Check for the CMake output directory. As people may name |
806 | | * their directories "run" (really?), also check for the |
807 | | * CMakeCache.txt file before assuming a CMake output dir. |
808 | | */ |
809 | 0 | if (strcmp(dir_end, "/run") == 0) { |
810 | 0 | char *cmake_file; |
811 | 0 | cmake_file = ws_strdup_printf("%.*s/CMakeCache.txt", |
812 | 0 | (int)(dir_end - prog_pathname), |
813 | 0 | prog_pathname); |
814 | 0 | if (file_exists(cmake_file)) |
815 | 0 | running_in_build_directory_flag = true; |
816 | 0 | g_free(cmake_file); |
817 | 0 | } |
818 | | #ifdef ENABLE_APPLICATION_BUNDLE |
819 | | { |
820 | | /* |
821 | | * Scan up the path looking for a component |
822 | | * named "Contents". If we find it, we assume |
823 | | * we're in a bundle, and that the top-level |
824 | | * directory of the bundle is the one containing |
825 | | * "Contents". |
826 | | * |
827 | | * Not all executables are in the Contents/MacOS |
828 | | * directory, so we can't just check for those |
829 | | * in the path and strip them off. |
830 | | * |
831 | | * XXX - should we assume that it's either |
832 | | * Contents/MacOS or Resources/bin? |
833 | | */ |
834 | | char *component_end, *p; |
835 | | |
836 | | component_end = strchr(prog_pathname, '\0'); |
837 | | p = component_end; |
838 | | for (;;) { |
839 | | while (p >= prog_pathname && *p != '/') |
840 | | p--; |
841 | | if (p == prog_pathname) { |
842 | | /* |
843 | | * We're looking at the first component of |
844 | | * the pathname now, so we're definitely |
845 | | * not in a bundle, even if we're in |
846 | | * "/Contents". |
847 | | */ |
848 | | break; |
849 | | } |
850 | | if (strncmp(p, "/Contents", component_end - p) == 0) { |
851 | | /* Found it. */ |
852 | | appbundle_dir = (char *)g_malloc(p - prog_pathname + 1); |
853 | | memcpy(appbundle_dir, prog_pathname, p - prog_pathname); |
854 | | appbundle_dir[p - prog_pathname] = '\0'; |
855 | | break; |
856 | | } |
857 | | component_end = p; |
858 | | p--; |
859 | | } |
860 | | } |
861 | | #endif |
862 | 0 | } |
863 | 14 | } |
864 | | |
865 | | /* |
866 | | * OK, we have the path we want. |
867 | | */ |
868 | 14 | progfile_dir = prog_pathname; |
869 | 14 | trim_progfile_dir(app_flavor); |
870 | 14 | } else { |
871 | | /* |
872 | | * This "shouldn't happen"; we apparently |
873 | | * have no "/" in the pathname. |
874 | | * Just free up prog_pathname. |
875 | | */ |
876 | 0 | retstr = ws_strdup_printf("No / found in \"%s\"", prog_pathname); |
877 | 0 | g_free(prog_pathname); |
878 | 0 | return retstr; |
879 | 0 | } |
880 | | |
881 | | /* |
882 | | * We already have the program_dir. Find the installation prefix. |
883 | | * This is one level up from the bin_dir. If the program_dir does |
884 | | * not end with "bin" then assume we are running in the build directory |
885 | | * and the "installation prefix" (staging directory) is the same as |
886 | | * the program_dir. |
887 | | */ |
888 | 14 | g_free(install_prefix); |
889 | 14 | if (g_str_has_suffix(progfile_dir, _S"bin")) { |
890 | 0 | install_prefix = trim_last_dir_from_path(progfile_dir); |
891 | 0 | } |
892 | 14 | else { |
893 | 14 | install_prefix = g_strdup(progfile_dir); |
894 | 14 | running_in_build_directory_flag = true; |
895 | 14 | } |
896 | | |
897 | 14 | return NULL; |
898 | 14 | } |
899 | | #endif /* ?_WIN32 */ |
900 | | |
901 | | char * |
902 | | configuration_init(const char* arg0, const char* app_flavor_lower) |
903 | 14 | { |
904 | | #ifdef _WIN32 |
905 | | return configuration_init_w32(app_flavor_lower, arg0); |
906 | | #else |
907 | 14 | return configuration_init_posix(app_flavor_lower, arg0); |
908 | 14 | #endif |
909 | 14 | } |
910 | | |
911 | | /* |
912 | | * Get the directory in which the program resides. |
913 | | */ |
914 | | const char * |
915 | | get_progfile_dir(void) |
916 | 28 | { |
917 | 28 | return progfile_dir; |
918 | 28 | } |
919 | | |
920 | | extern const char * |
921 | | get_current_working_dir(void) |
922 | 2 | { |
923 | 2 | if (current_working_dir != NULL) { |
924 | 0 | return current_working_dir; |
925 | 0 | } |
926 | | |
927 | | /* |
928 | | * It's good to cache this because on Windows Microsoft cautions |
929 | | * against using GetCurrentDirectory except early on, e.g. when |
930 | | * parsing command line options. |
931 | | */ |
932 | 2 | current_working_dir = g_get_current_dir(); |
933 | | /* |
934 | | * The above always returns something, with a fallback, e.g., on macOS |
935 | | * if the program is run from Finder, of G_DIR_SEPARATOR_S. |
936 | | * On Windows when run from a shortcut / taskbar it returns whatever |
937 | | * the "run in" directory is on the shortcut, which is usually the |
938 | | * directory where the program resides, which isn't that useful. |
939 | | * Should we set it to the home directory on macOS or the |
940 | | * "My Documents" folder on Windows in those cases, |
941 | | * as we do in get_persdatafile_dir()? This isn't the default preference |
942 | | * setting so perhaps caveat emptor is ok. |
943 | | */ |
944 | 2 | return current_working_dir; |
945 | 2 | } |
946 | | |
947 | | /* |
948 | | * Get the directory in which the global configuration and data files are |
949 | | * stored. |
950 | | * |
951 | | * On Windows, we use the directory in which the executable for this |
952 | | * process resides. |
953 | | * |
954 | | * On macOS (when executed from an app bundle), use a directory within |
955 | | * that app bundle. |
956 | | * |
957 | | * Otherwise, if the program was executed from the build directory, use the |
958 | | * directory in which the executable for this process resides. In all other |
959 | | * cases, use the DATA_DIR value that was set at compile time. |
960 | | * |
961 | | * XXX - if we ever make libwireshark a real library, used by multiple |
962 | | * applications (more than just TShark and versions of Wireshark with |
963 | | * various UIs), should the configuration files belong to the library |
964 | | * (and be shared by all those applications) or to the applications? |
965 | | * |
966 | | * If they belong to the library, that could be done on UNIX by the |
967 | | * configure script, but it's trickier on Windows, as you can't just |
968 | | * use the pathname of the executable. |
969 | | * |
970 | | * If they belong to the application, that could be done on Windows |
971 | | * by using the pathname of the executable, but we'd have to have it |
972 | | * passed in as an argument, in some call, on UNIX. |
973 | | * |
974 | | * Note that some of those configuration files might be used by code in |
975 | | * libwireshark, some of them might be used by dissectors (would they |
976 | | * belong to libwireshark, the application, or a separate library?), |
977 | | * and some of them might be used by other code (the Wireshark preferences |
978 | | * file includes resolver preferences that control the behavior of code |
979 | | * in libwireshark, dissector preferences, and UI preferences, for |
980 | | * example). |
981 | | */ |
982 | | const char * |
983 | | get_datafile_dir(const char* app_env_var_prefix) |
984 | 2.08k | { |
985 | 2.08k | if (datafile_dir != NULL) |
986 | 2.07k | return datafile_dir; |
987 | | |
988 | 14 | char *data_dir_envar = g_strdup_printf("%s_DATA_DIR", app_env_var_prefix); |
989 | 14 | char* app_lower = g_ascii_strdown(app_env_var_prefix, -1); |
990 | 14 | if (g_getenv(data_dir_envar) && !started_with_special_privs()) { |
991 | | /* |
992 | | * The user specified a different directory for data files |
993 | | * and we aren't running with special privileges. |
994 | | * Let {WIRESHARK,STRATOSHARK}_DATA_DIR take precedence. |
995 | | * XXX - We might be able to dispense with the priv check |
996 | | */ |
997 | 0 | datafile_dir = g_strdup(g_getenv(data_dir_envar)); |
998 | 0 | } |
999 | | |
1000 | | #if defined(HAVE_MSYSTEM) |
1001 | | if (running_in_build_directory_flag) { |
1002 | | datafile_dir = g_strdup(install_prefix); |
1003 | | } else { |
1004 | | datafile_dir = g_build_filename(install_prefix, DATA_DIR, app_lower, (char *)NULL); |
1005 | | } |
1006 | | #elif defined(_WIN32) |
1007 | | /* |
1008 | | * Do we have the pathname of the program? If so, assume we're |
1009 | | * running an installed version of the program. If we fail, |
1010 | | * we don't change "datafile_dir", and thus end up using the |
1011 | | * default. |
1012 | | * |
1013 | | * XXX - does NSIS put the installation directory into |
1014 | | * "\HKEY_LOCAL_MACHINE\SOFTWARE\Wireshark\InstallDir"? |
1015 | | * If so, perhaps we should read that from the registry, |
1016 | | * instead. |
1017 | | */ |
1018 | | if (progfile_dir != NULL) { |
1019 | | /* |
1020 | | * Yes, we do; use that. |
1021 | | */ |
1022 | | datafile_dir = g_strdup(progfile_dir); |
1023 | | } else { |
1024 | | /* |
1025 | | * No, we don't. |
1026 | | * Fall back on the default installation directory. |
1027 | | */ |
1028 | | datafile_dir = g_strdup("C:\\Program Files\\Wireshark\\"); |
1029 | | } |
1030 | | #else |
1031 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1032 | | /* |
1033 | | * If we're running from an app bundle and weren't started |
1034 | | * with special privileges, use the Contents/Resources/share/wireshark |
1035 | | * subdirectory of the app bundle. |
1036 | | * |
1037 | | * (appbundle_dir is not set to a non-null value if we're |
1038 | | * started with special privileges, so we need only check |
1039 | | * it; we don't need to call started_with_special_privs().) |
1040 | | */ |
1041 | | else if (appbundle_dir != NULL) { |
1042 | | datafile_dir = ws_strdup_printf("%s/Contents/Resources/share/%s", |
1043 | | appbundle_dir, app_lower); |
1044 | | } |
1045 | | #endif |
1046 | 14 | else if (running_in_build_directory_flag && progfile_dir != NULL) { |
1047 | | /* |
1048 | | * We're (probably) being run from the build directory and |
1049 | | * weren't started with special privileges. |
1050 | | * |
1051 | | * (running_in_build_directory_flag is never set to true |
1052 | | * if we're started with special privileges, so we need |
1053 | | * only check it; we don't need to call started_with_special_privs().) |
1054 | | * |
1055 | | * Data files (dtds/, radius/, etc.) are copied to the build |
1056 | | * directory during the build which also contains executables. A special |
1057 | | * exception is macOS (when built with an app bundle). |
1058 | | */ |
1059 | 14 | datafile_dir = g_strdup(progfile_dir); |
1060 | 14 | } else { |
1061 | 0 | if (g_path_is_absolute(DATA_DIR)) { |
1062 | 0 | datafile_dir = g_build_filename(DATA_DIR, app_lower, (char *)NULL); |
1063 | 0 | } else { |
1064 | 0 | datafile_dir = g_build_filename(install_prefix, DATA_DIR, app_lower, (char *)NULL); |
1065 | 0 | } |
1066 | 0 | } |
1067 | 14 | #endif |
1068 | 14 | g_free(app_lower); |
1069 | 14 | g_free(data_dir_envar); |
1070 | 14 | return datafile_dir; |
1071 | 2.08k | } |
1072 | | |
1073 | | const char * |
1074 | | get_doc_dir(const char* app_env_var_prefix _U_) |
1075 | 0 | { |
1076 | 0 | if (doc_dir != NULL) |
1077 | 0 | return doc_dir; |
1078 | | |
1079 | | /* No environment variable for this. */ |
1080 | 0 | if (false) { |
1081 | 0 | ; |
1082 | 0 | } |
1083 | | |
1084 | | #if defined(HAVE_MSYSTEM) |
1085 | | if (running_in_build_directory_flag) { |
1086 | | doc_dir = g_strdup(install_prefix); |
1087 | | } else { |
1088 | | doc_dir = g_build_filename(install_prefix, DOC_DIR, (char *)NULL); |
1089 | | } |
1090 | | #elif defined(_WIN32) |
1091 | | if (progfile_dir != NULL) { |
1092 | | doc_dir = g_strdup(progfile_dir); |
1093 | | } else { |
1094 | | /* Fall back on the default installation directory. */ |
1095 | | doc_dir = g_strdup("C:\\Program Files\\Wireshark\\"); |
1096 | | } |
1097 | | #else |
1098 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1099 | | /* |
1100 | | * If we're running from an app bundle and weren't started |
1101 | | * with special privileges, use the Contents/Resources/share/wireshark |
1102 | | * subdirectory of the app bundle. |
1103 | | * |
1104 | | * (appbundle_dir is not set to a non-null value if we're |
1105 | | * started with special privileges, so we need only check |
1106 | | * it; we don't need to call started_with_special_privs().) |
1107 | | */ |
1108 | | else if (appbundle_dir != NULL) { |
1109 | | doc_dir = g_strdup(get_datafile_dir(app_env_var_prefix)); |
1110 | | } |
1111 | | #endif |
1112 | 0 | else if (running_in_build_directory_flag && progfile_dir != NULL) { |
1113 | | /* |
1114 | | * We're (probably) being run from the build directory and |
1115 | | * weren't started with special privileges. |
1116 | | */ |
1117 | 0 | doc_dir = g_strdup(progfile_dir); |
1118 | 0 | } else { |
1119 | 0 | if (g_path_is_absolute(DOC_DIR)) { |
1120 | 0 | doc_dir = g_strdup(DOC_DIR); |
1121 | 0 | } else { |
1122 | 0 | doc_dir = g_build_filename(install_prefix, DOC_DIR, (char *)NULL); |
1123 | 0 | } |
1124 | 0 | } |
1125 | 0 | #endif |
1126 | 0 | return doc_dir; |
1127 | 0 | } |
1128 | | |
1129 | | /* |
1130 | | * Find the directory where the plugins are stored. |
1131 | | * |
1132 | | * On Windows, we use the plugin\{VERSION} subdirectory of the datafile |
1133 | | * directory, where {VERSION} is the version number of this version of |
1134 | | * Wireshark. |
1135 | | * |
1136 | | * On UN*X: |
1137 | | * |
1138 | | * if we appear to be run from the build directory, we use the |
1139 | | * "plugin" subdirectory of the datafile directory; |
1140 | | * |
1141 | | * otherwise, if the WIRESHARK_PLUGIN_DIR environment variable is |
1142 | | * set and we aren't running with special privileges, we use the |
1143 | | * value of that environment variable; |
1144 | | * |
1145 | | * otherwise, if we're running from an app bundle in macOS, we |
1146 | | * use the Contents/PlugIns/wireshark subdirectory of the app bundle; |
1147 | | * |
1148 | | * otherwise, we use the PLUGIN_DIR value supplied by the |
1149 | | * configure script. |
1150 | | */ |
1151 | | static char *plugin_dir; |
1152 | | static char *plugin_dir_with_version; |
1153 | | static char *plugin_pers_dir; |
1154 | | static char *plugin_pers_dir_with_version; |
1155 | | static char *extcap_pers_dir; |
1156 | | |
1157 | | static void |
1158 | | init_plugin_dir(const char* app_env_var_prefix) |
1159 | 0 | { |
1160 | 0 | char* plugin_dir_envar = g_strdup_printf("%s_PLUGIN_DIR", app_env_var_prefix); |
1161 | 0 | char* app_lower = g_ascii_strdown(app_env_var_prefix, -1); |
1162 | 0 | if (g_getenv(plugin_dir_envar) && !started_with_special_privs()) { |
1163 | | /* |
1164 | | * The user specified a different directory for plugins |
1165 | | * and we aren't running with special privileges. |
1166 | | * Let {WIRESHARK,STRATOSHARK}_PLUGIN_DIR take precedence. |
1167 | | */ |
1168 | 0 | plugin_dir = g_strdup(g_getenv(plugin_dir_envar)); |
1169 | 0 | } |
1170 | |
|
1171 | | #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) |
1172 | | #if defined(HAVE_MSYSTEM) |
1173 | | else if (running_in_build_directory_flag) { |
1174 | | plugin_dir = g_build_filename(install_prefix, "plugins", (char *)NULL); |
1175 | | } else { |
1176 | | plugin_dir = g_build_filename(install_prefix, PLUGIN_DIR, (char *)NULL); |
1177 | | } |
1178 | | #elif defined(_WIN32) |
1179 | | else { |
1180 | | /* |
1181 | | * On Windows, plugins are stored under the program file directory |
1182 | | * in both the build and the installation directories. |
1183 | | */ |
1184 | | plugin_dir = g_build_filename(get_progfile_dir(), "plugins", (char *)NULL); |
1185 | | } |
1186 | | #else |
1187 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1188 | | /* |
1189 | | * If we're running from an app bundle and weren't started |
1190 | | * with special privileges, use the Contents/PlugIns/wireshark |
1191 | | * subdirectory of the app bundle. |
1192 | | * |
1193 | | * (appbundle_dir is not set to a non-null value if we're |
1194 | | * started with special privileges, so we need only check |
1195 | | * it; we don't need to call started_with_special_privs().) |
1196 | | */ |
1197 | | else if (appbundle_dir != NULL) { |
1198 | | plugin_dir = g_build_filename(appbundle_dir, "Contents/PlugIns", |
1199 | | app_lower, (char *)NULL); |
1200 | | } |
1201 | | #endif // ENABLE_APPLICATION_BUNDLE |
1202 | | else if (running_in_build_directory_flag) { |
1203 | | /* |
1204 | | * We're (probably) being run from the build directory and |
1205 | | * weren't started with special privileges, so we'll use |
1206 | | * the "plugins" subdirectory of the directory where the program |
1207 | | * we're running is (that's the build directory). |
1208 | | */ |
1209 | | plugin_dir = g_build_filename(get_progfile_dir(), "plugins", (char *)NULL); |
1210 | | } else { |
1211 | | if (g_path_is_absolute(PLUGIN_DIR)) { |
1212 | | plugin_dir = g_strdup(PLUGIN_DIR); |
1213 | | } else { |
1214 | | plugin_dir = g_build_filename(install_prefix, PLUGIN_DIR, (char *)NULL); |
1215 | | } |
1216 | | } |
1217 | | #endif // HAVE_MSYSTEM / _WIN32 |
1218 | | #endif /* defined(HAVE_PLUGINS) || defined(HAVE_LUA) */ |
1219 | 0 | g_free(app_lower); |
1220 | 0 | g_free(plugin_dir_envar); |
1221 | 0 | } |
1222 | | |
1223 | | static void |
1224 | | init_plugin_pers_dir(const char* app_env_var_prefix _U_) |
1225 | 0 | { |
1226 | | #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) |
1227 | | #ifdef _WIN32 |
1228 | | plugin_pers_dir = get_persconffile_path(PLUGINS_DIR_NAME, false, app_env_var_prefix); |
1229 | | #else |
1230 | | char* app_lower = g_ascii_strdown(app_env_var_prefix, -1); |
1231 | | plugin_pers_dir = g_build_filename(g_get_home_dir(), ".local/lib", |
1232 | | app_lower, PLUGINS_DIR_NAME, (char *)NULL); |
1233 | | g_free(app_lower); |
1234 | | #endif |
1235 | | #endif /* defined(HAVE_PLUGINS) || defined(HAVE_LUA) */ |
1236 | 0 | } |
1237 | | |
1238 | | /* |
1239 | | * Get the directory in which the plugins are stored. |
1240 | | */ |
1241 | | const char * |
1242 | | get_plugins_dir(const char* app_env_var_prefix) |
1243 | 0 | { |
1244 | 0 | if (!plugin_dir) |
1245 | 0 | init_plugin_dir(app_env_var_prefix); |
1246 | 0 | return plugin_dir; |
1247 | 0 | } |
1248 | | |
1249 | | const char * |
1250 | | get_plugins_dir_with_version(const char* app_env_var_prefix) |
1251 | 0 | { |
1252 | 0 | if (!plugin_dir) |
1253 | 0 | init_plugin_dir(app_env_var_prefix); |
1254 | 0 | if (plugin_dir && !plugin_dir_with_version) |
1255 | 0 | plugin_dir_with_version = g_build_filename(plugin_dir, PLUGIN_PATH_ID, (char *)NULL); |
1256 | 0 | return plugin_dir_with_version; |
1257 | 0 | } |
1258 | | |
1259 | | /* Get the personal plugin dir */ |
1260 | | const char * |
1261 | | get_plugins_pers_dir(const char* app_env_var_prefix) |
1262 | 0 | { |
1263 | 0 | if (!plugin_pers_dir) |
1264 | 0 | init_plugin_pers_dir(app_env_var_prefix); |
1265 | 0 | return plugin_pers_dir; |
1266 | 0 | } |
1267 | | |
1268 | | const char * |
1269 | | get_plugins_pers_dir_with_version(const char* app_env_var_prefix) |
1270 | 0 | { |
1271 | 0 | if (!plugin_pers_dir) |
1272 | 0 | init_plugin_pers_dir(app_env_var_prefix); |
1273 | 0 | if (plugin_pers_dir && !plugin_pers_dir_with_version) |
1274 | 0 | plugin_pers_dir_with_version = g_build_filename(plugin_pers_dir, PLUGIN_PATH_ID, (char *)NULL); |
1275 | 0 | return plugin_pers_dir_with_version; |
1276 | 0 | } |
1277 | | |
1278 | | /* |
1279 | | * Find the directory where the extcap hooks are stored. |
1280 | | * |
1281 | | * If the WIRESHARK_EXTCAP_DIR environment variable is set and we are not |
1282 | | * running with special privileges, use that. Otherwise: |
1283 | | * |
1284 | | * On Windows, we use the "extcap" subdirectory of the program directory. |
1285 | | * |
1286 | | * On UN*X: |
1287 | | * |
1288 | | * if we appear to be run from the build directory, we use the |
1289 | | * "extcap" subdirectory of the build directory. |
1290 | | * |
1291 | | * otherwise, if we're running from an app bundle in macOS, we |
1292 | | * use the Contents/MacOS/extcap subdirectory of the app bundle; |
1293 | | * |
1294 | | * otherwise, we use the EXTCAP_DIR value supplied by CMake. |
1295 | | */ |
1296 | | static char *extcap_dir; |
1297 | | |
1298 | | static void |
1299 | | init_extcap_dir(const char* app_env_var_prefix, const char* dir_extcap _U_) |
1300 | 0 | { |
1301 | 0 | char *extcap_dir_envar = g_strdup_printf("%s_EXTCAP_DIR", app_env_var_prefix); |
1302 | 0 | char* app_lower = g_ascii_strdown(app_env_var_prefix, -1); |
1303 | 0 | if (g_getenv(extcap_dir_envar) && !started_with_special_privs()) { |
1304 | | /* |
1305 | | * The user specified a different directory for extcap hooks |
1306 | | * and we aren't running with special privileges. |
1307 | | */ |
1308 | 0 | extcap_dir = g_strdup(g_getenv(extcap_dir_envar)); |
1309 | 0 | } |
1310 | | |
1311 | | #if defined(HAVE_MSYSTEM) |
1312 | | else if (running_in_build_directory_flag) { |
1313 | | extcap_dir = g_build_filename(install_prefix, "extcap", (char *)NULL); |
1314 | | } else { |
1315 | | extcap_dir = g_build_filename(install_prefix, EXTCAP_DIR, (char *)NULL); |
1316 | | } |
1317 | | #elif defined(_WIN32) |
1318 | | /* |
1319 | | * On Windows, extcap utilities are stored in "extcap/<program name>" |
1320 | | * in the build directory and in "extcap" in the installation |
1321 | | * directory. |
1322 | | */ |
1323 | | else if (running_in_build_directory_flag) { |
1324 | | extcap_dir = g_build_filename(get_progfile_dir(), EXTCAP_DIR_NAME, |
1325 | | app_lower, (char *)NULL); |
1326 | | } else { |
1327 | | extcap_dir = g_build_filename(get_progfile_dir(), EXTCAP_DIR_NAME, |
1328 | | (char *)NULL); |
1329 | | } |
1330 | | #else |
1331 | | #ifdef ENABLE_APPLICATION_BUNDLE |
1332 | | else if (appbundle_dir != NULL) { |
1333 | | /* |
1334 | | * If we're running from an app bundle and weren't started |
1335 | | * with special privileges, use the Contents/MacOS/extcap |
1336 | | * subdirectory of the app bundle. |
1337 | | * |
1338 | | * (appbundle_dir is not set to a non-null value if we're |
1339 | | * started with special privileges, so we need only check |
1340 | | * it; we don't need to call started_with_special_privs().) |
1341 | | */ |
1342 | | extcap_dir = g_build_filename(appbundle_dir, "Contents/MacOS/extcap", (char *)NULL); |
1343 | | } |
1344 | | #endif // ENABLE_APPLICATION_BUNDLE |
1345 | 0 | else if (running_in_build_directory_flag) { |
1346 | | /* |
1347 | | * We're (probably) being run from the build directory and |
1348 | | * weren't started with special privileges, so we'll use |
1349 | | * the "extcap hooks" subdirectory of the directory where the program |
1350 | | * we're running is (that's the build directory). |
1351 | | */ |
1352 | 0 | extcap_dir = g_build_filename(get_progfile_dir(), EXTCAP_DIR_NAME, |
1353 | 0 | app_lower, (char *)NULL); |
1354 | 0 | } |
1355 | 0 | else { |
1356 | 0 | if (g_path_is_absolute(EXTCAP_DIR)) |
1357 | 0 | extcap_dir = g_strdup(dir_extcap); |
1358 | 0 | else |
1359 | 0 | extcap_dir = g_build_filename(install_prefix, dir_extcap, (char*)NULL); |
1360 | 0 | } |
1361 | 0 | #endif // HAVE_MSYSTEM / _WIN32 |
1362 | 0 | g_free(app_lower); |
1363 | 0 | g_free(extcap_dir_envar); |
1364 | 0 | } |
1365 | | |
1366 | | static void |
1367 | | init_extcap_pers_dir(const char* app_env_var_prefix) |
1368 | 0 | { |
1369 | | #ifdef _WIN32 |
1370 | | extcap_pers_dir = get_persconffile_path(EXTCAP_DIR_NAME, false, app_env_var_prefix); |
1371 | | #else |
1372 | 0 | char* app_lower = g_ascii_strdown(app_env_var_prefix, -1); |
1373 | 0 | extcap_pers_dir = g_build_filename(g_get_home_dir(), ".local/lib", |
1374 | 0 | app_lower, EXTCAP_DIR_NAME, (char *)NULL); |
1375 | 0 | g_free(app_lower); |
1376 | 0 | #endif |
1377 | 0 | } |
1378 | | |
1379 | | /* |
1380 | | * Get the directory in which the extcap hooks are stored. |
1381 | | * |
1382 | | */ |
1383 | | const char * |
1384 | | get_extcap_dir(const char* app_env_var_prefix, const char* dir_extcap) |
1385 | 0 | { |
1386 | 0 | if (!extcap_dir) |
1387 | 0 | init_extcap_dir(app_env_var_prefix, dir_extcap); |
1388 | 0 | return extcap_dir; |
1389 | 0 | } |
1390 | | |
1391 | | /* Get the personal plugin dir */ |
1392 | | const char * |
1393 | | get_extcap_pers_dir(const char* app_env_var_prefix) |
1394 | 0 | { |
1395 | 0 | if (!extcap_pers_dir) |
1396 | 0 | init_extcap_pers_dir(app_env_var_prefix); |
1397 | 0 | return extcap_pers_dir; |
1398 | 0 | } |
1399 | | |
1400 | | /* |
1401 | | * Get the flag indicating whether we're running from a build |
1402 | | * directory. |
1403 | | */ |
1404 | | bool |
1405 | | running_in_build_directory(void) |
1406 | 0 | { |
1407 | 0 | return running_in_build_directory_flag; |
1408 | 0 | } |
1409 | | |
1410 | | /* |
1411 | | * Get the directory in which files that, at least on UNIX, are |
1412 | | * system files (such as "/etc/ethers") are stored; on Windows, |
1413 | | * there's no "/etc" directory, so we get them from the global |
1414 | | * configuration and data file directory. |
1415 | | */ |
1416 | | const char * |
1417 | | get_systemfile_dir(const char* app_env_var_prefix _U_) |
1418 | 56 | { |
1419 | | #ifdef _WIN32 |
1420 | | return get_datafile_dir(app_env_var_prefix); |
1421 | | #else |
1422 | 56 | return "/etc"; |
1423 | 56 | #endif |
1424 | 56 | } |
1425 | | |
1426 | | void |
1427 | | set_profile_name(const char *profilename) |
1428 | 0 | { |
1429 | 0 | g_free (persconfprofile); |
1430 | |
|
1431 | 0 | if (profilename && strlen(profilename) > 0 && |
1432 | 0 | strcmp(profilename, DEFAULT_PROFILE) != 0) { |
1433 | 0 | persconfprofile = g_strdup (profilename); |
1434 | 0 | } else { |
1435 | | /* Default Profile */ |
1436 | 0 | persconfprofile = NULL; |
1437 | 0 | } |
1438 | 0 | } |
1439 | | |
1440 | | const char * |
1441 | | get_profile_name(void) |
1442 | 0 | { |
1443 | 0 | if (persconfprofile) { |
1444 | 0 | return persconfprofile; |
1445 | 0 | } else { |
1446 | 0 | return DEFAULT_PROFILE; |
1447 | 0 | } |
1448 | 0 | } |
1449 | | |
1450 | | bool |
1451 | | is_default_profile(void) |
1452 | 0 | { |
1453 | 0 | return (!persconfprofile || strcmp(persconfprofile, DEFAULT_PROFILE) == 0) ? true : false; |
1454 | 0 | } |
1455 | | |
1456 | | bool |
1457 | | has_global_profiles(const char* app_env_var_prefix) |
1458 | 0 | { |
1459 | 0 | WS_DIR *dir; |
1460 | 0 | WS_DIRENT *file; |
1461 | 0 | char *global_dir = get_global_profiles_dir(app_env_var_prefix); |
1462 | 0 | char *filename; |
1463 | 0 | bool has_global = false; |
1464 | |
|
1465 | 0 | if ((test_for_directory(global_dir) == EISDIR) && |
1466 | 0 | ((dir = ws_dir_open(global_dir, 0, NULL)) != NULL)) |
1467 | 0 | { |
1468 | 0 | while ((file = ws_dir_read_name(dir)) != NULL) { |
1469 | 0 | filename = ws_strdup_printf ("%s%s%s", global_dir, G_DIR_SEPARATOR_S, |
1470 | 0 | ws_dir_get_name(file)); |
1471 | 0 | if (test_for_directory(filename) == EISDIR) { |
1472 | 0 | has_global = true; |
1473 | 0 | g_free (filename); |
1474 | 0 | break; |
1475 | 0 | } |
1476 | 0 | g_free (filename); |
1477 | 0 | } |
1478 | 0 | ws_dir_close(dir); |
1479 | 0 | } |
1480 | 0 | g_free(global_dir); |
1481 | 0 | return has_global; |
1482 | 0 | } |
1483 | | |
1484 | | void |
1485 | | profile_store_persconffiles(bool store) |
1486 | 0 | { |
1487 | 0 | if (store) { |
1488 | 0 | profile_files = g_hash_table_new (g_str_hash, g_str_equal); |
1489 | 0 | } |
1490 | 0 | do_store_persconffiles = store; |
1491 | 0 | } |
1492 | | |
1493 | | void |
1494 | | profile_register_persconffile(const char *filename) |
1495 | 2.19k | { |
1496 | 2.19k | if (do_store_persconffiles && !g_hash_table_lookup (profile_files, filename)) { |
1497 | | /* Store filenames so we know which filenames belongs to a configuration profile */ |
1498 | 0 | g_hash_table_insert (profile_files, g_strdup(filename), g_strdup(filename)); |
1499 | 0 | } |
1500 | 2.19k | } |
1501 | | |
1502 | | /* |
1503 | | * Get the directory in which personal configuration files reside. |
1504 | | * |
1505 | | * On Windows, it's "Wireshark", under %APPDATA% or, if %APPDATA% isn't set, |
1506 | | * it's "%USERPROFILE%\Application Data" (which is what %APPDATA% normally |
1507 | | * is on Windows 2000). |
1508 | | * |
1509 | | * On UNIX-compatible systems, we first look in XDG_CONFIG_HOME/wireshark |
1510 | | * and, if that doesn't exist, ~/.wireshark, for backwards compatibility. |
1511 | | * If neither exists, we use XDG_CONFIG_HOME/wireshark, so that the directory |
1512 | | * is initially created as XDG_CONFIG_HOME/wireshark. We use that regardless |
1513 | | * of whether the user is running under an XDG desktop or not, so that |
1514 | | * if the user's home directory is on a server and shared between |
1515 | | * different desktop environments on different machines, they can all |
1516 | | * share the same configuration file directory. |
1517 | | * |
1518 | | * XXX - what about stuff that shouldn't be shared between machines, |
1519 | | * such as plugins in the form of shared loadable images? |
1520 | | */ |
1521 | | static const char * |
1522 | | get_persconffile_dir_no_profile(const char* app_env_var_prefix) |
1523 | 2.42k | { |
1524 | 2.42k | const char *env; |
1525 | | |
1526 | | /* Return the cached value, if available */ |
1527 | 2.42k | if (persconffile_dir != NULL) |
1528 | 2.40k | return persconffile_dir; |
1529 | | |
1530 | | /* |
1531 | | * See if the user has selected an alternate environment. |
1532 | | */ |
1533 | 14 | char* config_dir_envar = g_strdup_printf("%s_CONFIG_DIR", app_env_var_prefix); |
1534 | 14 | env = g_getenv(config_dir_envar); |
1535 | 14 | g_free(config_dir_envar); |
1536 | 14 | char* app_lower = g_ascii_strdown(app_env_var_prefix, -1); |
1537 | 14 | char* app_proper = g_strdup(app_lower); |
1538 | 14 | app_proper[0] = g_ascii_toupper(app_proper[0]); |
1539 | | |
1540 | | #ifdef _WIN32 |
1541 | | if (env == NULL) { |
1542 | | /* |
1543 | | * The PortableApps launcher sets this environment variable. |
1544 | | * XXX - That's only for the GUI. We don't have launchers/batch |
1545 | | * scripts for the command line tools, and just package the same |
1546 | | * binaries as built for NSIS and WiX, so if the user is running |
1547 | | * tshark from the PortableApps directory, how do we tell? (#20095) |
1548 | | */ |
1549 | | env = g_getenv("WIRESHARK_APPDATA"); |
1550 | | } |
1551 | | #endif |
1552 | 14 | if (env != NULL) { |
1553 | 0 | persconffile_dir = g_strdup(env); |
1554 | 0 | goto return_dir; |
1555 | 0 | } |
1556 | | |
1557 | | #ifdef _WIN32 |
1558 | | /* |
1559 | | * Use %APPDATA% or %USERPROFILE%, so that configuration |
1560 | | * files are stored in the user profile, rather than in |
1561 | | * the home directory. The Windows convention is to store |
1562 | | * configuration information in the user profile, and doing |
1563 | | * so means you can use Wireshark even if the home directory |
1564 | | * is an inaccessible network drive. |
1565 | | */ |
1566 | | env = g_getenv("APPDATA"); |
1567 | | if (env != NULL) { |
1568 | | /* |
1569 | | * Concatenate %APPDATA% with "\Wireshark" or "\Stratoshark". |
1570 | | */ |
1571 | | persconffile_dir = g_build_filename(env, app_proper, NULL); |
1572 | | goto return_dir; |
1573 | | } |
1574 | | |
1575 | | /* |
1576 | | * OK, %APPDATA% wasn't set, so use %USERPROFILE%\Application Data. |
1577 | | */ |
1578 | | env = g_getenv("USERPROFILE"); |
1579 | | if (env != NULL) { |
1580 | | persconffile_dir = g_build_filename(env, "Application Data", app_proper, NULL); |
1581 | | goto return_dir; |
1582 | | } |
1583 | | |
1584 | | /* |
1585 | | * Give up and use "C:". |
1586 | | */ |
1587 | | persconffile_dir = g_build_filename("C:", app_proper, NULL); |
1588 | | goto return_dir; |
1589 | | #else |
1590 | 14 | char *xdg_path, *path; |
1591 | 14 | struct passwd *pwd; |
1592 | 14 | const char *homedir; |
1593 | | |
1594 | | /* |
1595 | | * Check if XDG_CONFIG_HOME/wireshark exists and is a directory. |
1596 | | */ |
1597 | 14 | xdg_path = g_build_filename(g_get_user_config_dir(), app_lower, NULL); |
1598 | 14 | if (g_file_test(xdg_path, G_FILE_TEST_IS_DIR)) { |
1599 | 0 | persconffile_dir = xdg_path; |
1600 | 0 | goto return_dir; |
1601 | 0 | } |
1602 | | |
1603 | | /* |
1604 | | * It doesn't exist, or it does but isn't a directory, so try |
1605 | | * ~/.wireshark. |
1606 | | * |
1607 | | * If $HOME is set, use that for ~. |
1608 | | * |
1609 | | * (Note: before GLib 2.36, g_get_home_dir() didn't look at $HOME, |
1610 | | * but we always want to do so, so we don't use g_get_home_dir().) |
1611 | | */ |
1612 | 14 | homedir = g_getenv("HOME"); |
1613 | 14 | if (homedir == NULL) { |
1614 | | /* |
1615 | | * It's not set. |
1616 | | * |
1617 | | * Get their home directory from the password file. |
1618 | | * If we can't even find a password file entry for them, |
1619 | | * use "/tmp". |
1620 | | */ |
1621 | 0 | pwd = getpwuid(getuid()); |
1622 | 0 | if (pwd != NULL) { |
1623 | 0 | homedir = pwd->pw_dir; |
1624 | 0 | } else { |
1625 | 0 | homedir = "/tmp"; |
1626 | 0 | } |
1627 | 0 | } |
1628 | 14 | char *dotted_app = g_strdup_printf(".%s", app_lower); |
1629 | 14 | path = g_build_filename(homedir, dotted_app, NULL); |
1630 | 14 | g_free(dotted_app); |
1631 | 14 | if (g_file_test(path, G_FILE_TEST_IS_DIR)) { |
1632 | 0 | g_free(xdg_path); |
1633 | 0 | persconffile_dir = path; |
1634 | 0 | goto return_dir; |
1635 | 0 | } |
1636 | | |
1637 | | /* |
1638 | | * Neither are directories that exist; use the XDG path, so we'll |
1639 | | * create that as necessary. |
1640 | | */ |
1641 | 14 | g_free(path); |
1642 | 14 | persconffile_dir = xdg_path; |
1643 | 14 | #endif |
1644 | 14 | return_dir: |
1645 | | //Clean up before exiting |
1646 | 14 | g_free(app_lower); |
1647 | 14 | g_free(app_proper); |
1648 | 14 | return persconffile_dir; |
1649 | 14 | } |
1650 | | |
1651 | | void |
1652 | | set_persconffile_dir(const char *p) |
1653 | 0 | { |
1654 | 0 | g_free(persconffile_dir); |
1655 | 0 | persconffile_dir = g_strdup(p); |
1656 | 0 | } |
1657 | | |
1658 | | char * |
1659 | | get_profiles_dir(const char* app_env_var_prefix) |
1660 | 0 | { |
1661 | 0 | return ws_strdup_printf ("%s%s%s", get_persconffile_dir_no_profile (app_env_var_prefix), |
1662 | 0 | G_DIR_SEPARATOR_S, PROFILES_DIR); |
1663 | 0 | } |
1664 | | |
1665 | | int |
1666 | | create_profiles_dir(const char* app_env_var_prefix, char **pf_dir_path_return) |
1667 | 0 | { |
1668 | 0 | char *pf_dir_path; |
1669 | 0 | ws_statb64 s_buf; |
1670 | | |
1671 | | /* |
1672 | | * Create the "Default" personal configuration files directory, if necessary. |
1673 | | */ |
1674 | 0 | if (create_persconffile_profile(app_env_var_prefix, NULL, pf_dir_path_return) == -1) { |
1675 | 0 | return -1; |
1676 | 0 | } |
1677 | | |
1678 | | /* |
1679 | | * Check if profiles directory exists. |
1680 | | * If not then create it. |
1681 | | */ |
1682 | 0 | pf_dir_path = get_profiles_dir(app_env_var_prefix); |
1683 | 0 | if (ws_stat64(pf_dir_path, &s_buf) != 0) { |
1684 | 0 | if (errno != ENOENT) { |
1685 | | /* Some other problem; give up now. */ |
1686 | 0 | *pf_dir_path_return = pf_dir_path; |
1687 | 0 | return -1; |
1688 | 0 | } |
1689 | | |
1690 | | /* |
1691 | | * It doesn't exist; try to create it. |
1692 | | */ |
1693 | 0 | int ret = ws_mkdir(pf_dir_path, 0755); |
1694 | 0 | if (ret == -1) { |
1695 | 0 | *pf_dir_path_return = pf_dir_path; |
1696 | 0 | return ret; |
1697 | 0 | } |
1698 | 0 | } |
1699 | 0 | g_free(pf_dir_path); |
1700 | |
|
1701 | 0 | return 0; |
1702 | 0 | } |
1703 | | |
1704 | | char * |
1705 | | get_global_profiles_dir(const char* app_env_var_prefix) |
1706 | 0 | { |
1707 | 0 | return ws_strdup_printf ("%s%s%s", get_datafile_dir(app_env_var_prefix), |
1708 | 0 | G_DIR_SEPARATOR_S, PROFILES_DIR); |
1709 | 0 | } |
1710 | | |
1711 | | static char * |
1712 | | get_persconffile_dir(const char* app_env_var_prefix, const char *profilename) |
1713 | 2.42k | { |
1714 | 2.42k | char *persconffile_profile_dir = NULL, *profile_dir; |
1715 | | |
1716 | 2.42k | if (profilename && strlen(profilename) > 0 && |
1717 | 0 | strcmp(profilename, DEFAULT_PROFILE) != 0) { |
1718 | 0 | profile_dir = get_profiles_dir(app_env_var_prefix); |
1719 | 0 | persconffile_profile_dir = ws_strdup_printf ("%s%s%s", profile_dir, |
1720 | 0 | G_DIR_SEPARATOR_S, profilename); |
1721 | 0 | g_free(profile_dir); |
1722 | 2.42k | } else { |
1723 | 2.42k | persconffile_profile_dir = g_strdup (get_persconffile_dir_no_profile(app_env_var_prefix)); |
1724 | 2.42k | } |
1725 | | |
1726 | 2.42k | return persconffile_profile_dir; |
1727 | 2.42k | } |
1728 | | |
1729 | | char * |
1730 | | get_profile_dir(const char* app_env_var_prefix, const char *profilename, bool is_global) |
1731 | 0 | { |
1732 | 0 | char *profile_dir; |
1733 | |
|
1734 | 0 | if (is_global) { |
1735 | 0 | if (profilename && strlen(profilename) > 0 && |
1736 | 0 | strcmp(profilename, DEFAULT_PROFILE) != 0) |
1737 | 0 | { |
1738 | 0 | char *global_path = get_global_profiles_dir(app_env_var_prefix); |
1739 | 0 | profile_dir = g_build_filename(global_path, profilename, NULL); |
1740 | 0 | g_free(global_path); |
1741 | 0 | } else { |
1742 | 0 | profile_dir = g_strdup(get_datafile_dir(app_env_var_prefix)); |
1743 | 0 | } |
1744 | 0 | } else { |
1745 | | /* |
1746 | | * If we didn't supply a profile name, i.e. if profilename is |
1747 | | * null, get_persconffile_dir() returns the default profile. |
1748 | | */ |
1749 | 0 | profile_dir = get_persconffile_dir(app_env_var_prefix, profilename); |
1750 | 0 | } |
1751 | |
|
1752 | 0 | return profile_dir; |
1753 | 0 | } |
1754 | | |
1755 | | bool |
1756 | | profile_exists(const char* app_env_var_prefix, const char *profilename, bool global) |
1757 | 0 | { |
1758 | 0 | char *path = NULL; |
1759 | 0 | bool exists; |
1760 | | |
1761 | | /* |
1762 | | * If we're looking up a global profile, we must have a |
1763 | | * profile name. |
1764 | | */ |
1765 | 0 | if (global && !profilename) |
1766 | 0 | return false; |
1767 | | |
1768 | 0 | path = get_profile_dir(app_env_var_prefix, profilename, global); |
1769 | 0 | exists = (test_for_directory(path) == EISDIR) ? true : false; |
1770 | |
|
1771 | 0 | g_free(path); |
1772 | 0 | return exists; |
1773 | 0 | } |
1774 | | |
1775 | | static int |
1776 | | delete_directory (const char *directory, char **pf_dir_path_return) |
1777 | 0 | { |
1778 | 0 | WS_DIR *dir; |
1779 | 0 | WS_DIRENT *file; |
1780 | 0 | char *filename; |
1781 | 0 | int ret = 0; |
1782 | |
|
1783 | 0 | if ((dir = ws_dir_open(directory, 0, NULL)) != NULL) { |
1784 | 0 | while ((file = ws_dir_read_name(dir)) != NULL) { |
1785 | 0 | filename = ws_strdup_printf ("%s%s%s", directory, G_DIR_SEPARATOR_S, |
1786 | 0 | ws_dir_get_name(file)); |
1787 | 0 | if (test_for_directory(filename) != EISDIR) { |
1788 | 0 | ret = ws_remove(filename); |
1789 | | #if 0 |
1790 | | } else { |
1791 | | /* The user has manually created a directory in the profile directory */ |
1792 | | /* I do not want to delete the directory recursively yet */ |
1793 | | ret = delete_directory (filename, pf_dir_path_return); |
1794 | | #endif |
1795 | 0 | } |
1796 | 0 | if (ret != 0) { |
1797 | 0 | *pf_dir_path_return = filename; |
1798 | 0 | break; |
1799 | 0 | } |
1800 | 0 | g_free (filename); |
1801 | 0 | } |
1802 | 0 | ws_dir_close(dir); |
1803 | 0 | } |
1804 | |
|
1805 | 0 | if (ret == 0 && (ret = ws_remove(directory)) != 0) { |
1806 | 0 | *pf_dir_path_return = g_strdup (directory); |
1807 | 0 | } |
1808 | |
|
1809 | 0 | return ret; |
1810 | 0 | } |
1811 | | |
1812 | | /* Copy files from one directory to another. Does not recursively copy directories */ |
1813 | | static int |
1814 | | copy_directory(const char *from_dir, const char *to_dir, char **pf_filename_return) |
1815 | 0 | { |
1816 | 0 | int ret = 0; |
1817 | 0 | char *from_file, *to_file; |
1818 | 0 | const char *filename; |
1819 | 0 | WS_DIR *dir; |
1820 | 0 | WS_DIRENT *file; |
1821 | |
|
1822 | 0 | if ((dir = ws_dir_open(from_dir, 0, NULL)) != NULL) { |
1823 | 0 | while ((file = ws_dir_read_name(dir)) != NULL) { |
1824 | 0 | filename = ws_dir_get_name(file); |
1825 | 0 | from_file = ws_strdup_printf ("%s%s%s", from_dir, G_DIR_SEPARATOR_S, filename); |
1826 | 0 | if (test_for_directory(from_file) != EISDIR) { |
1827 | 0 | to_file = ws_strdup_printf ("%s%s%s", to_dir, G_DIR_SEPARATOR_S, filename); |
1828 | 0 | if (!copy_file_binary_mode(from_file, to_file)) { |
1829 | 0 | *pf_filename_return = g_strdup(filename); |
1830 | 0 | g_free (from_file); |
1831 | 0 | g_free (to_file); |
1832 | 0 | ret = -1; |
1833 | 0 | break; |
1834 | 0 | } |
1835 | 0 | g_free (to_file); |
1836 | | #if 0 |
1837 | | } else { |
1838 | | /* The user has manually created a directory in the profile |
1839 | | * directory. Do not copy the directory recursively (yet?) |
1840 | | */ |
1841 | | #endif |
1842 | 0 | } |
1843 | 0 | g_free (from_file); |
1844 | 0 | } |
1845 | 0 | ws_dir_close(dir); |
1846 | 0 | } |
1847 | |
|
1848 | 0 | return ret; |
1849 | 0 | } |
1850 | | |
1851 | | static int |
1852 | | reset_default_profile(const char* app_env_var_prefix, char **pf_dir_path_return) |
1853 | 0 | { |
1854 | 0 | char *profile_dir = get_persconffile_dir(app_env_var_prefix, NULL); |
1855 | 0 | char *filename, *del_file; |
1856 | 0 | GList *files, *file; |
1857 | 0 | int ret = 0; |
1858 | |
|
1859 | 0 | files = g_hash_table_get_keys(profile_files); |
1860 | 0 | file = g_list_first(files); |
1861 | 0 | while (file) { |
1862 | 0 | filename = (char *)file->data; |
1863 | 0 | del_file = ws_strdup_printf("%s%s%s", profile_dir, G_DIR_SEPARATOR_S, filename); |
1864 | |
|
1865 | 0 | if (file_exists(del_file)) { |
1866 | 0 | ret = ws_remove(del_file); |
1867 | 0 | if (ret != 0) { |
1868 | 0 | *pf_dir_path_return = profile_dir; |
1869 | 0 | g_free(del_file); |
1870 | 0 | break; |
1871 | 0 | } |
1872 | 0 | } |
1873 | | |
1874 | 0 | g_free(del_file); |
1875 | 0 | file = g_list_next(file); |
1876 | 0 | } |
1877 | 0 | g_list_free(files); |
1878 | |
|
1879 | 0 | g_free(profile_dir); |
1880 | 0 | return ret; |
1881 | 0 | } |
1882 | | |
1883 | | int |
1884 | | delete_persconffile_profile(const char* app_env_var_prefix, const char *profilename, char **pf_dir_path_return) |
1885 | 0 | { |
1886 | 0 | if (strcmp(profilename, DEFAULT_PROFILE) == 0) { |
1887 | 0 | return reset_default_profile(app_env_var_prefix, pf_dir_path_return); |
1888 | 0 | } |
1889 | | |
1890 | 0 | char *profile_dir = get_persconffile_dir(app_env_var_prefix, profilename); |
1891 | 0 | int ret = 0; |
1892 | |
|
1893 | 0 | if (test_for_directory (profile_dir) == EISDIR) { |
1894 | 0 | ret = delete_directory (profile_dir, pf_dir_path_return); |
1895 | 0 | } |
1896 | |
|
1897 | 0 | g_free(profile_dir); |
1898 | 0 | return ret; |
1899 | 0 | } |
1900 | | |
1901 | | int |
1902 | | rename_persconffile_profile(const char* app_env_var_prefix, const char *fromname, const char *toname, |
1903 | | char **pf_from_dir_path_return, char **pf_to_dir_path_return) |
1904 | 0 | { |
1905 | 0 | char *from_dir = get_persconffile_dir(app_env_var_prefix, fromname); |
1906 | 0 | char *to_dir = get_persconffile_dir(app_env_var_prefix, toname); |
1907 | 0 | int ret = 0; |
1908 | |
|
1909 | 0 | ret = ws_rename (from_dir, to_dir); |
1910 | 0 | if (ret != 0) { |
1911 | 0 | *pf_from_dir_path_return = from_dir; |
1912 | 0 | *pf_to_dir_path_return = to_dir; |
1913 | 0 | return ret; |
1914 | 0 | } |
1915 | | |
1916 | 0 | g_free (from_dir); |
1917 | 0 | g_free (to_dir); |
1918 | |
|
1919 | 0 | return 0; |
1920 | 0 | } |
1921 | | |
1922 | | /* |
1923 | | * Create the directory that holds personal configuration files, if |
1924 | | * necessary. If we attempted to create it, and failed, return -1 and |
1925 | | * set "*pf_dir_path_return" to the pathname of the directory we failed |
1926 | | * to create (it's g_mallocated, so our caller should free it); otherwise, |
1927 | | * return 0. |
1928 | | */ |
1929 | | int |
1930 | | create_persconffile_profile(const char* app_env_var_prefix, const char *profilename, char **pf_dir_path_return) |
1931 | 0 | { |
1932 | 0 | char *pf_dir_path; |
1933 | | #ifdef _WIN32 |
1934 | | char *pf_dir_path_copy, *pf_dir_parent_path; |
1935 | | size_t pf_dir_parent_path_len; |
1936 | | int save_errno; |
1937 | | #endif |
1938 | 0 | ws_statb64 s_buf; |
1939 | 0 | int ret; |
1940 | |
|
1941 | 0 | if (profilename) { |
1942 | | /* |
1943 | | * Create the personal profiles directory, if necessary. |
1944 | | */ |
1945 | 0 | if (create_profiles_dir(app_env_var_prefix, pf_dir_path_return) == -1) { |
1946 | 0 | return -1; |
1947 | 0 | } |
1948 | 0 | } |
1949 | | |
1950 | 0 | pf_dir_path = get_persconffile_dir(app_env_var_prefix, profilename); |
1951 | 0 | if (ws_stat64(pf_dir_path, &s_buf) != 0) { |
1952 | 0 | if (errno != ENOENT) { |
1953 | | /* Some other problem; give up now. */ |
1954 | 0 | *pf_dir_path_return = pf_dir_path; |
1955 | 0 | return -1; |
1956 | 0 | } |
1957 | | #ifdef _WIN32 |
1958 | | /* |
1959 | | * Does the parent directory of that directory |
1960 | | * exist? %APPDATA% may not exist even though |
1961 | | * %USERPROFILE% does. |
1962 | | * |
1963 | | * We check for the existence of the directory |
1964 | | * by first checking whether the parent directory |
1965 | | * is just a drive letter and, if it's not, by |
1966 | | * doing a "stat()" on it. If it's a drive letter, |
1967 | | * or if the "stat()" succeeds, we assume it exists. |
1968 | | */ |
1969 | | pf_dir_path_copy = g_strdup(pf_dir_path); |
1970 | | pf_dir_parent_path = get_dirname(pf_dir_path_copy); |
1971 | | pf_dir_parent_path_len = strlen(pf_dir_parent_path); |
1972 | | if (pf_dir_parent_path_len > 0 |
1973 | | && pf_dir_parent_path[pf_dir_parent_path_len - 1] != ':' |
1974 | | && ws_stat64(pf_dir_parent_path, &s_buf) != 0) { |
1975 | | /* |
1976 | | * Not a drive letter and the stat() failed. |
1977 | | */ |
1978 | | if (errno != ENOENT) { |
1979 | | /* Some other problem; give up now. */ |
1980 | | *pf_dir_path_return = pf_dir_path; |
1981 | | save_errno = errno; |
1982 | | g_free(pf_dir_path_copy); |
1983 | | errno = save_errno; |
1984 | | return -1; |
1985 | | } |
1986 | | /* |
1987 | | * No, it doesn't exist - make it first. |
1988 | | */ |
1989 | | ret = ws_mkdir(pf_dir_parent_path, 0755); |
1990 | | if (ret == -1) { |
1991 | | *pf_dir_path_return = pf_dir_parent_path; |
1992 | | save_errno = errno; |
1993 | | g_free(pf_dir_path); |
1994 | | errno = save_errno; |
1995 | | return -1; |
1996 | | } |
1997 | | } |
1998 | | g_free(pf_dir_path_copy); |
1999 | | ret = ws_mkdir(pf_dir_path, 0755); |
2000 | | #else |
2001 | 0 | ret = g_mkdir_with_parents(pf_dir_path, 0755); |
2002 | 0 | #endif |
2003 | 0 | } else { |
2004 | | /* |
2005 | | * Something with that pathname exists; if it's not |
2006 | | * a directory, we'll get an error if we try to put |
2007 | | * something in it, so we don't fail here, we wait |
2008 | | * for that attempt to fail. |
2009 | | */ |
2010 | 0 | ret = 0; |
2011 | 0 | } |
2012 | 0 | if (ret == -1) |
2013 | 0 | *pf_dir_path_return = pf_dir_path; |
2014 | 0 | else |
2015 | 0 | g_free(pf_dir_path); |
2016 | |
|
2017 | 0 | return ret; |
2018 | 0 | } |
2019 | | |
2020 | | const GHashTable * |
2021 | | allowed_profile_filenames(void) |
2022 | 0 | { |
2023 | 0 | return profile_files; |
2024 | 0 | } |
2025 | | |
2026 | | int |
2027 | | create_persconffile_dir(const char* app_env_var_prefix, char **pf_dir_path_return) |
2028 | 0 | { |
2029 | 0 | return create_persconffile_profile(app_env_var_prefix, persconfprofile, pf_dir_path_return); |
2030 | 0 | } |
2031 | | |
2032 | | int |
2033 | | copy_persconffile_profile(const char* app_env_var_prefix, const char *toname, const char *fromname, bool from_global, |
2034 | | char **pf_filename_return, char **pf_to_dir_path_return, char **pf_from_dir_path_return) |
2035 | 0 | { |
2036 | 0 | int ret = 0; |
2037 | 0 | char *from_dir; |
2038 | 0 | char *to_dir = get_persconffile_dir(app_env_var_prefix, toname); |
2039 | 0 | char *from_file, *to_file; |
2040 | 0 | const char *filename; |
2041 | 0 | GHashTableIter files; |
2042 | 0 | void * file; |
2043 | |
|
2044 | 0 | from_dir = get_profile_dir(app_env_var_prefix, fromname, from_global); |
2045 | |
|
2046 | 0 | if (!profile_files || do_store_persconffiles) { |
2047 | | /* Either the profile_files hashtable does not exist yet |
2048 | | * (this is very early in startup) or we are still adding |
2049 | | * files to it. Just copy all the non-directories. |
2050 | | */ |
2051 | 0 | ret = copy_directory(from_dir, to_dir, pf_filename_return); |
2052 | 0 | } else { |
2053 | |
|
2054 | 0 | g_hash_table_iter_init(&files, profile_files); |
2055 | 0 | while (g_hash_table_iter_next(&files, &file, NULL)) { |
2056 | 0 | filename = (const char *)file; |
2057 | 0 | from_file = ws_strdup_printf ("%s%s%s", from_dir, G_DIR_SEPARATOR_S, filename); |
2058 | 0 | to_file = ws_strdup_printf ("%s%s%s", to_dir, G_DIR_SEPARATOR_S, filename); |
2059 | |
|
2060 | 0 | if (test_for_regular_file(from_file) && !copy_file_binary_mode(from_file, to_file)) { |
2061 | 0 | *pf_filename_return = g_strdup(filename); |
2062 | 0 | g_free (from_file); |
2063 | 0 | g_free (to_file); |
2064 | 0 | ret = -1; |
2065 | 0 | break; |
2066 | 0 | } |
2067 | | |
2068 | 0 | g_free (to_file); |
2069 | 0 | g_free (from_file); |
2070 | 0 | } |
2071 | 0 | } |
2072 | |
|
2073 | 0 | if (ret != 0) { |
2074 | 0 | *pf_to_dir_path_return = to_dir; |
2075 | 0 | *pf_from_dir_path_return = from_dir; |
2076 | 0 | } else { |
2077 | 0 | g_free (to_dir); |
2078 | 0 | g_free (from_dir); |
2079 | 0 | } |
2080 | |
|
2081 | 0 | return ret; |
2082 | 0 | } |
2083 | | |
2084 | | /* |
2085 | | * Get the (default) directory in which personal data is stored. |
2086 | | * |
2087 | | * On Win32, this is the "Documents" folder in the personal profile. |
2088 | | * On UNIX this is simply the current directory, unless that's "/", |
2089 | | * which it will be, for example, when Wireshark is run from the |
2090 | | * Finder in macOS, in which case we use the user's home directory. |
2091 | | */ |
2092 | | extern const char * |
2093 | | get_persdatafile_dir(void) |
2094 | 14 | { |
2095 | | /* Return the cached value, if available */ |
2096 | 14 | if (persdatafile_dir != NULL) |
2097 | 0 | return persdatafile_dir; |
2098 | | |
2099 | | #ifdef _WIN32 |
2100 | | TCHAR tszPath[MAX_PATH]; |
2101 | | |
2102 | | /* |
2103 | | * We should SHGetKnownFolderPath instead, but that appears to require |
2104 | | * a desktop session. |
2105 | | */ |
2106 | | if (SHGetSpecialFolderPath(NULL, tszPath, CSIDL_PERSONAL, false)) { |
2107 | | persdatafile_dir = g_utf16_to_utf8(tszPath, -1, NULL, NULL, NULL); |
2108 | | return persdatafile_dir; |
2109 | | } else { |
2110 | | return ""; |
2111 | | } |
2112 | | #else |
2113 | | /* |
2114 | | * Get the current directory. |
2115 | | */ |
2116 | 14 | persdatafile_dir = g_get_current_dir(); |
2117 | 14 | if (persdatafile_dir == NULL) { |
2118 | | /* XXX - can this fail? */ |
2119 | | /* |
2120 | | * g_get_home_dir() returns a const gchar *; g_strdup() it |
2121 | | * so that it's something that can be freed. |
2122 | | */ |
2123 | 0 | persdatafile_dir = g_strdup(g_get_home_dir()); |
2124 | 14 | } else if (strcmp(persdatafile_dir, "/") == 0) { |
2125 | 0 | g_free(persdatafile_dir); |
2126 | | /* |
2127 | | * See above. |
2128 | | */ |
2129 | 0 | persdatafile_dir = g_strdup(g_get_home_dir()); |
2130 | 0 | } |
2131 | 14 | return persdatafile_dir; |
2132 | 14 | #endif |
2133 | 14 | } |
2134 | | |
2135 | | void |
2136 | | set_persdatafile_dir(const char *p) |
2137 | 0 | { |
2138 | 0 | g_free(persdatafile_dir); |
2139 | 0 | persdatafile_dir = g_strdup(p); |
2140 | 0 | } |
2141 | | |
2142 | | /* |
2143 | | * Construct the path name of a personal configuration file, given the |
2144 | | * file name. |
2145 | | * |
2146 | | * On Win32, if "for_writing" is false, we check whether the file exists |
2147 | | * and, if not, construct a path name relative to the ".wireshark" |
2148 | | * subdirectory of the user's home directory, and check whether that |
2149 | | * exists; if it does, we return that, so that configuration files |
2150 | | * from earlier versions can be read. |
2151 | | * |
2152 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
2153 | | * caller is done with it. |
2154 | | */ |
2155 | | char * |
2156 | | get_persconffile_path(const char *filename, bool from_profile, const char* app_env_var_prefix) |
2157 | 2.42k | { |
2158 | 2.42k | char *path, *dir = NULL; |
2159 | | |
2160 | 2.42k | if (from_profile) { |
2161 | | /* Store filenames so we know which filenames belongs to a configuration profile */ |
2162 | 2.19k | profile_register_persconffile(filename); |
2163 | | |
2164 | 2.19k | dir = get_persconffile_dir(app_env_var_prefix, persconfprofile); |
2165 | 2.19k | } else { |
2166 | 225 | dir = get_persconffile_dir(app_env_var_prefix, NULL); |
2167 | 225 | } |
2168 | 2.42k | path = g_build_filename(dir, filename, NULL); |
2169 | | |
2170 | 2.42k | g_free(dir); |
2171 | 2.42k | return path; |
2172 | 2.42k | } |
2173 | | |
2174 | | /* |
2175 | | * Construct the path name of a global configuration file, given the |
2176 | | * file name. |
2177 | | * |
2178 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
2179 | | * caller is done with it. |
2180 | | */ |
2181 | | char * |
2182 | | get_datafile_path(const char *filename, const char* app_env_var_prefix) |
2183 | 2.11k | { |
2184 | 2.11k | if (running_in_build_directory_flag && !strcmp(filename, "hosts")) { |
2185 | | /* We're running in the build directory and the requested file is a |
2186 | | * generated (or a test) file. Return the file name in the build |
2187 | | * directory (not in the source/data directory). |
2188 | | * (Oh the things we do to keep the source directory pristine...) |
2189 | | */ |
2190 | 28 | return g_build_filename(get_progfile_dir(), filename, (char *)NULL); |
2191 | 2.08k | } else { |
2192 | 2.08k | return g_build_filename(get_datafile_dir(app_env_var_prefix), filename, (char *)NULL); |
2193 | 2.08k | } |
2194 | 2.11k | } |
2195 | | |
2196 | | /* |
2197 | | * Construct the path name of a global documentation file, given the |
2198 | | * file name. |
2199 | | * |
2200 | | * The returned file name was g_malloc()'d so it must be g_free()d when the |
2201 | | * caller is done with it. |
2202 | | */ |
2203 | | char * |
2204 | | get_docfile_path(const char *filename, const char* app_env_var_prefix) |
2205 | 0 | { |
2206 | 0 | if (running_in_build_directory_flag) { |
2207 | | /* We're running in the build directory and the requested file is a |
2208 | | * generated (or a test) file. Return the file name in the build |
2209 | | * directory (not in the source/data directory). |
2210 | | * (Oh the things we do to keep the source directory pristine...) |
2211 | | */ |
2212 | 0 | return g_build_filename(get_progfile_dir(), filename, (char *)NULL); |
2213 | 0 | } else { |
2214 | 0 | return g_build_filename(get_doc_dir(app_env_var_prefix), filename, (char *)NULL); |
2215 | 0 | } |
2216 | 0 | } |
2217 | | |
2218 | | /* |
2219 | | * Return an error message for UNIX-style errno indications on open or |
2220 | | * create operations. |
2221 | | */ |
2222 | | const char * |
2223 | | file_open_error_message(int err, bool for_writing) |
2224 | 0 | { |
2225 | 0 | const char *errmsg; |
2226 | 0 | static char errmsg_errno[1024+1]; |
2227 | |
|
2228 | 0 | switch (err) { |
2229 | | |
2230 | 0 | case ENOENT: |
2231 | 0 | if (for_writing) |
2232 | 0 | errmsg = "The path to the file \"%s\" doesn't exist."; |
2233 | 0 | else |
2234 | 0 | errmsg = "The file \"%s\" doesn't exist."; |
2235 | 0 | break; |
2236 | | |
2237 | 0 | case EACCES: |
2238 | 0 | if (for_writing) |
2239 | 0 | errmsg = "You don't have permission to create or write to the file \"%s\"."; |
2240 | 0 | else |
2241 | 0 | errmsg = "You don't have permission to read the file \"%s\"."; |
2242 | 0 | break; |
2243 | | |
2244 | 0 | case EISDIR: |
2245 | 0 | errmsg = "\"%s\" is a directory (folder), not a file."; |
2246 | 0 | break; |
2247 | | |
2248 | 0 | case ENOSPC: |
2249 | 0 | errmsg = "The file \"%s\" could not be created because there is no space left on the file system."; |
2250 | 0 | break; |
2251 | | |
2252 | 0 | #ifdef EDQUOT |
2253 | 0 | case EDQUOT: |
2254 | 0 | errmsg = "The file \"%s\" could not be created because you are too close to, or over, your disk quota."; |
2255 | 0 | break; |
2256 | 0 | #endif |
2257 | | |
2258 | 0 | case EINVAL: |
2259 | 0 | errmsg = "The file \"%s\" could not be created because an invalid filename was specified."; |
2260 | 0 | break; |
2261 | | |
2262 | 0 | #ifdef ENAMETOOLONG |
2263 | 0 | case ENAMETOOLONG: |
2264 | | /* XXX Make sure we truncate on a character boundary. */ |
2265 | 0 | errmsg = "The file name \"%.80s" UTF8_HORIZONTAL_ELLIPSIS "\" is too long."; |
2266 | 0 | break; |
2267 | 0 | #endif |
2268 | | |
2269 | 0 | case ENOMEM: |
2270 | | /* |
2271 | | * The problem probably has nothing to do with how much RAM the |
2272 | | * user has on their machine, so don't confuse them by saying |
2273 | | * "memory". The problem is probably either virtual address |
2274 | | * space or swap space. |
2275 | | */ |
2276 | | #if GLIB_SIZEOF_VOID_P == 4 |
2277 | | /* |
2278 | | * ILP32; we probably ran out of virtual address space. |
2279 | | */ |
2280 | | #define ENOMEM_REASON "it can't be handled by a 32-bit application" |
2281 | | #else |
2282 | | /* |
2283 | | * LP64 or LLP64; we probably ran out of swap space. |
2284 | | */ |
2285 | | #if defined(_WIN32) |
2286 | | /* |
2287 | | * You need to make the pagefile bigger. |
2288 | | */ |
2289 | | #define ENOMEM_REASON "the pagefile is too small" |
2290 | | #elif defined(ENABLE_APPLICATION_BUNDLE) |
2291 | | /* |
2292 | | * dynamic_pager couldn't, or wouldn't, create more swap files. |
2293 | | */ |
2294 | | #define ENOMEM_REASON "your system ran out of swap file space" |
2295 | | #else |
2296 | | /* |
2297 | | * Either you have a fixed swap partition or a fixed swap file, |
2298 | | * and it needs to be made bigger. |
2299 | | * |
2300 | | * This is UN*X, but it's not macOS, so we assume the user is |
2301 | | * *somewhat* nerdy. |
2302 | | */ |
2303 | 0 | #define ENOMEM_REASON "your system is out of swap space" |
2304 | 0 | #endif |
2305 | 0 | #endif /* GLIB_SIZEOF_VOID_P == 4 */ |
2306 | 0 | if (for_writing) |
2307 | 0 | errmsg = "The file \"%s\" could not be created because " ENOMEM_REASON "."; |
2308 | 0 | else |
2309 | 0 | errmsg = "The file \"%s\" could not be opened because " ENOMEM_REASON "."; |
2310 | 0 | break; |
2311 | | |
2312 | 0 | default: |
2313 | 0 | snprintf(errmsg_errno, sizeof(errmsg_errno), |
2314 | 0 | "The file \"%%s\" could not be %s: %s.", |
2315 | 0 | for_writing ? "created" : "opened", |
2316 | 0 | g_strerror(err)); |
2317 | 0 | errmsg = errmsg_errno; |
2318 | 0 | break; |
2319 | 0 | } |
2320 | 0 | return errmsg; |
2321 | 0 | } |
2322 | | |
2323 | | /* |
2324 | | * Return an error message for UNIX-style errno indications on write |
2325 | | * operations. |
2326 | | */ |
2327 | | const char * |
2328 | | file_write_error_message(int err) |
2329 | 0 | { |
2330 | 0 | const char *errmsg; |
2331 | 0 | static char errmsg_errno[1024+1]; |
2332 | |
|
2333 | 0 | switch (err) { |
2334 | | |
2335 | 0 | case ENOSPC: |
2336 | 0 | errmsg = "The file \"%s\" could not be saved because there is no space left on the file system."; |
2337 | 0 | break; |
2338 | | |
2339 | 0 | #ifdef EDQUOT |
2340 | 0 | case EDQUOT: |
2341 | 0 | errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota."; |
2342 | 0 | break; |
2343 | 0 | #endif |
2344 | | |
2345 | 0 | default: |
2346 | 0 | snprintf(errmsg_errno, sizeof(errmsg_errno), |
2347 | 0 | "An error occurred while writing to the file \"%%s\": %s.", |
2348 | 0 | g_strerror(err)); |
2349 | 0 | errmsg = errmsg_errno; |
2350 | 0 | break; |
2351 | 0 | } |
2352 | 0 | return errmsg; |
2353 | 0 | } |
2354 | | |
2355 | | |
2356 | | bool |
2357 | | file_exists(const char *fname) |
2358 | 3.92k | { |
2359 | 3.92k | ws_statb64 file_stat; |
2360 | | |
2361 | 3.92k | if (!fname) { |
2362 | 0 | return false; |
2363 | 0 | } |
2364 | | |
2365 | 3.92k | if (ws_stat64(fname, &file_stat) != 0 && errno == ENOENT) { |
2366 | 3.92k | return false; |
2367 | 3.92k | } else { |
2368 | 0 | return true; |
2369 | 0 | } |
2370 | 3.92k | } |
2371 | | |
2372 | | bool config_file_exists_with_entries(const char *fname, char comment_char) |
2373 | 0 | { |
2374 | 0 | bool start_of_line = true; |
2375 | 0 | bool has_entries = false; |
2376 | 0 | FILE *file; |
2377 | 0 | int c; |
2378 | |
|
2379 | 0 | if (!fname) { |
2380 | 0 | return false; |
2381 | 0 | } |
2382 | | |
2383 | 0 | if ((file = ws_fopen(fname, "r")) == NULL) { |
2384 | 0 | return false; |
2385 | 0 | } |
2386 | | |
2387 | 0 | do { |
2388 | 0 | c = ws_getc_unlocked(file); |
2389 | 0 | if (start_of_line && c != comment_char && !g_ascii_isspace(c) && g_ascii_isprint(c)) { |
2390 | 0 | has_entries = true; |
2391 | 0 | break; |
2392 | 0 | } |
2393 | 0 | if (c == '\n' || !g_ascii_isspace(c)) { |
2394 | 0 | start_of_line = (c == '\n'); |
2395 | 0 | } |
2396 | 0 | } while (c != EOF); |
2397 | |
|
2398 | 0 | fclose(file); |
2399 | 0 | return has_entries; |
2400 | 0 | } |
2401 | | |
2402 | | /* |
2403 | | * Check that the from file is not the same as to file |
2404 | | * We do it here so we catch all cases ... |
2405 | | * Unfortunately, the file requester gives us an absolute file |
2406 | | * name and the read file name may be relative (if supplied on |
2407 | | * the command line), so we can't just compare paths. From Joerg Mayer. |
2408 | | */ |
2409 | | bool |
2410 | | files_identical(const char *fname1, const char *fname2) |
2411 | 14 | { |
2412 | | /* Two different implementations, because st_ino isn't filled in with |
2413 | | * a meaningful value on Windows. Use the Windows API and FILE_ID_INFO |
2414 | | * instead. |
2415 | | */ |
2416 | | #ifdef _WIN32 |
2417 | | |
2418 | | FILE_ID_INFO filestat1, filestat2; |
2419 | | |
2420 | | /* |
2421 | | * Compare VolumeSerialNumber and FileId. |
2422 | | */ |
2423 | | |
2424 | | /* |
2425 | | * "You must set [FILE_FLAG_BACKUP_SEMANTICS] to obtain a handle to a |
2426 | | * directory." - Otherwise, CreateFile returns an invalid value. |
2427 | | * |
2428 | | * "The system ensures that the calling process overrides file security |
2429 | | * checks when the process has SE_BACKUP_NAME and SE_RESTORE_NAME |
2430 | | * privileges." - That shouldn't have any effect, because we open the |
2431 | | * file with neither GENERIC_READ nor GENERIC_WRITE access, only get file |
2432 | | * information and then close the handle. |
2433 | | * |
2434 | | * https://learn.microsoft.com/en-us/windows/win32/fileio/obtaining-a-handle-to-a-directory |
2435 | | * https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea |
2436 | | */ |
2437 | | HANDLE h1 = CreateFile(utf_8to16(fname1), 0, |
2438 | | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
2439 | | NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
2440 | | |
2441 | | if (h1 == INVALID_HANDLE_VALUE) { |
2442 | | return false; |
2443 | | } |
2444 | | |
2445 | | if (!GetFileInformationByHandleEx(h1, FileIdInfo, &filestat1, sizeof(FILE_ID_INFO))) { |
2446 | | CloseHandle(h1); |
2447 | | return false; |
2448 | | } |
2449 | | CloseHandle(h1); |
2450 | | |
2451 | | HANDLE h2 = CreateFile(utf_8to16(fname2), 0, |
2452 | | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, |
2453 | | NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); |
2454 | | |
2455 | | if (h2 == INVALID_HANDLE_VALUE) { |
2456 | | return false; |
2457 | | } |
2458 | | |
2459 | | if (!GetFileInformationByHandleEx(h2, FileIdInfo, &filestat2, sizeof(FILE_ID_INFO))) { |
2460 | | CloseHandle(h2); |
2461 | | return false; |
2462 | | } |
2463 | | CloseHandle(h2); |
2464 | | |
2465 | | return ((memcmp(&filestat1.FileId, &filestat2.FileId, sizeof(FILE_ID_128)) == 0) && |
2466 | | filestat1.VolumeSerialNumber == filestat2.VolumeSerialNumber); |
2467 | | #else |
2468 | 14 | ws_statb64 filestat1, filestat2; |
2469 | | |
2470 | | /* |
2471 | | * Compare st_dev and st_ino. |
2472 | | */ |
2473 | 14 | if (ws_stat64(fname1, &filestat1) == -1) |
2474 | 14 | return false; /* can't get info about the first file */ |
2475 | 0 | if (ws_stat64(fname2, &filestat2) == -1) |
2476 | 0 | return false; /* can't get info about the second file */ |
2477 | 0 | return (filestat1.st_dev == filestat2.st_dev && |
2478 | 0 | filestat1.st_ino == filestat2.st_ino); |
2479 | 0 | #endif |
2480 | 0 | } |
2481 | | |
2482 | | bool |
2483 | | file_needs_reopen(int fd, const char* filename) |
2484 | 0 | { |
2485 | | #ifdef _WIN32 |
2486 | | /* Windows handles st_dev in a way unsuitable here: |
2487 | | * * _fstat() simply casts the file descriptor (ws_fileno(fp)) to unsigned |
2488 | | * and assigns this value to st_dev and st_rdev |
2489 | | * * _wstat() converts drive letter (eg. C) to number (A=0, B=1, C=2, ...) |
2490 | | * and assigns such number to st_dev and st_rdev |
2491 | | * |
2492 | | * The st_ino parameter is simply zero as there is no specific assignment |
2493 | | * to it in the Universal CRT source code. |
2494 | | * |
2495 | | * Thus instead of using fstat(), use Windows specific API. |
2496 | | */ |
2497 | | |
2498 | | HANDLE open_handle = (HANDLE)_get_osfhandle(fd); |
2499 | | HANDLE current_handle = CreateFile(utf_8to16(filename), FILE_READ_ATTRIBUTES, |
2500 | | FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, |
2501 | | NULL, OPEN_EXISTING, 0, NULL); |
2502 | | BY_HANDLE_FILE_INFORMATION open_info, current_info; |
2503 | | |
2504 | | if (current_handle == INVALID_HANDLE_VALUE) { |
2505 | | return true; |
2506 | | } |
2507 | | |
2508 | | #if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) |
2509 | | FILE_ID_INFO open_id, current_id; |
2510 | | if (GetFileInformationByHandleEx(open_handle, FileIdInfo, &open_id, sizeof(open_id)) && |
2511 | | GetFileInformationByHandleEx(current_handle, FileIdInfo, ¤t_id, sizeof(current_id))) { |
2512 | | /* 128-bit identifier is available, use it */ |
2513 | | CloseHandle(current_handle); |
2514 | | return open_id.VolumeSerialNumber != current_id.VolumeSerialNumber || |
2515 | | memcmp(&open_id.FileId, ¤t_id.FileId, sizeof(open_id.FileId)) != 0; |
2516 | | } |
2517 | | #endif /* _WIN32_WINNT >= _WIN32_WINNT_WIN8 */ |
2518 | | if (GetFileInformationByHandle(open_handle, &open_info) && |
2519 | | GetFileInformationByHandle(current_handle, ¤t_info)) { |
2520 | | /* Fallback to 64-bit identifier */ |
2521 | | CloseHandle(current_handle); |
2522 | | uint64_t open_size = (((uint64_t)open_info.nFileSizeHigh) << 32) | open_info.nFileSizeLow; |
2523 | | uint64_t current_size = (((uint64_t)current_info.nFileSizeHigh) << 32) | current_info.nFileSizeLow; |
2524 | | return open_info.dwVolumeSerialNumber != current_info.dwVolumeSerialNumber || |
2525 | | open_info.nFileIndexHigh != current_info.nFileIndexHigh || |
2526 | | open_info.nFileIndexLow != current_info.nFileIndexLow || |
2527 | | open_size > current_size; |
2528 | | } |
2529 | | CloseHandle(current_handle); |
2530 | | return true; |
2531 | | #else |
2532 | 0 | ws_statb64 open_stat, current_stat; |
2533 | | |
2534 | | /* consider a file deleted when stat fails for either file, |
2535 | | * or when the residing device / inode has changed. */ |
2536 | 0 | if (0 != ws_fstat64(fd, &open_stat)) |
2537 | 0 | return true; |
2538 | 0 | if (0 != ws_stat64(filename, ¤t_stat)) |
2539 | 0 | return true; |
2540 | | |
2541 | 0 | return open_stat.st_dev != current_stat.st_dev || |
2542 | 0 | open_stat.st_ino != current_stat.st_ino || |
2543 | 0 | open_stat.st_size > current_stat.st_size; |
2544 | 0 | #endif |
2545 | 0 | } |
2546 | | |
2547 | | bool |
2548 | | write_file_binary_mode(const char *filename, const void *content, size_t content_len) |
2549 | 0 | { |
2550 | 0 | int fd; |
2551 | 0 | size_t bytes_left; |
2552 | 0 | unsigned int bytes_to_write; |
2553 | 0 | ssize_t bytes_written; |
2554 | 0 | const uint8_t *ptr; |
2555 | 0 | int err; |
2556 | |
|
2557 | 0 | fd = ws_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); |
2558 | 0 | if (fd == -1) { |
2559 | 0 | report_open_failure(filename, errno, true); |
2560 | 0 | return false; |
2561 | 0 | } |
2562 | | |
2563 | | /* |
2564 | | * The third argument to _write() on Windows is an unsigned int, |
2565 | | * so, on Windows, that's the size of the third argument to |
2566 | | * ws_write(). |
2567 | | * |
2568 | | * The third argument to write() on UN*X is a size_t, although |
2569 | | * the return value is an ssize_t, so one probably shouldn't |
2570 | | * write more than the max value of an ssize_t. |
2571 | | * |
2572 | | * In either case, there's no guarantee that a size_t such as |
2573 | | * content_len can be passed to ws_write(), so we write in |
2574 | | * chunks of at most 2^31 bytes. |
2575 | | */ |
2576 | | |
2577 | 0 | ptr = (const uint8_t *)content; |
2578 | 0 | bytes_left = content_len; |
2579 | 0 | while (bytes_left != 0) { |
2580 | 0 | if (bytes_left > 0x40000000) { |
2581 | 0 | bytes_to_write = 0x40000000; |
2582 | 0 | } else { |
2583 | 0 | bytes_to_write = (unsigned int)bytes_left; |
2584 | 0 | } |
2585 | 0 | bytes_written = ws_write(fd, ptr, bytes_to_write); |
2586 | 0 | if (bytes_written <= 0) { |
2587 | 0 | if (bytes_written < 0) { |
2588 | 0 | err = errno; |
2589 | 0 | } else { |
2590 | 0 | err = FILE_ERR_SHORT_WRITE; |
2591 | 0 | } |
2592 | 0 | report_write_failure(filename, err); |
2593 | 0 | ws_close(fd); |
2594 | 0 | return false; |
2595 | 0 | } |
2596 | 0 | bytes_left -= (size_t)bytes_written; |
2597 | 0 | ptr += bytes_written; |
2598 | 0 | } |
2599 | | |
2600 | 0 | ws_close(fd); |
2601 | 0 | return true; |
2602 | 0 | } |
2603 | | |
2604 | | /* |
2605 | | * Copy a file in binary mode, for those operating systems that care about |
2606 | | * such things. This should be OK for all files, even text files, as |
2607 | | * we'll copy the raw bytes, and we don't look at the bytes as we copy |
2608 | | * them. |
2609 | | * |
2610 | | * Returns true on success, false on failure. If a failure, it also |
2611 | | * displays a simple dialog window with the error message. |
2612 | | */ |
2613 | | bool |
2614 | | copy_file_binary_mode(const char *from_filename, const char *to_filename) |
2615 | 0 | { |
2616 | 0 | int from_fd, to_fd, err; |
2617 | 0 | ws_file_ssize_t nread, nwritten; |
2618 | 0 | uint8_t *pd = NULL; |
2619 | | |
2620 | | /* Copy the raw bytes of the file. */ |
2621 | 0 | from_fd = ws_open(from_filename, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */); |
2622 | 0 | if (from_fd < 0) { |
2623 | 0 | report_open_failure(from_filename, errno, false); |
2624 | 0 | goto done; |
2625 | 0 | } |
2626 | | |
2627 | | /* Use open() instead of creat() so that we can pass the O_BINARY |
2628 | | flag, which is relevant on Win32; it appears that "creat()" |
2629 | | may open the file in text mode, not binary mode, but we want |
2630 | | to copy the raw bytes of the file, so we need the output file |
2631 | | to be open in binary mode. */ |
2632 | 0 | to_fd = ws_open(to_filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); |
2633 | 0 | if (to_fd < 0) { |
2634 | 0 | report_open_failure(to_filename, errno, true); |
2635 | 0 | ws_close(from_fd); |
2636 | 0 | goto done; |
2637 | 0 | } |
2638 | | |
2639 | 0 | #define FS_READ_SIZE 65536 |
2640 | 0 | pd = (uint8_t *)g_malloc(FS_READ_SIZE); |
2641 | 0 | while ((nread = ws_read(from_fd, pd, FS_READ_SIZE)) > 0) { |
2642 | 0 | nwritten = ws_write(to_fd, pd, (size_t)nread); |
2643 | 0 | if (nwritten < nread) { |
2644 | 0 | if (nwritten < 0) |
2645 | 0 | err = errno; |
2646 | 0 | else |
2647 | 0 | err = FILE_ERR_SHORT_WRITE; |
2648 | 0 | report_write_failure(to_filename, err); |
2649 | 0 | ws_close(from_fd); |
2650 | 0 | ws_close(to_fd); |
2651 | 0 | goto done; |
2652 | 0 | } |
2653 | 0 | } |
2654 | 0 | if (nread < 0) { |
2655 | 0 | err = errno; |
2656 | 0 | report_read_failure(from_filename, err); |
2657 | 0 | ws_close(from_fd); |
2658 | 0 | ws_close(to_fd); |
2659 | 0 | goto done; |
2660 | 0 | } |
2661 | 0 | ws_close(from_fd); |
2662 | 0 | if (ws_close(to_fd) < 0) { |
2663 | 0 | report_write_failure(to_filename, errno); |
2664 | 0 | goto done; |
2665 | 0 | } |
2666 | | |
2667 | 0 | g_free(pd); |
2668 | 0 | pd = NULL; |
2669 | 0 | return true; |
2670 | | |
2671 | 0 | done: |
2672 | 0 | g_free(pd); |
2673 | 0 | return false; |
2674 | 0 | } |
2675 | | |
2676 | | char * |
2677 | | data_file_url(const char *filename, const char* app_env_var_prefix) |
2678 | 0 | { |
2679 | 0 | char *file_path; |
2680 | 0 | char *uri; |
2681 | | |
2682 | | /* Absolute path? */ |
2683 | 0 | if(g_path_is_absolute(filename)) { |
2684 | 0 | file_path = g_strdup(filename); |
2685 | 0 | } else { |
2686 | 0 | file_path = ws_strdup_printf("%s/%s", get_datafile_dir(app_env_var_prefix), filename); |
2687 | 0 | } |
2688 | | |
2689 | | /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */ |
2690 | | |
2691 | | /* convert filename to uri */ |
2692 | 0 | uri = g_filename_to_uri(file_path, NULL, NULL); |
2693 | 0 | g_free(file_path); |
2694 | 0 | return uri; |
2695 | 0 | } |
2696 | | |
2697 | | char * |
2698 | | doc_file_url(const char *filename, const char* app_env_var_prefix) |
2699 | 0 | { |
2700 | 0 | char *file_path; |
2701 | 0 | char *uri; |
2702 | | |
2703 | | /* Absolute path? */ |
2704 | 0 | if(g_path_is_absolute(filename)) { |
2705 | 0 | file_path = g_strdup(filename); |
2706 | 0 | } else { |
2707 | 0 | file_path = ws_strdup_printf("%s/%s", get_doc_dir(app_env_var_prefix), filename); |
2708 | 0 | } |
2709 | | |
2710 | | /* XXX - check, if the file is really existing, otherwise display a simple_dialog about the problem */ |
2711 | | |
2712 | | /* convert filename to uri */ |
2713 | 0 | uri = g_filename_to_uri(file_path, NULL, NULL); |
2714 | 0 | g_free(file_path); |
2715 | 0 | return uri; |
2716 | 0 | } |
2717 | | |
2718 | | void |
2719 | | free_progdirs(void) |
2720 | 0 | { |
2721 | 0 | g_free(persconffile_dir); |
2722 | 0 | persconffile_dir = NULL; |
2723 | 0 | g_free(datafile_dir); |
2724 | 0 | datafile_dir = NULL; |
2725 | 0 | g_free(persdatafile_dir); |
2726 | 0 | persdatafile_dir = NULL; |
2727 | 0 | g_free(persconfprofile); |
2728 | 0 | persconfprofile = NULL; |
2729 | 0 | g_free(progfile_dir); |
2730 | 0 | progfile_dir = NULL; |
2731 | 0 | g_free(doc_dir); |
2732 | 0 | doc_dir = NULL; |
2733 | 0 | g_free(install_prefix); |
2734 | 0 | install_prefix = NULL; |
2735 | 0 | g_free(current_working_dir); |
2736 | 0 | current_working_dir = NULL; |
2737 | | #if defined(HAVE_PLUGINS) || defined(HAVE_LUA) |
2738 | | g_free(plugin_dir); |
2739 | | plugin_dir = NULL; |
2740 | | g_free(plugin_dir_with_version); |
2741 | | plugin_dir_with_version = NULL; |
2742 | | g_free(plugin_pers_dir); |
2743 | | plugin_pers_dir = NULL; |
2744 | | g_free(plugin_pers_dir_with_version); |
2745 | | plugin_pers_dir_with_version = NULL; |
2746 | | #endif |
2747 | 0 | g_free(extcap_dir); |
2748 | 0 | extcap_dir = NULL; |
2749 | 0 | g_free(extcap_pers_dir); |
2750 | | extcap_pers_dir = NULL; |
2751 | 0 | } |
2752 | | |
2753 | | /* |
2754 | | * Editor modelines |
2755 | | * |
2756 | | * Local Variables: |
2757 | | * c-basic-offset: 4 |
2758 | | * tab-width: 8 |
2759 | | * indent-tabs-mode: nil |
2760 | | * End: |
2761 | | * |
2762 | | * ex: set shiftwidth=4 tabstop=8 expandtab: |
2763 | | * :indentSize=4:tabSize=8:noTabs=true: |
2764 | | */ |