diff options
Diffstat (limited to 'gnu/usr.bin/cvs/lib/subr.c')
-rw-r--r-- | gnu/usr.bin/cvs/lib/subr.c | 912 |
1 files changed, 912 insertions, 0 deletions
diff --git a/gnu/usr.bin/cvs/lib/subr.c b/gnu/usr.bin/cvs/lib/subr.c new file mode 100644 index 000000000000..2b728c24b982 --- /dev/null +++ b/gnu/usr.bin/cvs/lib/subr.c @@ -0,0 +1,912 @@ +/* + * Copyright (c) 1992, Brian Berliner and Jeff Polk + * Copyright (c) 1989-1992, Brian Berliner + * + * You may distribute under the terms of the GNU General Public License as + * specified in the README file that comes with the CVS 1.3 kit. + * + * Various useful functions for the CVS support code. + */ + +#include "cvs.h" + +#ifdef _MINIX +#undef POSIX /* Minix 1.6 doesn't support POSIX.1 sigaction yet */ +#endif + +#ifndef VPRINTF_MISSING +#if __STDC__ +#include <stdarg.h> +#define VA_START(args, lastarg) va_start(args, lastarg) +#else +#include <varargs.h> +#define VA_START(args, lastarg) va_start(args) +#endif +#else +#define va_alist a1, a2, a3, a4, a5, a6, a7, a8 +#define va_dcl char *a1, *a2, *a3, *a4, *a5, *a6, *a7, *a8; +#endif + +#ifndef lint +static char rcsid[] = "@(#)subr.c 1.52 92/03/31"; +#endif + +#if __STDC__ +static void run_add_arg (char *s); +static void run_init_prog (void); +#else +static void run_add_arg (); +static void run_init_prog (); +#endif /* __STDC__ */ + +extern char *getlogin (); +extern char *strtok (); + +/* + * Copies "from" to "to". mallocs a buffer large enough to hold the entire + * file and does one read/one write to do the copy. This is reasonable, + * since source files are typically not too large. + */ +void +copy_file (from, to) + char *from; + char *to; +{ + struct stat sb; + struct utimbuf t; + int fdin, fdout; + char *buf; + + if (trace) + (void) fprintf (stderr, "-> copy(%s,%s)\n", from, to); + if (noexec) + return; + + if ((fdin = open (from, O_RDONLY)) < 0) + error (1, errno, "cannot open %s for copying", from); + if (fstat (fdin, &sb) < 0) + error (1, errno, "cannot fstat %s", from); + if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) + error (1, errno, "cannot create %s for copying", to); + if (sb.st_size > 0) + { + buf = xmalloc ((int) sb.st_size); + if (read (fdin, buf, (int) sb.st_size) != (int) sb.st_size) + error (1, errno, "cannot read file %s for copying", from); + if (write (fdout, buf, (int) sb.st_size) != (int) sb.st_size +#ifndef FSYNC_MISSING + || fsync (fdout) == -1 +#endif + ) + { + error (1, errno, "cannot write file %s for copying", to); + } + free (buf); + } + (void) close (fdin); + if (close (fdout) < 0) + error (1, errno, "cannot close %s", to); + + /* now, set the times for the copied file to match those of the original */ + t.actime = sb.st_atime; + t.modtime = sb.st_mtime; + (void) utime (to, &t); +} + +/* + * Returns non-zero if the argument file is a directory, or is a symbolic + * link which points to a directory. + */ +int +isdir (file) + char *file; +{ + struct stat sb; + + if (stat (file, &sb) < 0) + return (0); + return (S_ISDIR (sb.st_mode)); +} + +/* + * Returns non-zero if the argument file is a symbolic link. + */ +int +islink (file) + char *file; +{ +#ifdef S_ISLNK + struct stat sb; + + if (lstat (file, &sb) < 0) + return (0); + return (S_ISLNK (sb.st_mode)); +#else + return (0); +#endif +} + +/* + * Returns non-zero if the argument file exists. + */ +int +isfile (file) + char *file; +{ + struct stat sb; + + if (stat (file, &sb) < 0) + return (0); + return (1); +} + +/* + * Returns non-zero if the argument file is readable. + * XXX - must be careful if "cvs" is ever made setuid! + */ +int +isreadable (file) + char *file; +{ + return (access (file, R_OK) != -1); +} + +/* + * Returns non-zero if the argument file is writable + * XXX - muct be careful if "cvs" is ever made setuid! + */ +int +iswritable (file) + char *file; +{ + return (access (file, W_OK) != -1); +} + +/* + * Open a file and die if it fails + */ +FILE * +open_file (name, mode) + char *name; + char *mode; +{ + FILE *fp; + + if ((fp = fopen (name, mode)) == NULL) + error (1, errno, "cannot open %s", name); + return (fp); +} + +/* + * Open a file if allowed and return. + */ +FILE * +Fopen (name, mode) + char *name; + char *mode; +{ + if (trace) + (void) fprintf (stderr, "-> fopen(%s,%s)\n", name, mode); + if (noexec) + return (NULL); + + return (fopen (name, mode)); +} + +/* + * Make a directory and die if it fails + */ +void +make_directory (name) + char *name; +{ + struct stat buf; + + if (stat (name, &buf) == 0) + { + if (S_ISDIR (buf.st_mode)) + { + if (access (name, (R_OK | W_OK | X_OK)) == 0) + { + error (0, 0, "Directory %s already exists", name); + return; + } + else + { + error (0, 0, + "Directory %s already exists but is protected from you", + name); + } + } + else + error (0, 0, "%s already exists but is not a directory", name); + } + if (!noexec && mkdir (name, 0777) < 0) + error (1, errno, "cannot make directory %s", name); +} + +/* + * Make a path to the argument directory, printing a message if something + * goes wrong. + */ +void +make_directories (name) + char *name; +{ + char *cp; + + if (noexec) + return; + + if (mkdir (name, 0777) == 0 || errno == EEXIST) + return; + if (errno != ENOENT) + { + error (0, errno, "cannot make path to %s", name); + return; + } + if ((cp = rindex (name, '/')) == NULL) + return; + *cp = '\0'; + make_directories (name); + *cp++ = '/'; + if (*cp == '\0') + return; + (void) mkdir (name, 0777); +} + +/* + * malloc some data and die if it fails + */ +char * +xmalloc (bytes) + int bytes; +{ + char *cp; + + if (bytes <= 0) + error (1, 0, "bad malloc size %d", bytes); + if ((cp = malloc ((unsigned) bytes)) == NULL) + error (1, 0, "malloc failed"); + return (cp); +} + +/* + * realloc data and die if it fails [I've always wanted to have "realloc" do + * a "malloc" if the argument is NULL, but you can't depend on it. Here, I + * can *force* it. + */ +char * +xrealloc (ptr, bytes) + char *ptr; + int bytes; +{ + char *cp; + + if (!ptr) + return (xmalloc (bytes)); + + if (bytes <= 0) + error (1, 0, "bad realloc size %d", bytes); + if ((cp = realloc (ptr, (unsigned) bytes)) == NULL) + error (1, 0, "realloc failed"); + return (cp); +} + +/* + * Duplicate a string, calling xmalloc to allocate some dynamic space + */ +char * +xstrdup (str) + char *str; +{ + char *s; + + if (str == NULL) + return ((char *) NULL); + s = xmalloc (strlen (str) + 1); + (void) strcpy (s, str); + return (s); +} + +/* + * Change the mode of a file, either adding write permissions, or removing + * all write permissions. Adding write permissions honors the current umask + * setting. + */ +void +xchmod (fname, writable) + char *fname; + int writable; +{ + struct stat sb; + int mode, oumask; + + if (stat (fname, &sb) < 0) + { + if (!noexec) + error (0, errno, "cannot stat %s", fname); + return; + } + if (writable) + { + oumask = umask (0); + (void) umask (oumask); + mode = sb.st_mode | ((S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask); + } + else + { + mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH); + } + + if (trace) + (void) fprintf (stderr, "-> chmod(%s,%o)\n", fname, mode); + if (noexec) + return; + + if (chmod (fname, mode) < 0) + error (0, errno, "cannot change mode of file %s", fname); +} + +/* + * Rename a file and die if it fails + */ +void +rename_file (from, to) + char *from; + char *to; +{ + if (trace) + (void) fprintf (stderr, "-> rename(%s,%s)\n", from, to); + if (noexec) + return; + + if (rename (from, to) < 0) + error (1, errno, "cannot rename file %s to %s", from, to); +} + +/* + * link a file, if possible. + */ +int +link_file (from, to) + char *from, *to; +{ + if (trace) + (void) fprintf (stderr, "-> link(%s,%s)\n", from, to); + if (noexec) + return (0); + + return (link (from, to)); +} + +/* + * unlink a file, if possible. + */ +int +unlink_file (f) + char *f; +{ + if (trace) + (void) fprintf (stderr, "-> unlink(%s)\n", f); + if (noexec) + return (0); + + return (unlink (f)); +} + +/* + * Compare "file1" to "file2". Return non-zero if they don't compare exactly. + * + * mallocs a buffer large enough to hold the entire file and does two reads to + * load the buffer and calls bcmp to do the cmp. This is reasonable, since + * source files are typically not too large. + */ +int +xcmp (file1, file2) + char *file1; + char *file2; +{ + register char *buf1, *buf2; + struct stat sb; + off_t size; + int ret, fd1, fd2; + + if ((fd1 = open (file1, O_RDONLY)) < 0) + error (1, errno, "cannot open file %s for comparing", file1); + if ((fd2 = open (file2, O_RDONLY)) < 0) + error (1, errno, "cannot open file %s for comparing", file2); + if (fstat (fd1, &sb) < 0) + error (1, errno, "cannot fstat %s", file1); + size = sb.st_size; + if (fstat (fd2, &sb) < 0) + error (1, errno, "cannot fstat %s", file2); + if (size == sb.st_size) + { + if (size == 0) + ret = 0; + else + { + buf1 = xmalloc ((int) size); + buf2 = xmalloc ((int) size); + if (read (fd1, buf1, (int) size) != (int) size) + error (1, errno, "cannot read file %s cor comparing", file1); + if (read (fd2, buf2, (int) size) != (int) size) + error (1, errno, "cannot read file %s for comparing", file2); + ret = bcmp (buf1, buf2, (int) size); + free (buf1); + free (buf2); + } + } + else + ret = 1; + (void) close (fd1); + (void) close (fd2); + return (ret); +} + +/* + * Recover the space allocated by Find_Names() and line2argv() + */ +void +free_names (pargc, argv) + int *pargc; + char *argv[]; +{ + register int i; + + for (i = 0; i < *pargc; i++) + { /* only do through *pargc */ + free (argv[i]); + } + *pargc = 0; /* and set it to zero when done */ +} + +/* + * Convert a line into argc/argv components and return the result in the + * arguments as passed. Use free_names() to return the memory allocated here + * back to the free pool. + */ +void +line2argv (pargc, argv, line) + int *pargc; + char *argv[]; + char *line; +{ + char *cp; + + *pargc = 0; + for (cp = strtok (line, " \t"); cp; cp = strtok ((char *) NULL, " \t")) + { + argv[*pargc] = xstrdup (cp); + (*pargc)++; + } +} + +/* + * Returns the number of dots ('.') found in an RCS revision number + */ +int +numdots (s) + char *s; +{ + char *cp; + int dots = 0; + + for (cp = s; *cp; cp++) + { + if (*cp == '.') + dots++; + } + return (dots); +} + +/* + * Get the caller's login from his uid. If the real uid is "root" try LOGNAME + * USER or getlogin(). If getlogin() and getpwuid() both fail, return + * the uid as a string. + */ +char * +getcaller () +{ + static char uidname[20]; + struct passwd *pw; + char *name; + int uid; + + uid = getuid (); + if (uid == 0) + { + /* super-user; try getlogin() to distinguish */ + if (((name = getenv("LOGNAME")) || (name = getenv("USER")) || + (name = getlogin ())) && *name) + return (name); + } + if ((pw = (struct passwd *) getpwuid (uid)) == NULL) + { + (void) sprintf (uidname, "uid%d", uid); + return (uidname); + } + return (pw->pw_name); +} + +/* + * To exec a program under CVS, first call run_setup() to setup any initial + * arguments. The options to run_setup are essentially like printf(). The + * arguments will be parsed into whitespace separated words and added to the + * global run_argv list. + * + * Then, optionally call run_arg() for each additional argument that you'd like + * to pass to the executed program. + * + * Finally, call run_exec() to execute the program with the specified arguments. + * The execvp() syscall will be used, so that the PATH is searched correctly. + * File redirections can be performed in the call to run_exec(). + */ +static char *run_prog; +static char **run_argv; +static int run_argc; +static int run_argc_allocated; + +/* VARARGS */ +#if !defined (VPRINTF_MISSING) && __STDC__ +void +run_setup (char *fmt,...) +#else +void +run_setup (fmt, va_alist) + char *fmt; + va_dcl + +#endif +{ +#ifndef VPRINTF_MISSING + va_list args; + +#endif + char *cp; + int i; + + run_init_prog (); + + /* clean out any malloc'ed values from run_argv */ + for (i = 0; i < run_argc; i++) + { + if (run_argv[i]) + { + free (run_argv[i]); + run_argv[i] = (char *) 0; + } + } + run_argc = 0; + + /* process the varargs into run_prog */ +#ifndef VPRINTF_MISSING + VA_START (args, fmt); + (void) vsprintf (run_prog, fmt, args); + va_end (args); +#else + (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8); +#endif + + /* put each word into run_argv, allocating it as we go */ + for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t")) + run_add_arg (cp); +} + +void +run_arg (s) + char *s; +{ + run_add_arg (s); +} + +/* VARARGS */ +#if !defined (VPRINTF_MISSING) && __STDC__ +void +run_args (char *fmt,...) +#else +void +run_args (fmt, va_alist) + char *fmt; + va_dcl + +#endif +{ +#ifndef VPRINTF_MISSING + va_list args; + +#endif + + run_init_prog (); + + /* process the varargs into run_prog */ +#ifndef VPRINTF_MISSING + VA_START (args, fmt); + (void) vsprintf (run_prog, fmt, args); + va_end (args); +#else + (void) sprintf (run_prog, fmt, a1, a2, a3, a4, a5, a6, a7, a8); +#endif + + /* and add the (single) argument to the run_argv list */ + run_add_arg (run_prog); +} + +static void +run_add_arg (s) + char *s; +{ + /* allocate more argv entries if we've run out */ + if (run_argc >= run_argc_allocated) + { + run_argc_allocated += 50; + run_argv = (char **) xrealloc ((char *) run_argv, + run_argc_allocated * sizeof (char **)); + } + + if (s) + run_argv[run_argc++] = xstrdup (s); + else + run_argv[run_argc] = (char *) 0;/* not post-incremented on purpose! */ +} + +static void +run_init_prog () +{ + /* make sure that run_prog is allocated once */ + if (run_prog == (char *) 0) + run_prog = xmalloc (10 * 1024); /* 10K of args for _setup and _arg */ +} + +int +run_exec (stin, stout, sterr, flags) + char *stin; + char *stout; + char *sterr; + int flags; +{ + int shin, shout, sherr; + int mode_out, mode_err; + int status = -1; + int rerrno = 0; + int pid, w; + +#ifdef POSIX + sigset_t sigset_mask, sigset_omask; + struct sigaction act, iact, qact; + +#else +#ifdef BSD_SIGNALS + int mask; + struct sigvec vec, ivec, qvec; + +#else + SIGTYPE (*istat) (), (*qstat) (); +#endif +#endif + + if (trace) + { + (void) fprintf (stderr, "-> system("); + run_print (stderr); + (void) fprintf (stderr, ")\n"); + } + if (noexec && (flags & RUN_REALLY) == 0) + return (0); + + /* make sure that we are null terminated, since we didn't calloc */ + run_add_arg ((char *) 0); + + /* setup default file descriptor numbers */ + shin = 0; + shout = 1; + sherr = 2; + + /* set the file modes for stdout and stderr */ + mode_out = mode_err = O_WRONLY | O_CREAT; + mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC); + mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC); + + if (stin && (shin = open (stin, O_RDONLY)) == -1) + { + rerrno = errno; + error (0, errno, "cannot open %s for reading (prog %s)", + stin, run_argv[0]); + goto out0; + } + if (stout && (shout = open (stout, mode_out, 0666)) == -1) + { + rerrno = errno; + error (0, errno, "cannot open %s for writing (prog %s)", + stout, run_argv[0]); + goto out1; + } + if (sterr && (flags & RUN_COMBINED) == 0) + { + if ((sherr = open (sterr, mode_err, 0666)) == -1) + { + rerrno = errno; + error (0, errno, "cannot open %s for writing (prog %s)", + sterr, run_argv[0]); + goto out2; + } + } + + /* The output files, if any, are now created. Do the fork and dups */ +#ifdef VFORK_MISSING + pid = fork (); +#else + pid = vfork (); +#endif + if (pid == 0) + { + if (shin != 0) + { + (void) dup2 (shin, 0); + (void) close (shin); + } + if (shout != 1) + { + (void) dup2 (shout, 1); + (void) close (shout); + } + if (flags & RUN_COMBINED) + (void) dup2 (1, 2); + else if (sherr != 2) + { + (void) dup2 (sherr, 2); + (void) close (sherr); + } + + /* dup'ing is done. try to run it now */ + (void) execvp (run_argv[0], run_argv); + _exit (127); + } + else if (pid == -1) + { + rerrno = errno; + goto out; + } + + /* the parent. Ignore some signals for now */ +#ifdef POSIX + if (flags & RUN_SIGIGNORE) + { + act.sa_handler = SIG_IGN; + (void) sigemptyset (&act.sa_mask); + act.sa_flags = 0; + (void) sigaction (SIGINT, &act, &iact); + (void) sigaction (SIGQUIT, &act, &qact); + } + else + { + (void) sigemptyset (&sigset_mask); + (void) sigaddset (&sigset_mask, SIGINT); + (void) sigaddset (&sigset_mask, SIGQUIT); + (void) sigprocmask (SIG_SETMASK, &sigset_mask, &sigset_omask); + } +#else +#ifdef BSD_SIGNALS + if (flags & RUN_SIGIGNORE) + { + bzero ((char *) &vec, sizeof (vec)); + vec.sv_handler = SIG_IGN; + (void) sigvec (SIGINT, &vec, &ivec); + (void) sigvec (SIGQUIT, &vec, &qvec); + } + else + mask = sigblock (sigmask (SIGINT) | sigmask (SIGQUIT)); +#else + istat = signal (SIGINT, SIG_IGN); + qstat = signal (SIGQUIT, SIG_IGN); +#endif +#endif + + /* wait for our process to die and munge return status */ +#ifdef POSIX + while ((w = waitpid (pid, &status, 0)) == -1 && errno == EINTR) + ; +#else + while ((w = wait (&status)) != pid) + { + if (w == -1 && errno != EINTR) + break; + } +#endif + if (w == -1) + { + status = -1; + rerrno = errno; + } + else if (WIFEXITED (status)) + status = WEXITSTATUS (status); + else if (WIFSIGNALED (status)) + { + if (WTERMSIG (status) == SIGPIPE) + error (1, 0, "broken pipe"); + status = 2; + } + else + status = 1; + + /* restore the signals */ +#ifdef POSIX + if (flags & RUN_SIGIGNORE) + { + (void) sigaction (SIGINT, &iact, (struct sigaction *) NULL); + (void) sigaction (SIGQUIT, &qact, (struct sigaction *) NULL); + } + else + (void) sigprocmask (SIG_SETMASK, &sigset_omask, (sigset_t *) NULL); +#else +#ifdef BSD_SIGNALS + if (flags & RUN_SIGIGNORE) + { + (void) sigvec (SIGINT, &ivec, (struct sigvec *) NULL); + (void) sigvec (SIGQUIT, &qvec, (struct sigvec *) NULL); + } + else + (void) sigsetmask (mask); +#else + (void) signal (SIGINT, istat); + (void) signal (SIGQUIT, qstat); +#endif +#endif + + /* cleanup the open file descriptors */ + out: + if (sterr) + (void) close (sherr); + out2: + if (stout) + (void) close (shout); + out1: + if (stin) + (void) close (shin); + + out0: + if (rerrno) + errno = rerrno; + return (status); +} + +void +run_print (fp) + FILE *fp; +{ + int i; + + for (i = 0; i < run_argc; i++) + { + (void) fprintf (fp, "%s", run_argv[i]); + if (i != run_argc - 1) + (void) fprintf (fp, " "); + } +} + +FILE * +Popen (cmd, mode) + char *cmd, *mode; +{ + if (trace) + (void) fprintf (stderr, "-> Popen(%s,%s)\n", cmd, mode); + if (noexec) + return (NULL); + return (popen (cmd, mode)); +} + +#ifdef lint +#ifndef __GNUC__ +/* ARGSUSED */ +time_t +get_date (date, now) + char *date; + struct timeb *now; +{ + time_t foo = 0; + + return (foo); +} +#endif +#endif |