a27016e0bf8d2d3419433ee0f00cdc8c479cdb0c
[obnox/wireshark/wip.git] / epan / filesystem.c
1 /* filesystem.c
2  * Filesystem utility routines
3  *
4  * $Id: filesystem.c,v 1.12 2001/10/23 08:15:11 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  * XXX - should this be ".libepan"? For backwards-compatibility, I'll keep
298  * it ".ethereal" for now.
299  */
300 #define PF_DIR ".ethereal"
301
302 /*
303  * Get the directory in which personal configuration files reside;
304  * it's PF_DIR, under the user's home directory.
305  */
306 const char *
307 get_persconffile_dir(void)
308 {
309 #ifndef WIN32
310         struct passwd *pwd;
311 #endif
312         char *homedir;
313         static char *pf_dir = NULL;
314
315         /* Return the cached value, if available */
316         if (pf_dir != NULL)
317                 return pf_dir;
318
319 #ifdef WIN32
320         /*
321          * Use %USERPROFILE%, so that configuration files are stored
322          * in the user profile, rather than in the home directory.
323          * The Windows convention is to store configuration information
324          * in the user profile, and doing so means you can use
325          * Ethereal even if the home directory is an inaccessible
326          * network drive.
327          */
328         homedir = getenv("USERPROFILE");
329         if (homedir == NULL) {
330                 /*
331                  * Give up and use "C:".
332                  */
333                 homedir = "C:";
334         }
335 #else
336         /*
337          * If $HOME is set, use that.
338          */
339         homedir = getenv("HOME");
340         if (homedir == NULL) {
341                 /*
342                  * Get their home directory from the password file.
343                  * If we can't even find a password file entry for them,
344                  * use "/tmp".
345                  */
346                 pwd = getpwuid(getuid());
347                 if (pwd != NULL) {
348                         /*
349                          * This is cached, so we don't need to worry
350                          * about allocating multiple ones of them.
351                          */
352                         homedir = g_strdup(pwd->pw_dir);
353                 } else
354                         homedir = "/tmp";
355         }
356 #endif
357
358         pf_dir = g_malloc(strlen(homedir) + strlen(PF_DIR) + 2);
359         sprintf(pf_dir, "%s" G_DIR_SEPARATOR_S "%s", homedir, PF_DIR);
360         return pf_dir;
361 }
362
363 /*
364  * Create the directory that holds personal configuration files, if
365  * necessary.  If we attempted to create it, and failed, return -1 and
366  * set "*pf_dir_path_return" to the pathname of the directory; otherwise,
367  * return 0.
368  */
369 int
370 create_persconffile_dir(const char **pf_dir_path_return)
371 {
372         const char *pf_dir_path;
373         struct stat s_buf;
374         int ret;
375
376         pf_dir_path = get_persconffile_dir();
377         if (stat(pf_dir_path, &s_buf) != 0) {
378 #ifdef WIN32
379                 ret = mkdir(pf_dir_path);
380 #else
381                 ret = mkdir(pf_dir_path, 0755);
382 #endif
383         } else {
384                 /*
385                  * Something with that pathname exists; if it's not
386                  * a directory, we'll get an error if we try to put
387                  * something in it, so we don't fail here, we wait
388                  * for that attempt fo fail.
389                  */
390                 ret = 0;
391         }
392         if (ret == -1)
393                 *pf_dir_path_return = pf_dir_path;
394         return ret;
395 }