summaryrefslogtreecommitdiffstats
path: root/src/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util.c')
-rw-r--r--src/util.c305
1 files changed, 305 insertions, 0 deletions
diff --git a/src/util.c b/src/util.c
new file mode 100644
index 0000000..d074451
--- /dev/null
+++ b/src/util.c
@@ -0,0 +1,305 @@
+/*
+ * util.c
+ *
+ * Copyright (c) 1990, 1991, John W. Eaton.
+ *
+ * You may distribute under the terms of the GNU General Public
+ * License as specified in the file COPYING that comes with the man
+ * distribution.
+ *
+ * John W. Eaton
+ * jwe@che.utexas.edu
+ * Department of Chemical Engineering
+ * The University of Texas at Austin
+ * Austin, Texas 78712
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "gripes.h"
+#include "man.h" /* for debug */
+
+/*
+ * Extract last element of a name like /foo/bar/baz.
+ */
+const char *
+mkprogname (const char *s) {
+ const char *t;
+
+ t = strrchr (s, '/');
+ if (t == (char *)NULL)
+ t = s;
+ else
+ t++;
+
+ return my_strdup (t);
+}
+
+/*
+ * Is file a nonempty and newer than file b?
+ *
+ * case:
+ *
+ * a newer than b returns 1
+ * a older than b returns 0
+ * stat on a fails or a empty returns -1
+ * stat on b fails or b empty returns -2
+ * both fail or empty returns -3
+ */
+int
+is_newer (const char *fa, const char *fb) {
+ struct stat fa_sb;
+ struct stat fb_sb;
+ register int fa_stat;
+ register int fb_stat;
+ register int status = 0;
+
+ fa_stat = stat (fa, &fa_sb);
+ if (fa_stat != 0 || fa_sb.st_size == 0)
+ status = 1;
+
+ fb_stat = stat (fb, &fb_sb);
+ if (fb_stat != 0 || fb_sb.st_size == 0)
+ status |= 2;
+
+ if (status != 0)
+ return -status;
+
+ return (fa_sb.st_mtime > fb_sb.st_mtime);
+}
+
+int ruid, rgid, euid, egid, suid;
+
+void
+get_permissions (void) {
+ ruid = getuid();
+ euid = geteuid();
+ rgid = getgid();
+ egid = getegid();
+ suid = (ruid != euid || rgid != egid);
+}
+
+void
+no_privileges (void) {
+ if (suid) {
+#if !defined (__CYGWIN__) && !defined (__BEOS__)
+ setreuid(ruid, ruid);
+ setregid(rgid, rgid);
+#endif
+ suid = 0;
+ }
+}
+
+/*
+ * What to do upon an interrupt? Experience shows that
+ * if we exit immediately, sh notices that its child has
+ * died and will try to fiddle with the tty.
+ * Simultaneously, also less will fiddle with the tty,
+ * resetting the mode before exiting.
+ * This leads to undesirable races. So, we catch SIGINT here
+ * and exit after the child has exited.
+ */
+static int interrupted = 0;
+static void catch_int(int a) {
+ interrupted = 1;
+}
+
+static int
+system1 (const char *command) {
+ void (*prev_handler)(int) = signal (SIGINT,catch_int);
+ int ret = system(command);
+
+ /* child terminated with signal? */
+ if (WIFSIGNALED(ret) &&
+ (WTERMSIG(ret) == SIGINT || WTERMSIG(ret) == SIGQUIT))
+ exit(1);
+
+ /* or we caught an interrupt? */
+ if (interrupted)
+ exit(1);
+
+ signal(SIGINT,prev_handler);
+ return ret;
+}
+
+static int
+my_system (const char *command) {
+ int pid, pid2, status, stat;
+
+ if (!suid)
+ return system1 (command);
+
+#ifdef _POSIX_SAVED_IDS
+
+ /* we need not fork */
+ setuid(ruid);
+ setgid(rgid);
+ status = system1(command);
+ setuid(euid);
+ setgid(egid);
+ return (WIFEXITED(status) ? WEXITSTATUS(status) : 127);
+#endif
+
+ fflush(stdout); fflush(stderr);
+ pid = fork();
+ if (pid == -1) {
+ perror(progname);
+ fatal (CANNOT_FORK, command);
+ }
+ if (pid == 0) {
+ setuid(ruid);
+ setgid(rgid);
+ status = system1 (command);
+ exit(WIFEXITED(status) ? WEXITSTATUS(status) : 127);
+ }
+ pid2 = wait (&stat);
+ if (pid2 == -1) {
+ perror(progname);
+ fatal (WAIT_FAILED, command); /* interrupted? */
+ }
+ if (pid2 != pid)
+ fatal (GOT_WRONG_PID);
+ if (WIFEXITED(stat) && WEXITSTATUS(stat) != 127)
+ return WEXITSTATUS(stat);
+ fatal (CHILD_TERMINATED_ABNORMALLY, command);
+ return -1; /* not reached */
+}
+
+FILE *
+my_popen(const char *command, const char *type) {
+ FILE *r;
+
+ if (!suid)
+ return popen(command, type);
+
+#ifdef _POSIX_SAVED_IDS
+ setuid(ruid);
+ setgid(rgid);
+ r = popen(command, type);
+ setuid(euid);
+ setgid(egid);
+ return r;
+#endif
+
+ no_privileges();
+ return popen(command, type);
+}
+
+#define NOT_SAFE "/unsafe/"
+
+/*
+ * Attempt a system () call.
+ */
+int
+do_system_command (const char *command, int silent) {
+ int status = 0;
+
+ /*
+ * If we're debugging, don't really execute the command
+ */
+ if ((debug & 1) || !strncmp(command, NOT_SAFE, strlen(NOT_SAFE)))
+ fatal (NO_EXEC, command);
+ else
+ status = my_system (command);
+
+ if (status && !silent)
+ gripe (SYSTEM_FAILED, command, status);
+
+ return status;
+}
+
+char *
+my_malloc (int n) {
+ char *s = malloc(n);
+ if (!s)
+ fatal (OUT_OF_MEMORY, n);
+ return s;
+}
+
+char *
+my_strdup (const char *s) {
+ char *t = my_malloc(strlen(s) + 1);
+ strcpy(t, s);
+ return t;
+}
+
+/*
+ * Call: my_xsprintf(format,s1,s2,...) where format only contains %s/%S/%Q
+ * (or %d or %o) and all %s/%S/%Q parameters are strings.
+ * Result: allocates a new string containing the sprintf result.
+ * The %S parameters are checked for being shell safe.
+ * The %Q parameters are checked for being shell safe inside single quotes.
+ */
+
+static int
+is_shell_safe(const char *ss, int quoted) {
+ char *bad = " ;'\\\"<>|&";
+ char *p;
+
+ if (quoted)
+ bad++; /* allow a space inside quotes */
+ for (p = bad; *p; p++)
+ if (strchr(ss, *p))
+ return 0;
+ return 1;
+}
+
+static void
+nothing(int x) {}
+
+char *
+my_xsprintf (char *format, ...) {
+ va_list p;
+ char *s, *ss, *fm;
+ int len;
+
+ len = strlen(format) + 1;
+ fm = my_strdup(format);
+
+ va_start(p, format);
+ for (s = fm; *s; s++) {
+ if (*s == '%') {
+ switch (s[1]) {
+ case 'Q':
+ case 'S': /* check and turn into 's' */
+ ss = va_arg(p, char *);
+ if (!is_shell_safe(ss, (s[1] == 'Q')))
+ return NOT_SAFE;
+ len += strlen(ss);
+ s[1] = 's';
+ break;
+ case 's':
+ len += strlen(va_arg(p, char *));
+ break;
+ case 'd':
+ case 'o':
+ case 'c':
+ len += 20;
+ nothing(va_arg(p, int)); /* advance */
+ break;
+ default:
+ fprintf(stderr,
+ "my_xsprintf called with %s\n",
+ format);
+ exit(1);
+ }
+ }
+ }
+ va_end(p);
+
+ s = my_malloc(len);
+ va_start(p, format);
+ vsprintf(s, fm, p);
+ va_end(p);
+
+ return s;
+}