2 Various utilities - Unix variants
4 Copyright (C) 1994-2014
5 Free Software Foundation, Inc.
8 Miguel de Icaza, 1994, 1995, 1996
9 Janne Kukonlehto, 1994, 1995, 1996
10 Dugan Porter, 1994, 1995, 1996
11 Jakub Jelinek, 1994, 1995, 1996
12 Mauricio Plaza, 1994, 1995, 1996
14 The mc_realpath routine is mostly from uClibc package, written
15 by Rick Sladkey <jrs@world.std.com>
17 This file is part of the Midnight Commander.
19 The Midnight Commander is free software: you can redistribute it
20 and/or modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation, either version 3 of the License,
22 or (at your option) any later version.
24 The Midnight Commander is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 * \brief Source: various utilities - Unix variant
48 #ifdef HAVE_SYS_PARAM_H
49 #include <sys/param.h>
51 #include <sys/types.h>
54 #ifdef HAVE_SYS_IOCTL_H
55 #include <sys/ioctl.h>
57 #ifdef HAVE_GET_PROCESS_STATS
58 #include <sys/procstats.h>
63 #include "lib/global.h"
65 #include "lib/unixcompat.h"
66 #include "lib/vfs/vfs.h" /* VFS_ENCODING_PREFIX */
67 #include "lib/strutil.h" /* str_move() */
69 #include "lib/widget.h" /* message() */
70 #include "lib/vfs/xdirentry.h"
73 #include "lib/charsets.h"
78 /*** global variables ****************************************************************************/
80 struct sigaction startup_handler
;
82 /*** file scope macro definitions ****************************************************************/
84 #define UID_CACHE_SIZE 200
85 #define GID_CACHE_SIZE 30
87 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
88 /* More than that would be unportable */
89 #define MAX_PIPE_SIZE 4096
91 /*** file scope type declarations ****************************************************************/
108 struct sigaction intr
;
109 struct sigaction quit
;
110 struct sigaction stop
;
111 } my_system_sigactions_t
;
113 /*** file scope variables ************************************************************************/
115 static int_cache uid_cache
[UID_CACHE_SIZE
];
116 static int_cache gid_cache
[GID_CACHE_SIZE
];
118 static int error_pipe
[2]; /* File descriptors of error pipe */
119 static int old_error
; /* File descriptor of old standard error */
121 /*** file scope functions ************************************************************************/
122 /* --------------------------------------------------------------------------------------------- */
125 i_cache_match (int id
, int_cache
* cache
, int size
)
129 for (i
= 0; i
< size
; i
++)
130 if (cache
[i
].index
== id
)
131 return cache
[i
].string
;
135 /* --------------------------------------------------------------------------------------------- */
138 i_cache_add (int id
, int_cache
* cache
, int size
, char *text
, int *last
)
140 g_free (cache
[*last
].string
);
141 cache
[*last
].string
= g_strdup (text
);
142 cache
[*last
].index
= id
;
143 *last
= ((*last
) + 1) % size
;
146 /* --------------------------------------------------------------------------------------------- */
148 static my_fork_state_t
157 fprintf (stderr
, "\n\nfork () = -1\n");
168 if (waitpid (pid
, &status
, 0) > 0)
169 return WEXITSTATUS (status
) == 0 ? FORK_PARENT
: FORK_ERROR
;
176 /* --------------------------------------------------------------------------------------------- */
179 my_system__save_sigaction_handlers (my_system_sigactions_t
* sigactions
)
181 struct sigaction ignore
;
183 memset (&ignore
, 0, sizeof (ignore
));
184 ignore
.sa_handler
= SIG_IGN
;
185 sigemptyset (&ignore
.sa_mask
);
187 sigaction (SIGINT
, &ignore
, &sigactions
->intr
);
188 sigaction (SIGQUIT
, &ignore
, &sigactions
->quit
);
190 /* Restore the original SIGTSTP handler, we don't want ncurses' */
191 /* handler messing the screen after the SIGCONT */
192 sigaction (SIGTSTP
, &startup_handler
, &sigactions
->stop
);
195 /* --------------------------------------------------------------------------------------------- */
198 my_system__restore_sigaction_handlers (my_system_sigactions_t
* sigactions
)
200 sigaction (SIGINT
, &sigactions
->intr
, NULL
);
201 sigaction (SIGQUIT
, &sigactions
->quit
, NULL
);
202 sigaction (SIGTSTP
, &sigactions
->stop
, NULL
);
205 /* --------------------------------------------------------------------------------------------- */
208 my_system_make_arg_array (int flags
, const char *shell
, char **execute_name
)
210 GPtrArray
*args_array
;
212 args_array
= g_ptr_array_new ();
214 if ((flags
& EXECUTE_AS_SHELL
) != 0)
216 g_ptr_array_add (args_array
, g_strdup (shell
));
217 g_ptr_array_add (args_array
, g_strdup ("-c"));
218 *execute_name
= g_strdup (shell
);
224 shell_token
= shell
!= NULL
? strchr (shell
, ' ') : NULL
;
225 if (shell_token
== NULL
)
226 *execute_name
= g_strdup (shell
);
228 *execute_name
= g_strndup (shell
, (gsize
) (shell_token
- shell
));
230 g_ptr_array_add (args_array
, g_strdup (shell
));
235 /* --------------------------------------------------------------------------------------------- */
236 /*** public functions ****************************************************************************/
237 /* --------------------------------------------------------------------------------------------- */
246 name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
);
250 pwd
= getpwuid (uid
);
253 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
258 static char ibuf
[10];
260 g_snprintf (ibuf
, sizeof (ibuf
), "%d", uid
);
265 /* --------------------------------------------------------------------------------------------- */
274 name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
);
278 grp
= getgrgid (gid
);
281 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
286 static char gbuf
[10];
288 g_snprintf (gbuf
, sizeof (gbuf
), "%d", gid
);
293 /* --------------------------------------------------------------------------------------------- */
294 /* Since ncurses uses a handler that automatically refreshes the */
295 /* screen after a SIGCONT, and we don't want this behavior when */
296 /* spawning a child, we save the original handler here */
299 save_stop_handler (void)
301 sigaction (SIGTSTP
, NULL
, &startup_handler
);
304 /* --------------------------------------------------------------------------------------------- */
306 * Wrapper for _exit() system call.
307 * The _exit() function has gcc's attribute 'noreturn', and this is reason why we can't
310 * @param status exit code
319 /* --------------------------------------------------------------------------------------------- */
321 * Call external programs.
323 * @parameter flags addition conditions for running external programs.
324 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
325 * Shell (or command) will be found in paths described in PATH variable
326 * (if shell parameter doesn't begin from path delimiter)
327 * @parameter command Command for shell (or first parameter for command, if flags contain EXECUTE_AS_SHELL)
328 * @return 0 if successfull, -1 otherwise
332 my_system (int flags
, const char *shell
, const char *command
)
334 return my_systeml (flags
, shell
, command
, NULL
);
337 /* --------------------------------------------------------------------------------------------- */
339 * Call external programs with various parameters number.
341 * @parameter flags addition conditions for running external programs.
342 * @parameter shell shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
343 * Shell (or command) will be found in pathes described in PATH variable
344 * (if shell parameter doesn't begin from path delimiter)
345 * @parameter ... Command for shell with addition parameters for shell
346 * (or parameters for command, if flags contain EXECUTE_AS_SHELL).
347 * Should be NULL terminated.
348 * @return 0 if successfull, -1 otherwise
353 my_systeml (int flags
, const char *shell
, ...)
355 GPtrArray
*args_array
;
360 args_array
= g_ptr_array_new ();
362 va_start (vargs
, shell
);
363 while ((one_arg
= va_arg (vargs
, char *)) != NULL
)
364 g_ptr_array_add (args_array
, one_arg
);
367 g_ptr_array_add (args_array
, NULL
);
368 status
= my_systemv_flags (flags
, shell
, (char *const *) args_array
->pdata
);
370 g_ptr_array_free (args_array
, TRUE
);
375 /* --------------------------------------------------------------------------------------------- */
377 * Call external programs with array of strings as parameters.
379 * @parameter command command to run. Command will be found in paths described in PATH variable
380 * (if command parameter doesn't begin from path delimiter)
381 * @parameter argv Array of strings (NULL-terminated) with parameters for command
382 * @return 0 if successfull, -1 otherwise
386 my_systemv (const char *command
, char *const argv
[])
388 my_fork_state_t fork_state
;
390 my_system_sigactions_t sigactions
;
392 my_system__save_sigaction_handlers (&sigactions
);
394 fork_state
= my_fork ();
402 signal (SIGINT
, SIG_DFL
);
403 signal (SIGQUIT
, SIG_DFL
);
404 signal (SIGTSTP
, SIG_DFL
);
405 signal (SIGCHLD
, SIG_DFL
);
407 execvp (command
, argv
);
408 my_exit (127); /* Exec error */
415 my_system__restore_sigaction_handlers (&sigactions
);
420 /* --------------------------------------------------------------------------------------------- */
422 * Call external programs with flags and with array of strings as parameters.
424 * @parameter flags addition conditions for running external programs.
425 * @parameter command shell (if flags contain EXECUTE_AS_SHELL), command to run otherwise.
426 * Shell (or command) will be found in paths described in PATH variable
427 * (if shell parameter doesn't begin from path delimiter)
428 * @parameter argv Array of strings (NULL-terminated) with parameters for command
429 * @return 0 if successfull, -1 otherwise
433 my_systemv_flags (int flags
, const char *command
, char *const argv
[])
435 char *execute_name
= NULL
;
436 GPtrArray
*args_array
;
439 args_array
= my_system_make_arg_array (flags
, command
, &execute_name
);
441 for (; argv
!= NULL
&& *argv
!= NULL
; argv
++)
442 g_ptr_array_add (args_array
, *argv
);
444 g_ptr_array_add (args_array
, NULL
);
445 status
= my_systemv (execute_name
, (char *const *) args_array
->pdata
);
447 g_free (execute_name
);
448 g_ptr_array_free (args_array
, TRUE
);
453 /* --------------------------------------------------------------------------------------------- */
456 * Perform tilde expansion if possible.
458 * @param directory pointer to the path
460 * @return newly allocated string, even if it's unchanged.
464 tilde_expand (const char *directory
)
466 struct passwd
*passwd
;
469 if (*directory
!= '~')
470 return g_strdup (directory
);
474 /* d = "~" or d = "~/" */
475 if (!(*p
) || (*p
== PATH_SEP
))
477 passwd
= getpwuid (geteuid ());
478 q
= (*p
== PATH_SEP
) ? p
+ 1 : "";
482 q
= strchr (p
, PATH_SEP
);
485 passwd
= getpwnam (p
);
491 name
= g_strndup (p
, q
- p
);
492 passwd
= getpwnam (name
);
498 /* If we can't figure the user name, leave tilde unexpanded */
500 return g_strdup (directory
);
502 return g_strconcat (passwd
->pw_dir
, PATH_SEP_STR
, q
, (char *) NULL
);
505 /* --------------------------------------------------------------------------------------------- */
507 * Creates a pipe to hold standard error for a later analysis.
508 * The pipe can hold 4096 bytes. Make sure no more is written
509 * or a deadlock might occur.
513 open_error_pipe (void)
515 if (pipe (error_pipe
) < 0)
517 message (D_NORMAL
, _("Warning"), _("Pipe failed"));
519 old_error
= dup (STDERR_FILENO
);
520 if (old_error
< 0 || close (STDERR_FILENO
) != 0 || dup (error_pipe
[1]) != STDERR_FILENO
)
522 message (D_NORMAL
, _("Warning"), _("Dup failed"));
524 close (error_pipe
[0]);
530 * Settng stderr in nonblocking mode as we close it earlier, than
531 * program stops. We try to read some error at program startup,
532 * but we should not block on it.
534 * TODO: make piped stdin/stderr poll()/select()able to get rid
538 fd_flags
= fcntl (error_pipe
[0], F_GETFL
, NULL
);
541 fd_flags
|= O_NONBLOCK
;
542 if (fcntl (error_pipe
[0], F_SETFL
, fd_flags
) == -1)
544 /* TODO: handle it somehow */
548 /* we never write there */
549 close (error_pipe
[1]);
553 /* --------------------------------------------------------------------------------------------- */
557 * @param error '-1' - ignore errors, '0' - display warning, '1' - display error
558 * @param text is prepended to the error message from the pipe
560 * @return not 0 if an error was displayed
564 close_error_pipe (int error
, const char *text
)
567 char msg
[MAX_PIPE_SIZE
];
571 if (error_pipe
[0] == -1)
574 if (error
< 0 || (error
> 0 && (error
& D_ERROR
) != 0))
577 title
= _("Warning");
580 if (dup2 (old_error
, STDERR_FILENO
) == -1)
585 message (error
, MSG_ERROR
, _("Error dup'ing old error pipe"));
589 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
- 1);
593 close (error_pipe
[0]);
597 return 0; /* Just ignore error message */
601 return 0; /* Nothing to show */
603 /* Show message from pipe */
604 message (error
, title
, "%s", msg
);
608 /* Show given text and possible message from pipe */
609 message (error
, title
, "%s\n%s", text
, msg
);
614 /* --------------------------------------------------------------------------------------------- */
616 * Canonicalize path, and return a new path. Do everything in place.
617 * The new path differs from path in:
618 * Multiple '/'s are collapsed to a single '/'.
619 * Leading './'s and trailing '/.'s are removed.
620 * Trailing '/'s are removed.
621 * Non-leading '../'s and trailing '..'s are handled by removing
622 * portions of the path.
623 * Well formed UNC paths are modified only in the local part.
627 custom_canonicalize_pathname (char *path
, CANON_PATH_FLAGS flags
)
630 char *lpath
= path
; /* path without leading UNC part */
631 const size_t url_delim_len
= strlen (VFS_PATH_URL_DELIMITER
);
633 /* Detect and preserve UNC paths: //server/... */
634 if ((flags
& CANON_PATH_GUARDUNC
) && path
[0] == PATH_SEP
&& path
[1] == PATH_SEP
)
637 while (p
[0] && p
[0] != '/')
639 if (p
[0] == '/' && p
> path
+ 2)
643 if (!lpath
[0] || !lpath
[1])
646 if (flags
& CANON_PATH_JOINSLASHES
)
648 /* Collapse multiple slashes */
652 if (p
[0] == PATH_SEP
&& p
[1] == PATH_SEP
&& (p
== lpath
|| *(p
- 1) != ':'))
655 while (*(++s
) == PATH_SEP
);
662 if (flags
& CANON_PATH_JOINSLASHES
)
664 /* Collapse "/./" -> "/" */
668 if (p
[0] == PATH_SEP
&& p
[1] == '.' && p
[2] == PATH_SEP
)
675 if (flags
& CANON_PATH_REMSLASHDOTS
)
679 /* Remove trailing slashes */
680 p
= lpath
+ strlen (lpath
) - 1;
681 while (p
> lpath
&& *p
== PATH_SEP
)
683 if (p
>= lpath
- (url_delim_len
+ 1)
684 && strncmp (p
- url_delim_len
+ 1, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
689 /* Remove leading "./" */
690 if (lpath
[0] == '.' && lpath
[1] == PATH_SEP
)
699 str_move (lpath
, lpath
+ 2);
703 /* Remove trailing "/" or "/." */
704 len
= strlen (lpath
);
707 if (lpath
[len
- 1] == PATH_SEP
708 && (len
< url_delim_len
709 || strncmp (lpath
+ len
- url_delim_len
, VFS_PATH_URL_DELIMITER
,
710 url_delim_len
) != 0))
712 lpath
[len
- 1] = '\0';
716 if (lpath
[len
- 1] == '.' && lpath
[len
- 2] == PATH_SEP
)
725 lpath
[len
- 2] = '\0';
731 if (flags
& CANON_PATH_REMDOUBLEDOTS
)
734 const size_t enc_prefix_len
= strlen (VFS_ENCODING_PREFIX
);
735 #endif /* HAVE_CHARSET */
737 /* Collapse "/.." with the previous part of path */
739 while (p
[0] && p
[1] && p
[2])
741 if ((p
[0] != PATH_SEP
|| p
[1] != '.' || p
[2] != '.') || (p
[3] != PATH_SEP
&& p
[3] != 0))
747 /* search for the previous token */
749 if (s
>= lpath
+ url_delim_len
- 2
750 && strncmp (s
- url_delim_len
+ 2, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
752 s
-= (url_delim_len
- 2);
753 while (s
>= lpath
&& *s
-- != PATH_SEP
);
758 if (s
- url_delim_len
> lpath
759 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
761 char *vfs_prefix
= s
- url_delim_len
;
762 struct vfs_class
*vclass
;
764 while (vfs_prefix
> lpath
&& *--vfs_prefix
!= PATH_SEP
);
765 if (*vfs_prefix
== PATH_SEP
)
767 *(s
- url_delim_len
) = '\0';
769 vclass
= vfs_prefix_to_class (vfs_prefix
);
770 *(s
- url_delim_len
) = *VFS_PATH_URL_DELIMITER
;
774 struct vfs_s_subclass
*sub
= (struct vfs_s_subclass
*) vclass
->data
;
775 if (sub
!= NULL
&& sub
->flags
& VFS_S_REMOTE
)
791 /* If the previous token is "..", we cannot collapse it */
792 if (s
[0] == '.' && s
[1] == '.' && s
+ 2 == p
)
800 if (s
== lpath
&& *s
== PATH_SEP
)
802 /* "/../foo" -> "/foo" */
803 str_move (s
+ 1, p
+ 4);
807 /* "token/../foo" -> "foo" */
809 if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
810 && (is_supported_encoding (s
+ enc_prefix_len
)))
811 /* special case: remove encoding */
814 #endif /* HAVE_CHARSET */
817 p
= (s
> lpath
) ? s
- 1 : s
;
824 /* "token/.." -> "." */
825 if (lpath
[0] != PATH_SEP
)
833 /* "foo/token/.." -> "foo" */
837 else if ((strncmp (s
, VFS_ENCODING_PREFIX
, enc_prefix_len
) == 0)
838 && (is_supported_encoding (s
+ enc_prefix_len
)))
840 /* special case: remove encoding */
845 /* search for the previous token */
846 /* s[-1] == PATH_SEP */
848 while (p
>= lpath
&& *p
!= PATH_SEP
)
854 #endif /* HAVE_CHARSET */
857 if (s
>= lpath
+ url_delim_len
858 && strncmp (s
- url_delim_len
, VFS_PATH_URL_DELIMITER
, url_delim_len
) == 0)
870 /* --------------------------------------------------------------------------------------------- */
873 canonicalize_pathname (char *path
)
875 custom_canonicalize_pathname (path
, CANON_PATH_ALL
);
878 /* --------------------------------------------------------------------------------------------- */
880 #ifdef HAVE_GET_PROCESS_STATS
882 gettimeofday (struct timeval
*tp
, void *tzp
)
884 return get_process_stats (tp
, PS_SELF
, 0, 0);
886 #endif /* HAVE_GET_PROCESS_STATS */
888 /* --------------------------------------------------------------------------------------------- */
890 #ifndef HAVE_REALPATH
892 mc_realpath (const char *path
, char *resolved_path
)
894 char copy_path
[PATH_MAX
];
895 char link_path
[PATH_MAX
];
896 char got_path
[PATH_MAX
];
897 char *new_path
= got_path
;
902 /* Make a copy of the source path since we may need to modify it. */
903 if (strlen (path
) >= PATH_MAX
- 2)
905 errno
= ENAMETOOLONG
;
908 strcpy (copy_path
, path
);
910 max_path
= copy_path
+ PATH_MAX
- 2;
911 /* If it's a relative pathname use getwd for starters. */
915 new_path
= g_get_current_dir ();
916 if (new_path
== NULL
)
918 strcpy (got_path
, "");
922 g_snprintf (got_path
, PATH_MAX
, "%s", new_path
);
927 new_path
+= strlen (got_path
);
928 if (new_path
[-1] != '/')
936 /* Expand each slash-separated pathname component. */
937 while (*path
!= '\0')
939 /* Ignore stray "/". */
948 if (path
[1] == '\0' || path
[1] == '/')
955 if (path
[2] == '\0' || path
[2] == '/')
958 /* Ignore ".." at root. */
959 if (new_path
== got_path
+ 1)
961 /* Handle ".." by backing up. */
962 while ((--new_path
)[-1] != '/');
967 /* Safely copy the next pathname component. */
968 while (*path
!= '\0' && *path
!= '/')
972 errno
= ENAMETOOLONG
;
975 *new_path
++ = *path
++;
978 /* Protect against infinite loops. */
979 if (readlinks
++ > MAXSYMLINKS
)
984 /* See if latest pathname component is a symlink. */
986 n
= readlink (got_path
, link_path
, PATH_MAX
- 1);
989 /* EINVAL means the file exists but isn't a symlink. */
992 /* Make sure it's null terminated. */
994 strcpy (resolved_path
, got_path
);
1000 /* Note: readlink doesn't add the null byte. */
1001 link_path
[n
] = '\0';
1002 if (*link_path
== '/')
1003 /* Start over for an absolute symlink. */
1004 new_path
= got_path
;
1006 /* Otherwise back up over this component. */
1007 while (*(--new_path
) != '/');
1008 /* Safe sex check. */
1009 if (strlen (path
) + n
>= PATH_MAX
- 2)
1011 errno
= ENAMETOOLONG
;
1014 /* Insert symlink contents into path. */
1015 strcat (link_path
, path
);
1016 strcpy (copy_path
, link_path
);
1019 #endif /* S_IFLNK */
1022 /* Delete trailing slash but don't whomp a lone slash. */
1023 if (new_path
!= got_path
+ 1 && new_path
[-1] == '/')
1025 /* Make sure it's null terminated. */
1027 strcpy (resolved_path
, got_path
);
1028 return resolved_path
;
1030 #endif /* HAVE_REALPATH */
1032 /* --------------------------------------------------------------------------------------------- */
1034 * Return the index of the permissions triplet
1039 get_user_permissions (struct stat
*st
)
1041 static gboolean initialized
= FALSE
;
1042 static gid_t
*groups
;
1051 ngroups
= getgroups (0, NULL
);
1053 ngroups
= 0; /* ignore errors */
1055 /* allocate space for one element in addition to what
1056 * will be filled by getgroups(). */
1057 groups
= g_new (gid_t
, ngroups
+ 1);
1061 ngroups
= getgroups (ngroups
, groups
);
1063 ngroups
= 0; /* ignore errors */
1066 /* getgroups() may or may not return the effective group ID,
1067 * so we always include it at the end of the list. */
1068 groups
[ngroups
++] = getegid ();
1073 if (st
->st_uid
== uid
|| uid
== 0)
1076 for (i
= 0; i
< ngroups
; i
++)
1078 if (st
->st_gid
== groups
[i
])
1085 /* --------------------------------------------------------------------------------------------- */
1087 * Build filename from arguments.
1088 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1092 mc_build_filenamev (const char *first_element
, va_list args
)
1095 const char *element
= first_element
;
1099 if (element
== NULL
)
1102 path
= g_string_new ("");
1104 absolute
= (*first_element
!= '\0' && *first_element
== PATH_SEP
);
1108 if (*element
== '\0')
1109 element
= va_arg (args
, char *);
1116 tmp_element
= g_strdup (element
);
1118 element
= va_arg (args
, char *);
1120 canonicalize_pathname (tmp_element
);
1121 len
= strlen (tmp_element
);
1122 start
= (tmp_element
[0] == PATH_SEP
) ? tmp_element
+ 1 : tmp_element
;
1124 g_string_append (path
, start
);
1125 if (tmp_element
[len
- 1] != PATH_SEP
&& element
!= NULL
)
1126 g_string_append_c (path
, PATH_SEP
);
1128 g_free (tmp_element
);
1131 while (element
!= NULL
);
1134 g_string_prepend_c (path
, PATH_SEP
);
1136 ret
= g_string_free (path
, FALSE
);
1137 canonicalize_pathname (ret
);
1142 /* --------------------------------------------------------------------------------------------- */
1144 * Build filename from arguments.
1145 * Like to g_build_filename(), but respect VFS_PATH_URL_DELIMITER
1149 mc_build_filename (const char *first_element
, ...)
1154 if (first_element
== NULL
)
1157 va_start (args
, first_element
);
1158 ret
= mc_build_filenamev (first_element
, args
);
1163 /* --------------------------------------------------------------------------------------------- */