xref: /csrg-svn/old/dbx/library.c (revision 16611)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 static char sccsid[] = "@(#)library.c 1.3 8/7/83";
4 
5 static char rcsid[] = "$Header: library.c,v 1.3 84/03/27 10:21:12 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  * Change the action on receipt of an error.
401  */
402 
403 public onsyserr(n, f)
404 int n;
405 INTFUNC *f;
406 {
407     if (errinfo == nil(ERRINFO *)) {
408 	initErrInfo();
409     }
410     errinfo[n].func = f;
411 }
412 
413 /*
414  * Print the message associated with the given signal.
415  * Like a "perror" for signals.
416  */
417 
418 public int sys_nsig = NSIG;
419 public String sys_siglist[] = {
420     "no signal",
421     "hangup",
422     "interrupt",
423     "quit",
424     "illegal instruction",
425     "trace trap",
426     "IOT instruction",
427     "EMT instruction",
428     "floating point exception",
429     "kill",
430     "bus error",
431     "segmentation violation",
432     "bad argument to system call",
433     "broken pipe",
434     "alarm clock",
435     "soft kill",
436     "urgent I/O condition",
437     "stop signal not from tty",
438     "stop signal from tty",
439     "continue",
440     "child termination",
441     "stop (tty input)",
442     "stop (tty output)",
443     "possible input/output",
444     "exceeded CPU time limit",
445     "exceeded file size limit",
446     nil(String)
447 };
448 
449 public psignal(s, n)
450 String s;
451 integer n;
452 {
453     String msg;
454     integer len;
455 
456     if (n >= 0 and n < sys_nsig) {
457 	msg = sys_siglist[n];
458     } else {
459 	msg = "Unknown signal";
460     }
461     len = strlen(s);
462     if (len > 0) {
463 	write(2, s, len);
464 	write(2, ": ", 2);
465     }
466     write(2, msg, strlen(msg));
467     write(2, "\n", 1);
468 }
469 
470 /*
471  * Standard error handling routines.
472  */
473 
474 private short nerrs;
475 private short nwarnings;
476 
477 /*
478  * Main driver of error message reporting.
479  */
480 
481 /* VARARGS2 */
482 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m)
483 String errname;
484 boolean shouldquit;
485 String s;
486 {
487     fflush(stdout);
488     if (shouldquit and cmdname != nil(String)) {
489 	fprintf(stderr, "%s: ", cmdname);
490     }
491     if (errfilename != nil(Filename)) {
492 	fprintf(stderr, "%s: ", errfilename);
493     }
494     if (errlineno > 0) {
495 	fprintf(stderr, "%d: ", errlineno);
496     }
497     if (errname != nil(String)) {
498 	fprintf(stderr, "%s: ", errname);
499     }
500     fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
501     putc('\n', stderr);
502     if (shouldquit) {
503 	quit(1);
504     }
505 }
506 
507 /*
508  * For when printf isn't sufficient for printing the error message ...
509  */
510 
511 public beginerrmsg()
512 {
513     fflush(stdout);
514     if (errfilename != nil(String)) {
515 	fprintf(stderr, "%s: ", errfilename);
516     }
517     if (errlineno > 0) {
518 	fprintf(stderr, "%d: ", errlineno);
519     }
520 }
521 
522 public enderrmsg()
523 {
524     putc('\n', stderr);
525     erecover();
526 }
527 
528 /*
529  * The messages are listed in increasing order of seriousness.
530  *
531  * First are warnings.
532  */
533 
534 /* VARARGS1 */
535 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
536 String s;
537 {
538     nwarnings++;
539     errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
540 }
541 
542 /*
543  * Errors are a little worse, they mean something is wrong,
544  * but not so bad that processing can't continue.
545  *
546  * The routine "erecover" is called to recover from the error,
547  * a default routine is provided that does nothing.
548  */
549 
550 /* VARARGS1 */
551 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
552 String s;
553 {
554     extern erecover();
555 
556     nerrs++;
557     errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
558     erecover();
559 }
560 
561 /*
562  * Non-recoverable user error.
563  */
564 
565 /* VARARGS1 */
566 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
567 String s;
568 {
569     errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
570 }
571 
572 /*
573  * Panics indicate an internal program error.
574  */
575 
576 /* VARARGS1 */
577 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
578 String s;
579 {
580     errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
581 }
582 
583 short numerrors()
584 {
585     short r;
586 
587     r = nerrs;
588     nerrs = 0;
589     return r;
590 }
591 
592 short numwarnings()
593 {
594     short r;
595 
596     r = nwarnings;
597     nwarnings = 0;
598     return r;
599 }
600 
601 /*
602  * Recover from an error.
603  *
604  * This is the default routine which we aren't using since we have our own.
605  *
606 public erecover()
607 {
608 }
609  *
610  */
611 
612 /*
613  * Default way to quit from a program is just to exit.
614  *
615 public quit(r)
616 int r;
617 {
618     exit(r);
619 }
620  *
621  */
622 
623 /*
624  * Compare n-byte areas pointed to by s1 and s2
625  * if n is 0 then compare up until one has a null byte.
626  */
627 
628 public int cmp(s1, s2, n)
629 register char *s1, *s2;
630 register unsigned int n;
631 {
632     if (s1 == nil(char *) || s2 == nil(char *)) {
633 	panic("cmp: nil pointer");
634     }
635     if (n == 0) {
636 	while (*s1 == *s2++) {
637 	    if (*s1++ == '\0') {
638 		return(0);
639 	    }
640 	}
641 	return(*s1 - *(s2-1));
642     } else {
643 	for (; n != 0; n--) {
644 	    if (*s1++ != *s2++) {
645 		return(*(s1-1) - *(s2-1));
646 	    }
647 	}
648 	return(0);
649     }
650 }
651 
652 /*
653  * Move n bytes from src to dest.
654  * If n is 0 move until a null is found.
655  */
656 
657 public mov(src, dest, n)
658 register char *src, *dest;
659 register unsigned int n;
660 {
661     if (src == nil(char *))
662 	panic("mov: nil source");
663     if (dest == nil(char *))
664 	panic("mov: nil destination");
665     if (n != 0) {
666 	for (; n != 0; n--) {
667 	    *dest++ = *src++;
668 	}
669     } else {
670 	while ((*dest++ = *src++) != '\0');
671     }
672 }
673