xref: /csrg-svn/old/dbx/library.c (revision 18221)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static	char sccsid[] = "@(#)library.c	1.6 (Berkeley) 03/01/85";
4 
5 static char rcsid[] = "$Header: library.c,v 1.5 84/12/26 10:39:52 linton Exp $";
6 
7 /*
8  * General purpose routines.
9  */
10 
11 #include <stdio.h>
12 #include <errno.h>
13 #include <signal.h>
14 
15 #define public
16 #define private static
17 #define and &&
18 #define or ||
19 #define not !
20 #define ord(enumcon)	((int) enumcon)
21 #define nil(type)	((type) 0)
22 
23 typedef int integer;
24 typedef enum { FALSE, TRUE } boolean;
25 typedef char *String;
26 typedef FILE *File;
27 typedef String Filename;
28 
29 #undef FILE
30 
31 String cmdname;			/* name of command for error messages */
32 Filename errfilename;		/* current file associated with error */
33 short errlineno;		/* line number associated with error */
34 
35 /*
36  * Definitions for doing memory allocation.
37  */
38 
39 extern char *malloc();
40 
41 #define alloc(n, type)	((type *) malloc((unsigned) (n) * sizeof(type)))
42 #define dispose(p)	{ free((char *) p); p = 0; }
43 
44 /*
45  * Macros for doing freads + fwrites.
46  */
47 
48 #define get(fp, var)	fread((char *) &(var), sizeof(var), 1, fp)
49 #define put(fp, var)	fwrite((char *) &(var), sizeof(var), 1, fp)
50 
51 /*
52  * String definitions.
53  */
54 
55 extern String strcpy(), index(), rindex();
56 extern int strlen();
57 
58 #define strdup(s)		strcpy(malloc((unsigned) strlen(s) + 1), s)
59 #define streq(s1, s2)	(strcmp(s1, s2) == 0)
60 
61 typedef int INTFUNC();
62 
63 typedef struct {
64     INTFUNC *func;
65 } ERRINFO;
66 
67 #define ERR_IGNORE ((INTFUNC *) 0)
68 #define ERR_CATCH  ((INTFUNC *) 1)
69 
70 /*
71  * Call a program.
72  *
73  * Four entries:
74  *
75  *	call, callv - call a program and wait for it, returning status
76  *	back, backv - call a program and don't wait, returning process id
77  *
78  * The command's standard input and output are passed as FILE's.
79  */
80 
81 
82 #define MAXNARGS 1000    /* unchecked upper limit on max num of arguments */
83 #define BADEXEC 127	/* exec fails */
84 
85 #define ischild(pid)    ((pid) == 0)
86 
87 /* VARARGS3 */
88 public int call(name, in, out, args)
89 String name;
90 File in;
91 File out;
92 String args;
93 {
94     String *ap, *argp;
95     String argv[MAXNARGS];
96 
97     argp = &argv[0];
98     *argp++ = name;
99     ap = &args;
100     while (*ap != nil(String)) {
101 	*argp++ = *ap++;
102     }
103     *argp = nil(String);
104     return callv(name, in, out, argv);
105 }
106 
107 /* VARARGS3 */
108 public int back(name, in, out, args)
109 String name;
110 File in;
111 File out;
112 String args;
113 {
114     String *ap, *argp;
115     String argv[MAXNARGS];
116 
117     argp = &argv[0];
118     *argp++ = name;
119     ap = &args;
120     while (*ap != nil(String)) {
121 	*argp++ = *ap++;
122     }
123     *argp = nil(String);
124     return backv(name, in, out, argv);
125 }
126 
127 public int callv(name, in, out, argv)
128 String name;
129 File in;
130 File out;
131 String *argv;
132 {
133     int pid, status;
134 
135     pid = backv(name, in, out, argv);
136     pwait(pid, &status);
137     return status;
138 }
139 
140 public int backv(name, in, out, argv)
141 String name;
142 File in;
143 File out;
144 String *argv;
145 {
146     int pid;
147 
148     fflush(stdout);
149     if (ischild(pid = fork())) {
150 	fswap(0, fileno(in));
151 	fswap(1, fileno(out));
152 	onsyserr(EACCES, ERR_IGNORE);
153 	execvp(name, argv);
154 	_exit(BADEXEC);
155     }
156     return pid;
157 }
158 
159 /*
160  * Swap file numbers so as to redirect standard input and output.
161  */
162 
163 private fswap(oldfd, newfd)
164 int oldfd;
165 int newfd;
166 {
167     if (oldfd != newfd) {
168 	close(oldfd);
169 	dup(newfd);
170 	close(newfd);
171     }
172 }
173 
174 /*
175  * Invoke a shell on a command line.
176  */
177 
178 #define DEF_SHELL	"csh"
179 
180 public shell(s)
181 String s;
182 {
183     extern String getenv();
184     String sh;
185 
186     if ((sh = getenv("SHELL")) == nil(String)) {
187 	sh = DEF_SHELL;
188     }
189     if (s != nil(String) and *s != '\0') {
190 	call(sh, stdin, stdout, "-c", s, 0);
191     } else {
192 	call(sh, stdin, stdout, 0);
193     }
194 }
195 
196 /*
197  * Wait for a process the right way.  We wait for a particular
198  * process and if any others come along in between, we remember them
199  * in case they are eventually waited for.
200  *
201  * This routine is not very efficient when the number of processes
202  * to be remembered is large.
203  *
204  * To deal with a kernel idiosyncrasy, we keep a list on the side
205  * of "traced" processes, and do not notice them when waiting for
206  * another process.
207  */
208 
209 typedef struct pidlist {
210     int pid;
211     int status;
212     struct pidlist *next;
213 } Pidlist;
214 
215 private Pidlist *pidlist, *ptrclist, *pfind();
216 
217 public ptraced(pid)
218 int pid;
219 {
220     Pidlist *p;
221 
222     p = alloc(1, Pidlist);
223     p->pid = pid;
224     p->next = ptrclist;
225     ptrclist = p;
226 }
227 
228 public unptraced(pid)
229 int pid;
230 {
231     register Pidlist *p, *prev;
232 
233     prev = nil(Pidlist *);
234     p = ptrclist;
235     while (p != nil(Pidlist *) and p->pid != pid) {
236 	prev = p;
237 	p = p->next;
238     }
239     if (p != nil(Pidlist *)) {
240 	if (prev == nil(Pidlist *)) {
241 	    ptrclist = p->next;
242 	} else {
243 	    prev->next = p->next;
244 	}
245 	dispose(p);
246     }
247 }
248 
249 private boolean isptraced(pid)
250 int pid;
251 {
252     register Pidlist *p;
253 
254     p = ptrclist;
255     while (p != nil(Pidlist *) and p->pid != pid) {
256 	p = p->next;
257     }
258     return (boolean) (p != nil(Pidlist *));
259 }
260 
261 public pwait(pid, statusp)
262 int pid, *statusp;
263 {
264     Pidlist *p;
265     int pnum, status;
266 
267     p = pfind(pid);
268     if (p != nil(Pidlist *)) {
269 	*statusp = p->status;
270 	dispose(p);
271     } else {
272 	pnum = wait(&status);
273 	while (pnum != pid and pnum >= 0) {
274 	    if (not isptraced(pnum)) {
275 		p = alloc(1, Pidlist);
276 		p->pid = pnum;
277 		p->status = status;
278 		p->next = pidlist;
279 		pidlist = p;
280 	    }
281 	    pnum = wait(&status);
282 	}
283 	if (pnum < 0) {
284 	    p = pfind(pid);
285 	    if (p == nil(Pidlist *)) {
286 		panic("pwait: pid %d not found", pid);
287 	    }
288 	    *statusp = p->status;
289 	    dispose(p);
290 	} else {
291 	    *statusp = status;
292 	}
293     }
294 }
295 
296 /*
297  * Look for the given process id on the pidlist.
298  *
299  * Unlink it from list if found.
300  */
301 
302 private Pidlist *pfind(pid)
303 int pid;
304 {
305     register Pidlist *p, *prev;
306 
307     prev = nil(Pidlist *);
308     for (p = pidlist; p != nil(Pidlist *); p = p->next) {
309 	if (p->pid == pid) {
310 	    break;
311 	}
312 	prev = p;
313     }
314     if (p != nil(Pidlist *)) {
315 	if (prev == nil(Pidlist *)) {
316 	    pidlist = p->next;
317 	} else {
318 	    prev->next = p->next;
319 	}
320     }
321     return p;
322 }
323 
324 /*
325  * System call error handler.
326  *
327  * The syserr routine is called when a system call is about to
328  * set the c-bit to report an error.  Certain errors are caught
329  * and cause the process to print a message and immediately exit.
330  */
331 
332 extern int sys_nerr;
333 extern char *sys_errlist[];
334 
335 /*
336  * Before calling syserr, the integer errno is set to contain the
337  * number of the error.  The routine "_mycerror" is a dummy which
338  * is used to force the loader to get my version of cerror rather
339  * than the usual one.
340  */
341 
342 extern int errno;
343 extern _mycerror();
344 
345 /*
346  * Initialize error information, setting defaults for handling errors.
347  */
348 
349 private ERRINFO *errinfo;
350 
351 private initErrInfo ()
352 {
353     integer i;
354 
355     errinfo = alloc(sys_nerr, ERRINFO);
356     for (i = 0; i < sys_nerr; i++) {
357 	errinfo[i].func = ERR_CATCH;
358     }
359     errinfo[0].func = ERR_IGNORE;
360     errinfo[EPERM].func = ERR_IGNORE;
361     errinfo[ENOENT].func = ERR_IGNORE;
362     errinfo[ESRCH].func = ERR_IGNORE;
363     errinfo[EBADF].func = ERR_IGNORE;
364     errinfo[ENOTTY].func = ERR_IGNORE;
365     errinfo[EOPNOTSUPP].func = ERR_IGNORE;
366 }
367 
368 public syserr()
369 {
370     ERRINFO *e;
371 
372     if (errno < 0 or errno > sys_nerr) {
373 	fatal("errno %d", errno);
374     } else {
375 	if (errinfo == nil(ERRINFO *)) {
376 	    initErrInfo();
377 	}
378 	e = &(errinfo[errno]);
379 	if (e->func == ERR_CATCH) {
380 	    fatal(sys_errlist[errno]);
381 	} else if (e->func != ERR_IGNORE) {
382 	    (*e->func)();
383 	}
384     }
385 }
386 
387 /*
388  * Catcherrs' purpose is to initialize the errinfo table, get this module
389  * loaded, and make sure my cerror is loaded (only applicable when this is
390  * in a library).
391  */
392 
393 public catcherrs()
394 {
395     _mycerror();
396     initErrInfo();
397 }
398 
399 /*
400  * Turn off the error catching mechanism completely by having all errors
401  * ignored.  This is most useful between a fork and an exec.
402  */
403 
404 public nocatcherrs()
405 {
406     integer i;
407 
408     for (i = 0; i < sys_nerr; i++) {
409 	errinfo[i].func = ERR_IGNORE;
410     }
411 }
412 
413 /*
414  * Change the action on receipt of an error.
415  */
416 
417 public onsyserr(n, f)
418 int n;
419 INTFUNC *f;
420 {
421     if (errinfo == nil(ERRINFO *)) {
422 	initErrInfo();
423     }
424     errinfo[n].func = f;
425 }
426 
427 /*
428  * Print the message associated with the given signal.
429  * Like a "perror" for signals.
430  */
431 
432 public int sys_nsig = NSIG;
433 public String sys_siglist[] = {
434     "no signal",
435     "hangup",
436     "interrupt",
437     "quit",
438     "illegal instruction",
439     "trace trap",
440     "IOT instruction",
441     "EMT instruction",
442     "floating point exception",
443     "kill",
444     "bus error",
445     "segmentation violation",
446     "bad argument to system call",
447     "broken pipe",
448     "alarm clock",
449     "soft kill",
450     "urgent I/O condition",
451     "stop signal not from tty",
452     "stop signal from tty",
453     "continue",
454     "child termination",
455     "stop (tty input)",
456     "stop (tty output)",
457     "possible input/output",
458     "exceeded CPU time limit",
459     "exceeded file size limit",
460     nil(String)
461 };
462 
463 public psignal(s, n)
464 String s;
465 integer n;
466 {
467     String msg;
468     integer len;
469 
470     if (n >= 0 and n < sys_nsig) {
471 	msg = sys_siglist[n];
472     } else {
473 	msg = "Unknown signal";
474     }
475     len = strlen(s);
476     if (len > 0) {
477 	write(2, s, len);
478 	write(2, ": ", 2);
479     }
480     write(2, msg, strlen(msg));
481     write(2, "\n", 1);
482 }
483 
484 /*
485  * Standard error handling routines.
486  */
487 
488 private short nerrs;
489 private short nwarnings;
490 
491 /*
492  * Main driver of error message reporting.
493  */
494 
495 /* VARARGS2 */
496 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m)
497 String errname;
498 boolean shouldquit;
499 String s;
500 {
501     fflush(stdout);
502     if (shouldquit and cmdname != nil(String)) {
503 	fprintf(stderr, "%s: ", cmdname);
504     }
505     if (errfilename != nil(Filename)) {
506 	fprintf(stderr, "%s: ", errfilename);
507     }
508     if (errlineno > 0) {
509 	fprintf(stderr, "%d: ", errlineno);
510     }
511     if (errname != nil(String)) {
512 	fprintf(stderr, "%s: ", errname);
513     }
514     fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
515     putc('\n', stderr);
516     if (shouldquit) {
517 	quit(1);
518     }
519 }
520 
521 /*
522  * For when printf isn't sufficient for printing the error message ...
523  */
524 
525 public beginerrmsg()
526 {
527     fflush(stdout);
528     if (errfilename != nil(String)) {
529 	fprintf(stderr, "%s: ", errfilename);
530     }
531     if (errlineno > 0) {
532 	fprintf(stderr, "%d: ", errlineno);
533     }
534 }
535 
536 public enderrmsg()
537 {
538     putc('\n', stderr);
539     erecover();
540 }
541 
542 /*
543  * The messages are listed in increasing order of seriousness.
544  *
545  * First are warnings.
546  */
547 
548 /* VARARGS1 */
549 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
550 String s;
551 {
552     nwarnings++;
553     errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
554 }
555 
556 /*
557  * Errors are a little worse, they mean something is wrong,
558  * but not so bad that processing can't continue.
559  *
560  * The routine "erecover" is called to recover from the error,
561  * a default routine is provided that does nothing.
562  */
563 
564 /* VARARGS1 */
565 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
566 String s;
567 {
568     extern erecover();
569 
570     nerrs++;
571     errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
572     erecover();
573 }
574 
575 /*
576  * Non-recoverable user error.
577  */
578 
579 /* VARARGS1 */
580 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
581 String s;
582 {
583     errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
584 }
585 
586 /*
587  * Panics indicate an internal program error.
588  */
589 
590 /* VARARGS1 */
591 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
592 String s;
593 {
594     errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
595 }
596 
597 short numerrors()
598 {
599     short r;
600 
601     r = nerrs;
602     nerrs = 0;
603     return r;
604 }
605 
606 short numwarnings()
607 {
608     short r;
609 
610     r = nwarnings;
611     nwarnings = 0;
612     return r;
613 }
614 
615 /*
616  * Recover from an error.
617  *
618  * This is the default routine which we aren't using since we have our own.
619  *
620 public erecover()
621 {
622 }
623  *
624  */
625 
626 /*
627  * Default way to quit from a program is just to exit.
628  *
629 public quit(r)
630 int r;
631 {
632     exit(r);
633 }
634  *
635  */
636 
637 /*
638  * Compare n-byte areas pointed to by s1 and s2
639  * if n is 0 then compare up until one has a null byte.
640  */
641 
642 public int cmp(s1, s2, n)
643 register char *s1, *s2;
644 register unsigned int n;
645 {
646     if (s1 == nil(char *) || s2 == nil(char *)) {
647 	panic("cmp: nil pointer");
648     }
649     if (n == 0) {
650 	while (*s1 == *s2++) {
651 	    if (*s1++ == '\0') {
652 		return(0);
653 	    }
654 	}
655 	return(*s1 - *(s2-1));
656     } else {
657 	for (; n != 0; n--) {
658 	    if (*s1++ != *s2++) {
659 		return(*(s1-1) - *(s2-1));
660 	    }
661 	}
662 	return(0);
663     }
664 }
665 
666 /*
667  * Move n bytes from src to dest.
668  * If n is 0 move until a null is found.
669  */
670 
671 public mov(src, dest, n)
672 register char *src, *dest;
673 register unsigned int n;
674 {
675     if (src == nil(char *))
676 	panic("mov: nil source");
677     if (dest == nil(char *))
678 	panic("mov: nil destination");
679     if (n != 0) {
680 	for (; n != 0; n--) {
681 	    *dest++ = *src++;
682 	}
683     } else {
684 	while ((*dest++ = *src++) != '\0');
685     }
686 }
687