various code cleanup:
[obnox/wireshark/wip.git] / epan / filesystem.c
1 /* filesystem.c
2  * Filesystem utility routines
3  *
4  * $Id$
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_STAT_H
41 #include <sys/stat.h>
42 #endif
43
44 #ifdef HAVE_WINDOWS_H
45 #include <windows.h>
46 #endif
47
48 #ifdef HAVE_DIRECT_H
49 #include <direct.h>             /* to declare "mkdir()" on Windows */
50 #endif
51
52 #ifndef _WIN32
53 #include <pwd.h>
54 #endif
55
56 #include "filesystem.h"
57
58 /*
59  * Given a pathname, return a pointer to the last pathname separator
60  * character in the pathname, or NULL if the pathname contains no
61  * separators.
62  */
63 static char *
64 find_last_pathname_separator(const char *path)
65 {
66         char *separator;
67
68 #ifdef _WIN32
69         char c;
70
71         /*
72          * We have to scan for '\' or '/'.
73          * Get to the end of the string.
74          */
75         separator = strchr(path, '\0');         /* points to ending '\0' */
76         while (separator > path) {
77                 c = *--separator;
78                 if (c == '\\' || c == '/')
79                         return separator;       /* found it */
80         }
81
82         /*
83          * OK, we didn't find any, so no directories - but there might
84          * be a drive letter....
85          */
86         return strchr(path, ':');
87 #else
88         separator = strrchr(path, '/');
89 #endif
90         return separator;
91 }
92
93 /*
94  * Given a pathname, return the last component.
95  */
96 const char *
97 get_basename(const char *path)
98 {
99         const char *filename;
100
101         g_assert(path != NULL);
102         filename = find_last_pathname_separator(path);
103         if (filename == NULL) {
104                 /*
105                  * There're no directories, drive letters, etc. in the
106                  * name; the pathname *is* the file name.
107                  */
108                 filename = path;
109         } else {
110                 /*
111                  * Skip past the pathname or drive letter separator.
112                  */
113                 filename++;
114         }
115         return filename;
116 }
117
118 /*
119  * Given a pathname, return a string containing everything but the
120  * last component.  NOTE: this overwrites the pathname handed into
121  * it....
122  */
123 char *
124 get_dirname(char *path)
125 {
126         char *separator;
127
128         g_assert(path != NULL);
129         separator = find_last_pathname_separator(path);
130         if (separator == NULL) {
131                 /*
132                  * There're no directories, drive letters, etc. in the
133                  * name; there is no directory path to return.
134                  */
135                 return NULL;
136         }
137
138         /*
139          * Get rid of the last pathname separator and the final file
140          * name following it.
141          */
142         *separator = '\0';
143
144         /*
145          * "path" now contains the pathname of the directory containing
146          * the file/directory to which it referred.
147          */
148         return path;
149 }
150
151 /*
152  * Given a pathname, return:
153  *
154  *      the errno, if an attempt to "stat()" the file fails;
155  *
156  *      EISDIR, if the attempt succeeded and the file turned out
157  *      to be a directory;
158  *
159  *      0, if the attempt succeeded and the file turned out not
160  *      to be a directory.
161  */
162
163 /*
164  * Visual C++ on Win32 systems doesn't define these.  (Old UNIX systems don't
165  * define them either.)
166  *
167  * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
168  */
169 #ifndef S_ISREG
170 #define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
171 #endif
172 #ifndef S_IFIFO
173 #define S_IFIFO _S_IFIFO
174 #endif
175 #ifndef S_ISFIFO
176 #define S_ISFIFO(mode)  (((mode) & S_IFMT) == S_IFIFO)
177 #endif
178 #ifndef S_ISDIR
179 #define S_ISDIR(mode)   (((mode) & S_IFMT) == S_IFDIR)
180 #endif
181
182 int
183 test_for_directory(const char *path)
184 {
185         struct stat statb;
186
187         if (stat(path, &statb) < 0)
188                 return errno;
189
190         if (S_ISDIR(statb.st_mode))
191                 return EISDIR;
192         else
193                 return 0;
194 }
195
196 int
197 test_for_fifo(const char *path)
198 {
199         struct stat statb;
200
201         if (stat(path, &statb) < 0)
202                 return errno;
203
204         if (S_ISFIFO(statb.st_mode))
205                 return ESPIPE;
206         else
207                 return 0;
208 }
209
210 /*
211  * Get the directory in which Ethereal's global configuration and data
212  * files are stored.
213  *
214  * XXX - if we ever make libethereal a real library, used by multiple
215  * applications (more than just Tethereal and versions of Ethereal with
216  * various UIs), should the configuration files belong to the library
217  * (and be shared by all those applications) or to the applications?
218  *
219  * If they belong to the library, that could be done on UNIX by the
220  * configure script, but it's trickier on Windows, as you can't just
221  * use the pathname of the executable.
222  *
223  * If they belong to the application, that could be done on Windows
224  * by using the pathname of the executable, but we'd have to have it
225  * passed in as an argument, in some call, on UNIX.
226  *
227  * Note that some of those configuration files might be used by code in
228  * libethereal, some of them might be used by dissectors (would they
229  * belong to libethereal, the application, or a separate library?),
230  * and some of them might be used by other code (the Ethereal preferences
231  * file includes resolver preferences that control the behavior of code
232  * in libethereal, dissector preferences, and UI preferences, for
233  * example).
234  */
235 const char *
236 get_datafile_dir(void)
237 {
238 #ifdef _WIN32
239         char prog_pathname[_MAX_PATH+2];
240         char *dir_end;
241         size_t datafile_dir_len;
242         static char *datafile_dir;
243
244         /*
245          * Have we already gotten the pathname?
246          * If so, just return it.
247          */
248         if (datafile_dir != NULL)
249                 return datafile_dir;
250
251         /*
252          * No, we haven't.
253          * Start out by assuming it's the default installation directory.
254          */
255         datafile_dir = "C:\\Program Files\\Ethereal\\";
256
257         /*
258          * Now we attempt to get the full pathname of the currently running
259          * program, under the assumption that we're running an installed
260          * version of the program.  If we fail, we don't change "datafile_dir",
261          * and thus end up using the default.
262          *
263          * XXX - does NSIS put the installation directory into
264          * "\HKEY_LOCAL_MACHINE\SOFTWARE\Ethereal\InstallDir"?
265          * If so, perhaps we should read that from the registry,
266          * instead.
267          */
268         if (GetModuleFileName(NULL, prog_pathname, sizeof prog_pathname) != 0) {
269                 /*
270                  * If the program is an installed version, the full pathname
271                  * includes the pathname of the directory in which it was
272                  * installed; get that directory's pathname, and construct
273                  * from it the pathname of the directory in which the
274                  * plugins were installed.
275                  *
276                  * First, find the last "\\" in the directory, as that
277                  * marks the end of the directory pathname.
278                  *
279                  * XXX - Can the pathname be something such as
280                  * "C:ethereal.exe"?  Or is it always a full pathname
281                  * beginning with "\\" after the drive letter?
282                  */
283                 dir_end = strrchr(prog_pathname, '\\');
284                 if (dir_end != NULL) {
285                         /*
286                          * Found it - now figure out how long the datafile
287                          * directory pathname will be.
288                          */
289                         datafile_dir_len = (dir_end - prog_pathname);
290
291                         /*
292                          * Allocate a buffer for the plugin directory
293                          * pathname, and construct it.
294                          */
295                         datafile_dir = g_malloc(datafile_dir_len + 1);
296                         strncpy(datafile_dir, prog_pathname, datafile_dir_len);
297                         datafile_dir[datafile_dir_len] = '\0';
298                 }
299         }
300         return datafile_dir;
301 #else
302         /*
303          * Just use DATAFILE_DIR, as that's what the configure script
304          * set it to be.
305          */
306         return DATAFILE_DIR;
307 #endif
308 }
309
310 /*
311  * Get the directory in which files that, at least on UNIX, are
312  * system files (such as "/etc/ethers") are stored; on Windows,
313  * there's no "/etc" directory, so we get them from the Ethereal
314  * global configuration and data file directory.
315  */
316 const char *
317 get_systemfile_dir(void)
318 {
319 #ifdef _WIN32
320         return get_datafile_dir();
321 #else
322         return "/etc";
323 #endif
324 }
325
326 /*
327  * Name of directory, under the user's home directory, in which
328  * personal configuration files are stored.
329  */
330 #ifdef _WIN32
331 #define PF_DIR "Ethereal"
332 #else
333 /*
334  * XXX - should this be ".libepan"? For backwards-compatibility, I'll keep
335  * it ".ethereal" for now.
336  */
337 #define PF_DIR ".ethereal"
338 #endif
339
340 /*
341  * Get the directory in which personal configuration files reside;
342  * in UNIX-compatible systems, it's ".ethereal", under the user's home
343  * directory, and on Windows systems, it's "Ethereal", under %APPDATA%
344  * or, if %APPDATA% isn't set, it's "%USERPROFILE%\Application Data"
345  * (which is what %APPDATA% normally is on Windows 2000).
346  */
347 static const char *
348 get_persconffile_dir(void)
349 {
350 #ifdef _WIN32
351         char *appdatadir;
352         char *userprofiledir;
353 #else
354         const char *homedir;
355         struct passwd *pwd;
356 #endif
357         static char *pf_dir = NULL;
358
359         /* Return the cached value, if available */
360         if (pf_dir != NULL)
361                 return pf_dir;
362
363 #ifdef _WIN32
364         /*
365          * Use %APPDATA% or %USERPROFILE%, so that configuration files are
366          * stored in the user profile, rather than in the home directory.
367          * The Windows convention is to store configuration information
368          * in the user profile, and doing so means you can use
369          * Ethereal even if the home directory is an inaccessible
370          * network drive.
371          */
372         appdatadir = getenv("APPDATA");
373         if (appdatadir != NULL) {
374                 /*
375                  * Concatenate %APPDATA% with "\Ethereal".
376                  */
377                 pf_dir = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", 
378                         appdatadir, PF_DIR);
379         } else {
380                 /*
381                  * OK, %APPDATA% wasn't set, so use
382                  * %USERPROFILE%\Application Data.
383                  */
384                 userprofiledir = getenv("USERPROFILE");
385                 if (userprofiledir != NULL) {
386                         pf_dir = g_strdup_printf(
387                             "%s" G_DIR_SEPARATOR_S "Application Data" G_DIR_SEPARATOR_S "%s",
388                             userprofiledir, PF_DIR);
389                 } else {
390                         /*
391                          * Give up and use "C:".
392                          */
393                         pf_dir = g_strdup_printf("C:" G_DIR_SEPARATOR_S "%s", PF_DIR);
394                 }
395         }
396 #else
397         /*
398          * If $HOME is set, use that.
399          */
400         homedir = getenv("HOME");
401         if (homedir == NULL) {
402                 /*
403                  * Get their home directory from the password file.
404                  * If we can't even find a password file entry for them,
405                  * use "/tmp".
406                  */
407                 pwd = getpwuid(getuid());
408                 if (pwd != NULL) {
409                         /*
410                          * This is cached, so we don't need to worry
411                          * about allocating multiple ones of them.
412                          */
413                         homedir = g_strdup(pwd->pw_dir);
414                 } else
415                         homedir = "/tmp";
416         }
417         pf_dir = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", homedir, PF_DIR);
418 #endif
419
420         return pf_dir;
421 }
422
423 /*
424  * Create the directory that holds personal configuration files, if
425  * necessary.  If we attempted to create it, and failed, return -1 and
426  * set "*pf_dir_path_return" to the pathname of the directory we failed
427  * to create (it's g_mallocated, so our caller should free it); otherwise,
428  * return 0.
429  */
430 int
431 create_persconffile_dir(char **pf_dir_path_return)
432 {
433         const char *pf_dir_path;
434 #ifdef _WIN32
435         char *pf_dir_path_copy, *pf_dir_parent_path;
436         size_t pf_dir_parent_path_len;
437 #endif
438         struct stat s_buf;
439         int ret;
440
441         pf_dir_path = get_persconffile_dir();
442         if (stat(pf_dir_path, &s_buf) != 0 && errno == ENOENT) {
443 #ifdef _WIN32
444                 /*
445                  * Does the parent directory of that directory
446                  * exist?  %APPDATA% may not exist even though
447                  * %USERPROFILE% does.
448                  *
449                  * We check for the existence of the directory
450                  * by first checking whether the parent directory
451                  * is just a drive letter and, if it's not, by
452                  * doing a "stat()" on it.  If it's a drive letter,
453                  * or if the "stat()" succeeds, we assume it exists.
454                  */
455                 pf_dir_path_copy = g_strdup(pf_dir_path);
456                 pf_dir_parent_path = get_dirname(pf_dir_path_copy);
457                 pf_dir_parent_path_len = strlen(pf_dir_parent_path);
458                 if (pf_dir_parent_path_len > 0
459                     && pf_dir_parent_path[pf_dir_parent_path_len - 1] != ':'
460                     && stat(pf_dir_parent_path, &s_buf) != 0) {
461                         /*
462                          * No, it doesn't exist - make it first.
463                          */
464                         ret = mkdir(pf_dir_parent_path);
465                         if (ret == -1) {
466                                 *pf_dir_path_return = pf_dir_parent_path;
467                                 return -1;
468                         }
469                 }
470                 g_free(pf_dir_path_copy);
471                 ret = mkdir(pf_dir_path);
472 #else
473                 ret = mkdir(pf_dir_path, 0755);
474 #endif
475         } else {
476                 /*
477                  * Something with that pathname exists; if it's not
478                  * a directory, we'll get an error if we try to put
479                  * something in it, so we don't fail here, we wait
480                  * for that attempt fo fail.
481                  */
482                 ret = 0;
483         }
484         if (ret == -1)
485                 *pf_dir_path_return = g_strdup(pf_dir_path);
486         return ret;
487 }
488
489 #ifdef _WIN32
490 /*
491  * Returns the user's home directory on Win32.
492  */
493 static const char *
494 get_home_dir(void)
495 {
496         static const char *home = NULL;
497         char *homedrive, *homepath;
498         char *homestring;
499         char *lastsep;
500
501         /* Return the cached value, if available */
502         if (home)
503                 return home;
504
505         /*
506          * XXX - should we use USERPROFILE anywhere in this process?
507          * Is there a chance that it might be set but one or more of
508          * HOMEDRIVE or HOMEPATH isn't set?
509          */
510         homedrive = getenv("HOMEDRIVE");
511         if (homedrive != NULL) {
512                 homepath = getenv("HOMEPATH");
513                 if (homepath != NULL) {
514                         /*
515                          * This is cached, so we don't need to worry about
516                          * allocating multiple ones of them.
517                          */
518                         homestring =
519                             g_malloc(strlen(homedrive) + strlen(homepath) + 1);
520                         strcpy(homestring, homedrive);
521                         strcat(homestring, homepath);
522
523                         /*
524                          * Trim off any trailing slash or backslash.
525                          */
526                         lastsep = find_last_pathname_separator(homestring);
527                         if (lastsep != NULL && *(lastsep + 1) == '\0') {
528                                 /*
529                                  * Last separator is the last character
530                                  * in the string.  Nuke it.
531                                  */
532                                 *lastsep = '\0';
533                         }
534                         home = homestring;
535                 } else
536                         home = homedrive;
537         } else {
538                 /*
539                  * Give up and use C:.
540                  */
541                 home = "C:";
542         }
543
544         return home;
545 }
546 #endif
547
548 /*
549  * Construct the path name of a personal configuration file, given the
550  * file name.
551  *
552  * On Win32, if "for_writing" is FALSE, we check whether the file exists
553  * and, if not, construct a path name relative to the ".ethereal"
554  * subdirectory of the user's home directory, and check whether that
555  * exists; if it does, we return that, so that configuration files
556  * from earlier versions can be read.
557  */
558 char *
559 get_persconffile_path(const char *filename, gboolean for_writing
560 #ifndef _WIN32
561         _U_
562 #endif
563 )
564 {
565         char *path;
566 #ifdef _WIN32
567         struct stat s_buf;
568         char *old_path;
569 #endif
570
571         path = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", get_persconffile_dir(),
572             filename);
573 #ifdef _WIN32
574         if (!for_writing) {
575                 if (stat(path, &s_buf) != 0 && errno == ENOENT) {
576                         /*
577                          * OK, it's not in the personal configuration file
578                          * directory; is it in the ".ethereal" subdirectory
579                          * of their home directory?
580                          */
581                         old_path = g_strdup_printf(
582                             "%s" G_DIR_SEPARATOR_S ".ethereal" G_DIR_SEPARATOR_S "%s",
583                             get_home_dir(), filename);
584                         if (stat(old_path, &s_buf) == 0) {
585                                 /*
586                                  * OK, it exists; return it instead.
587                                  */
588                                 g_free(path);
589                                 path = old_path;
590                         }
591                 }
592         }
593 #endif
594
595         return path;
596 }
597
598 /*
599  * Construct the path name of a global configuration file, given the
600  * file name.
601  */
602 char *
603 get_datafile_path(const char *filename)
604 {
605
606         return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", get_datafile_dir(),
607             filename);
608 }
609
610 /* Delete a file */
611 gboolean
612 deletefile(const char *path)
613 {
614         return unlink(path) == 0;
615 }
616
617 /*
618  * Construct and return the path name of a file in the
619  * appropriate temporary file directory.
620  */
621 char *get_tempfile_path(const char *filename)
622 {
623
624         return g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", g_get_tmp_dir(), filename);
625 }
626
627 /*
628  * Return an error message for UNIX-style errno indications on open or
629  * create operations.
630  */
631 const char *
632 file_open_error_message(int err, gboolean for_writing)
633 {
634         const char *errmsg;
635         static char errmsg_errno[1024+1];
636
637         switch (err) {
638
639         case ENOENT:
640                 if (for_writing)
641                         errmsg = "The path to the file \"%s\" doesn't exist.";
642                 else
643                         errmsg = "The file \"%s\" doesn't exist.";
644                 break;
645
646         case EACCES:
647                 if (for_writing)
648                         errmsg = "You don't have permission to create or write to the file \"%s\".";
649                 else
650                         errmsg = "You don't have permission to read the file \"%s\".";
651                 break;
652
653         case EISDIR:
654                 errmsg = "\"%s\" is a directory (folder), not a file.";
655                 break;
656
657         case ENOSPC:
658                 errmsg = "The file \"%s\" could not be created because there is no space left on the file system.";
659                 break;
660
661 #ifdef EDQUOT
662         case EDQUOT:
663                 errmsg = "The file \"%s\" could not be created because you are too close to, or over, your disk quota.";
664                 break;
665 #endif
666
667         default:
668                 g_snprintf(errmsg_errno, sizeof(errmsg_errno),
669                                 "The file \"%%s\" could not be %s: %s.",
670                                 for_writing ? "created" : "opened",
671                                 strerror(err));
672                 errmsg = errmsg_errno;
673                 break;
674         }
675         return errmsg;
676 }
677
678 /*
679  * Return an error message for UNIX-style errno indications on write
680  * operations.
681  */
682 const char *
683 file_write_error_message(int err)
684 {
685         const char *errmsg;
686         static char errmsg_errno[1024+1];
687
688         switch (err) {
689
690         case ENOSPC:
691                 errmsg = "The file \"%s\" could not be saved because there is no space left on the file system.";
692                 break;
693
694 #ifdef EDQUOT
695         case EDQUOT:
696                 errmsg = "The file \"%s\" could not be saved because you are too close to, or over, your disk quota.";
697                 break;
698 #endif
699
700         default:
701                 g_snprintf(errmsg_errno, sizeof(errmsg_errno),
702                     "An error occurred while writing to the file \"%%s\": %s.",
703                     strerror(err));
704                 errmsg = errmsg_errno;
705                 break;
706         }
707         return errmsg;
708 }
709
710
711 gboolean
712 file_exists(const char *fname)
713 {
714   struct stat   file_stat;
715
716
717   /*
718    * This is a bit tricky on win32. The st_ino field is documented as:
719    * "The inode, and therefore st_ino, has no meaning in the FAT, ..."
720    * but it *is* set to zero if stat() returns without an error,
721    * so this is working, but maybe not quite the way expected. ULFL
722    */
723    file_stat.st_ino = 1;   /* this will make things work if an error occured */
724    stat(fname, &file_stat);
725    if (file_stat.st_ino == 0) {
726        return TRUE;
727    } else {
728        return FALSE;
729    }
730
731 }
732
733
734 gboolean
735 files_identical(const char *fname1, const char *fname2)
736 {
737     /* Two different implementations, because:
738      * - _fullpath is not available on unix 
739      * - the stat inode will not work as expected on Win32, so two different implementations.
740      *
741      * XXX - will _fullpath work with UNC?
742      */
743 #ifdef _WIN32
744     char full1[MAX_PATH], full2[MAX_PATH];
745
746
747     if( _fullpath( full1, fname1, MAX_PATH ) == NULL ) {
748         return FALSE;
749     }
750
751     if( _fullpath( full2, fname2, MAX_PATH ) == NULL ) {
752         return FALSE;
753     }
754     
755     if(strcmp(full1, full2) == 0) {
756         return TRUE;
757     } else {
758         return FALSE;
759     }
760 #else
761   struct stat   infile, outfile;
762
763   /*
764    * Check that the from file is not the same as to file
765    * We do it here so we catch all cases ...
766    * Unfortunately, the file requester gives us an absolute file
767    * name and the read file name may be relative (if supplied on
768    * the command line). From Joerg Mayer.
769    *
770    * This is a bit tricky on win32. The st_ino field is documented as:
771    * "The inode, and therefore st_ino, has no meaning in the FAT, ..."
772    * but it *is* set to zero if stat() returns without an error,
773    * so this is not working, as it only checks if both files existing. ULFL
774    */
775    infile.st_ino = 1;   /* These prevent us from getting equality         */
776    outfile.st_ino = 2;  /* If one or other of the files is not accessible */
777    stat(fname1, &infile);
778    stat(fname2, &outfile);
779    if (infile.st_ino == outfile.st_ino) {
780        return TRUE;
781    } else {
782        return FALSE;
783    }
784
785 #endif
786 }
787