*** empty log message ***
[midnight-commander.git] / src / utilunix.c
blobd99e5ab0b0f69fe4aa995c493fa6927c47ff91af
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 The file_date routine is mostly from GNU's fileutils package,
8 written by Richard Stallman and David MacKenzie.
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 #include <config.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #ifdef HAVE_UNISTD_H
28 # include <unistd.h>
29 #endif
30 #include <fcntl.h>
31 #include <signal.h> /* my_system */
32 #include <limits.h> /* INT_MAX */
33 #include <sys/stat.h>
34 #include <stdarg.h>
35 #include <errno.h> /* my_system */
36 #include <string.h>
37 #include <ctype.h>
38 #ifdef HAVE_SYS_IOCTL_H
39 # include <sys/ioctl.h>
40 #endif
41 #ifdef __QNX__
42 # include <unix.h> /* exec*() from <process.h> */
43 #endif
45 #include "global.h"
46 #include "fsusage.h"
47 #include "mountlist.h"
48 #include "dialog.h" /* message() */
49 #include "../vfs/vfs.h" /* mc_read() */
51 struct sigaction startup_handler;
53 int
54 max_open_files (void)
56 static int files;
58 if (files)
59 return files;
61 #ifdef HAVE_SYSCONF
62 files = sysconf (_SC_OPEN_MAX);
63 if (files != -1)
64 return files;
65 #endif
66 #ifdef OPEN_MAX
67 return files = OPEN_MAX;
68 #else
69 return files = 256;
70 #endif
73 #ifndef VFS_STANDALONE
74 /* uid of the MC user */
75 uid_t current_user_uid = -1;
76 /* List of the gids of the user */
77 GTree *current_user_gid = NULL;
79 /* Helper function to compare 2 gids */
80 static gint
81 mc_gid_compare (gconstpointer v, gconstpointer v2)
83 return ((GPOINTER_TO_UINT(v) > GPOINTER_TO_UINT(v2)) ? 1 :
84 (GPOINTER_TO_UINT(v) < GPOINTER_TO_UINT(v2)) ? -1 : 0);
87 /* Helper function to delete keys of the gids tree */
88 static gint
89 mc_gid_destroy (gpointer key, gpointer value, gpointer data)
91 g_free (value);
93 return FALSE;
96 /* This function initialize global GTree with the gids of groups,
97 to which user belongs. Tree also store corresponding string
98 with the name of the group.
99 FIXME: Do we need this names at all? If not, we can simplify
100 initialization by eliminating g_strdup's.
102 void init_groups (void)
104 int i;
105 struct passwd *pwd;
106 struct group *grp;
108 current_user_uid = getuid ();
110 pwd = getpwuid (current_user_uid);
112 g_return_if_fail (pwd != NULL);
114 grp = getgrgid (pwd->pw_gid);
116 g_return_if_fail (grp != NULL);
118 current_user_gid = g_tree_new (mc_gid_compare);
120 g_tree_insert (current_user_gid,
121 GUINT_TO_POINTER(grp->gr_gid), g_strdup(grp->gr_name));
123 setgrent ();
125 while ((grp = getgrent ()))
127 for (i = 0; grp->gr_mem[i]; i++)
129 if (!strcmp (pwd->pw_name, grp->gr_mem[i]) &&
130 !g_tree_lookup (current_user_gid, GUINT_TO_POINTER(grp->gr_gid)))
132 g_tree_insert (current_user_gid,
133 GUINT_TO_POINTER(grp->gr_gid), g_strdup(grp->gr_name));
134 break;
138 endgrent ();
141 /* Return the index of the permissions triplet */
143 get_user_permissions (struct stat *buf) {
145 if (buf->st_uid == current_user_uid || current_user_uid == 0)
146 return 0;
148 if(current_user_gid && g_tree_lookup (current_user_gid, GUINT_TO_POINTER(buf->st_gid)))
149 return 1;
151 return 2;
154 /* Completely destroys the gids tree */
155 void
156 destroy_groups (void)
158 g_tree_traverse (current_user_gid, mc_gid_destroy, G_POST_ORDER, NULL);
159 g_tree_destroy (current_user_gid);
162 #define UID_CACHE_SIZE 200
163 #define GID_CACHE_SIZE 30
165 typedef struct {
166 int index;
167 char *string;
168 } int_cache;
170 static int_cache uid_cache [UID_CACHE_SIZE];
171 static int_cache gid_cache [GID_CACHE_SIZE];
173 static char *i_cache_match (int id, int_cache *cache, int size)
175 int i;
177 for (i = 0; i < size; i++)
178 if (cache [i].index == id)
179 return cache [i].string;
180 return 0;
183 static void i_cache_add (int id, int_cache *cache, int size, char *text,
184 int *last)
186 if (cache [*last].string)
187 g_free (cache [*last].string);
188 cache [*last].string = g_strdup (text);
189 cache [*last].index = id;
190 *last = ((*last)+1) % size;
193 char *get_owner (int uid)
195 struct passwd *pwd;
196 static char ibuf [10];
197 char *name;
198 static int uid_last;
200 if ((name = i_cache_match (uid, uid_cache, UID_CACHE_SIZE)) != NULL)
201 return name;
203 pwd = getpwuid (uid);
204 if (pwd){
205 i_cache_add (uid, uid_cache, UID_CACHE_SIZE, pwd->pw_name, &uid_last);
206 return pwd->pw_name;
208 else {
209 g_snprintf (ibuf, sizeof (ibuf), "%d", uid);
210 return ibuf;
214 char *get_group (int gid)
216 struct group *grp;
217 static char gbuf [10];
218 char *name;
219 static int gid_last;
221 if ((name = i_cache_match (gid, gid_cache, GID_CACHE_SIZE)) != NULL)
222 return name;
224 grp = getgrgid (gid);
225 if (grp){
226 i_cache_add (gid, gid_cache, GID_CACHE_SIZE, grp->gr_name, &gid_last);
227 return grp->gr_name;
228 } else {
229 g_snprintf (gbuf, sizeof (gbuf), "%d", gid);
230 return gbuf;
234 /* Since ncurses uses a handler that automatically refreshes the */
235 /* screen after a SIGCONT, and we don't want this behavior when */
236 /* spawning a child, we save the original handler here */
237 void save_stop_handler (void)
239 sigaction (SIGTSTP, NULL, &startup_handler);
241 #endif /* VFS_STANDALONE */
243 int my_system (int flags, const char *shell, const char *command)
245 struct sigaction ignore, save_intr, save_quit, save_stop;
246 pid_t pid;
247 int status = 0;
249 ignore.sa_handler = SIG_IGN;
250 sigemptyset (&ignore.sa_mask);
251 ignore.sa_flags = 0;
253 sigaction (SIGINT, &ignore, &save_intr);
254 sigaction (SIGQUIT, &ignore, &save_quit);
256 /* Restore the original SIGTSTP handler, we don't want ncurses' */
257 /* handler messing the screen after the SIGCONT */
258 sigaction (SIGTSTP, &startup_handler, &save_stop);
260 if ((pid = fork ()) < 0){
261 fprintf (stderr, "\n\nfork () = -1\n");
262 return -1;
264 if (pid == 0){
265 signal (SIGINT, SIG_DFL);
266 signal (SIGQUIT, SIG_DFL);
267 signal (SIGTSTP, SIG_DFL);
268 signal (SIGCHLD, SIG_DFL);
270 #if 0
271 prepare_environment ();
272 #endif
274 #ifdef USE_VFS
275 if (flags & EXECUTE_SETUID)
276 # if defined (HAVE_SETUID)
277 setuid (vfs_uid);
278 # elif defined (HAVE_SETREUID)
279 setreuid (vfs_uid, vfs_uid);
280 # else
281 ; /* Cannot drop privileges */
282 # endif
283 #endif
285 if (flags & EXECUTE_AS_SHELL)
286 execl (shell, shell, "-c", command, NULL);
287 else
288 execlp (shell, shell, command, NULL);
290 _exit (127); /* Exec error */
291 } else {
292 while (waitpid (pid, &status, 0) < 0)
293 if (errno != EINTR){
294 status = -1;
295 break;
298 sigaction (SIGINT, &save_intr, NULL);
299 sigaction (SIGQUIT, &save_quit, NULL);
300 sigaction (SIGTSTP, &save_stop, NULL);
302 #ifdef SCO_FLAVOR
303 waitpid(-1, NULL, WNOHANG);
304 #endif /* SCO_FLAVOR */
306 return WEXITSTATUS(status);
309 /* Returns a newly allocated string, if directory does not exist, return 0 */
310 char *tilde_expand (const char *directory)
312 struct passwd *passwd;
313 const char *p;
314 char *name;
316 if (*directory != '~')
317 return g_strdup (directory);
319 directory++;
321 p = strchr (directory, PATH_SEP);
323 /* d = "~" or d = "~/" */
324 if (!(*directory) || (*directory == PATH_SEP)){
325 passwd = getpwuid (geteuid ());
326 p = (*directory == PATH_SEP) ? directory+1 : "";
327 } else {
328 if (!p){
329 passwd = getpwnam (directory);
330 } else {
331 name = g_malloc (p - directory + 1);
332 strncpy (name, directory, p - directory);
333 name [p - directory] = 0;
334 passwd = getpwnam (name);
335 g_free (name);
339 /* If we can't figure the user name, return NULL */
340 if (!passwd)
341 return 0;
343 return g_strconcat (passwd->pw_dir, PATH_SEP_STR, p, NULL);
346 #ifndef VFS_STANDALONE
348 set_nonblocking (int fd)
350 int val;
352 val = fcntl (fd, F_GETFL, 0);
353 val |= O_NONBLOCK;
354 return fcntl (fd, F_SETFL, val) != -1;
357 /* Pipes are guaranteed to be able to hold at least 4096 bytes */
358 /* More than that would be unportable */
359 #define MAX_PIPE_SIZE 4096
361 static int error_pipe[2]; /* File descriptors of error pipe */
362 static int old_error; /* File descriptor of old standard error */
364 /* Creates a pipe to hold standard error for a later analysis. */
365 /* The pipe can hold 4096 bytes. Make sure no more is written */
366 /* or a deadlock might occur. */
367 void open_error_pipe (void)
369 if (pipe (error_pipe) < 0){
370 message (0, _(" Warning "), _(" Pipe failed "));
372 old_error = dup (2);
373 if(old_error < 0 || close(2) || dup (error_pipe[1]) != 2){
374 message (0, _(" Warning "), _(" Dup failed "));
375 close (error_pipe[0]);
376 close (error_pipe[1]);
378 close (error_pipe[1]);
382 * Returns true if an error was displayed
385 close_error_pipe (int error, char *text)
387 char *title;
388 char msg[MAX_PIPE_SIZE];
389 int len = 0;
391 if (error)
392 title = MSG_ERROR;
393 else
394 title = _(" Warning ");
395 if (old_error >= 0){
396 close (2);
397 dup (old_error);
398 close (old_error);
399 len = read (error_pipe[0], msg, MAX_PIPE_SIZE);
401 if (len >= 0)
402 msg[len] = 0;
403 close (error_pipe[0]);
405 if (error < 0)
406 return 0; /* Just ignore error message */
407 if (text == NULL){
408 if (len == 0) return 0; /* Nothing to show */
410 /* Show message from pipe */
411 message (error, title, msg);
412 } else {
413 /* Show given text and possible message from pipe */
414 message (error, title, " %s \n %s ", text, msg);
416 return 1;
419 /* Checks for messages in the error pipe,
420 * closes the pipe and displays an error box if needed
422 void check_error_pipe (void)
424 char error[MAX_PIPE_SIZE];
425 int len = 0;
426 if (old_error >= 0){
427 while (len < MAX_PIPE_SIZE)
429 fd_set select_set;
430 struct timeval timeout;
431 FD_ZERO (&select_set);
432 FD_SET (error_pipe[0], &select_set);
433 timeout.tv_sec = 0;
434 timeout.tv_usec = 0;
435 select (FD_SETSIZE, &select_set, 0, 0, &timeout);
436 if (!FD_ISSET (0, &select_set))
437 break;
438 read (error_pipe[0], error + len, 1);
439 len ++;
441 error[len] = 0;
442 close (error_pipe[0]);
444 if (len > 0)
445 message (0, _(" Warning "), error);
447 #endif
449 static struct sigaction ignore, save_intr, save_quit, save_stop;
451 /* INHANDLE is a result of some mc_open call to any vfs, this function
452 returns a normal handle (to be used with read) of a pipe for reading
453 of the output of COMMAND with arguments ... (must include argv[0] as
454 well) which gets as its input at most INLEN bytes from the INHANDLE
455 using mc_read. You have to call mc_doublepclose to close the returned
456 handle afterwards. If INLEN is -1, we read as much as we can :) */
457 int mc_doublepopen (int inhandle, int inlen, pid_t *the_pid, char *command, ...)
459 int pipe0 [2], pipe1 [2];
460 pid_t pid;
462 #define closepipes() close(pipe0[0]);close(pipe0[1]);close(pipe1[0]);close(pipe1[1])
464 pipe (pipe0); pipe (pipe1);
465 ignore.sa_handler = SIG_IGN;
466 sigemptyset (&ignore.sa_mask);
467 ignore.sa_flags = 0;
469 sigaction (SIGINT, &ignore, &save_intr);
470 sigaction (SIGQUIT, &ignore, &save_quit);
471 sigaction (SIGTSTP, &startup_handler, &save_stop);
473 switch (pid = fork ()) {
474 case -1:
475 closepipes ();
476 return -1;
477 case 0: {
478 sigaction (SIGINT, &save_intr, NULL);
479 sigaction (SIGQUIT, &save_quit, NULL);
480 switch (pid = fork ()) {
481 case -1:
482 closepipes ();
483 _exit (1);
484 case 0: {
485 #define MAXARGS 16
486 int argno;
487 char *args[MAXARGS];
488 va_list ap;
489 int nulldevice;
491 nulldevice = open ("/dev/null", O_WRONLY);
492 close (0);
493 dup (pipe0 [0]);
494 close (1);
495 dup (pipe1 [1]);
496 close (2);
497 dup (nulldevice);
498 close (nulldevice);
499 closepipes ();
500 va_start (ap, command);
501 argno = 0;
502 while ((args[argno++] = va_arg(ap, char *)) != NULL)
503 if (argno == (MAXARGS - 1)) {
504 args[argno] = NULL;
505 break;
507 va_end (ap);
508 execvp (command, args);
510 /* If we are here exec has failed */
511 _exit (0);
513 default:
515 char buffer [8192];
516 int i;
518 close (pipe0 [0]);
519 close (pipe1 [0]);
520 close (pipe1 [1]);
521 while ((i = mc_read (inhandle, buffer,
522 (inlen == -1 || inlen > 8192)
523 ? 8192 : inlen)) > 0) {
524 write (pipe0 [1], buffer, i);
525 if (inlen != -1) {
526 inlen -= i;
527 if (!inlen)
528 break;
531 close (inhandle);
532 close (pipe0 [1]);
533 while (waitpid (pid, &i, 0) < 0)
534 if (errno != EINTR)
535 break;
537 _exit (i);
541 default:
542 *the_pid = pid;
543 break;
545 close (pipe0 [0]);
546 close (pipe0 [1]);
547 close (pipe1 [1]);
548 return pipe1 [0];
551 int mc_doublepclose (int pipe, pid_t pid)
553 int status = 0;
555 close (pipe);
556 waitpid (pid, &status, 0);
557 #ifdef SCO_FLAVOR
558 waitpid (-1, NULL, WNOHANG);
559 #endif /* SCO_FLAVOR */
560 sigaction (SIGINT, &save_intr, NULL);
561 sigaction (SIGQUIT, &save_quit, NULL);
562 sigaction (SIGTSTP, &save_stop, NULL);
564 return status;
567 /* Canonicalize path, and return a new path. Do everything in situ.
568 The new path differs from path in:
569 Multiple `/'s are collapsed to a single `/'.
570 Leading `./'s and trailing `/.'s are removed.
571 Trailing `/'s are removed.
572 Non-leading `../'s and trailing `..'s are handled by removing
573 portions of the path. */
574 char *canonicalize_pathname (char *path)
576 int i, start;
577 char stub_char;
579 stub_char = (*path == PATH_SEP) ? PATH_SEP : '.';
581 /* Walk along path looking for things to compact. */
582 i = 0;
583 while (path[i]) {
585 while (path[i] && path[i] != PATH_SEP)
586 i++;
588 start = i++;
590 /* If we didn't find any slashes, then there is nothing left to do. */
591 if (!path[start])
592 break;
594 #if defined(__QNX__)
596 ** QNX accesses the directories of nodes on its peer-to-peer
597 ** network by prefixing their directories with "//[nid]".
598 ** We don't want to collapse two '/'s if they're at the start
599 ** of the path, followed by digits, and ending with a '/'.
601 if (start == 0 && i == 1)
603 char *p = path + 2;
604 char *q = strchr(p, PATH_SEP);
606 if (q > p)
608 *q = 0;
609 if (!strcspn(p, "0123456789"))
611 start = q - path;
612 i = start + 1;
614 *q = PATH_SEP;
617 #endif
619 /* Handle multiple `/'s in a row. */
620 while (path[i] == PATH_SEP)
621 i++;
623 if ((start + 1) != i) {
624 strcpy (path + start + 1, path + i);
625 i = start + 1;
628 /* Check for trailing `/'. */
629 if (start && !path[i]) {
630 zero_last:
631 path[--i] = '\0';
632 break;
635 /* Check for `../', `./' or trailing `.' by itself. */
636 if (path[i] == '.') {
637 /* Handle trailing `.' by itself. */
638 if (!path[i + 1])
639 goto zero_last;
641 /* Handle `./'. */
642 if (path[i + 1] == PATH_SEP) {
643 strcpy (path + i, path + i + 1);
644 i = start;
645 continue;
648 /* Handle `../' or trailing `..' by itself.
649 Remove the previous ?/ part with the exception of
650 ../, which we should leave intact. */
651 if (path[i + 1] == '.' && (path[i + 2] == PATH_SEP || !path[i + 2])) {
652 while (--start > -1 && path[start] != PATH_SEP);
653 if (!strncmp (path + start + 1, "../", 3))
654 continue;
655 strcpy (path + start + 1, path + i + 2);
656 i = start;
657 continue;
662 if (!*path) {
663 *path = stub_char;
664 path[1] = '\0';
666 return path;
669 #ifndef VFS_STANDALONE
670 #ifdef SCO_FLAVOR
671 int gettimeofday( struct timeval * tv, struct timezone * tz)
673 struct timeb tb;
674 struct tm * l;
676 ftime( &tb );
677 if (errno == EFAULT)
678 return -1;
679 l = localtime(&tb.time);
680 tv->tv_sec = l->tm_sec;
681 tv->tv_usec = (long) tb.millitm;
682 return 0;
684 #endif /* SCO_FLAVOR */
686 #ifndef HAVE_PUTENV
688 /* The following piece of code was copied from the GNU C Library */
689 /* And is provided here for nextstep who lacks putenv */
691 extern char **environ;
693 #ifndef HAVE_GNU_LD
694 #define __environ environ
695 #endif
698 /* Put STRING, which is of the form "NAME=VALUE", in the environment. */
700 putenv (const char *string)
702 const char *const name_end = strchr (string, '=');
703 register size_t size;
704 register char **ep;
706 if (name_end == NULL){
707 /* Remove the variable from the environment. */
708 size = strlen (string);
709 for (ep = __environ; *ep != NULL; ++ep)
710 if (!strncmp (*ep, string, size) && (*ep)[size] == '='){
711 while (ep[1] != NULL){
712 ep[0] = ep[1];
713 ++ep;
715 *ep = NULL;
716 return 0;
720 size = 0;
721 for (ep = __environ; *ep != NULL; ++ep)
722 if (!strncmp (*ep, string, name_end - string) &&
723 (*ep)[name_end - string] == '=')
724 break;
725 else
726 ++size;
728 if (*ep == NULL){
729 static char **last_environ = NULL;
730 char **new_environ = g_new (char *, size + 2);
731 if (new_environ == NULL)
732 return -1;
733 (void) memcpy ((void *) new_environ, (void *) __environ,
734 size * sizeof (char *));
735 new_environ[size] = (char *) string;
736 new_environ[size + 1] = NULL;
737 if (last_environ != NULL)
738 g_free ((void *) last_environ);
739 last_environ = new_environ;
740 __environ = new_environ;
742 else
743 *ep = (char *) string;
745 return 0;
747 #endif /* !HAVE_PUTENV */
749 #ifdef HAVE_GET_PROCESS_STATS
750 # include <sys/procstats.h>
752 int gettimeofday (struct timeval *tp, void *tzp)
754 return get_process_stats(tp, PS_SELF, 0, 0);
756 #endif
758 #ifdef SCO_FLAVOR
759 /* Define this only for SCO */
760 #ifdef USE_NETCODE
761 #ifndef HAVE_SOCKETPAIR
764 The code for s_pipe function is adapted from Section 7.9
765 of the "UNIX Network Programming" by W. Richard Stevens,
766 published by Prentice Hall, ISBN 0-13-949876-1
767 (c) 1990 by P T R Prentice Hall
769 It is used to implement socketpair function for SVR3 systems
770 that lack it.
773 #include <sys/types.h>
774 #include <sys/stream.h> /* defines queue_t */
775 #include <stropts.h> /* defines struct strtdinsert */
776 #include <fcntl.h>
778 #define SPX_DEVICE "/dev/spx"
779 #define S_PIPE_HANDLE_ERRNO 1
780 /* if the above is defined to 1, we will attempt to
781 save and restore errno to indicate failure
782 reason to the caller;
783 Please note that this will not work in environments
784 where errno is not just an integer
787 #if S_PIPE_HANDLE_ERRNO
788 #include <errno.h>
789 /* This is for "extern int errno;" */
790 #endif
792 /* s_pipe returns 0 if OK, -1 on error */
793 /* two file descriptors are returned */
794 int s_pipe(int fd[2])
796 struct strfdinsert ins; /* stream I_FDINSERT ioctl format */
797 queue_t *pointer;
798 #if S_PIPE_HANDLE_ERRNO
799 int err_save;
800 #endif
802 * First open the stream clone device "dev/spx" twice,
803 * obtaining the two file descriptors
806 if ( (fd[0] = open(SPX_DEVICE, O_RDWR)) < 0)
807 return -1;
808 if ( (fd[1] = open(SPX_DEVICE, O_RDWR)) < 0) {
809 #if S_PIPE_HANDLE_ERRNO
810 err_save = errno;
811 #endif
812 close(fd[0]);
813 #if S_PIPE_HANDLE_ERRNO
814 errno = err_save;
815 #endif
816 return -1;
820 * Now link these two stream together with an I_FDINSERT ioctl.
823 ins.ctlbuf.buf = (char *) &pointer; /* no control information, just the pointer */
824 ins.ctlbuf.maxlen = sizeof pointer;
825 ins.ctlbuf.len = sizeof pointer;
826 ins.databuf.buf = (char *) 0; /* no data to be sent */
827 ins.databuf.maxlen = 0;
828 ins.databuf.len = -1; /* magic: must be -1 rather than 0 for stream pipes */
830 ins.fildes = fd[1]; /* the fd to connect with fd[0] */
831 ins.flags = 0; /* nonpriority message */
832 ins.offset = 0; /* offset of pointer in control buffer */
834 if (ioctl(fd[0], I_FDINSERT, (char *) &ins) < 0) {
835 #if S_PIPE_HANDLE_ERRNO
836 err_save = errno;
837 #endif
838 close(fd[0]);
839 close(fd[1]);
840 #if S_PIPE_HANDLE_ERRNO
841 errno = err_save;
842 #endif
843 return -1;
845 /* all is OK if we came here, indicate success */
846 return 0;
849 int socketpair(int dummy1, int dummy2, int dummy3, int fd[2])
851 return s_pipe(fd);
854 #endif /* ifndef HAVE_SOCKETPAIR */
855 #endif /* ifdef USE_NETCODE */
856 #endif /* SCO_FLAVOR */
857 #endif /* VFS_STANDALONE */
859 char *
860 g_readlink (char *path)
862 char small_buffer [80];
863 char *str;
864 int n, size;
866 n = readlink (path, small_buffer, sizeof (small_buffer)-1);
867 if (n == -1)
868 return NULL;
870 if (n < sizeof (small_buffer)-1){
871 small_buffer [n] = 0;
872 return g_strdup (small_buffer);
875 str = NULL;
876 for (size = 256; size < 8192; size += 128){
877 if (str)
878 g_free (str);
880 str = g_malloc (size);
882 n = readlink (path, str, size-1);
883 if (n == -1){
884 g_free (str);
885 return NULL;
888 if (n < size-1){
889 char *s;
891 str [n] = 0;
892 s = g_strdup (str);
893 g_free (str);
894 return s;
897 str [n] = 0;
898 return str;