1 /* Various utilities - Unix variants
2 Copyright (C) 1994, 1995, 1996 the Free Software Foundation.
3 Written 1994, 1995, 1996 by:
4 Miguel de Icaza, Janne Kukonlehto, Dugan Porter,
5 Jakub Jelinek, Mauricio Plaza.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program 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
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include <sys/types.h>
27 #include <signal.h> /* struct sigaction */
28 #include <limits.h> /* INT_MAX */
31 #include <errno.h> /* errno */
34 #ifdef HAVE_SYS_IOCTL_H
35 # include <sys/ioctl.h>
40 #include "wtools.h" /* message() */
42 struct sigaction startup_handler
;
44 /* uid of the MC user */
45 static uid_t current_user_uid
= -1;
46 /* List of the gids of the user */
47 static GTree
*current_user_gid
= NULL
;
49 /* Helper function to compare 2 gids */
51 mc_gid_compare (gconstpointer v
, gconstpointer v2
)
53 return ((GPOINTER_TO_UINT(v
) > GPOINTER_TO_UINT(v2
)) ? 1 :
54 (GPOINTER_TO_UINT(v
) < GPOINTER_TO_UINT(v2
)) ? -1 : 0);
57 /* Helper function to delete keys of the gids tree */
59 mc_gid_destroy (gpointer key
, gpointer value
, gpointer data
)
66 /* This function initialize global GTree with the gids of groups,
67 to which user belongs. Tree also store corresponding string
68 with the name of the group.
69 FIXME: Do we need this names at all? If not, we can simplify
70 initialization by eliminating g_strdup's.
72 void init_groups (void)
78 current_user_uid
= getuid ();
79 pwd
= getpwuid (current_user_uid
);
80 g_return_if_fail (pwd
!= NULL
);
82 current_user_gid
= g_tree_new (mc_gid_compare
);
84 /* Put user's primary group first. */
85 if ((grp
= getgrgid (pwd
->pw_gid
)) != NULL
) {
86 g_tree_insert (current_user_gid
,
87 GUINT_TO_POINTER ((int) grp
->gr_gid
),
88 g_strdup (grp
->gr_name
));
92 while ((grp
= getgrent ()) != NULL
) {
93 for (i
= 0; grp
->gr_mem
[i
]; i
++) {
94 if (!strcmp (pwd
->pw_name
, grp
->gr_mem
[i
]) &&
95 !g_tree_lookup (current_user_gid
,
96 GUINT_TO_POINTER ((int) grp
->gr_gid
))) {
97 g_tree_insert (current_user_gid
,
98 GUINT_TO_POINTER ((int) grp
->gr_gid
),
99 g_strdup (grp
->gr_name
));
107 /* Return the index of the permissions triplet */
109 get_user_permissions (struct stat
*buf
) {
111 if (buf
->st_uid
== current_user_uid
|| current_user_uid
== 0)
114 if (current_user_gid
&& g_tree_lookup (current_user_gid
,
115 GUINT_TO_POINTER((int) buf
->st_gid
)))
121 /* Completely destroys the gids tree */
123 destroy_groups (void)
125 g_tree_traverse (current_user_gid
, mc_gid_destroy
, G_POST_ORDER
, NULL
);
126 g_tree_destroy (current_user_gid
);
129 #define UID_CACHE_SIZE 200
130 #define GID_CACHE_SIZE 30
137 static int_cache uid_cache
[UID_CACHE_SIZE
];
138 static int_cache gid_cache
[GID_CACHE_SIZE
];
140 static char *i_cache_match (int id
, int_cache
*cache
, int size
)
144 for (i
= 0; i
< size
; i
++)
145 if (cache
[i
].index
== id
)
146 return cache
[i
].string
;
150 static void i_cache_add (int id
, int_cache
*cache
, int size
, char *text
,
153 g_free (cache
[*last
].string
);
154 cache
[*last
].string
= g_strdup (text
);
155 cache
[*last
].index
= id
;
156 *last
= ((*last
)+1) % size
;
159 char *get_owner (int uid
)
162 static char ibuf
[10];
166 if ((name
= i_cache_match (uid
, uid_cache
, UID_CACHE_SIZE
)) != NULL
)
169 pwd
= getpwuid (uid
);
171 i_cache_add (uid
, uid_cache
, UID_CACHE_SIZE
, pwd
->pw_name
, &uid_last
);
175 g_snprintf (ibuf
, sizeof (ibuf
), "%d", uid
);
180 char *get_group (int gid
)
183 static char gbuf
[10];
187 if ((name
= i_cache_match (gid
, gid_cache
, GID_CACHE_SIZE
)) != NULL
)
190 grp
= getgrgid (gid
);
192 i_cache_add (gid
, gid_cache
, GID_CACHE_SIZE
, grp
->gr_name
, &gid_last
);
195 g_snprintf (gbuf
, sizeof (gbuf
), "%d", gid
);
200 /* Since ncurses uses a handler that automatically refreshes the */
201 /* screen after a SIGCONT, and we don't want this behavior when */
202 /* spawning a child, we save the original handler here */
203 void save_stop_handler (void)
205 sigaction (SIGTSTP
, NULL
, &startup_handler
);
208 int my_system (int flags
, const char *shell
, const char *command
)
210 struct sigaction ignore
, save_intr
, save_quit
, save_stop
;
214 ignore
.sa_handler
= SIG_IGN
;
215 sigemptyset (&ignore
.sa_mask
);
218 sigaction (SIGINT
, &ignore
, &save_intr
);
219 sigaction (SIGQUIT
, &ignore
, &save_quit
);
221 /* Restore the original SIGTSTP handler, we don't want ncurses' */
222 /* handler messing the screen after the SIGCONT */
223 sigaction (SIGTSTP
, &startup_handler
, &save_stop
);
225 if ((pid
= fork ()) < 0){
226 fprintf (stderr
, "\n\nfork () = -1\n");
230 signal (SIGINT
, SIG_DFL
);
231 signal (SIGQUIT
, SIG_DFL
);
232 signal (SIGTSTP
, SIG_DFL
);
233 signal (SIGCHLD
, SIG_DFL
);
235 if (flags
& EXECUTE_AS_SHELL
)
236 execl (shell
, shell
, "-c", command
, (char *) NULL
);
238 execlp (shell
, shell
, command
, (char *) NULL
);
240 _exit (127); /* Exec error */
242 while (waitpid (pid
, &status
, 0) < 0)
248 sigaction (SIGINT
, &save_intr
, NULL
);
249 sigaction (SIGQUIT
, &save_quit
, NULL
);
250 sigaction (SIGTSTP
, &save_stop
, NULL
);
252 return WEXITSTATUS(status
);
257 * Perform tilde expansion if possible.
258 * Always return a newly allocated string, even if it's unchanged.
261 tilde_expand (const char *directory
)
263 struct passwd
*passwd
;
267 if (*directory
!= '~')
268 return g_strdup (directory
);
272 /* d = "~" or d = "~/" */
273 if (!(*p
) || (*p
== PATH_SEP
)) {
274 passwd
= getpwuid (geteuid ());
275 q
= (*p
== PATH_SEP
) ? p
+ 1 : "";
277 q
= strchr (p
, PATH_SEP
);
279 passwd
= getpwnam (p
);
281 name
= g_strndup (p
, q
- p
);
282 passwd
= getpwnam (name
);
288 /* If we can't figure the user name, leave tilde unexpanded */
290 return g_strdup (directory
);
292 return g_strconcat (passwd
->pw_dir
, PATH_SEP_STR
, q
, (char *) NULL
);
297 * Return the directory where mc should keep its temporary files.
298 * This directory is (in Bourne shell terms) "${TMPDIR=/tmp}/mc-$USER"
299 * When called the first time, the directory is created if needed.
300 * The first call should be done early, since we are using fprintf()
301 * and not message() to report possible problems.
306 static char buffer
[64];
307 static const char *tmpdir
;
311 const char *error
= NULL
;
313 /* Check if already initialized */
317 sys_tmp
= getenv ("TMPDIR");
319 sys_tmp
= TMPDIR_DEFAULT
;
322 pwd
= getpwuid (getuid ());
325 g_snprintf (buffer
, sizeof (buffer
), "%s/mc-%s", sys_tmp
,
328 g_snprintf (buffer
, sizeof (buffer
), "%s/mc-%lu", sys_tmp
,
329 (unsigned long) getuid ());
331 canonicalize_pathname (buffer
);
333 if (lstat (buffer
, &st
) == 0) {
334 /* Sanity check for existing directory */
335 if (!S_ISDIR (st
.st_mode
))
336 error
= _("%s is not a directory\n");
337 else if (st
.st_uid
!= getuid ())
338 error
= _("Directory %s is not owned by you\n");
339 else if (((st
.st_mode
& 0777) != 0700)
340 && (chmod (buffer
, 0700) != 0))
341 error
= _("Cannot set correct permissions for directory %s\n");
343 /* Need to create directory */
344 if (mkdir (buffer
, S_IRWXU
) != 0) {
346 _("Cannot create temporary directory %s: %s\n"),
347 buffer
, unix_error_string (errno
));
360 fprintf (stderr
, error
, buffer
);
362 /* Test if sys_tmp is suitable for temporary files */
364 test_fd
= mc_mkstemps (&test_fn
, "mctest", NULL
);
367 test_fd
= open (test_fn
, O_RDONLY
);
376 fprintf (stderr
, _("Temporary files will be created in %s\n"),
380 fprintf (stderr
, _("Temporary files will not be created\n"));
381 tmpdir
= "/dev/null/";
384 fprintf (stderr
, "%s\n", _("Press any key to continue..."));
389 setenv ("MC_TMPDIR", tmpdir
, 1);
395 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
396 /* More than that would be unportable */
397 #define MAX_PIPE_SIZE 4096
399 static int error_pipe
[2]; /* File descriptors of error pipe */
400 static int old_error
; /* File descriptor of old standard error */
402 /* Creates a pipe to hold standard error for a later analysis. */
403 /* The pipe can hold 4096 bytes. Make sure no more is written */
404 /* or a deadlock might occur. */
405 void open_error_pipe (void)
407 if (pipe (error_pipe
) < 0){
408 message (0, _("Warning"), _(" Pipe failed "));
411 if(old_error
< 0 || close(2) || dup (error_pipe
[1]) != 2){
412 message (0, _("Warning"), _(" Dup failed "));
413 close (error_pipe
[0]);
414 close (error_pipe
[1]);
416 close (error_pipe
[1]);
420 * Returns true if an error was displayed
421 * error: -1 - ignore errors, 0 - display warning, 1 - display error
422 * text is prepended to the error message from the pipe
425 close_error_pipe (int error
, const char *text
)
428 char msg
[MAX_PIPE_SIZE
];
434 title
= _("Warning");
439 len
= read (error_pipe
[0], msg
, MAX_PIPE_SIZE
- 1);
443 close (error_pipe
[0]);
446 return 0; /* Just ignore error message */
449 return 0; /* Nothing to show */
451 /* Show message from pipe */
452 message (error
, title
, "%s", msg
);
454 /* Show given text and possible message from pipe */
455 message (error
, title
, " %s \n %s ", text
, msg
);
461 * Canonicalize path, and return a new path. Do everything in place.
462 * The new path differs from path in:
463 * Multiple `/'s are collapsed to a single `/'.
464 * Leading `./'s and trailing `/.'s are removed.
465 * Trailing `/'s are removed.
466 * Non-leading `../'s and trailing `..'s are handled by removing
467 * portions of the path.
468 * Well formed UNC paths are modified only in the local part.
471 canonicalize_pathname (char *path
)
475 char *lpath
= path
; /* path without leading UNC part */
477 /* Detect and preserve UNC paths: //server/... */
478 if (path
[0] == PATH_SEP
&& path
[1] == PATH_SEP
) {
480 while (p
[0] && p
[0] != '/')
482 if (p
[0] == '/' && p
> path
+ 2)
486 if (!lpath
[0] || !lpath
[1])
489 /* Collapse multiple slashes */
492 if (p
[0] == PATH_SEP
&& p
[1] == PATH_SEP
) {
494 while (*(++s
) == PATH_SEP
);
500 /* Collapse "/./" -> "/" */
503 if (p
[0] == PATH_SEP
&& p
[1] == '.' && p
[2] == PATH_SEP
)
509 /* Remove trailing slashes */
510 p
= lpath
+ strlen (lpath
) - 1;
511 while (p
> lpath
&& *p
== PATH_SEP
)
514 /* Remove leading "./" */
515 if (lpath
[0] == '.' && lpath
[1] == PATH_SEP
) {
520 strcpy (lpath
, lpath
+ 2);
524 /* Remove trailing "/" or "/." */
525 len
= strlen (lpath
);
528 if (lpath
[len
- 1] == PATH_SEP
) {
531 if (lpath
[len
- 1] == '.' && lpath
[len
- 2] == PATH_SEP
) {
541 /* Collapse "/.." with the previous part of path */
543 while (p
[0] && p
[1] && p
[2]) {
544 if ((p
[0] != PATH_SEP
|| p
[1] != '.' || p
[2] != '.')
545 || (p
[3] != PATH_SEP
&& p
[3] != 0)) {
550 /* search for the previous token */
552 while (s
>= lpath
&& *s
!= PATH_SEP
)
557 /* If the previous token is "..", we cannot collapse it */
558 if (s
[0] == '.' && s
[1] == '.' && s
+ 2 == p
) {
564 if (s
== lpath
&& *s
== PATH_SEP
) {
565 /* "/../foo" -> "/foo" */
566 strcpy (s
+ 1, p
+ 4);
568 /* "token/../foo" -> "foo" */
571 p
= (s
> lpath
) ? s
- 1 : s
;
577 /* "token/.." -> "." */
578 if (lpath
[0] != PATH_SEP
) {
583 /* "foo/token/.." -> "foo" */
595 #ifdef HAVE_GET_PROCESS_STATS
596 # include <sys/procstats.h>
598 int gettimeofday (struct timeval
*tp
, void *tzp
)
600 return get_process_stats(tp
, PS_SELF
, 0, 0);
602 #endif /* HAVE_GET_PROCESS_STATS */
606 /* The following piece of code was copied from the GNU C Library */
607 /* And is provided here for nextstep who lacks putenv */
609 extern char **environ
;
612 #define __environ environ
616 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
618 putenv (char *string
)
620 const char *const name_end
= strchr (string
, '=');
621 register size_t size
;
624 if (name_end
== NULL
){
625 /* Remove the variable from the environment. */
626 size
= strlen (string
);
627 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
628 if (!strncmp (*ep
, string
, size
) && (*ep
)[size
] == '='){
629 while (ep
[1] != NULL
){
639 for (ep
= __environ
; *ep
!= NULL
; ++ep
)
640 if (!strncmp (*ep
, string
, name_end
- string
) &&
641 (*ep
)[name_end
- string
] == '=')
647 static char **last_environ
= NULL
;
648 char **new_environ
= g_new (char *, size
+ 2);
649 if (new_environ
== NULL
)
651 (void) memcpy ((void *) new_environ
, (void *) __environ
,
652 size
* sizeof (char *));
653 new_environ
[size
] = (char *) string
;
654 new_environ
[size
+ 1] = NULL
;
655 g_free ((void *) last_environ
);
656 last_environ
= new_environ
;
657 __environ
= new_environ
;
660 *ep
= (char *) string
;
664 #endif /* !HAVE_PUTENV */