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