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