/* * Copyright (c) 1983 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. */ #ifndef lint static char sccsid[] = "@(#)library.c 5.1 (Berkeley) 5/31/85"; #endif not lint static char rcsid[] = "$Header: library.c,v 1.5 84/12/26 10:39:52 linton Exp $"; /* * General purpose routines. */ #include #include #include #define public #define private static #define and && #define or || #define not ! #define ord(enumcon) ((int) enumcon) #define nil(type) ((type) 0) typedef int integer; typedef enum { FALSE, TRUE } boolean; typedef char *String; typedef FILE *File; typedef String Filename; #undef FILE String cmdname; /* name of command for error messages */ Filename errfilename; /* current file associated with error */ short errlineno; /* line number associated with error */ /* * Definitions for doing memory allocation. */ extern char *malloc(); #define alloc(n, type) ((type *) malloc((unsigned) (n) * sizeof(type))) #define dispose(p) { free((char *) p); p = 0; } /* * Macros for doing freads + fwrites. */ #define get(fp, var) fread((char *) &(var), sizeof(var), 1, fp) #define put(fp, var) fwrite((char *) &(var), sizeof(var), 1, fp) /* * String definitions. */ extern String strcpy(), index(), rindex(); extern int strlen(); #define strdup(s) strcpy(malloc((unsigned) strlen(s) + 1), s) #define streq(s1, s2) (strcmp(s1, s2) == 0) typedef int INTFUNC(); typedef struct { INTFUNC *func; } ERRINFO; #define ERR_IGNORE ((INTFUNC *) 0) #define ERR_CATCH ((INTFUNC *) 1) /* * Call a program. * * Four entries: * * call, callv - call a program and wait for it, returning status * back, backv - call a program and don't wait, returning process id * * The command's standard input and output are passed as FILE's. */ #define MAXNARGS 1000 /* unchecked upper limit on max num of arguments */ #define BADEXEC 127 /* exec fails */ #define ischild(pid) ((pid) == 0) /* VARARGS3 */ public int call(name, in, out, args) String name; File in; File out; String args; { String *ap, *argp; String argv[MAXNARGS]; argp = &argv[0]; *argp++ = name; ap = &args; while (*ap != nil(String)) { *argp++ = *ap++; } *argp = nil(String); return callv(name, in, out, argv); } /* VARARGS3 */ public int back(name, in, out, args) String name; File in; File out; String args; { String *ap, *argp; String argv[MAXNARGS]; argp = &argv[0]; *argp++ = name; ap = &args; while (*ap != nil(String)) { *argp++ = *ap++; } *argp = nil(String); return backv(name, in, out, argv); } public int callv(name, in, out, argv) String name; File in; File out; String *argv; { int pid, status; pid = backv(name, in, out, argv); pwait(pid, &status); return status; } public int backv(name, in, out, argv) String name; File in; File out; String *argv; { int pid; fflush(stdout); if (ischild(pid = fork())) { fswap(0, fileno(in)); fswap(1, fileno(out)); onsyserr(EACCES, ERR_IGNORE); execvp(name, argv); _exit(BADEXEC); } return pid; } /* * Swap file numbers so as to redirect standard input and output. */ private fswap(oldfd, newfd) int oldfd; int newfd; { if (oldfd != newfd) { close(oldfd); dup(newfd); close(newfd); } } /* * Invoke a shell on a command line. */ #define DEF_SHELL "csh" public shell(s) String s; { extern String getenv(); String sh; if ((sh = getenv("SHELL")) == nil(String)) { sh = DEF_SHELL; } if (s != nil(String) and *s != '\0') { call(sh, stdin, stdout, "-c", s, 0); } else { call(sh, stdin, stdout, 0); } } /* * Wait for a process the right way. We wait for a particular * process and if any others come along in between, we remember them * in case they are eventually waited for. * * This routine is not very efficient when the number of processes * to be remembered is large. * * To deal with a kernel idiosyncrasy, we keep a list on the side * of "traced" processes, and do not notice them when waiting for * another process. */ typedef struct pidlist { int pid; int status; struct pidlist *next; } Pidlist; private Pidlist *pidlist, *ptrclist, *pfind(); public ptraced(pid) int pid; { Pidlist *p; p = alloc(1, Pidlist); p->pid = pid; p->next = ptrclist; ptrclist = p; } public unptraced(pid) int pid; { register Pidlist *p, *prev; prev = nil(Pidlist *); p = ptrclist; while (p != nil(Pidlist *) and p->pid != pid) { prev = p; p = p->next; } if (p != nil(Pidlist *)) { if (prev == nil(Pidlist *)) { ptrclist = p->next; } else { prev->next = p->next; } dispose(p); } } private boolean isptraced(pid) int pid; { register Pidlist *p; p = ptrclist; while (p != nil(Pidlist *) and p->pid != pid) { p = p->next; } return (boolean) (p != nil(Pidlist *)); } public pwait(pid, statusp) int pid, *statusp; { Pidlist *p; int pnum, status; p = pfind(pid); if (p != nil(Pidlist *)) { *statusp = p->status; dispose(p); } else { pnum = wait(&status); while (pnum != pid and pnum >= 0) { if (not isptraced(pnum)) { p = alloc(1, Pidlist); p->pid = pnum; p->status = status; p->next = pidlist; pidlist = p; } pnum = wait(&status); } if (pnum < 0) { p = pfind(pid); if (p == nil(Pidlist *)) { panic("pwait: pid %d not found", pid); } *statusp = p->status; dispose(p); } else { *statusp = status; } } } /* * Look for the given process id on the pidlist. * * Unlink it from list if found. */ private Pidlist *pfind(pid) int pid; { register Pidlist *p, *prev; prev = nil(Pidlist *); for (p = pidlist; p != nil(Pidlist *); p = p->next) { if (p->pid == pid) { break; } prev = p; } if (p != nil(Pidlist *)) { if (prev == nil(Pidlist *)) { pidlist = p->next; } else { prev->next = p->next; } } return p; } /* * System call error handler. * * The syserr routine is called when a system call is about to * set the c-bit to report an error. Certain errors are caught * and cause the process to print a message and immediately exit. */ extern int sys_nerr; extern char *sys_errlist[]; /* * Before calling syserr, the integer errno is set to contain the * number of the error. The routine "_mycerror" is a dummy which * is used to force the loader to get my version of cerror rather * than the usual one. */ extern int errno; extern _mycerror(); /* * Initialize error information, setting defaults for handling errors. */ private ERRINFO *errinfo; private initErrInfo () { integer i; errinfo = alloc(sys_nerr, ERRINFO); for (i = 0; i < sys_nerr; i++) { errinfo[i].func = ERR_CATCH; } errinfo[0].func = ERR_IGNORE; errinfo[EPERM].func = ERR_IGNORE; errinfo[ENOENT].func = ERR_IGNORE; errinfo[ESRCH].func = ERR_IGNORE; errinfo[EBADF].func = ERR_IGNORE; errinfo[ENOTTY].func = ERR_IGNORE; errinfo[EOPNOTSUPP].func = ERR_IGNORE; } public syserr() { ERRINFO *e; if (errno < 0 or errno > sys_nerr) { fatal("errno %d", errno); } else { if (errinfo == nil(ERRINFO *)) { initErrInfo(); } e = &(errinfo[errno]); if (e->func == ERR_CATCH) { fatal(sys_errlist[errno]); } else if (e->func != ERR_IGNORE) { (*e->func)(); } } } /* * Catcherrs' purpose is to initialize the errinfo table, get this module * loaded, and make sure my cerror is loaded (only applicable when this is * in a library). */ public catcherrs() { _mycerror(); initErrInfo(); } /* * Turn off the error catching mechanism completely by having all errors * ignored. This is most useful between a fork and an exec. */ public nocatcherrs() { integer i; for (i = 0; i < sys_nerr; i++) { errinfo[i].func = ERR_IGNORE; } } /* * Change the action on receipt of an error. */ public onsyserr(n, f) int n; INTFUNC *f; { if (errinfo == nil(ERRINFO *)) { initErrInfo(); } errinfo[n].func = f; } /* * Print the message associated with the given signal. * Like a "perror" for signals. */ public int sys_nsig = NSIG; public psignal(s, n) String s; integer n; { String msg; integer len; extern String sys_siglist[]; if (n >= 0 and n < sys_nsig) { msg = sys_siglist[n]; } else { msg = "Unknown signal"; } len = strlen(s); if (len > 0) { write(2, s, len); write(2, ": ", 2); } write(2, msg, strlen(msg)); write(2, "\n", 1); } /* * Standard error handling routines. */ private short nerrs; private short nwarnings; /* * Main driver of error message reporting. */ /* VARARGS2 */ private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) String errname; boolean shouldquit; String s; { fflush(stdout); if (shouldquit and cmdname != nil(String)) { fprintf(stderr, "%s: ", cmdname); } if (errfilename != nil(Filename)) { fprintf(stderr, "%s: ", errfilename); } if (errlineno > 0) { fprintf(stderr, "%d: ", errlineno); } if (errname != nil(String)) { fprintf(stderr, "%s: ", errname); } fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); putc('\n', stderr); if (shouldquit) { quit(1); } } /* * For when printf isn't sufficient for printing the error message ... */ public beginerrmsg() { fflush(stdout); if (errfilename != nil(String)) { fprintf(stderr, "%s: ", errfilename); } if (errlineno > 0) { fprintf(stderr, "%d: ", errlineno); } } public enderrmsg() { putc('\n', stderr); erecover(); } /* * The messages are listed in increasing order of seriousness. * * First are warnings. */ /* VARARGS1 */ public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) String s; { nwarnings++; errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); } /* * Errors are a little worse, they mean something is wrong, * but not so bad that processing can't continue. * * The routine "erecover" is called to recover from the error, * a default routine is provided that does nothing. */ /* VARARGS1 */ public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) String s; { extern erecover(); nerrs++; errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); erecover(); } /* * Non-recoverable user error. */ /* VARARGS1 */ public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) String s; { errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); } /* * Panics indicate an internal program error. */ /* VARARGS1 */ public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) String s; { errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); } short numerrors() { short r; r = nerrs; nerrs = 0; return r; } short numwarnings() { short r; r = nwarnings; nwarnings = 0; return r; } /* * Recover from an error. * * This is the default routine which we aren't using since we have our own. * public erecover() { } * */ /* * Default way to quit from a program is just to exit. * public quit(r) int r; { exit(r); } * */ /* * Compare n-byte areas pointed to by s1 and s2 * if n is 0 then compare up until one has a null byte. */ public int cmp(s1, s2, n) register char *s1, *s2; register unsigned int n; { if (s1 == nil(char *) || s2 == nil(char *)) { panic("cmp: nil pointer"); } if (n == 0) { while (*s1 == *s2++) { if (*s1++ == '\0') { return(0); } } return(*s1 - *(s2-1)); } else { for (; n != 0; n--) { if (*s1++ != *s2++) { return(*(s1-1) - *(s2-1)); } } return(0); } } /* * Move n bytes from src to dest. * If n is 0 move until a null is found. */ public mov(src, dest, n) register char *src, *dest; register unsigned int n; { if (src == nil(char *)) panic("mov: nil source"); if (dest == nil(char *)) panic("mov: nil destination"); if (n != 0) { for (; n != 0; n--) { *dest++ = *src++; } } else { while ((*dest++ = *src++) != '\0'); } }