source: trunk/third/gnome-vfs/libgnomevfs/gnome-vfs-private-utils.c @ 21269

Revision 21269, 14.1 KB checked in by ghudson, 20 years ago (diff)
Fix type compatibility error noticed by gcc 3.4.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* gnome-vfs-utils.c - Private utility functions for the GNOME Virtual
3   File System.
4
5   Copyright (C) 1999 Free Software Foundation
6
7   The Gnome Library is free software; you can redistribute it and/or
8   modify it under the terms of the GNU Library General Public License as
9   published by the Free Software Foundation; either version 2 of the
10   License, or (at your option) any later version.
11
12   The Gnome Library is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15   Library General Public License for more details.
16
17   You should have received a copy of the GNU Library General Public
18   License along with the Gnome Library; see the file COPYING.LIB.  If not,
19   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.
21
22   Author: Ettore Perazzoli <ettore@comm2000.it>
23
24   `gnome_vfs_canonicalize_pathname()' derived from code by Brian Fox and Chet
25   Ramey in GNU Bash, the Bourne Again SHell.  Copyright (C) 1987, 1988, 1989,
26   1990, 1991, 1992 Free Software Foundation, Inc.  */
27
28#include <config.h>
29#include "gnome-vfs-private-utils.h"
30
31#include "gnome-vfs-cancellation.h"
32#include "gnome-vfs-ops.h"
33#include "gnome-vfs-uri.h"
34#include <ctype.h>
35#include <errno.h>
36#include <stdlib.h>
37#include <string.h>
38#include <sys/types.h>
39#include <sys/wait.h>
40#include <time.h>
41#include <unistd.h>
42
43static int
44find_next_slash (const char *path, int current_offset)
45{
46        const char *match;
47       
48        g_assert (current_offset <= strlen (path));
49       
50        match = strchr (path + current_offset, GNOME_VFS_URI_PATH_CHR);
51        return match == NULL ? -1 : match - path;
52}
53
54static int
55find_slash_before_offset (const char *path, int to)
56{
57        int result;
58        int next_offset;
59
60        result = -1;
61        next_offset = 0;
62        for (;;) {
63                next_offset = find_next_slash (path, next_offset);
64                if (next_offset < 0 || next_offset >= to) {
65                        break;
66                }
67                result = next_offset;
68                next_offset++;
69        }
70        return result;
71}
72
73static void
74collapse_slash_runs (char *path, int from_offset)
75{
76        int i;
77        /* Collapse multiple `/'s in a row. */
78        for (i = from_offset;; i++) {
79                if (path[i] != GNOME_VFS_URI_PATH_CHR) {
80                        break;
81                }
82        }
83
84        if (from_offset < i) {
85                strcpy (path + from_offset, path + i);
86                i = from_offset + 1;
87        }
88}
89
90/* Canonicalize path, and return a new path.  Do everything in situ.  The new
91   path differs from path in:
92
93     Multiple `/'s are collapsed to a single `/'.
94     Leading `./'s and trailing `/.'s are removed.
95     Non-leading `../'s and trailing `..'s are handled by removing
96     portions of the path.  */
97gchar *
98gnome_vfs_canonicalize_pathname (gchar *path)
99{
100        int i, marker;
101
102        if (path == NULL || strlen (path) == 0) {
103                return "";
104        }
105
106        /* Walk along path looking for things to compact. */
107        for (i = 0, marker = 0;;) {
108                if (!path[i])
109                        break;
110
111                /* Check for `../', `./' or trailing `.' by itself. */
112                if (path[i] == '.') {
113                        /* Handle trailing `.' by itself. */
114                        if (path[i + 1] == '\0') {
115                                if (i > 1 && path[i - 1] == GNOME_VFS_URI_PATH_CHR) {
116                                        /* strip the trailing /. */
117                                        path[i - 1] = '\0';
118                                } else {
119                                        /* convert path "/." to "/" */
120                                        path[i] = '\0';
121                                }
122                                break;
123                        }
124
125                        /* Handle `./'. */
126                        if (path[i + 1] == GNOME_VFS_URI_PATH_CHR) {
127                                strcpy (path + i, path + i + 2);
128                                if (i == 0) {
129                                        /* don't leave leading '/' for paths that started
130                                         * as relative (.//foo)
131                                         */
132                                        collapse_slash_runs (path, i);
133                                        marker = 0;
134                                }
135                                continue;
136                        }
137
138                        /* Handle `../' or trailing `..' by itself.
139                         * Remove the previous xxx/ part
140                         */
141                        if (path[i + 1] == '.'
142                            && (path[i + 2] == GNOME_VFS_URI_PATH_CHR
143                                || path[i + 2] == '\0')) {
144
145                                /* ignore ../ at the beginning of a path */
146                                if (i != 0) {
147                                        marker = find_slash_before_offset (path, i - 1);
148
149                                        /* Either advance past '/' or point to the first character */
150                                        marker ++;
151                                        if (path [i + 2] == '\0' && marker > 1) {
152                                                /* If we are looking at a /.. at the end of the uri and we
153                                                 * need to eat the last '/' too.
154                                                 */
155                                                 marker--;
156                                        }
157                                        g_assert(marker < i);
158                                       
159                                        if (path[i + 2] == GNOME_VFS_URI_PATH_CHR) {
160                                                /* strip the entire ../ string */
161                                                i++;
162                                        }
163
164                                        strcpy (path + marker, path + i + 2);
165                                        i = marker;
166                                } else {
167                                        i = 2;
168                                        if (path[i] == GNOME_VFS_URI_PATH_CHR) {
169                                                i++;
170                                        }
171                                }
172                                collapse_slash_runs (path, i);
173                                continue;
174                        }
175                }
176               
177                /* advance to the next '/' */
178                i = find_next_slash (path, i);
179
180                /* If we didn't find any slashes, then there is nothing left to do. */
181                if (i < 0) {
182                        break;
183                }
184
185                marker = i++;
186                collapse_slash_runs (path, i);
187        }
188        return path;
189}
190
191static glong
192get_max_fds (void)
193{
194#if defined _SC_OPEN_MAX
195        return sysconf (_SC_OPEN_MAX);
196#elif defined RLIMIT_NOFILE
197        {
198                struct rlimit rlimit;
199
200                if (getrlimit (RLIMIT_NOFILE, &rlimit) == 0)
201                        return rlimit.rlim_max;
202                else
203                        return -1;
204        }
205#elif defined HAVE_GETDTABLESIZE
206        return getdtablesize();
207#else
208#warning Cannot determine the number of available file descriptors
209        return 1024;            /* bogus */
210#endif
211}
212
213/* Close all the currrently opened file descriptors.  */
214static void
215shut_down_file_descriptors (void)
216{
217        glong i, max_fds;
218
219        max_fds = get_max_fds ();
220
221        for (i = 3; i < max_fds; i++)
222                close (i);
223}
224
225pid_t
226gnome_vfs_forkexec (const gchar *file_name,
227                    const gchar * const argv[],
228                    GnomeVFSProcessOptions options,
229                    GnomeVFSProcessInitFunc init_func,
230                    gpointer init_data)
231{
232        pid_t child_pid;
233
234        child_pid = fork ();
235        if (child_pid == 0) {
236                if (init_func != NULL)
237                        (* init_func) (init_data);
238                if (options & GNOME_VFS_PROCESS_SETSID)
239                        setsid ();
240                if (options & GNOME_VFS_PROCESS_CLOSEFDS)
241                        shut_down_file_descriptors ();
242                if (options & GNOME_VFS_PROCESS_USEPATH)
243                        execvp (file_name, (char **) argv);
244                else
245                        execv (file_name, (char **) argv);
246                _exit (1);
247        }
248
249        return child_pid;
250}
251
252/**
253 * gnome_vfs_process_run_cancellable:
254 * @file_name: Name of the executable to run
255 * @argv: NULL-terminated argument list
256 * @options: Options
257 * @cancellation: Cancellation object
258 * @return_value: Pointer to an integer that will contain the exit value
259 * on return.
260 *
261 * Run @file_name with argument list @argv, according to the specified
262 * @options.
263 *
264 * Return value:
265 **/
266GnomeVFSProcessRunResult
267gnome_vfs_process_run_cancellable (const gchar *file_name,
268                                   const gchar * const argv[],
269                                   GnomeVFSProcessOptions options,
270                                   GnomeVFSCancellation *cancellation,
271                                   guint *exit_value)
272{
273        pid_t child_pid;
274
275        child_pid = gnome_vfs_forkexec (file_name, argv, options, NULL, NULL);
276        if (child_pid == -1)
277                return GNOME_VFS_PROCESS_RUN_ERROR;
278
279        while (1) {
280                pid_t pid;
281                int status;
282
283                pid = waitpid (child_pid, &status, WUNTRACED);
284                if (pid == -1) {
285                        if (errno != EINTR)
286                                return GNOME_VFS_PROCESS_RUN_ERROR;
287                        if (gnome_vfs_cancellation_check (cancellation)) {
288                                *exit_value = 0;
289                                return GNOME_VFS_PROCESS_RUN_CANCELLED;
290                        }
291                } else if (pid == child_pid) {
292                        if (WIFEXITED (status)) {
293                                *exit_value = WEXITSTATUS (status);
294                                return GNOME_VFS_PROCESS_RUN_OK;
295                        }
296                        if (WIFSIGNALED (status)) {
297                                *exit_value = WTERMSIG (status);
298                                return GNOME_VFS_PROCESS_RUN_SIGNALED;
299                        }
300                        if (WIFSTOPPED (status)) {
301                                *exit_value = WSTOPSIG (status);
302                                return GNOME_VFS_PROCESS_RUN_SIGNALED;
303                        }
304                }
305        }
306
307}
308
309/**
310 * gnome_vfs_create_temp:
311 * @prefix: Prefix for the name of the temporary file
312 * @name_return: Pointer to a pointer that, on return, will point to
313 * the dynamically allocated name for the new temporary file created.
314 * @handle_return: Pointer to a variable that will hold a file handle for
315 * the new temporary file on return.
316 *
317 * Create a temporary file whose name is prefixed with @prefix, and return an
318 * open file handle for it in @*handle_return.
319 *
320 * Return value: An integer value representing the result of the operation
321 **/
322GnomeVFSResult
323gnome_vfs_create_temp (const gchar *prefix,
324                       gchar **name_return,
325                       GnomeVFSHandle **handle_return)
326{
327        GnomeVFSHandle *handle;
328        GnomeVFSResult result;
329        gchar *name;
330        gint fd;
331
332        while (1) {
333                name = g_strdup_printf("%sXXXXXX", prefix);
334                fd = mkstemp(name);
335
336                if (fd < 0)
337                        return GNOME_VFS_ERROR_INTERNAL;
338
339                fchmod(fd, 0600);
340                close(fd);
341
342                result = gnome_vfs_open
343                        (&handle, name,
344                         GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_READ);
345
346                if (result == GNOME_VFS_OK) {
347                        *name_return = name;
348                        *handle_return = handle;
349                        return GNOME_VFS_OK;
350                }
351
352                if (result != GNOME_VFS_ERROR_FILE_EXISTS) {
353                        *name_return = NULL;
354                        *handle_return = NULL;
355                        return result;
356                }
357        }
358}
359
360/* The following comes from GNU Wget with minor changes by myself.
361   Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.  */
362
363/* Converts struct tm to time_t, assuming the data in tm is UTC rather
364   than local timezone (mktime assumes the latter).
365
366   Contributed by Roger Beeman <beeman@cisco.com>, with the help of
367   Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO.  */
368static time_t
369mktime_from_utc (struct tm *t)
370{
371        time_t tl, tb;
372
373        tl = mktime (t);
374        if (tl == -1)
375                return -1;
376        tb = mktime (gmtime (&tl));
377        return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
378}
379
380/* Check whether the result of strptime() indicates success.
381   strptime() returns the pointer to how far it got to in the string.
382   The processing has been successful if the string is at `GMT' or
383   `+X', or at the end of the string.
384
385   In extended regexp parlance, the function returns 1 if P matches
386   "^ *(GMT|[+-][0-9]|$)", 0 otherwise.  P being NULL (a valid result of
387   strptime()) is considered a failure and 0 is returned.  */
388static int
389check_end (const gchar *p)
390{
391        if (!p)
392                return 0;
393        while (isspace ((unsigned char)*p))
394                ++p;
395        if (!*p
396            || (p[0] == 'G' && p[1] == 'M' && p[2] == 'T')
397            || ((p[0] == '+' || p[1] == '-') && isdigit ((unsigned char)p[1])))
398                return 1;
399        else
400                return 0;
401}
402
403/* Convert TIME_STRING time to time_t.  TIME_STRING can be in any of
404   the three formats RFC2068 allows the HTTP servers to emit --
405   RFC1123-date, RFC850-date or asctime-date.  Timezones are ignored,
406   and should be GMT.
407
408   We use strptime() to recognize various dates, which makes it a
409   little bit slacker than the RFC1123/RFC850/asctime (e.g. it always
410   allows shortened dates and months, one-digit days, etc.).  It also
411   allows more than one space anywhere where the specs require one SP.
412   The routine should probably be even more forgiving (as recommended
413   by RFC2068), but I do not have the time to write one.
414
415   Return the computed time_t representation, or -1 if all the
416   schemes fail.
417
418   Needless to say, what we *really* need here is something like
419   Marcus Hennecke's atotm(), which is forgiving, fast, to-the-point,
420   and does not use strptime().  atotm() is to be found in the sources
421   of `phttpd', a little-known HTTP server written by Peter Erikson.  */
422gboolean
423gnome_vfs_atotm (const gchar *time_string,
424                 time_t *value_return)
425{
426        struct tm t;
427
428        /* Roger Beeman says: "This function dynamically allocates struct tm
429           t, but does no initialization.  The only field that actually
430           needs initialization is tm_isdst, since the others will be set by
431           strptime.  Since strptime does not set tm_isdst, it will return
432           the data structure with whatever data was in tm_isdst to begin
433           with.  For those of us in timezones where DST can occur, there
434           can be a one hour shift depending on the previous contents of the
435           data area where the data structure is allocated."  */
436        t.tm_isdst = -1;
437
438        /* Note that under foreign locales Solaris strptime() fails to
439           recognize English dates, which renders this function useless.  I
440           assume that other non-GNU strptime's are plagued by the same
441           disease.  We solve this by setting only LC_MESSAGES in
442           i18n_initialize(), instead of LC_ALL.
443
444           Another solution could be to temporarily set locale to C, invoke
445           strptime(), and restore it back.  This is slow and dirty,
446           however, and locale support other than LC_MESSAGES can mess other
447           things, so I rather chose to stick with just setting LC_MESSAGES.
448
449           Also note that none of this is necessary under GNU strptime(),
450           because it recognizes both international and local dates.  */
451
452        /* NOTE: We don't use `%n' for white space, as OSF's strptime uses
453           it to eat all white space up to (and including) a newline, and
454           the function fails if there is no newline (!).
455
456           Let's hope all strptime() implementations use ` ' to skip *all*
457           whitespace instead of just one (it works that way on all the
458           systems I've tested it on).  */
459
460        /* RFC1123: Thu, 29 Jan 1998 22:12:57 */
461        if (check_end (strptime (time_string, "%a, %d %b %Y %T", &t))) {
462                *value_return = mktime_from_utc (&t);
463                return TRUE;
464        }
465
466        /* RFC850:  Thu, 29-Jan-98 22:12:57 */
467        if (check_end (strptime (time_string, "%a, %d-%b-%y %T", &t))) {
468                *value_return = mktime_from_utc (&t);
469                return TRUE;
470        }
471
472        /* asctime: Thu Jan 29 22:12:57 1998 */
473        if (check_end (strptime (time_string, "%a %b %d %T %Y", &t))) {
474                *value_return = mktime_from_utc (&t);
475                return TRUE;
476        }
477
478        /* Failure.  */
479        return FALSE;
480}
481
482/* gnome_vfs_istr_has_prefix
483 * copy-pasted from Nautilus
484 */
485gboolean
486gnome_vfs_istr_has_prefix (const char *haystack, const char *needle)
487{
488        const char *h, *n;
489        char hc, nc;
490
491        /* Eat one character at a time. */
492        h = haystack == NULL ? "" : haystack;
493        n = needle == NULL ? "" : needle;
494        do {
495                if (*n == '\0') {
496                        return TRUE;
497                }
498                if (*h == '\0') {
499                        return FALSE;
500                }
501                hc = *h++;
502                nc = *n++;
503                hc = tolower ((guchar) hc);
504                nc = tolower ((guchar) nc);
505        } while (hc == nc);
506        return FALSE;
507}
508
509/* gnome_vfs_istr_has_suffix
510 * copy-pasted from Nautilus
511 */
512gboolean
513gnome_vfs_istr_has_suffix (const char *haystack, const char *needle)
514{
515        const char *h, *n;
516        char hc, nc;
517
518        if (needle == NULL) {
519                return TRUE;
520        }
521        if (haystack == NULL) {
522                return needle[0] == '\0';
523        }
524               
525        /* Eat one character at a time. */
526        h = haystack + strlen (haystack);
527        n = needle + strlen (needle);
528        do {
529                if (n == needle) {
530                        return TRUE;
531                }
532                if (h == haystack) {
533                        return FALSE;
534                }
535                hc = *--h;
536                nc = *--n;
537                hc = tolower ((guchar) hc);
538                nc = tolower ((guchar) nc);
539        } while (hc == nc);
540        return FALSE;
541}
Note: See TracBrowser for help on using the repository browser.