xref: /original-bsd/old/dbx/library.c (revision f0fd5f8a)
1 /* Copyright (c) 1982 Regents of the University of California */
2 
3 /* static char sccsid[] = "@(#)library.c 1.1 9/2/82"; */
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 enum { FALSE, TRUE } Boolean;
22 typedef char *String;
23 typedef FILE *File;
24 typedef String Filename;
25 
26 #undef FILE
27 
28 /*
29  * Definitions of standard C library routines that aren't in the
30  * standard I/O library, but which are generally useful.
31  */
32 
33 extern long atol();		/* ascii to long */
34 extern double atof();		/* ascii to floating point */
35 extern char *mktemp();		/* make a temporary file name */
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 100    /* 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 
211 typedef struct pidlist {
212     int pid;
213     int status;
214     struct pidlist *next;
215 } Pidlist;
216 
217 private Pidlist *pidlist, *pfind();
218 
219 public pwait(pid, statusp)
220 int pid, *statusp;
221 {
222 	Pidlist *p;
223 	int pnum, status;
224 
225 	p = pfind(pid);
226 	if (p != nil(Pidlist *)) {
227 	    *statusp = p->status;
228 	    dispose(p);
229 	    return;
230 	}
231 	while ((pnum = wait(&status)) != pid && pnum >= 0) {
232 	    p = alloc(1, Pidlist);
233 	    p->pid = pnum;
234 	    p->status = status;
235 	    p->next = pidlist;
236 	    pidlist = p;
237 	}
238 	if (pnum < 0) {
239 	    p = pfind(pid);
240 	    if (p == nil(Pidlist *)) {
241 		panic("pwait: pid %d not found", pid);
242 	    }
243 	    *statusp = p->status;
244 	    dispose(p);
245 	} else {
246 		*statusp = status;
247 	}
248 }
249 
250 /*
251  * Look for the given process id on the pidlist.
252  *
253  * Unlink it from list if found.
254  */
255 
256 private Pidlist *pfind(pid)
257 int pid;
258 {
259     register Pidlist *p, *prev;
260 
261     prev = nil(Pidlist *);
262     for (p = pidlist; p != nil(Pidlist *); p = p->next) {
263 	if (p->pid == pid) {
264 	    break;
265 	}
266 	prev = p;
267     }
268     if (p != nil(Pidlist *)) {
269 	if (prev == nil(Pidlist *)) {
270 	    pidlist = p->next;
271 	} else {
272 	    prev->next = p->next;
273 	}
274     }
275     return p;
276 }
277 
278 /*
279  * System call error handler.
280  *
281  * The syserr routine is called when a system call is about to
282  * set the c-bit to report an error.  Certain errors are caught
283  * and cause the process to print a message and immediately exit.
284  */
285 
286 extern int sys_nerr;
287 extern char *sys_errlist[];
288 
289 /*
290  * Before calling syserr, the integer errno is set to contain the
291  * number of the error.  The routine "_mycerror" is a dummy which
292  * is used to force the loader to get my version of cerror rather
293  * than the usual one.
294  */
295 
296 extern int errno;
297 extern _mycerror();
298 
299 /*
300  * Default error handling.
301  */
302 
303 private ERRINFO errinfo[] ={
304 /* no error */	ERR_IGNORE,
305 /* EPERM */	ERR_IGNORE,
306 /* ENOENT */	ERR_IGNORE,
307 /* ESRCH */	ERR_IGNORE,
308 /* EINTR */	ERR_CATCH,
309 /* EIO */	ERR_CATCH,
310 /* ENXIO */	ERR_CATCH,
311 /* E2BIG */	ERR_CATCH,
312 /* ENOEXEC */	ERR_CATCH,
313 /* EBADF */	ERR_IGNORE,
314 /* ECHILD */	ERR_CATCH,
315 /* EAGAIN */	ERR_CATCH,
316 /* ENOMEM */	ERR_CATCH,
317 /* EACCES */	ERR_CATCH,
318 /* EFAULT */	ERR_CATCH,
319 /* ENOTBLK */	ERR_CATCH,
320 /* EBUSY */	ERR_CATCH,
321 /* EEXIST */	ERR_CATCH,
322 /* EXDEV */	ERR_CATCH,
323 /* ENODEV */	ERR_CATCH,
324 /* ENOTDIR */	ERR_CATCH,
325 /* EISDIR */	ERR_CATCH,
326 /* EINVAL */	ERR_CATCH,
327 /* ENFILE */	ERR_CATCH,
328 /* EMFILE */	ERR_CATCH,
329 /* ENOTTY */	ERR_IGNORE,
330 /* ETXTBSY */	ERR_CATCH,
331 /* EFBIG */	ERR_CATCH,
332 /* ENOSPC */	ERR_CATCH,
333 /* ESPIPE */	ERR_CATCH,
334 /* EROFS */	ERR_CATCH,
335 /* EMLINK */	ERR_CATCH,
336 /* EPIPE */	ERR_CATCH,
337 /* EDOM */	ERR_CATCH,
338 /* ERANGE */	ERR_CATCH,
339 /* EQUOT */	ERR_CATCH,
340 };
341 
342 public syserr()
343 {
344     ERRINFO *e;
345 
346     e = &errinfo[errno];
347     if (e->func == ERR_CATCH) {
348 	if (errno < sys_nerr) {
349 	    fatal(sys_errlist[errno]);
350 	} else {
351 	    fatal("errno %d", errno);
352 	}
353     } else if (e->func != ERR_IGNORE) {
354 	(*e->func)();
355     }
356 }
357 
358 /*
359  * Catcherrs only purpose is to get this module loaded and make
360  * sure my cerror is loaded (only applicable when this is in a library).
361  */
362 
363 public catcherrs()
364 {
365     _mycerror();
366 }
367 
368 /*
369  * Change the action on receipt of an error.
370  */
371 
372 public onsyserr(n, f)
373 int n;
374 INTFUNC *f;
375 {
376     errinfo[n].func = f;
377 }
378 
379 /*
380  * Print the message associated with the given signal.
381  * Like a "perror" for signals.
382  */
383 
384 public int sys_nsig = NSIG;
385 public String sys_siglist[] = {
386     "no signal",
387     "hangup",
388     "interrupt",
389     "quit",
390     "illegal instruction",
391     "trace trap",
392     "IOT instruction",
393     "EMT instruction",
394     "floating point exception",
395     "kill",
396     "bus error",
397     "segmentation violation",
398     "bad argument to system call",
399     "broken pipe",
400     "alarm clock",
401     "soft kill",
402     "urgent I/O condition",
403     "stop signal not from tty",
404     "stop signal from tty",
405     "continue",
406     "child termination",
407     "stop (tty input)",
408     "stop (tty output)",
409     "possible input/output",
410     "exceeded CPU time limit",
411     "exceeded file size limit",
412     nil(String)
413 };
414 
415 public psig(s)
416 String s;
417 {
418     String c;
419     int n;
420 
421     c = "Unknown signal";
422     if (errno < sys_nsig) {
423 	c = sys_errlist[errno];
424     }
425     n = strlen(s);
426     if (n > 0) {
427 	write(2, s, n);
428 	write(2, ": ", 2);
429     }
430     write(2, c, strlen(c));
431     write(2, "\n", 1);
432 }
433 
434 /*
435  * Standard error handling routines.
436  */
437 
438 private short nerrs;
439 private short nwarnings;
440 
441 /*
442  * Main driver of error message reporting.
443  */
444 
445 /* VARARGS2 */
446 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m)
447 String errname;
448 Boolean shouldquit;
449 String s;
450 {
451     fflush(stdout);
452     if (shouldquit and cmdname != nil(String)) {
453 	fprintf(stderr, "%s: ", cmdname);
454     }
455     if (errfilename != nil(Filename)) {
456 	fprintf(stderr, "%s: ", errfilename);
457     }
458     if (errlineno > 0) {
459 	fprintf(stderr, "%d: ", errlineno);
460     }
461     if (errname != nil(String)) {
462 	fprintf(stderr, "%s: ", errname);
463     }
464     fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
465     putc('\n', stderr);
466     if (shouldquit) {
467 	quit(1);
468     }
469 }
470 
471 /*
472  * For when printf isn't sufficient for printing the error message ...
473  */
474 
475 public beginerrmsg()
476 {
477     fflush(stdout);
478     if (errfilename != nil(String)) {
479 	fprintf(stderr, "%s: ", errfilename);
480     }
481     if (errlineno > 0) {
482 	fprintf(stderr, "%d: ", errlineno);
483     }
484 }
485 
486 public enderrmsg()
487 {
488     putc('\n', stderr);
489     erecover();
490 }
491 
492 /*
493  * The messages are listed in increasing order of seriousness.
494  *
495  * First are warnings.
496  */
497 
498 /* VARARGS1 */
499 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
500 String s;
501 {
502     nwarnings++;
503     errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
504 }
505 
506 /*
507  * Errors are a little worse, they mean something is wrong,
508  * but not so bad that processing can't continue.
509  *
510  * The routine "erecover" is called to recover from the error,
511  * a default routine is provided that does nothing.
512  */
513 
514 /* VARARGS1 */
515 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
516 String s;
517 {
518     extern erecover();
519 
520     nerrs++;
521     errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
522     erecover();
523 }
524 
525 /*
526  * Non-recoverable user error.
527  */
528 
529 /* VARARGS1 */
530 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
531 String s;
532 {
533     errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
534 }
535 
536 /*
537  * Panics indicate an internal program error.
538  */
539 
540 /* VARARGS1 */
541 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
542 String s;
543 {
544     errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
545 }
546 
547 short numerrors()
548 {
549     short r;
550 
551     r = nerrs;
552     nerrs = 0;
553     return r;
554 }
555 
556 short numwarnings()
557 {
558     short r;
559 
560     r = nwarnings;
561     nwarnings = 0;
562     return r;
563 }
564 
565 /*
566  * Recover from an error.
567  *
568  * This is the default routine which we aren't using since we have our own.
569  *
570 public erecover()
571 {
572 }
573  *
574  */
575 
576 /*
577  * Default way to quit from a program is just to exit.
578  *
579 public quit(r)
580 int r;
581 {
582     exit(r);
583 }
584  *
585  */
586 
587 /*
588  * Compare n-byte areas pointed to by s1 and s2
589  * if n is 0 then compare up until one has a null byte.
590  */
591 
592 public int cmp(s1, s2, n)
593 register char *s1, *s2;
594 register unsigned int n;
595 {
596     if (s1 == nil(char *) || s2 == nil(char *)) {
597 	panic("cmp: nil pointer");
598     }
599     if (n == 0) {
600 	while (*s1 == *s2++) {
601 	    if (*s1++ == '\0') {
602 		return(0);
603 	    }
604 	}
605 	return(*s1 - *(s2-1));
606     } else {
607 	for (; n != 0; n--) {
608 	    if (*s1++ != *s2++) {
609 		return(*(s1-1) - *(s2-1));
610 	    }
611 	}
612 	return(0);
613     }
614 }
615 
616 /*
617  * Move n bytes from src to dest.
618  * If n is 0 move until a null is found.
619  */
620 
621 public mov(src, dest, n)
622 register char *src, *dest;
623 register unsigned int n;
624 {
625     if (src == nil(char *))
626 	panic("mov: nil source");
627     if (dest == nil(char *))
628 	panic("mov: nil destination");
629     if (n != 0) {
630 	for (; n != 0; n--) {
631 	    *dest++ = *src++;
632 	}
633     } else {
634 	while ((*dest++ = *src++) != '\0');
635     }
636 }
637