ftp://ftp.redhat.com/pub/redhat/linux/rawhide/SRPMS/SRPMS/gnome-vfs2-2.3.8-1.src.rpm
[gnome-vfs-httpcaptive.git] / libgnomevfs / gnome-vfs-private-utils.c
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-utils.h"
32 #include "gnome-vfs-cancellation.h"
33 #include "gnome-vfs-ops.h"
34 #include "gnome-vfs-uri.h"
35 #include <errno.h>
36 #include <glib/gmessages.h>
37 #include <glib/gstrfuncs.h>
38 #include <gconf/gconf-client.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <sys/types.h>
42 #include <sys/socket.h>
43 #include <sys/wait.h>
44 #include <time.h>
45 #include <unistd.h>
46
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"
50
51 /* Check whether the node is IPv6 enabled.*/
52 gboolean
53 _gnome_vfs_have_ipv6 ()
54 {
55 #ifdef ENABLE_IPV6
56         int s;
57
58         s = socket (AF_INET6, SOCK_STREAM, 0);
59         if (s != -1) {
60                 close (s);
61                 return TRUE;
62         }
63
64 #endif
65         return FALSE;
66 }
67
68 static int
69 find_next_slash (const char *path, int current_offset)
70 {
71         const char *match;
72         
73         g_assert (current_offset <= strlen (path));
74         
75         match = strchr (path + current_offset, GNOME_VFS_URI_PATH_CHR);
76         return match == NULL ? -1 : match - path;
77 }
78
79 static int
80 find_slash_before_offset (const char *path, int to)
81 {
82         int result;
83         int next_offset;
84
85         result = -1;
86         next_offset = 0;
87         for (;;) {
88                 next_offset = find_next_slash (path, next_offset);
89                 if (next_offset < 0 || next_offset >= to) {
90                         break;
91                 }
92                 result = next_offset;
93                 next_offset++;
94         }
95         return result;
96 }
97
98 static void
99 collapse_slash_runs (char *path, int from_offset)
100 {
101         int i;
102         /* Collapse multiple `/'s in a row. */
103         for (i = from_offset;; i++) {
104                 if (path[i] != GNOME_VFS_URI_PATH_CHR) {
105                         break;
106                 }
107         }
108
109         if (from_offset < i) {
110                 strcpy (path + from_offset, path + i);
111                 i = from_offset + 1;
112         }
113 }
114
115 /* Canonicalize path, and return a new path.  Do everything in situ.  The new
116    path differs from path in:
117
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.  */
122 gchar *
123 _gnome_vfs_canonicalize_pathname (gchar *path)
124 {
125         int i, marker;
126
127         if (path == NULL || strlen (path) == 0) {
128                 return "";
129         }
130
131         /* Walk along path looking for things to compact. */
132         for (i = 0, marker = 0;;) {
133                 if (!path[i])
134                         break;
135
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 /. */
142                                         path[i - 1] = '\0';
143                                 } else {
144                                         /* convert path "/." to "/" */
145                                         path[i] = '\0';
146                                 }
147                                 break;
148                         }
149
150                         /* Handle `./'. */
151                         if (path[i + 1] == GNOME_VFS_URI_PATH_CHR) {
152                                 strcpy (path + i, path + i + 2);
153                                 if (i == 0) {
154                                         /* don't leave leading '/' for paths that started
155                                          * as relative (.//foo)
156                                          */
157                                         collapse_slash_runs (path, i);
158                                         marker = 0;
159                                 }
160                                 continue;
161                         }
162
163                         /* Handle `../' or trailing `..' by itself. 
164                          * Remove the previous xxx/ part 
165                          */
166                         if (path[i + 1] == '.'
167                             && (path[i + 2] == GNOME_VFS_URI_PATH_CHR
168                                 || path[i + 2] == '\0')) {
169
170                                 /* ignore ../ at the beginning of a path */
171                                 if (i != 0) {
172                                         marker = find_slash_before_offset (path, i - 1);
173
174                                         /* Either advance past '/' or point to the first character */
175                                         marker ++;
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.
179                                                  */
180                                                  marker--;
181                                         }
182                                         g_assert(marker < i);
183                                         
184                                         if (path[i + 2] == GNOME_VFS_URI_PATH_CHR) {
185                                                 /* strip the entire ../ string */
186                                                 i++;
187                                         }
188
189                                         strcpy (path + marker, path + i + 2);
190                                         i = marker;
191                                 } else {
192                                         i = 2;
193                                         if (path[i] == GNOME_VFS_URI_PATH_CHR) {
194                                                 i++;
195                                         }
196                                 }
197                                 collapse_slash_runs (path, i);
198                                 continue;
199                         }
200                 }
201                 
202                 /* advance to the next '/' */
203                 i = find_next_slash (path, i);
204
205                 /* If we didn't find any slashes, then there is nothing left to do. */
206                 if (i < 0) {
207                         break;
208                 }
209
210                 marker = i++;
211                 collapse_slash_runs (path, i);
212         }
213         return path;
214 }
215
216 static glong
217 get_max_fds (void)
218 {
219 #if defined _SC_OPEN_MAX
220         return sysconf (_SC_OPEN_MAX);
221 #elif defined RLIMIT_NOFILE
222         {
223                 struct rlimit rlimit;
224
225                 if (getrlimit (RLIMIT_NOFILE, &rlimit) == 0)
226                         return rlimit.rlim_max;
227                 else
228                         return -1;
229         }
230 #elif defined HAVE_GETDTABLESIZE
231         return getdtablesize();
232 #else
233 #warning Cannot determine the number of available file descriptors
234         return 1024;            /* bogus */
235 #endif
236 }
237
238 /* Close all the currrently opened file descriptors.  */
239 static void
240 shut_down_file_descriptors (void)
241 {
242         glong i, max_fds;
243
244         max_fds = get_max_fds ();
245
246         for (i = 3; i < max_fds; i++)
247                 close (i);
248 }
249
250 pid_t
251 gnome_vfs_forkexec (const gchar *file_name,
252                     const gchar * const argv[],
253                     GnomeVFSProcessOptions options,
254                     GnomeVFSProcessInitFunc init_func,
255                     gpointer init_data)
256 {
257         pid_t child_pid;
258
259         child_pid = fork ();
260         if (child_pid == 0) {
261                 if (init_func != NULL)
262                         (* init_func) (init_data);
263                 if (options & GNOME_VFS_PROCESS_SETSID)
264                         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);
269                 else
270                         execv (file_name, (char **) argv);
271                 _exit (1);
272         }
273
274         return child_pid;
275 }
276
277 /**
278  * gnome_vfs_process_run_cancellable:
279  * @file_name: Name of the executable to run
280  * @argv: NULL-terminated argument list
281  * @options: Options
282  * @cancellation: Cancellation object
283  * @return_value: Pointer to an integer that will contain the exit value
284  * on return.
285  * 
286  * Run @file_name with argument list @argv, according to the specified
287  * @options.
288  * 
289  * Return value: 
290  **/
291 GnomeVFSProcessResult
292 gnome_vfs_process_run_cancellable (const gchar *file_name,
293                                    const gchar * const argv[],
294                                    GnomeVFSProcessOptions options,
295                                    GnomeVFSCancellation *cancellation,
296                                    guint *exit_value)
297 {
298         pid_t child_pid;
299
300         child_pid = gnome_vfs_forkexec (file_name, argv, options, NULL, NULL);
301         if (child_pid == -1)
302                 return GNOME_VFS_PROCESS_RUN_ERROR;
303
304         while (1) {
305                 pid_t pid;
306                 int status;
307
308                 pid = waitpid (child_pid, &status, WUNTRACED);
309                 if (pid == -1) {
310                         if (errno != EINTR)
311                                 return GNOME_VFS_PROCESS_RUN_ERROR;
312                         if (gnome_vfs_cancellation_check (cancellation)) {
313                                 *exit_value = 0;
314                                 return GNOME_VFS_PROCESS_RUN_CANCELLED;
315                         }
316                 } else if (pid == child_pid) {
317                         if (WIFEXITED (status)) {
318                                 *exit_value = WEXITSTATUS (status);
319                                 return GNOME_VFS_PROCESS_RUN_OK;
320                         }
321                         if (WIFSIGNALED (status)) {
322                                 *exit_value = WTERMSIG (status);
323                                 return GNOME_VFS_PROCESS_RUN_SIGNALED;
324                         }
325                         if (WIFSTOPPED (status)) {
326                                 *exit_value = WSTOPSIG (status);
327                                 return GNOME_VFS_PROCESS_RUN_SIGNALED;
328                         }
329                 }
330         }
331
332 }
333
334 /**
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.
341  * 
342  * Create a temporary file whose name is prefixed with @prefix, and return an
343  * open file handle for it in @*handle_return.
344  * 
345  * Return value: An integer value representing the result of the operation
346  **/
347 GnomeVFSResult
348 gnome_vfs_create_temp (const gchar *prefix,
349                        gchar **name_return,
350                        GnomeVFSHandle **handle_return)
351 {
352         GnomeVFSHandle *handle;
353         GnomeVFSResult result;
354         gchar *name;
355         gint fd;
356
357         while (1) {
358                 name = g_strdup_printf("%sXXXXXX", prefix);
359                 fd = mkstemp(name);
360
361                 if (fd < 0)
362                         return GNOME_VFS_ERROR_INTERNAL;
363
364                 fchmod(fd, 0600);
365                 close(fd);
366
367                 result = gnome_vfs_open
368                         (&handle, name,
369                          GNOME_VFS_OPEN_WRITE | GNOME_VFS_OPEN_READ);
370
371                 if (result == GNOME_VFS_OK) {
372                         *name_return = name;
373                         *handle_return = handle;
374                         return GNOME_VFS_OK;
375                 }
376
377                 if (result != GNOME_VFS_ERROR_FILE_EXISTS) {
378                         *name_return = NULL;
379                         *handle_return = NULL;
380                         return result;
381                 }
382         }
383 }
384
385 /* The following comes from GNU Wget with minor changes by myself.
386    Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.  */
387
388 /* Converts struct tm to time_t, assuming the data in tm is UTC rather
389    than local timezone (mktime assumes the latter).
390
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.  */
393 static time_t
394 mktime_from_utc (struct tm *t)
395 {
396         time_t tl, tb;
397
398         tl = mktime (t);
399         if (tl == -1)
400                 return -1;
401         tb = mktime (gmtime (&tl));
402         return (tl <= tb ? (tl + (tl - tb)) : (tl - (tb - tl)));
403 }
404
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.
409
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.  */
413 static int
414 check_end (const gchar *p)
415 {
416         if (!p)
417                 return 0;
418         while (g_ascii_isspace (*p))
419                 ++p;
420         if (!*p
421             || (p[0] == 'G' && p[1] == 'M' && p[2] == 'T')
422             || ((p[0] == '+' || p[1] == '-') && g_ascii_isdigit (p[1])))
423                 return 1;
424         else
425                 return 0;
426 }
427
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,
431    and should be GMT.
432
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.
439
440    Return the computed time_t representation, or -1 if all the
441    schemes fail.
442
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.  */
447 gboolean
448 gnome_vfs_atotm (const gchar *time_string,
449                  time_t *value_return)
450 {
451         struct tm t;
452
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."  */
461         t.tm_isdst = -1;
462
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.
468
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.
473
474            Also note that none of this is necessary under GNU strptime(),
475            because it recognizes both international and local dates.  */
476
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 (!).
480
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).  */
484
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);
488                 return TRUE;
489         }
490
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);
494                 return TRUE;
495         }
496
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);
500                 return TRUE;
501         }
502
503         /* Failure.  */
504         return FALSE;
505 }
506
507 /* _gnome_vfs_istr_has_prefix
508  * copy-pasted from Nautilus
509  */
510 gboolean
511 _gnome_vfs_istr_has_prefix (const char *haystack, const char *needle)
512 {
513         const char *h, *n;
514         char hc, nc;
515
516         /* Eat one character at a time. */
517         h = haystack == NULL ? "" : haystack;
518         n = needle == NULL ? "" : needle;
519         do {
520                 if (*n == '\0') {
521                         return TRUE;
522                 }
523                 if (*h == '\0') {
524                         return FALSE;
525                 }
526                 hc = *h++;
527                 nc = *n++;
528                 hc = g_ascii_tolower (hc);
529                 nc = g_ascii_tolower (nc);
530         } while (hc == nc);
531         return FALSE;
532 }
533
534 /* _gnome_vfs_istr_has_suffix
535  * copy-pasted from Nautilus
536  */
537 gboolean
538 _gnome_vfs_istr_has_suffix (const char *haystack, const char *needle)
539 {
540         const char *h, *n;
541         char hc, nc;
542
543         if (needle == NULL) {
544                 return TRUE;
545         }
546         if (haystack == NULL) {
547                 return needle[0] == '\0';
548         }
549                 
550         /* Eat one character at a time. */
551         h = haystack + strlen (haystack);
552         n = needle + strlen (needle);
553         do {
554                 if (n == needle) {
555                         return TRUE;
556                 }
557                 if (h == haystack) {
558                         return FALSE;
559                 }
560                 hc = *--h;
561                 nc = *--n;
562                 hc = g_ascii_tolower (hc);
563                 nc = g_ascii_tolower (nc);
564         } while (hc == nc);
565         return FALSE;
566 }
567
568 /**
569  * _gnome_vfs_use_handler_for_scheme:
570  * @scheme: the URI scheme
571  *
572  * Checks GConf to see if there is a URL handler
573  * defined for this scheme and if it is enabled.
574  *
575  * Return value: TRUE if handler is defined and enabled,
576  * FALSE otherwise.
577  *
578  * Since: 2.4
579  */
580 gboolean
581 _gnome_vfs_use_handler_for_scheme (const char *scheme)
582 {
583         GConfClient *client;
584         gboolean ret;
585         char *path;
586         
587         g_return_val_if_fail (scheme != NULL, FALSE);
588         
589         if (!gconf_is_initialized ()) {
590                 if (!gconf_init (0, NULL, NULL)) {
591                         return FALSE;
592                 }
593         }
594         
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);
598         
599         g_free (path);
600         g_object_unref (G_OBJECT (client));
601         
602         return ret;
603 }
604
605 /**
606  * _gnome_vfs_url_show_using_handler_with_env:
607  * @url: the url to show
608  * @envp: environment for the handler
609  * 
610  * Same as gnome_url_show_using_handler except that the handler
611  * will be launched with the given environment.
612  *
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.
619  *
620  * Since: 2.4
621  */
622 GnomeVFSResult
623 _gnome_vfs_url_show_using_handler_with_env (const char  *url,
624                                             char       **envp)
625 {
626         GConfClient *client;
627         char *path;
628         char *scheme;
629         char *template;
630         char **argv;
631         int argc;
632         int i;
633         gboolean ret;
634         
635         g_return_val_if_fail (url != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
636         
637         scheme = gnome_vfs_get_uri_scheme (url);
638         
639         g_return_val_if_fail (scheme != NULL, GNOME_VFS_ERROR_BAD_PARAMETERS);
640         
641         if (!gconf_is_initialized ()) {
642                 if (!gconf_init (0, NULL, NULL)) {
643                         g_free (scheme);
644                         return GNOME_VFS_ERROR_INTERNAL;
645                 }
646         }
647
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);
651         g_free (path);
652
653         if (template == NULL) {
654                 g_free (template);
655                 g_free (scheme);
656                 return GNOME_VFS_ERROR_NO_HANDLER;
657         }
658         
659         if (!g_shell_parse_argv (template,
660                                  &argc,
661                                  &argv,
662                                  NULL)) {
663                 g_free (template);
664                 g_free (scheme);
665                 return GNOME_VFS_ERROR_PARSE;
666         }
667
668         g_free (template);
669
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)) {
673                         g_free (path);
674                         g_free (scheme);
675                         g_strfreev (argv);
676                         return GNOME_VFS_ERROR_INTERNAL;
677                 }
678         }
679         g_free (path);
680         g_free (scheme);
681         
682         g_object_unref (G_OBJECT (client));
683
684         for (i = 0; i < argc; i++) {
685                 char *arg;
686
687                 if (strcmp (argv[i], "%s") != 0)
688                         continue;
689
690                 arg = argv[i];
691                 argv[i] = g_strdup (url);
692                 g_free (arg);
693         }
694
695         ret = g_spawn_async (NULL /* working directory */,
696                              argv,
697                              envp,
698                              G_SPAWN_SEARCH_PATH /* flags */,
699                              NULL /* child_setup */,
700                              NULL /* data */,
701                              NULL /* child_pid */,
702                              NULL);
703         g_strfreev (argv);
704
705         if (!ret) {
706                 return GNOME_VFS_ERROR_LAUNCH;
707         }
708
709         return GNOME_VFS_OK;
710 }
711
712 /* This is a copy from libgnome, internalized here to avoid
713    strange dependency loops */
714
715 /**
716  * _gnome_vfs_prepend_terminal_to_vector:
717  * @argc: a pointer to the vector size
718  * @argv: a pointer to the vector
719  *
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.
728  *
729  * Return value: TRUE if successful, FALSE otherwise.
730  *
731  * Since: 2.4
732  */
733 gboolean
734 _gnome_vfs_prepend_terminal_to_vector (int    *argc,
735                                        char ***argv)
736 {
737         char **real_argv;
738         int real_argc;
739         int i, j;
740         char **term_argv = NULL;
741         int term_argc = 0;
742         GConfClient *client;
743
744         char *terminal = NULL;
745         char **the_argv;
746
747         g_return_val_if_fail (argc != NULL, FALSE);
748         g_return_val_if_fail (argv != NULL, FALSE);
749
750         /* sanity */
751         if(*argv == NULL) {
752                 *argc = 0;
753         }
754         
755         the_argv = *argv;
756
757         /* compute size if not given */
758         if (*argc < 0) {
759                 for (i = 0; the_argv[i] != NULL; i++)
760                         ;
761                 *argc = i;
762         }
763
764         if (!gconf_is_initialized ()) {
765                 if (!gconf_init (0, NULL, NULL)) {
766                         return FALSE;
767                 }
768         }
769
770         client = gconf_client_get_default ();
771         terminal = gconf_client_get_string (client, GCONF_DEFAULT_TERMINAL_EXEC_PATH, NULL);
772         
773         if (terminal) {
774                 gchar *exec_flag;
775                 exec_flag = gconf_client_get_string (client, GCONF_DEFAULT_TERMINAL_ARG_PATH, NULL);
776
777                 if (exec_flag == NULL) {
778                         term_argc = 1;
779                         term_argv = g_new0 (char *, 2);
780                         term_argv[0] = terminal;
781                         term_argv[1] = NULL;
782                 } else {
783                         term_argc = 2;
784                         term_argv = g_new0 (char *, 3);
785                         term_argv[0] = terminal;
786                         term_argv[1] = exec_flag;
787                         term_argv[2] = NULL;
788                 }
789         }
790
791         g_object_unref (G_OBJECT (client));
792
793         if (term_argv == NULL) {
794                 char *check;
795
796                 term_argc = 2;
797                 term_argv = g_new0 (char *, 3);
798
799                 check = g_find_program_in_path ("gnome-terminal");
800                 if (check != NULL) {
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");
805                 } else {
806                         if (check == NULL)
807                                 check = g_find_program_in_path ("nxterm");
808                         if (check == NULL)
809                                 check = g_find_program_in_path ("color-xterm");
810                         if (check == NULL)
811                                 check = g_find_program_in_path ("rxvt");
812                         if (check == NULL)
813                                 check = g_find_program_in_path ("xterm");
814                         if (check == NULL)
815                                 check = g_find_program_in_path ("dtterm");
816                         if (check == NULL) {
817                                 check = g_strdup ("xterm");
818                                 g_warning ("couldn't find a terminal, falling back to xterm");
819                         }
820                         term_argv[0] = check;
821                         term_argv[1] = g_strdup ("-e");
822                 }
823         }
824
825         real_argc = term_argc + *argc;
826         real_argv = g_new (char *, real_argc + 1);
827
828         for (i = 0; i < term_argc; i++)
829                 real_argv[i] = term_argv[i];
830
831         for (j = 0; j < *argc; j++, i++)
832                 real_argv[i] = (char *)the_argv[j];
833
834         real_argv[i] = NULL;
835
836         g_free (*argv);
837         *argv = real_argv;
838         *argc = real_argc;
839
840         /* we use g_free here as we sucked all the inner strings
841          * out from it into real_argv */
842         g_free (term_argv);
843
844         return TRUE;
845 }                 
846