4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* support.c - (non-GUI) useful routines */
31 #include <sys/param.h>
48 static GHashTable
*uid_hash
= NULL
; /* UID -> User name */
49 static GHashTable
*gid_hash
= NULL
; /* GID -> Group name */
51 /* Static prototypes */
54 /* Like g_strdup, but does realpath() too (if possible) */
55 char *pathdup(char *path
)
57 char real
[MAXPATHLEN
];
59 g_return_val_if_fail(path
!= NULL
, NULL
);
61 if (realpath(path
, real
))
62 return g_strdup(real
);
64 return g_strdup(path
);
67 /* Join the path to the leaf (adding a / between them) and
68 * return a pointer to a buffer with the result. Buffer is valid until
69 * the next call to make_path.
71 GString
*make_path(char *dir
, char *leaf
)
73 static GString
*buffer
= NULL
;
76 buffer
= g_string_new(NULL
);
78 g_return_val_if_fail(dir
!= NULL
, buffer
);
79 g_return_val_if_fail(leaf
!= NULL
, buffer
);
81 g_string_sprintf(buffer
, "%s%s%s",
83 dir
[0] == '/' && dir
[1] == '\0' ? "" : "/",
89 /* Return our complete host name for DND */
90 char *our_host_name_for_dnd(void)
92 if (option_get_int("dnd_no_hostnames"))
94 return our_host_name();
97 /* Return our complete host name, unconditionally */
98 char *our_host_name(void)
100 static char *name
= NULL
;
106 if (gethostname(buffer
, 4096) == 0)
108 /* gethostname doesn't always return the full name... */
112 ent
= gethostbyname(buffer
);
113 name
= g_strdup(ent
? ent
->h_name
: buffer
);
117 g_warning("gethostname() failed - using localhost\n");
118 name
= g_strdup("localhost");
125 /* fork() and run a new program.
126 * Returns the new PID, or 0 on failure.
128 pid_t
spawn(char **argv
)
130 return spawn_full(argv
, NULL
);
133 /* As spawn(), but cd to dir first (if dir is non-NULL) */
134 pid_t
spawn_full(char **argv
, char *dir
)
141 return 0; /* Failure */
144 /* We are the child process */
147 fprintf(stderr
, "chdir() failed: %s\n",
149 execvp(argv
[0], argv
);
150 fprintf(stderr
, "execvp(%s, ...) failed: %s\n",
156 /* We are the parent */
160 void debug_free_string(void *data
)
162 g_print("Freeing string '%s'\n", (char *) data
);
166 char *user_name(uid_t uid
)
171 uid_hash
= g_hash_table_new(NULL
, NULL
);
173 retval
= g_hash_table_lookup(uid_hash
, GINT_TO_POINTER(uid
));
177 struct passwd
*passwd
;
179 passwd
= getpwuid(uid
);
180 retval
= passwd
? g_strdup(passwd
->pw_name
)
181 : g_strdup_printf("[%d]", (int) uid
);
182 g_hash_table_insert(uid_hash
, GINT_TO_POINTER(uid
), retval
);
188 char *group_name(gid_t gid
)
193 gid_hash
= g_hash_table_new(NULL
, NULL
);
195 retval
= g_hash_table_lookup(gid_hash
, GINT_TO_POINTER(gid
));
201 group
= getgrgid(gid
);
202 retval
= group
? g_strdup(group
->gr_name
)
203 : g_strdup_printf("[%d]", (int) gid
);
204 g_hash_table_insert(gid_hash
, GINT_TO_POINTER(gid
), retval
);
210 /* Return a string in the form '23Mb' in a static buffer valid until
213 char *format_size(unsigned long size
)
215 static char *buffer
= NULL
;
218 if (size
>= PRETTY_SIZE_LIMIT
)
222 if (size
>= PRETTY_SIZE_LIMIT
)
226 if (size
>= PRETTY_SIZE_LIMIT
)
245 buffer
= g_strdup_printf("%ld %s", size
, units
);
250 /* Return a string in the form '23Mb' in a static buffer valid until
251 * the next call. Aligned to the right.
253 char *format_size_aligned(unsigned long size
)
255 static char *buffer
= NULL
;
258 if (size
>= PRETTY_SIZE_LIMIT
)
262 if (size
>= PRETTY_SIZE_LIMIT
)
266 if (size
>= PRETTY_SIZE_LIMIT
)
283 buffer
= g_strdup_printf("%4ld%c", size
, units
);
289 * Similar to format_size(), but this one uses a double argument since
290 * unsigned long isn't wide enough on all platforms and we must be able to
291 * sum sizes above 4 GB.
293 gchar
*format_double_size(double size
)
295 static gchar
*buf
= NULL
;
298 if (size
>= PRETTY_SIZE_LIMIT
)
302 if (size
>= PRETTY_SIZE_LIMIT
)
306 if (size
>= PRETTY_SIZE_LIMIT
)
326 buf
= g_strdup_printf("%.0f %s", size
, units
);
331 /* Fork and exec argv. Wait and return the child's exit status.
334 int fork_exec_wait(char **argv
)
339 child
= spawn_full(argv
, NULL
);
343 if (waitpid(child
, &status
, 0) == -1)
355 /* If a file has this UID and GID, which permissions apply to us?
356 * 0 = User, 1 = Group, 2 = World
358 gint
applicable(uid_t uid
, gid_t gid
)
368 for (i
= 0; i
< ngroups
; i
++)
370 if (supplemental_groups
[i
] == gid
)
377 /* Converts a file's mode to a string. Result is a pointer
378 * to a static buffer, valid until the next call.
380 char *pretty_permissions(mode_t m
)
382 static char buffer
[] = "rwx,rwx,rwx/UGT";
384 buffer
[0] = m
& S_IRUSR
? 'r' : '-';
385 buffer
[1] = m
& S_IWUSR
? 'w' : '-';
386 buffer
[2] = m
& S_IXUSR
? 'x' : '-';
388 buffer
[4] = m
& S_IRGRP
? 'r' : '-';
389 buffer
[5] = m
& S_IWGRP
? 'w' : '-';
390 buffer
[6] = m
& S_IXGRP
? 'x' : '-';
392 buffer
[8] = m
& S_IROTH
? 'r' : '-';
393 buffer
[9] = m
& S_IWOTH
? 'w' : '-';
394 buffer
[10] = m
& S_IXOTH
? 'x' : '-';
396 buffer
[12] = m
& S_ISUID
? 'U' : '-';
397 buffer
[13] = m
& S_ISGID
? 'G' : '-';
399 buffer
[14] = m
& S_ISVTX
? 'T' : '-';
408 /* Gets the canonical name for address and compares to our_host_name() */
409 static gboolean
is_local_address(char *address
)
413 ent
= gethostbyname(address
);
415 return strcmp(our_host_name(), ent
? ent
->h_name
: address
) == 0;
418 /* Convert a URI to a local pathname (or NULL if it isn't local).
419 * The returned pointer points inside the input string.
426 char *get_local_path(char *uri
)
430 char *path
, *uri_host
;
433 return uri
; /* Just a local path - no host part */
435 path
= strchr(uri
+ 2, '/');
437 return NULL
; /* //something */
440 return path
; /* ///path */
442 uri_host
= g_strndup(uri
+ 2, path
- uri
- 2);
443 if (is_local_address(uri_host
))
446 return path
; /* //myhost/path */
450 return NULL
; /* From a different host */
454 if (strncasecmp(uri
, "file:", 5))
455 return NULL
; /* Don't know this format */
460 return get_local_path(uri
);
466 /* Set the close-on-exec flag for this FD.
467 * TRUE means that an exec()'d process will not get the FD.
469 void close_on_exec(int fd
, gboolean close
)
471 if (fcntl(fd
, F_SETFD
, close
))
472 g_warning("fcntl() failed: %s\n", g_strerror(errno
));
475 void set_blocking(int fd
, gboolean blocking
)
477 if (fcntl(fd
, F_SETFL
, blocking
? 0 : O_NONBLOCK
))
478 g_warning("fcntl() failed: %s\n", g_strerror(errno
));
481 /* Format this time nicely. The result is a pointer to a static buffer,
482 * valid until the next call.
484 char *pretty_time(time_t *time
)
486 static char time_buf
[32];
488 if (strftime(time_buf
, sizeof(time_buf
),
489 TIME_FORMAT
, localtime(time
)) == 0)
496 # define O_NOFOLLOW 0x0
500 /* Copy data from 'read_fd' FD to 'write_fd' FD until EOF or error.
501 * Returns 0 on success, -1 on error (and sets errno).
503 static int copy_fd(int read_fd
, int write_fd
)
508 while ((got
= mc_read(read_fd
, buffer
, sizeof(buffer
))) > 0)
516 c
= mc_write(write_fd
, buffer
+ sent
, got
- sent
);
527 /* 'from' and 'to' are complete pathnames of files (not dirs or symlinks).
528 * This spawns 'cp' to do the copy if lstat() succeeds, otherwise we
529 * do the copy manually using vfs.
531 * Returns an error string, or NULL on success.
533 guchar
*copy_file(guchar
*from
, guchar
*to
)
535 char *argv
[] = {"cp", "-pRf", NULL
, NULL
, NULL
};
540 if (lstat(from
, &info
))
546 if (mc_lstat(from
, &info
))
547 return g_strerror(errno
);
549 /* Regular lstat() can't find it, but mc_lstat() can,
550 * so try reading it with VFS.
553 if (!S_ISREG(info
.st_mode
))
556 read_fd
= mc_open(from
, O_RDONLY
);
559 write_fd
= mc_open(to
,
560 O_NOFOLLOW
| O_WRONLY
| O_CREAT
| O_TRUNC
,
561 info
.st_mode
& 0777);
565 if (copy_fd(read_fd
, write_fd
))
568 /* (yes, the single | is right) */
569 if (mc_close(read_fd
) | mc_close(write_fd
))
570 return g_strerror(errno
);
578 return error
? g_strerror(error
) : _("Copy error");
585 if (fork_exec_wait(argv
))
586 return _("Copy failed");
590 /* 'word' has all special characters escaped so that it may be inserted
591 * into a shell command.
592 * Eg: 'My Dir?' becomes 'My\ Dir\?'. g_free() the result.
594 guchar
*shell_escape(guchar
*word
)
599 tmp
= g_string_new(NULL
);
603 if (strchr(" ?*['\"$~\\|();!`&", *word
))
604 g_string_append_c(tmp
, '\\');
605 g_string_append_c(tmp
, *word
);
610 g_string_free(tmp
, FALSE
);
614 /* TRUE iff `sub' is (or would be) an object inside the directory `parent',
615 * (or the two are the same item/directory).
616 * FALSE if parent doesn't exist.
618 gboolean
is_sub_dir(char *sub
, char *parent
)
620 struct stat parent_info
;
622 /* These were pathdup()s, but that follows symlinks!
623 * I guess they were here for a reason - something is probably
628 if (mc_stat(parent
, &parent_info
))
629 return FALSE
; /* Parent doesn't exist */
636 if (mc_stat(sub
, &info
) == 0)
638 if (info
.st_dev
== parent_info
.st_dev
&&
639 info
.st_ino
== parent_info
.st_ino
)
646 slash
= strrchr(sub
, '/');
665 /* True if the string 'list' contains 'item'.
666 * Eg ("close", "close, help") -> TRUE
668 gboolean
in_list(guchar
*item
, guchar
*list
)
676 if (strncmp(item
, list
, len
) == 0 && !isalpha(list
[len
]))
678 list
= strchr(list
, ',');
681 while (isspace(*++list
))
688 /* Split a path into its components. Eg:
690 * /bob/fred -> ["bob", "fred"]
691 * ///a//b// -> ["a", "b"]
694 * The array and the strings in it must be freed after use.
696 GPtrArray
*split_path(guchar
*path
)
701 g_return_val_if_fail(path
!= NULL
, NULL
);
703 array
= g_ptr_array_new();
707 while (path
[0] == '/')
712 slash
= strchr(path
, '/');
715 g_ptr_array_add(array
, g_strndup(path
, slash
- path
));
719 g_ptr_array_add(array
, g_strdup(path
));
726 /* Return the shortest path from 'from' to 'to'.
727 * Eg: get_relative_path("/a/b/c", "a/d/e") -> "../d/e"
729 guchar
*get_relative_path(guchar
*from
, guchar
*to
)
733 GPtrArray
*src
, *dst
;
736 src
= split_path(from
);
737 dst
= split_path(to
);
739 /* The last component of src doesn't matter... */
742 g_free(src
->pdata
[src
->len
- 1]);
743 g_ptr_array_remove_index(src
, src
->len
- 1);
746 /* Strip off common path elements... */
748 while (i
< src
->len
&& i
< dst
->len
)
750 guchar
*a
= (guchar
*) src
->pdata
[i
];
751 guchar
*b
= (guchar
*) dst
->pdata
[i
];
753 if (strcmp(a
, b
) != 0)
758 /* Go up one dir for each element remaining in src */
759 path
= g_string_new(NULL
);
760 for (j
= i
; j
< src
->len
; j
++)
761 g_string_append(path
, "../");
763 /* Go down one dir for each element remaining in dst */
764 for (j
= i
; j
< dst
->len
; j
++)
766 g_string_append(path
, (guchar
*) dst
->pdata
[j
]);
767 g_string_append_c(path
, '/');
770 if (path
->str
[path
->len
- 1] == '/')
771 g_string_truncate(path
, path
->len
- 1);
773 g_string_assign(path
, ".");
775 /* Free the arrays */
776 for (i
= 0; i
< src
->len
; i
++)
777 g_free(src
->pdata
[i
]);
778 g_ptr_array_free(src
, TRUE
);
779 for (i
= 0; i
< dst
->len
; i
++)
780 g_free(dst
->pdata
[i
]);
781 g_ptr_array_free(dst
, TRUE
);
784 g_string_free(path
, FALSE
);
789 /* Called before gtk_init(). Override default styles with our defaults,
790 * and override them with user choices, if any.
792 void add_default_styles(void)
796 rc_file
= g_strconcat(app_dir
, "/Styles", NULL
);
797 gtk_rc_add_default_file(rc_file
);
800 rc_file
= choices_find_path_load("Styles", "ROX-Filer");
803 gtk_rc_add_default_file(rc_file
);