On Windows, put Ethereal configuration files under the "Application
[obnox/wireshark/wip.git] / epan / filesystem.c
1 /* filesystem.c
2  * Filesystem utility routines
3  *
4  * $Id: filesystem.c,v 1.13 2001/10/24 06:13:05 guy Exp $
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  * 
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.
14  * 
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.
19  * 
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.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include <glib.h>
35
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #ifdef HAVE_SYS_TYPES_H
41 #include <sys/types.h>
42 #endif
43
44 #ifdef HAVE_SYS_STAT_H
45 #include <sys/stat.h>
46 #endif
47
48 #ifdef HAVE_WINDOWS_H
49 #include <windows.h>
50 #endif
51
52 #ifdef HAVE_DIRECT_H
53 #include <direct.h>             /* to declare "mkdir()" on Windows */
54 #endif
55
56 #ifndef WIN32
57 #include <pwd.h>
58 #endif
59
60 #include "filesystem.h"
61
62 /*
63  * Given a pathname, return a pointer to the last pathname separator
64  * character in the pathname, or NULL if the pathname contains no
65  * separators.
66  */
67 char *
68 find_last_pathname_separator(char *path)
69 {
70         char *separator;
71
72 #ifdef WIN32
73         char c;
74
75         /*
76          * We have to scan for '\' or '/'.
77          * Get to the end of the string.
78          */
79         separator = path + strlen(path);        /* points to ending '\0' */
80         while (separator > path) {
81                 c = *--separator;
82                 if (c == '\\' || c == '/')
83                         return separator;       /* found it */
84         }
85
86         /*
87          * OK, we didn't find any, so no directories - but there might
88          * be a drive letter....
89          */
90         return strchr(path, ':');
91 #else
92         separator = strrchr(path, '/');
93 #endif
94         return separator;
95 }
96
97 /*
98  * Given a pathname, return the last component.
99  */
100 char *
101 get_basename(char *path)
102 {
103         char *filename;
104
105         filename = find_last_pathname_separator(path);
106         if (filename == NULL) {
107                 /*
108                  * There're no directories, drive letters, etc. in the
109                  * name; the pathname *is* the file name.
110                  */
111                 filename = path;
112         } else {
113                 /*
114                  * Skip past the pathname or drive letter separator.
115                  */
116                 filename++;
117         }
118         return filename;
119 }
120
121 /*
122  * Given a pathname, return a string containing everything but the
123  * last component.  NOTE: this overwrites the pathname handed into
124  * it....
125  */
126 char *
127 get_dirname(char *path)
128 {
129         char *separator;
130
131         separator = find_last_pathname_separator(path);
132         if (separator == NULL) {
133                 /*
134                  * There're no directories, drive letters, etc. in the
135                  * name; there is no directory path to return.
136                  */
137                 return NULL;
138         }
139
140         /*
141          * Get rid of the last pathname separator and the final file
142          * name following it.
143          */
144         *separator = '\0';
145
146         /*
147          * "path" now contains the pathname of the directory containing
148          * the file/directory to which it referred.
149          */
150         return path;
151 }
152
153 /*
154  * Given a pathname, return:
155  *
156  *      the errno, if an attempt to "stat()" the file fails;
157  *
158  *      EISDIR, if the attempt succeeded and the file turned out
159  *      to be a directory;
160  *
161  *      0, if the attempt succeeded and the file turned out not
162  *      to be a directory.
163  */
164
165 /*
166  * Visual C++ on Win32 systems doesn't define these.  (Old UNIX systems don't
167  * define them either.)
168  *
169  * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
170  */
171 #ifndef S_ISREG
172 #define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
173 #endif
174 #ifndef S_IFIFO
175 #define S_IFIFO _S_IFIFO
176 #endif
177 #ifndef S_ISFIFO
178 #define S_ISFIFO(mode)  (((mode) & S_IFMT) == S_IFIFO)
179 #endif
180 #ifndef S_ISDIR
181 #define S_ISDIR(mode)   (((mode) & S_IFMT) == S_IFDIR)
182 #endif
183
184 int
185 test_for_directory(const char *path)
186 {
187         struct stat statb;
188
189         if (stat(path, &statb) < 0)
190                 return errno;
191
192         if (S_ISDIR(statb.st_mode))
193                 return EISDIR;
194         else
195                 return 0;
196 }
197
198 /*
199  * Get the directory in which Ethereal's global configuration and data
200  * files are stored.
201  */
202 const char *
203 get_datafile_dir(void)
204 {
205 #ifdef WIN32
206         char prog_pathname[_MAX_PATH+2];
207         char *dir_end;
208         size_t datafile_dir_len;
209         static char *datafile_dir;
210
211         /*
212          * Have we already gotten the pathname?
213          * If so, just return it.
214          */
215         if (datafile_dir != NULL)
216                 return datafile_dir;
217
218         /*
219          * No, we haven't.
220          * Start out by assuming it's the default installation directory.
221          */
222         datafile_dir = "C:\\Program Files\\Ethereal\\";
223
224         /*
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.
229          *
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,
233          * instead.
234          */
235         if (GetModuleFileName(NULL, prog_pathname, sizeof prog_pathname) != 0) {
236                 /*
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.
242                  *
243                  * First, find the last "\\" in the directory, as that
244                  * marks the end of the directory pathname.
245                  *
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?
249                  */
250                 dir_end = strrchr(prog_pathname, '\\');
251                 if (dir_end != NULL) {
252                         /*
253                          * Found it - now figure out how long the datafile
254                          * directory pathname will be.
255                          */
256                         datafile_dir_len = (dir_end - prog_pathname);
257
258                         /*
259                          * Allocate a buffer for the plugin directory
260                          * pathname, and construct it.
261                          */
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';
265                 }
266         }
267         return datafile_dir;
268 #else
269         /*
270          * Just use DATAFILE_DIR, as that's what the configure script
271          * set it to be.
272          */
273         return DATAFILE_DIR;
274 #endif
275 }
276
277 /*
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.
282  */
283 const char *
284 get_systemfile_dir(void)
285 {
286 #ifdef WIN32
287         return get_datafile_dir();
288 #else
289         return "/etc";
290 #endif
291 }
292
293 /*
294  * Name of directory, under the user's home directory, in which
295  * personal configuration files are stored.
296  */
297 #ifdef WIN32
298 #define PF_DIR "Ethereal"
299 #else
300 /*
301  * XXX - should this be ".libepan"? For backwards-compatibility, I'll keep
302  * it ".ethereal" for now.
303  */
304 #define PF_DIR ".ethereal"
305 #endif
306
307 /*
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).
313  */
314 const char *
315 get_persconffile_dir(void)
316 {
317 #ifdef WIN32
318         char *appdatadir;
319         char *userprofiledir;
320 #else
321         char *homedir;
322         struct passwd *pwd;
323 #endif
324         static char *pf_dir = NULL;
325
326         /* Return the cached value, if available */
327         if (pf_dir != NULL)
328                 return pf_dir;
329
330 #ifdef WIN32
331         /*
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
337          * network drive.
338          */
339         appdatadir = getenv("APPDATA");
340         if (appdatadir != NULL) {
341                 /*
342                  * Concatenate %APPDATA% with "\Ethereal".
343                  */
344                 pf_dir = g_malloc(strlen(appdatadir) + strlen(PF_DIR) + 2);
345                 sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", appdatadir,
346                     PF_DIR);
347         } else {
348                 /*
349                  * OK, %APPDATA% wasn't set, so use
350                  * %USERPROFILE%\Application Data.
351                  */
352                 userprofiledir = getenv("USERPROFILE");
353                 if (userprofiledir != NULL) {
354                         pf_dir = g_malloc(strlen(userprofiledir) +
355                            strlen("Application Data" + strlen(PF_DIR) + 3);
356                         sprintf(pf_dir,
357                             "%s" G_DIR_SEPARATOR_S "Application Data" G_DIR_SEPARATOR_S "%s",
358                             userprofiledir, PF_DIR);
359                 } else {
360                         /*
361                          * Give up and use "C:".
362                          */
363                         pf_dir = g_malloc(strlen("C:") + strlen(PF_DIR) + 2);
364                         sprintf(pf_dir, "C:" G_DIR_SEPARATOR_S "%s", PF_DIR);
365                 }
366         }
367 #else
368         /*
369          * If $HOME is set, use that.
370          */
371         homedir = getenv("HOME");
372         if (homedir == NULL) {
373                 /*
374                  * Get their home directory from the password file.
375                  * If we can't even find a password file entry for them,
376                  * use "/tmp".
377                  */
378                 pwd = getpwuid(getuid());
379                 if (pwd != NULL) {
380                         /*
381                          * This is cached, so we don't need to worry
382                          * about allocating multiple ones of them.
383                          */
384                         homedir = g_strdup(pwd->pw_dir);
385                 } else
386                         homedir = "/tmp";
387         }
388         pf_dir = g_malloc(strlen(homedir) + strlen(PF_DIR) + 2);
389         sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", homedir, PF_DIR);
390 #endif
391
392         return pf_dir;
393 }
394
395 /*
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,
400  * return 0.
401  */
402 int
403 create_persconffile_dir(char **pf_dir_path_return)
404 {
405         const char *pf_dir_path;
406         struct stat s_buf;
407         int ret;
408
409         pf_dir_path = get_persconffile_dir();
410         if (stat(pf_dir_path, &s_buf) != 0 && errno == ENOENT) {
411 #ifdef WIN32
412                 /*
413                  * Does the parent directory of that directory
414                  * exist?  %APPDATA% may not exist even though
415                  * %USERPROFILE% does.
416                  */
417                 pf_dir_path_copy = g_strdup(pf_dir_path);
418                 pf_dir_parent_path = get_dirname(pf_dir_path_copy);
419                 if (stat(pf_dir_parent_path, &s_buf) != 0) {
420                         /*
421                          * No - make it first.
422                          */
423                         ret = mkdir(pf_dir_parent_path);
424                         if (ret == -1) {
425                                 *pf_dir_path_return = pf_dir_parent_path;
426                                 return -1;
427                         }
428                 }
429                 g_free(pf_dir_path_copy);
430                 ret = mkdir(pf_dir_path);
431 #else
432                 ret = mkdir(pf_dir_path, 0755);
433 #endif
434         } else {
435                 /*
436                  * Something with that pathname exists; if it's not
437                  * a directory, we'll get an error if we try to put
438                  * something in it, so we don't fail here, we wait
439                  * for that attempt fo fail.
440                  */
441                 ret = 0;
442         }
443         if (ret == -1)
444                 *pf_dir_path_return = g_strdup(pf_dir_path);
445         return ret;
446 }