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
5 Copyright (C) 1999 Free Software Foundation
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.
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.
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.
22 Author: Ettore Perazzoli <ettore@comm2000.it>
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. */
29 #include "gnome-vfs-private-utils.h"
31 #include "gnome-vfs-utils.h"
32 #include "gnome-vfs-cancellation.h"
33 #include "gnome-vfs-ops.h"
34 #include "gnome-vfs-uri.h"
36 #include <glib/gmessages.h>
37 #include <glib/gstrfuncs.h>
38 #include <gconf/gconf-client.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
47 #define GCONF_URL_HANDLER_PATH "/desktop/gnome/url-handlers/"
48 #define GCONF_DEFAULT_TERMINAL_EXEC_PATH "/desktop/gnome/applications/terminal/exec"
49 #define GCONF_DEFAULT_TERMINAL_ARG_PATH "/desktop/gnome/applications/terminal/exec_arg"
51 /* Check whether the node is IPv6 enabled.*/
53 _gnome_vfs_have_ipv6 ()
58 s = socket (AF_INET6, SOCK_STREAM, 0);
69 find_next_slash (const char *path, int current_offset)
73 g_assert (current_offset <= strlen (path));
75 match = strchr (path + current_offset, GNOME_VFS_URI_PATH_CHR);
76 return match == NULL ? -1 : match - path;
80 find_slash_before_offset (const char *path, int to)
88 next_offset = find_next_slash (path, next_offset);
89 if (next_offset < 0 || next_offset >= to) {
99 collapse_slash_runs (char *path, int from_offset)
102 /* Collapse multiple `/'s in a row. */
103 for (i = from_offset;; i++) {
104 if (path[i] != GNOME_VFS_URI_PATH_CHR) {
109 if (from_offset < i) {
110 strcpy (path + from_offset, path + i);
115 /* Canonicalize path, and return a new path. Do everything in situ. The new
116 path differs from path in:
118 Multiple `/'s are collapsed to a single `/'.
119 Leading `./'s and trailing `/.'s are removed.
120 Non-leading `../'s and trailing `..'s are handled by removing
121 portions of the path. */
123 _gnome_vfs_canonicalize_pathname (gchar *path)
127 if (path == NULL || strlen (path) == 0) {
131 /* Walk along path looking for things to compact. */
132 for (i = 0, marker = 0;;) {
136 /* Check for `../', `./' or trailing `.' by itself. */
137 if (path[i] == '.') {
138 /* Handle trailing `.' by itself. */
139 if (path[i + 1] == '\0') {
140 if (i > 1 && path[i - 1] == GNOME_VFS_URI_PATH_CHR) {
141 /* strip the trailing /. */
144 /* convert path "/." to "/" */
151 if (path[i + 1] == GNOME_VFS_URI_PATH_CHR) {
152 strcpy (path + i, path + i + 2);
154 /* don't leave leading '/' for paths that started
155 * as relative (.//foo)
157 collapse_slash_runs (path, i);
163 /* Handle `../' or trailing `..' by itself.
164 * Remove the previous xxx/ part
166 if (path[i + 1] == '.'
167 && (path[i + 2] == GNOME_VFS_URI_PATH_CHR
168 || path[i + 2] == '\0')) {
170 /* ignore ../ at the beginning of a path */
172 marker = find_slash_before_offset (path, i - 1);
174 /* Either advance past '/' or point to the first character */
176 if (path [i + 2] == '\0' && marker > 1) {
177 /* If we are looking at a /.. at the end of the uri and we
178 * need to eat the last '/' too.
182 g_assert(marker < i);
184 if (path[i + 2] == GNOME_VFS_URI_PATH_CHR) {
185 /* strip the entire ../ string */
189 strcpy (path + marker, path + i + 2);
193 if (path[i] == GNOME_VFS_URI_PATH_CHR) {
197 collapse_slash_runs (path, i);
202 /* advance to the next '/' */
203 i = find_next_slash (path, i);
205 /* If we didn't find any slashes, then there is nothing left to do. */
211 collapse_slash_runs (path, i);
219 #if defined _SC_OPEN_MAX
220 return sysconf (_SC_OPEN_MAX);
221 #elif defined RLIMIT_NOFILE
223 struct rlimit rlimit;
225 if (getrlimit (RLIMIT_NOFILE, &rlimit) == 0)
226 return rlimit.rlim_max;
230 #elif defined HAVE_GETDTABLESIZE
231 return getdtablesize();
233 #warning Cannot determine the number of available file descriptors
234 return 1024; /* bogus */
238 /* Close all the currrently opened file descriptors. */
240 shut_down_file_descriptors (void)
244 max_fds = get_max_fds ();
246 for (i = 3; i < max_fds; i++)
251 gnome_vfs_forkexec (const gchar *file_name,
252 const gchar * const argv[],
253 GnomeVFSProcessOptions options,
254 GnomeVFSProcessInitFunc init_func,
260 if (child_pid == 0) {
261 if (init_func != NULL)
262 (* init_func) (init_data);
263 if (options & GNOME_VFS_PROCESS_SETSID)
265 if (options & GNOME_VFS_PROCESS_CLOSEFDS)
266 shut_down_file_descriptors ();
267 if (options & GNOME_VFS_PROCESS_USEPATH)
268 execvp (file_name, (char **) argv);
270 execv (file_name, (char **) argv);
278 * gnome_vfs_process_run_cancellable:
279 * @file_name: Name of the executable to run
280 * @argv: NULL-terminated argument list
282 * @cancellation: Cancellation object
283 * @return_value: Pointer to an integer that will contain the exit value
286 * Run @file_name with argument list @argv, according to the specified
291 GnomeVFSProcessResult
292 gnome_vfs_process_run_cancellable (const gchar *file_name,
293 const gchar * const argv[],
294 GnomeVFSProcessOptions options,
295 GnomeVFSCancellation *cancellation,
300 child_pid = gnome_vfs_forkexec (file_name, argv, options, NULL, NULL);
302 return GNOME_VFS_PROCESS_RUN_ERROR;
308 pid = waitpid (child_pid, &status, WUNTRACED);
311 return GNOME_VFS_PROCESS_RUN_ERROR;
312 if (gnome_vfs_cancellation_check (cancellation)) {
314 return GNOME_VFS_PROCESS_RUN_CANCELLED;
316 } else if (pid == child_pid) {
317 if (WIFEXITED (status)) {
318 *exit_value = WEXITSTATUS (status);
319 return GNOME_VFS_PROCESS_RUN_OK;
321 if (WIFSIGNALED (status)) {
322 *exit_value = WTERMSIG (status);
323 return GNOME_VFS_PROCESS_RUN_SIGNALED;
325 if (WIFSTOPPED (status)) {
326 *exit_value = WSTOPSIG (status);
327 return GNOME_VFS_PROCESS_RUN_SIGNALED;
335 * gnome_vfs_create_temp:
336 * @prefix: Prefix for the name of the temporary file
337 * @name_return: Pointer to a pointer that, on return, will point to
338 * the dynamically allocated name for the new temporary file created.
339 * @handle_return: Pointer to a variable that will hold a file handle for
340 * the new temporary file on return.
342 * Create a temporary file whose name is prefixed with @prefix, and return an
343 * open file handle for it in @*handle_return.
345 * Return value: An integer value representing the result of the operation
348 gnome_vfs_create_temp (const gchar *prefix,
350 GnomeVFSHandle **handle_return)
352 GnomeVFSHandle *handle;
353 GnomeVFSResult result;
358 name = g_strdup_printf("%sXXXXXX", prefix);
362 return GNOME_VFS_ERROR_INTERNAL;
367 result = gnome_vfs_open
369 GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_READ);
371 if (result == GNOME_VFS_OK) {
373 *handle_return = handle;
377 if (result != GNOME_VFS_ERROR_FILE_EXISTS) {
379 *handle_return = NULL;
385 /* The following comes from GNU Wget with minor changes by myself.
386 Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. */
388 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
389 than local timezone (mktime assumes the latter).
391 Contributed by Roger Beeman <beeman@cisco.com>, with the help of
392 Mark Baushke <mdb@cisco.com> and the rest of the Gurus at CISCO. */
394 mktime_from_utc (struct tm *t)
401 tb = mktime (gmtime (&tl));
402 return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
405 /* Check whether the result of strptime() indicates success.
406 strptime() returns the pointer to how far it got to in the string.
407 The processing has been successful if the string is at `GMT' or
408 `+X', or at the end of the string.
410 In extended regexp parlance, the function returns 1 if P matches
411 "^ *(GMT|[+-][0-9]|$)", 0 otherwise. P being NULL (a valid result of
412 strptime()) is considered a failure and 0 is returned. */
414 check_end (const gchar *p)
418 while (g_ascii_isspace (*p))
421 || (p[0] == 'G' && p[1] == 'M' && p[2] == 'T')
422 || ((p[0] == '+' || p[1] == '-') && g_ascii_isdigit (p[1])))
428 /* Convert TIME_STRING time to time_t. TIME_STRING can be in any of
429 the three formats RFC2068 allows the HTTP servers to emit --
430 RFC1123-date, RFC850-date or asctime-date. Timezones are ignored,
433 We use strptime() to recognize various dates, which makes it a
434 little bit slacker than the RFC1123/RFC850/asctime (e.g. it always
435 allows shortened dates and months, one-digit days, etc.). It also
436 allows more than one space anywhere where the specs require one SP.
437 The routine should probably be even more forgiving (as recommended
438 by RFC2068), but I do not have the time to write one.
440 Return the computed time_t representation, or -1 if all the
443 Needless to say, what we *really* need here is something like
444 Marcus Hennecke's atotm(), which is forgiving, fast, to-the-point,
445 and does not use strptime(). atotm() is to be found in the sources
446 of `phttpd', a little-known HTTP server written by Peter Erikson. */
448 gnome_vfs_atotm (const gchar *time_string,
449 time_t *value_return)
453 /* Roger Beeman says: "This function dynamically allocates struct tm
454 t, but does no initialization. The only field that actually
455 needs initialization is tm_isdst, since the others will be set by
456 strptime. Since strptime does not set tm_isdst, it will return
457 the data structure with whatever data was in tm_isdst to begin
458 with. For those of us in timezones where DST can occur, there
459 can be a one hour shift depending on the previous contents of the
460 data area where the data structure is allocated." */
463 /* Note that under foreign locales Solaris strptime() fails to
464 recognize English dates, which renders this function useless. I
465 assume that other non-GNU strptime's are plagued by the same
466 disease. We solve this by setting only LC_MESSAGES in
467 i18n_initialize(), instead of LC_ALL.
469 Another solution could be to temporarily set locale to C, invoke
470 strptime(), and restore it back. This is slow and dirty,
471 however, and locale support other than LC_MESSAGES can mess other
472 things, so I rather chose to stick with just setting LC_MESSAGES.
474 Also note that none of this is necessary under GNU strptime(),
475 because it recognizes both international and local dates. */
477 /* NOTE: We don't use `%n' for white space, as OSF's strptime uses
478 it to eat all white space up to (and including) a newline, and
479 the function fails if there is no newline (!).
481 Let's hope all strptime() implementations use ` ' to skip *all*
482 whitespace instead of just one (it works that way on all the
483 systems I've tested it on). */
485 /* RFC1123: Thu, 29 Jan 1998 22:12:57 */
486 if (check_end (strptime (time_string, "%a, %d %b %Y %T", &t))) {
487 *value_return = mktime_from_utc (&t);
491 /* RFC850: Thu, 29-Jan-98 22:12:57 */
492 if (check_end (strptime (time_string, "%a, %d-%b-%y %T", &t))) {
493 *value_return = mktime_from_utc (&t);
497 /* asctime: Thu Jan 29 22:12:57 1998 */
498 if (check_end (strptime (time_string, "%a %b %d %T %Y", &t))) {
499 *value_return = mktime_from_utc (&t);
507 /* _gnome_vfs_istr_has_prefix
508 * copy-pasted from Nautilus
511 _gnome_vfs_istr_has_prefix (const char *haystack, const char *needle)
516 /* Eat one character at a time. */
517 h = haystack == NULL ? "" : haystack;
518 n = needle == NULL ? "" : needle;
528 hc = g_ascii_tolower (hc);
529 nc = g_ascii_tolower (nc);
534 /* _gnome_vfs_istr_has_suffix
535 * copy-pasted from Nautilus
538 _gnome_vfs_istr_has_suffix (const char *haystack, const char *needle)
543 if (needle == NULL) {
546 if (haystack == NULL) {
547 return needle[0] == '\0';
550 /* Eat one character at a time. */
551 h = haystack + strlen (haystack);
552 n = needle + strlen (needle);
562 hc = g_ascii_tolower (hc);
563 nc = g_ascii_tolower (nc);
569 * _gnome_vfs_use_handler_for_scheme:
570 * @scheme: the URI scheme
572 * Checks GConf to see if there is a URL handler
573 * defined for this scheme and if it is enabled.
575 * Return value: TRUE if handler is defined and enabled,
581 _gnome_vfs_use_handler_for_scheme (const char *scheme)
587 g_return_val_if_fail (scheme != NULL, FALSE);
589 if (!gconf_is_initialized ()) {
590 if (!gconf_init (0, NULL, NULL)) {
595 client = gconf_client_get_default ();
596 path = g_strconcat (GCONF_URL_HANDLER_PATH, scheme, "/enabled", NULL);
597 ret = gconf_client_get_bool (client, path, NULL);
600 g_object_unref (G_OBJECT (client));
606 * _gnome_vfs_url_show_using_handler_with_env:
607 * @url: the url to show
608 * @envp: environment for the handler
610 * Same as gnome_url_show_using_handler except that the handler
611 * will be launched with the given environment.
613 * Return value: GNOME_VFS_OK on success.
614 * GNOME_VFS_ERROR_BAD_PAREMETER if the URL is invalid.
615 * GNOME_VFS_ERROR_NOT_SUPPORTED if no handler is defined.
616 * GNOME_VFS_ERROR_PARSE if the handler command can not be parsed.
617 * GNOME_VFS_ERROR_LAUNCH if the handler command can not be launched.
618 * GNOME_VFS_ERROR_INTERNAL for internal/GConf errors.
623 _gnome_vfs_url_show_using_handler_with_env (const char *url,
635 g_return_val_if_fail (url != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
637 scheme = gnome_vfs_get_uri_scheme (url);
639 g_return_val_if_fail (scheme != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
641 if (!gconf_is_initialized ()) {
642 if (!gconf_init (0, NULL, NULL)) {
644 return GNOME_VFS_ERROR_INTERNAL;
648 client = gconf_client_get_default ();
649 path = g_strconcat (GCONF_URL_HANDLER_PATH, scheme, "/command", NULL);
650 template = gconf_client_get_string (client, path, NULL);
653 if (template == NULL) {
656 return GNOME_VFS_ERROR_NO_HANDLER;
659 if (!g_shell_parse_argv (template,
665 return GNOME_VFS_ERROR_PARSE;
670 path = g_strconcat (GCONF_URL_HANDLER_PATH, scheme, "/needs_terminal", NULL);
671 if (gconf_client_get_bool (client, path, NULL)) {
672 if (!_gnome_vfs_prepend_terminal_to_vector (&argc, &argv)) {
676 return GNOME_VFS_ERROR_INTERNAL;
682 g_object_unref (G_OBJECT (client));
684 for (i = 0; i < argc; i++) {
687 if (strcmp (argv[i], "%s") != 0)
691 argv[i] = g_strdup (url);
695 ret = g_spawn_async (NULL /* working directory */,
698 G_SPAWN_SEARCH_PATH /* flags */,
699 NULL /* child_setup */,
701 NULL /* child_pid */,
706 return GNOME_VFS_ERROR_LAUNCH;
712 /* This is a copy from libgnome, internalized here to avoid
713 strange dependency loops */
716 * _gnome_vfs_prepend_terminal_to_vector:
717 * @argc: a pointer to the vector size
718 * @argv: a pointer to the vector
720 * Prepends a terminal (either the one configured as default in GnomeVFS
721 * or one of the common xterm emulators) to the passed in vector, modifying
722 * it in the process. The vector should be allocated with #g_malloc, as
723 * this will #g_free the original vector. Also all elements must
724 * have been allocated separately. That is the standard glib/GNOME way of
725 * doing vectors. If the integer that @argc points to is negative, the
726 * size will first be computed. Also note that passing in pointers to a vector
727 * that is empty, will just create a new vector for you.
729 * Return value: TRUE if successful, FALSE otherwise.
734 _gnome_vfs_prepend_terminal_to_vector (int *argc,
740 char **term_argv = NULL;
744 char *terminal = NULL;
747 g_return_val_if_fail (argc != NULL, FALSE);
748 g_return_val_if_fail (argv != NULL, FALSE);
757 /* compute size if not given */
759 for (i = 0; the_argv[i] != NULL; i++)
764 if (!gconf_is_initialized ()) {
765 if (!gconf_init (0, NULL, NULL)) {
770 client = gconf_client_get_default ();
771 terminal = gconf_client_get_string (client, GCONF_DEFAULT_TERMINAL_EXEC_PATH, NULL);
775 exec_flag = gconf_client_get_string (client, GCONF_DEFAULT_TERMINAL_ARG_PATH, NULL);
777 if (exec_flag == NULL) {
779 term_argv = g_new0 (char *, 2);
780 term_argv[0] = terminal;
784 term_argv = g_new0 (char *, 3);
785 term_argv[0] = terminal;
786 term_argv[1] = exec_flag;
791 g_object_unref (G_OBJECT (client));
793 if (term_argv == NULL) {
797 term_argv = g_new0 (char *, 3);
799 check = g_find_program_in_path ("gnome-terminal");
801 term_argv[0] = check;
802 /* Note that gnome-terminal takes -x and
803 * as -e in gnome-terminal is broken we use that. */
804 term_argv[1] = g_strdup ("-x");
807 check = g_find_program_in_path ("nxterm");
809 check = g_find_program_in_path ("color-xterm");
811 check = g_find_program_in_path ("rxvt");
813 check = g_find_program_in_path ("xterm");
815 check = g_find_program_in_path ("dtterm");
817 check = g_strdup ("xterm");
818 g_warning ("couldn't find a terminal, falling back to xterm");
820 term_argv[0] = check;
821 term_argv[1] = g_strdup ("-e");
825 real_argc = term_argc + *argc;
826 real_argv = g_new (char *, real_argc + 1);
828 for (i = 0; i < term_argc; i++)
829 real_argv[i] = term_argv[i];
831 for (j = 0; j < *argc; j++, i++)
832 real_argv[i] = (char *)the_argv[j];
840 /* we use g_free here as we sucked all the inner strings
841 * out from it into real_argv */