2 * Filesystem utility routines
4 * $Id: filesystem.c,v 1.17 2002/03/02 20:48:10 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
44 #ifdef HAVE_SYS_STAT_H
53 #include <direct.h> /* to declare "mkdir()" on Windows */
60 #include "filesystem.h"
63 * Given a pathname, return a pointer to the last pathname separator
64 * character in the pathname, or NULL if the pathname contains no
68 find_last_pathname_separator(char *path)
76 * We have to scan for '\' or '/'.
77 * Get to the end of the string.
79 separator = path + strlen(path); /* points to ending '\0' */
80 while (separator > path) {
82 if (c == '\\' || c == '/')
83 return separator; /* found it */
87 * OK, we didn't find any, so no directories - but there might
88 * be a drive letter....
90 return strchr(path, ':');
92 separator = strrchr(path, '/');
98 * Given a pathname, return the last component.
101 get_basename(char *path)
105 filename = find_last_pathname_separator(path);
106 if (filename == NULL) {
108 * There're no directories, drive letters, etc. in the
109 * name; the pathname *is* the file name.
114 * Skip past the pathname or drive letter separator.
122 * Given a pathname, return a string containing everything but the
123 * last component. NOTE: this overwrites the pathname handed into
127 get_dirname(char *path)
131 separator = find_last_pathname_separator(path);
132 if (separator == NULL) {
134 * There're no directories, drive letters, etc. in the
135 * name; there is no directory path to return.
141 * Get rid of the last pathname separator and the final file
147 * "path" now contains the pathname of the directory containing
148 * the file/directory to which it referred.
154 * Given a pathname, return:
156 * the errno, if an attempt to "stat()" the file fails;
158 * EISDIR, if the attempt succeeded and the file turned out
161 * 0, if the attempt succeeded and the file turned out not
166 * Visual C++ on Win32 systems doesn't define these. (Old UNIX systems don't
167 * define them either.)
169 * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
172 #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
175 #define S_IFIFO _S_IFIFO
178 #define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO)
181 #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
185 test_for_directory(const char *path)
189 if (stat(path, &statb) < 0)
192 if (S_ISDIR(statb.st_mode))
199 * Get the directory in which Ethereal's global configuration and data
203 get_datafile_dir(void)
206 char prog_pathname[_MAX_PATH+2];
208 size_t datafile_dir_len;
209 static char *datafile_dir;
212 * Have we already gotten the pathname?
213 * If so, just return it.
215 if (datafile_dir != NULL)
220 * Start out by assuming it's the default installation directory.
222 datafile_dir = "C:\\Program Files\\Ethereal\\";
225 * Now we attempt to get the full pathname of the currently running
226 * program, under the assumption that we're running an installed
227 * version of the program. If we fail, we don't change "datafile_dir",
228 * and thus end up using DATAFILE_DIR.
230 * XXX - does NSIS put the installation directory into
231 * "\HKEY_LOCAL_MACHINE\SOFTWARE\Ethereal\InstallDir"?
232 * If so, perhaps we should read that from the registry,
235 if (GetModuleFileName(NULL, prog_pathname, sizeof prog_pathname) != 0) {
237 * If the program is an installed version, the full pathname
238 * includes the pathname of the directory in which it was
239 * installed; get that directory's pathname, and construct
240 * from it the pathname of the directory in which the
241 * plugins were installed.
243 * First, find the last "\\" in the directory, as that
244 * marks the end of the directory pathname.
246 * XXX - Can the pathname be something such as
247 * "C:ethereal.exe"? Or is it always a full pathname
248 * beginning with "\\" after the drive letter?
250 dir_end = strrchr(prog_pathname, '\\');
251 if (dir_end != NULL) {
253 * Found it - now figure out how long the datafile
254 * directory pathname will be.
256 datafile_dir_len = (dir_end - prog_pathname);
259 * Allocate a buffer for the plugin directory
260 * pathname, and construct it.
262 datafile_dir = g_malloc(datafile_dir_len + 1);
263 strncpy(datafile_dir, prog_pathname, datafile_dir_len);
264 datafile_dir[datafile_dir_len] = '\0';
270 * Just use DATAFILE_DIR, as that's what the configure script
278 * Get the directory in which files that, at least on UNIX, are
279 * system files (such as "/etc/ethers") are stored; on Windows,
280 * there's no "/etc" directory, so we get them from the Ethereal
281 * global configuration and data file directory.
284 get_systemfile_dir(void)
287 return get_datafile_dir();
294 * Name of directory, under the user's home directory, in which
295 * personal configuration files are stored.
298 #define PF_DIR "Ethereal"
301 * XXX - should this be ".libepan"? For backwards-compatibility, I'll keep
302 * it ".ethereal" for now.
304 #define PF_DIR ".ethereal"
308 * Get the directory in which personal configuration files reside;
309 * in UNIX-compatible systems, it's ".ethereal", under the user's home
310 * directory, and on Windows systems, it's "Ethereal", under %APPDATA%
311 * or, if %APPDATA% isn't set, it's "%USERPROFILE%\Application Data"
312 * (which is what %APPDATA% normally is on Windows 2000).
315 get_persconffile_dir(void)
319 char *userprofiledir;
324 static char *pf_dir = NULL;
326 /* Return the cached value, if available */
332 * Use %APPDATA% or %USERPROFILE%, so that configuration files are
333 * stored in the user profile, rather than in the home directory.
334 * The Windows convention is to store configuration information
335 * in the user profile, and doing so means you can use
336 * Ethereal even if the home directory is an inaccessible
339 appdatadir = getenv("APPDATA");
340 if (appdatadir != NULL) {
342 * Concatenate %APPDATA% with "\Ethereal".
344 pf_dir = g_malloc(strlen(appdatadir) + strlen(PF_DIR) + 2);
345 sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", appdatadir,
349 * OK, %APPDATA% wasn't set, so use
350 * %USERPROFILE%\Application Data.
352 userprofiledir = getenv("USERPROFILE");
353 if (userprofiledir != NULL) {
354 pf_dir = g_malloc(strlen(userprofiledir) +
355 strlen("Application Data") + strlen(PF_DIR) + 3);
357 "%s" G_DIR_SEPARATOR_S "Application Data" G_DIR_SEPARATOR_S "%s",
358 userprofiledir, PF_DIR);
361 * Give up and use "C:".
363 pf_dir = g_malloc(strlen("C:") + strlen(PF_DIR) + 2);
364 sprintf(pf_dir, "C:" G_DIR_SEPARATOR_S "%s", PF_DIR);
369 * If $HOME is set, use that.
371 homedir = getenv("HOME");
372 if (homedir == NULL) {
374 * Get their home directory from the password file.
375 * If we can't even find a password file entry for them,
378 pwd = getpwuid(getuid());
381 * This is cached, so we don't need to worry
382 * about allocating multiple ones of them.
384 homedir = g_strdup(pwd->pw_dir);
388 pf_dir = g_malloc(strlen(homedir) + strlen(PF_DIR) + 2);
389 sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", homedir, PF_DIR);
396 * Create the directory that holds personal configuration files, if
397 * necessary. If we attempted to create it, and failed, return -1 and
398 * set "*pf_dir_path_return" to the pathname of the directory we failed
399 * to create (it's g_mallocated, so our caller should free it); otherwise,
403 create_persconffile_dir(char **pf_dir_path_return)
405 const char *pf_dir_path;
407 char *pf_dir_path_copy, *pf_dir_parent_path;
408 size_t pf_dir_parent_path_len;
413 pf_dir_path = get_persconffile_dir();
414 if (stat(pf_dir_path, &s_buf) != 0 && errno == ENOENT) {
417 * Does the parent directory of that directory
418 * exist? %APPDATA% may not exist even though
419 * %USERPROFILE% does.
421 * We check for the existence of the directory
422 * by first checking whether the parent directory
423 * is just a drive letter and, if it's not, by
424 * doing a "stat()" on it. If it's a drive letter,
425 * or if the "stat()" succeeds, we assume it exists.
427 pf_dir_path_copy = g_strdup(pf_dir_path);
428 pf_dir_parent_path = get_dirname(pf_dir_path_copy);
429 pf_dir_parent_path_len = strlen(pf_dir_parent_path);
430 if (pf_dir_parent_path_len > 0
431 && pf_dir_parent_path[pf_dir_parent_path_len - 1] != ':'
432 && stat(pf_dir_parent_path, &s_buf) != 0) {
434 * No, it doesn't exist - make it first.
436 ret = mkdir(pf_dir_parent_path);
438 *pf_dir_path_return = pf_dir_parent_path;
442 g_free(pf_dir_path_copy);
443 ret = mkdir(pf_dir_path);
445 ret = mkdir(pf_dir_path, 0755);
449 * Something with that pathname exists; if it's not
450 * a directory, we'll get an error if we try to put
451 * something in it, so we don't fail here, we wait
452 * for that attempt fo fail.
457 *pf_dir_path_return = g_strdup(pf_dir_path);
463 * Returns the user's home directory on Win32.
468 static const char *home = NULL;
469 char *homedrive, *homepath;
473 /* Return the cached value, if available */
478 * XXX - should we use USERPROFILE anywhere in this process?
479 * Is there a chance that it might be set but one or more of
480 * HOMEDRIVE or HOMEPATH isn't set?
482 homedrive = getenv("HOMEDRIVE");
483 if (homedrive != NULL) {
484 homepath = getenv("HOMEPATH");
485 if (homepath != NULL) {
487 * This is cached, so we don't need to worry about
488 * allocating multiple ones of them.
491 g_malloc(strlen(homedrive) + strlen(homepath) + 1);
492 strcpy(homestring, homedrive);
493 strcat(homestring, homepath);
496 * Trim off any trailing slash or backslash.
498 lastsep = find_last_pathname_separator(homestring);
499 if (lastsep != NULL && *(lastsep + 1) == '\0') {
501 * Last separator is the last character
502 * in the string. Nuke it.
511 * Give up and use C:.
521 * Construct the path name of a personal configuration file, given the
524 * On Win32, if "for_writing" is FALSE, we check whether the file exists
525 * and, if not, construct a path name relative to the ".ethereal"
526 * subdirectory of the user's home directory, and check whether that
527 * exists; if it does, we return that, so that configuration files
528 * from earlier versions can be read.
531 get_persconffile_path(const char *filename, gboolean for_writing
543 path = (gchar *) g_malloc(strlen(get_persconffile_dir()) +
544 strlen(filename) + 2);
545 sprintf(path, "%s" G_DIR_SEPARATOR_S "%s", get_persconffile_dir(),
549 if (stat(path, &s_buf) != 0 && errno == ENOENT) {
551 * OK, it's not in the personal configuration file
552 * directory; is it in the ".ethereal" subdirectory
553 * of their home directory?
555 old_path = (gchar *) g_malloc(strlen(get_home_dir()) +
556 strlen(".ethereal") + strlen(filename) + 3);
558 "%s" G_DIR_SEPARATOR_S ".ethereal" G_DIR_SEPARATOR_S "%s",
559 get_home_dir(), filename);
560 if (stat(old_path, &s_buf) == 0) {
562 * OK, it exists; return it instead.