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