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