Re-apply r40728 and fix Coverity CID 1371 UNINIT again.
[obnox/wireshark/wip.git] / wsutil / file_util.c
1 /* file_util.c
2  *
3  * $Id$
4  *
5  * (Originally part of the Wiretap Library, now part of the Wireshark
6  *  utility library)
7  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  */
24
25 /* file wrapper functions to prevent the file functions from GLib like g_open(),
26  * as code compiled with MSVC 7 and above will collide with libs linked with msvcrt.dll (MSVC 6), lib GLib is
27  *
28  * DO NOT USE THESE FUNCTIONS DIRECTLY, USE ws_open() AND ALIKE FUNCTIONS FROM file_util.h INSTEAD!!!
29  *
30  * the following code is stripped down code copied from the GLib file glib/gstdio.h
31  * stipped down, because this is used on _WIN32 only and we use only wide char functions */
32
33 #ifndef _WIN32
34 #error "This is only for Windows"
35 #endif
36
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40
41 #include <glib.h>
42
43 #include <windows.h>
44 #include <errno.h>
45 #include <wchar.h>
46 #include <tchar.h>
47 /*#include <direct.h>*/
48 #include <io.h>
49 #include <stdlib.h>
50
51 #include "file_util.h"
52
53 static gchar *program_path = NULL;
54 static gchar *system_path = NULL;
55
56 /**
57  * g_open:
58  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
59  * @flags: as in open()
60  * @mode: as in open()
61  *
62  * A wrapper for the POSIX open() function. The open() function is
63  * used to convert a pathname into a file descriptor. Note that on
64  * POSIX systems file descriptors are implemented by the operating
65  * system. On Windows, it's the C library that implements open() and
66  * file descriptors. The actual Windows API for opening files is
67  * something different.
68  *
69  * See the C library manual for more details about open().
70  *
71  * Returns: a new file descriptor, or -1 if an error occurred. The
72  * return value can be used exactly like the return value from open().
73  *
74  * Since: 2.6
75  */
76 int
77 ws_stdio_open (const gchar *filename,
78         int          flags,
79         int          mode)
80 {
81       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
82       int retval;
83       int save_errno;
84
85       if (wfilename == NULL)
86         {
87           errno = EINVAL;
88           return -1;
89         }
90
91       retval = _wopen (wfilename, flags, mode);
92       save_errno = errno;
93
94       g_free (wfilename);
95
96       errno = save_errno;
97       return retval;
98 }
99
100
101 /**
102  * g_rename:
103  * @oldfilename: a pathname in the GLib file name encoding (UTF-8 on Windows)
104  * @newfilename: a pathname in the GLib file name encoding
105  *
106  * A wrapper for the POSIX rename() function. The rename() function
107  * renames a file, moving it between directories if required.
108  *
109  * See your C library manual for more details about how rename() works
110  * on your system. Note in particular that on Win9x it is not possible
111  * to rename a file if a file with the new name already exists. Also
112  * it is not possible in general on Windows to rename an open file.
113  *
114  * Returns: 0 if the renaming succeeded, -1 if an error occurred
115  *
116  * Since: 2.6
117  */
118 int
119 ws_stdio_rename (const gchar *oldfilename,
120           const gchar *newfilename)
121 {
122       wchar_t *woldfilename = g_utf8_to_utf16 (oldfilename, -1, NULL, NULL, NULL);
123       wchar_t *wnewfilename;
124       int retval;
125       int save_errno = 0;
126
127       if (woldfilename == NULL)
128         {
129           errno = EINVAL;
130           return -1;
131         }
132
133       wnewfilename = g_utf8_to_utf16 (newfilename, -1, NULL, NULL, NULL);
134
135       if (wnewfilename == NULL)
136         {
137           g_free (woldfilename);
138           errno = EINVAL;
139           return -1;
140         }
141
142       if (MoveFileExW (woldfilename, wnewfilename, MOVEFILE_REPLACE_EXISTING))
143         retval = 0;
144       else
145         {
146           retval = -1;
147           switch (GetLastError ())
148             {
149 #define CASE(a,b) case ERROR_##a: save_errno = b; break
150             CASE (FILE_NOT_FOUND, ENOENT);
151             CASE (PATH_NOT_FOUND, ENOENT);
152             CASE (ACCESS_DENIED, EACCES);
153             CASE (NOT_SAME_DEVICE, EXDEV);
154             CASE (LOCK_VIOLATION, EACCES);
155             CASE (SHARING_VIOLATION, EACCES);
156             CASE (FILE_EXISTS, EEXIST);
157             CASE (ALREADY_EXISTS, EEXIST);
158 #undef CASE
159             default: save_errno = EIO;
160             }
161         }
162
163       g_free (woldfilename);
164       g_free (wnewfilename);
165
166       errno = save_errno;
167       return retval;
168 }
169
170 /**
171  * g_mkdir:
172  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
173  * @mode: permissions to use for the newly created directory
174  *
175  * A wrapper for the POSIX mkdir() function. The mkdir() function
176  * attempts to create a directory with the given name and permissions.
177  *
178  * See the C library manual for more details about mkdir().
179  *
180  * Returns: 0 if the directory was successfully created, -1 if an error
181  *    occurred
182  *
183  * Since: 2.6
184  */
185 int
186 ws_stdio_mkdir (const gchar *filename,
187          int          mode)
188 {
189       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
190       int retval;
191       int save_errno;
192
193       if (wfilename == NULL)
194         {
195           errno = EINVAL;
196           return -1;
197         }
198
199       retval = _wmkdir (wfilename);
200       save_errno = errno;
201
202       g_free (wfilename);
203
204       errno = save_errno;
205       return retval;
206 }
207
208 /**
209  * g_stat:
210  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
211  * @buf: a pointer to a <structname>stat</structname> struct, which
212  *    will be filled with the file information
213  *
214  * A wrapper for the POSIX stat() function. The stat() function
215  * returns information about a file.
216  *
217  * See the C library manual for more details about stat().
218  *
219  * Returns: 0 if the information was successfully retrieved, -1 if an error
220  *    occurred
221  *
222  * Since: 2.6
223  */
224 int
225 ws_stdio_stat64 (const gchar *filename,
226         ws_statb64 *buf)
227 {
228       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
229       int retval;
230       int save_errno;
231       size_t len;
232
233       if (wfilename == NULL)
234         {
235           errno = EINVAL;
236           return -1;
237         }
238
239       len = wcslen (wfilename);
240       while (len > 0 && G_IS_DIR_SEPARATOR (wfilename[len-1]))
241         len--;
242       if (len > 0 &&
243           (!g_path_is_absolute (filename) || len > (size_t) (g_path_skip_root (filename) - filename)))
244         wfilename[len] = '\0';
245
246       retval = _wstati64 (wfilename, buf);
247       save_errno = errno;
248
249       g_free (wfilename);
250
251       errno = save_errno;
252       return retval;
253 }
254 /**
255  * g_unlink:
256  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
257  *
258  * A wrapper for the POSIX unlink() function. The unlink() function
259  * deletes a name from the filesystem. If this was the last link to the
260  * file and no processes have it opened, the diskspace occupied by the
261  * file is freed.
262  *
263  * See your C library manual for more details about unlink(). Note
264  * that on Windows, it is in general not possible to delete files that
265  * are open to some process, or mapped into memory.
266  *
267  * Returns: 0 if the name was successfully deleted, -1 if an error
268  *    occurred
269  *
270  * Since: 2.6
271  */
272
273 int
274 ws_stdio_unlink (const gchar *filename)
275 {
276       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
277       int retval;
278       int save_errno;
279
280       if (wfilename == NULL)
281         {
282           errno = EINVAL;
283           return -1;
284         }
285
286       retval = _wunlink (wfilename);
287       save_errno = errno;
288
289       g_free (wfilename);
290
291       errno = save_errno;
292       return retval;
293 }
294
295 /**
296  * g_remove:
297  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
298  *
299  * A wrapper for the POSIX remove() function. The remove() function
300  * deletes a name from the filesystem.
301  *
302  * See your C library manual for more details about how remove() works
303  * on your system. On Unix, remove() removes also directories, as it
304  * calls unlink() for files and rmdir() for directories. On Windows,
305  * although remove() in the C library only works for files, this
306  * function tries first remove() and then if that fails rmdir(), and
307  * thus works for both files and directories. Note however, that on
308  * Windows, it is in general not possible to remove a file that is
309  * open to some process, or mapped into memory.
310  *
311  * If this function fails on Windows you can't infer too much from the
312  * errno value. rmdir() is tried regardless of what caused remove() to
313  * fail. Any errno value set by remove() will be overwritten by that
314  * set by rmdir().
315  *
316  * Returns: 0 if the file was successfully removed, -1 if an error
317  *    occurred
318  *
319  * Since: 2.6
320  */
321 int
322 ws_stdio_remove (const gchar *filename)
323 {
324       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
325       int retval;
326       int save_errno;
327
328       if (wfilename == NULL)
329         {
330           errno = EINVAL;
331           return -1;
332         }
333
334       retval = _wremove (wfilename);
335       if (retval == -1)
336         retval = _wrmdir (wfilename);
337       save_errno = errno;
338
339       g_free (wfilename);
340
341       errno = save_errno;
342       return retval;
343 }
344
345 /**
346  * g_fopen:
347  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
348  * @mode: a string describing the mode in which the file should be
349  *   opened
350  *
351  * A wrapper for the POSIX fopen() function. The fopen() function opens
352  * a file and associates a new stream with it.
353  *
354  * See the C library manual for more details about fopen().
355  *
356  * Returns: A <type>FILE</type> pointer if the file was successfully
357  *    opened, or %NULL if an error occurred
358  *
359  * Since: 2.6
360  */
361 FILE *
362 ws_stdio_fopen (const gchar *filename,
363          const gchar *mode)
364 {
365       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
366       wchar_t *wmode;
367       FILE *retval;
368       int save_errno;
369
370       if (wfilename == NULL)
371         {
372           errno = EINVAL;
373           return NULL;
374         }
375
376       wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
377
378       if (wmode == NULL)
379         {
380           g_free (wfilename);
381           errno = EINVAL;
382           return NULL;
383         }
384
385       retval = _wfopen (wfilename, wmode);
386       save_errno = errno;
387
388       g_free (wfilename);
389       g_free (wmode);
390
391       errno = save_errno;
392       return retval;
393 }
394
395 /**
396  * g_freopen:
397  * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows)
398  * @mode: a string describing the mode in which the file should be
399  *   opened
400  * @stream: an existing stream which will be reused, or %NULL
401  *
402  * A wrapper for the POSIX freopen() function. The freopen() function
403  * opens a file and associates it with an existing stream.
404  *
405  * See the C library manual for more details about freopen().
406  *
407  * Returns: A <type>FILE</type> pointer if the file was successfully
408  *    opened, or %NULL if an error occurred.
409  *
410  * Since: 2.6
411  */
412 FILE *
413 ws_stdio_freopen (const gchar *filename,
414            const gchar *mode,
415            FILE        *stream)
416 {
417       wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
418       wchar_t *wmode;
419       FILE *retval;
420       int save_errno;
421
422       if (wfilename == NULL)
423         {
424           errno = EINVAL;
425           return NULL;
426         }
427
428       wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);
429
430       if (wmode == NULL)
431         {
432           g_free (wfilename);
433           errno = EINVAL;
434           return NULL;
435         }
436
437       retval = _wfreopen (wfilename, wmode, stream);
438       save_errno = errno;
439
440       g_free (wfilename);
441       g_free (wmode);
442
443       errno = save_errno;
444       return retval;
445 }
446
447
448 /* DLL loading */
449 static gboolean
450 init_dll_load_paths()
451 {
452       TCHAR path_w[MAX_PATH];
453
454       if (program_path && system_path)
455             return TRUE;
456
457       /* XXX - Duplicate code in filesystem.c:init_progfile_dir */
458       if (GetModuleFileName(NULL, path_w, MAX_PATH) == 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
459             return FALSE;
460       }
461
462       if (!program_path) {
463             gchar *app_path;
464             app_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
465             /* We could use PathRemoveFileSpec here but we'd have to link to Shlwapi.dll */
466             program_path = g_path_get_dirname(app_path);
467             g_free(app_path);
468       }
469
470       if (GetSystemDirectory(path_w, MAX_PATH) == 0) {
471             return FALSE;
472       }
473
474       if (!system_path) {
475             system_path = g_utf16_to_utf8(path_w, -1, NULL, NULL, NULL);
476       }
477
478       if (program_path && system_path)
479             return TRUE;
480
481       return FALSE;
482 }
483
484 gboolean
485 ws_init_dll_search_path()
486 {
487       gboolean dll_dir_set = FALSE;
488       wchar_t *program_path_w;
489
490       typedef BOOL (WINAPI *SetDllDirectoryHandler)(LPCTSTR);
491       SetDllDirectoryHandler PSetDllDirectory;
492
493       PSetDllDirectory = (SetDllDirectoryHandler) GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "SetDllDirectoryW");
494       if (PSetDllDirectory) {
495             dll_dir_set = PSetDllDirectory(_T(""));
496       }
497
498       if (!dll_dir_set && init_dll_load_paths()) {
499             program_path_w = g_utf8_to_utf16(program_path, -1, NULL, NULL, NULL);
500             SetCurrentDirectory(program_path_w);
501             g_free(program_path_w);
502       }
503
504       return dll_dir_set;
505 }
506
507 /*
508  * Internally g_module_open uses LoadLibrary on Windows and returns an
509  * HMODULE cast to a GModule *. However there's no guarantee that this
510  * will always be the case, so we call LoadLibrary and g_module_open
511  * separately.
512  */
513
514 void *
515 ws_load_library(gchar *library_name)
516 {
517       gchar   *full_path;
518       wchar_t *full_path_w;
519       HMODULE  dll_h;
520
521       if (!init_dll_load_paths() || !library_name)
522             return NULL;
523
524       /* First try the program directory */
525       full_path = g_module_build_path(program_path, library_name);
526       full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
527
528       if (full_path && full_path_w) {
529             dll_h = LoadLibraryW(full_path_w);
530             if (dll_h) {
531                   g_free(full_path);
532                   g_free(full_path_w);
533                   return dll_h;
534             }
535       }
536
537       /* Next try the system directory */
538       full_path = g_module_build_path(system_path, library_name);
539       full_path_w = g_utf8_to_utf16(full_path, -1, NULL, NULL, NULL);
540
541       if (full_path && full_path_w) {
542             dll_h = LoadLibraryW(full_path_w);
543             if (dll_h) {
544                   g_free(full_path);
545                   g_free(full_path_w);
546                   return dll_h;
547             }
548       }
549
550       return NULL;
551 }
552
553 GModule *
554 ws_module_open(gchar *module_name, GModuleFlags flags)
555 {
556       gchar   *full_path;
557       GModule *mod;
558
559       if (!init_dll_load_paths() || !module_name)
560             return NULL;
561
562       /* First try the program directory */
563       full_path = g_module_build_path(program_path, module_name);
564
565       if (full_path) {
566             mod = g_module_open(full_path, flags);
567             if (mod) {
568                   g_free(full_path);
569                   return mod;
570             }
571       }
572
573       /* Next try the system directory */
574       full_path = g_module_build_path(system_path, module_name);
575
576       if (full_path) {
577             mod = g_module_open(full_path, flags);
578             if (mod) {
579                   g_free(full_path);
580                   return mod;
581             }
582       }
583
584       return NULL;
585 }
586
587 /* utf8 version of getenv, needed to get win32 filename paths */
588 char *
589 getenv_utf8(const char *varname)
590 {
591         char *envvar;
592         wchar_t *envvarw;
593         wchar_t *varnamew;
594
595         envvar = getenv(varname);
596
597         /* since GLib 2.6 we need an utf8 version of the filename */
598         /* using the wide char version of getenv should work under all circumstances */
599
600         /* convert given varname to utf16, needed by _wgetenv */
601         varnamew = g_utf8_to_utf16(varname, -1, NULL, NULL, NULL);
602         if (varnamew == NULL) {
603                 return envvar;
604         }
605
606         /* use wide char version of getenv */
607         envvarw = _wgetenv(varnamew);
608         g_free(varnamew);
609         if (envvarw == NULL) {
610                 return envvar;
611         }
612
613         /* convert value to utf8 */
614         envvar = g_utf16_to_utf8(envvarw, -1, NULL, NULL, NULL);
615         /* XXX - memleak */
616
617         return envvar;
618 }