BSD 4_2 release
[unix-history] / usr / src / ucb / dbx / library.c
CommitLineData
d842e291 1#ifndef lint
0f4556f1 2static char sccsid[] = "@(#)library.c 1.4 (Berkeley) 8/13/83";
d842e291 3#endif
536150a4 4
d842e291 5/* Copyright (c) 1982 Regents of the University of California */
536150a4
ML
6
7/*
8 * General purpose routines.
9 */
10
11#include <stdio.h>
12#include <errno.h>
13#include <signal.h>
14
15#define public
16#define private static
17#define and &&
18#define or ||
19#define not !
20#define ord(enumcon) ((int) enumcon)
21#define nil(type) ((type) 0)
22
23typedef enum { FALSE, TRUE } Boolean;
24typedef char *String;
25typedef FILE *File;
26typedef String Filename;
27
28#undef FILE
29
30/*
31 * Definitions of standard C library routines that aren't in the
32 * standard I/O library, but which are generally useful.
33 */
34
35extern long atol(); /* ascii to long */
36extern double atof(); /* ascii to floating point */
37extern char *mktemp(); /* make a temporary file name */
38
39String cmdname; /* name of command for error messages */
40Filename errfilename; /* current file associated with error */
41short errlineno; /* line number associated with error */
42
43/*
44 * Definitions for doing memory allocation.
45 */
46
47extern char *malloc();
48
49#define alloc(n, type) ((type *) malloc((unsigned) (n) * sizeof(type)))
50#define dispose(p) { free((char *) p); p = 0; }
51
52/*
53 * Macros for doing freads + fwrites.
54 */
55
56#define get(fp, var) fread((char *) &(var), sizeof(var), 1, fp)
57#define put(fp, var) fwrite((char *) &(var), sizeof(var), 1, fp)
58
59/*
60 * String definitions.
61 */
62
63extern String strcpy(), index(), rindex();
64extern int strlen();
65
66#define strdup(s) strcpy(malloc((unsigned) strlen(s) + 1), s)
67#define streq(s1, s2) (strcmp(s1, s2) == 0)
68
69typedef int INTFUNC();
70
71typedef 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 100 /* unchecked upper limit on max num of arguments */
91#define BADEXEC 127 /* exec fails */
92
93#define ischild(pid) ((pid) == 0)
94
95/* VARARGS3 */
96public int call(name, in, out, args)
97String name;
98File in;
99File out;
100String 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 */
116public int back(name, in, out, args)
117String name;
118File in;
119File out;
120String 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
135public int callv(name, in, out, argv)
136String name;
137File in;
138File out;
139String *argv;
140{
141 int pid, status;
142
143 pid = backv(name, in, out, argv);
144 pwait(pid, &status);
145 return status;
146}
147
148public int backv(name, in, out, argv)
149String name;
150File in;
151File out;
152String *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
171private fswap(oldfd, newfd)
172int oldfd;
173int 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
188public shell(s)
189String 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.
ec4f7c5c
ML
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.
536150a4
ML
215 */
216
217typedef struct pidlist {
218 int pid;
219 int status;
220 struct pidlist *next;
221} Pidlist;
222
ec4f7c5c
ML
223private Pidlist *pidlist, *ptrclist, *pfind();
224
225public ptraced(pid)
226int pid;
227{
228 Pidlist *p;
229
230 p = alloc(1, Pidlist);
231 p->pid = pid;
232 p->next = ptrclist;
233 ptrclist = p;
234}
235
236public unptraced(pid)
237int 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
257private Boolean isptraced(pid)
258int 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}
536150a4
ML
268
269public pwait(pid, statusp)
270int pid, *statusp;
271{
b3738d36
ML
272 Pidlist *p;
273 int pnum, status;
536150a4 274
b3738d36
ML
275 p = pfind(pid);
276 if (p != nil(Pidlist *)) {
277 *statusp = p->status;
278 dispose(p);
b3738d36 279 } else {
ec4f7c5c
ML
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 }
b3738d36 301 }
536150a4
ML
302}
303
304/*
305 * Look for the given process id on the pidlist.
306 *
307 * Unlink it from list if found.
308 */
309
310private Pidlist *pfind(pid)
311int 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
340extern int sys_nerr;
341extern 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
350extern int errno;
351extern _mycerror();
352
353/*
354 * Default error handling.
355 */
356
357private ERRINFO errinfo[] ={
358/* no error */ ERR_IGNORE,
359/* EPERM */ ERR_IGNORE,
360/* ENOENT */ ERR_IGNORE,
361/* ESRCH */ ERR_IGNORE,
362/* EINTR */ ERR_CATCH,
363/* EIO */ ERR_CATCH,
364/* ENXIO */ ERR_CATCH,
365/* E2BIG */ ERR_CATCH,
366/* ENOEXEC */ ERR_CATCH,
367/* EBADF */ ERR_IGNORE,
368/* ECHILD */ ERR_CATCH,
369/* EAGAIN */ ERR_CATCH,
370/* ENOMEM */ ERR_CATCH,
371/* EACCES */ ERR_CATCH,
372/* EFAULT */ ERR_CATCH,
373/* ENOTBLK */ ERR_CATCH,
374/* EBUSY */ ERR_CATCH,
375/* EEXIST */ ERR_CATCH,
376/* EXDEV */ ERR_CATCH,
377/* ENODEV */ ERR_CATCH,
378/* ENOTDIR */ ERR_CATCH,
379/* EISDIR */ ERR_CATCH,
380/* EINVAL */ ERR_CATCH,
381/* ENFILE */ ERR_CATCH,
382/* EMFILE */ ERR_CATCH,
383/* ENOTTY */ ERR_IGNORE,
384/* ETXTBSY */ ERR_CATCH,
385/* EFBIG */ ERR_CATCH,
386/* ENOSPC */ ERR_CATCH,
387/* ESPIPE */ ERR_CATCH,
388/* EROFS */ ERR_CATCH,
389/* EMLINK */ ERR_CATCH,
390/* EPIPE */ ERR_CATCH,
391/* EDOM */ ERR_CATCH,
392/* ERANGE */ ERR_CATCH,
393/* EQUOT */ ERR_CATCH,
394};
395
396public syserr()
397{
398 ERRINFO *e;
399
400 e = &errinfo[errno];
401 if (e->func == ERR_CATCH) {
402 if (errno < sys_nerr) {
403 fatal(sys_errlist[errno]);
404 } else {
405 fatal("errno %d", errno);
406 }
407 } else if (e->func != ERR_IGNORE) {
408 (*e->func)();
409 }
410}
411
412/*
413 * Catcherrs only purpose is to get this module loaded and make
414 * sure my cerror is loaded (only applicable when this is in a library).
415 */
416
417public catcherrs()
418{
419 _mycerror();
420}
421
422/*
423 * Change the action on receipt of an error.
424 */
425
426public onsyserr(n, f)
427int n;
428INTFUNC *f;
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
438public int sys_nsig = NSIG;
439public String sys_siglist[] = {
440 "no signal",
441 "hangup",
442 "interrupt",
443 "quit",
444 "illegal instruction",
445 "trace trap",
446 "IOT instruction",
447 "EMT instruction",
448 "floating point exception",
449 "kill",
450 "bus error",
451 "segmentation violation",
452 "bad argument to system call",
453 "broken pipe",
454 "alarm clock",
455 "soft kill",
456 "urgent I/O condition",
457 "stop signal not from tty",
458 "stop signal from tty",
459 "continue",
460 "child termination",
461 "stop (tty input)",
462 "stop (tty output)",
463 "possible input/output",
464 "exceeded CPU time limit",
465 "exceeded file size limit",
466 nil(String)
467};
468
469public psig(s)
470String s;
471{
472 String c;
473 int n;
474
475 c = "Unknown signal";
476 if (errno < sys_nsig) {
477 c = sys_errlist[errno];
478 }
479 n = strlen(s);
480 if (n > 0) {
481 write(2, s, n);
482 write(2, ": ", 2);
483 }
484 write(2, c, strlen(c));
485 write(2, "\n", 1);
486}
487
488/*
489 * Standard error handling routines.
490 */
491
492private short nerrs;
493private short nwarnings;
494
495/*
496 * Main driver of error message reporting.
497 */
498
499/* VARARGS2 */
500private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m)
501String errname;
502Boolean shouldquit;
503String s;
504{
505 fflush(stdout);
506 if (shouldquit and cmdname != nil(String)) {
507 fprintf(stderr, "%s: ", cmdname);
508 }
509 if (errfilename != nil(Filename)) {
510 fprintf(stderr, "%s: ", errfilename);
511 }
512 if (errlineno > 0) {
513 fprintf(stderr, "%d: ", errlineno);
514 }
515 if (errname != nil(String)) {
516 fprintf(stderr, "%s: ", errname);
517 }
518 fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
519 putc('\n', stderr);
520 if (shouldquit) {
521 quit(1);
522 }
523}
524
525/*
526 * For when printf isn't sufficient for printing the error message ...
527 */
528
529public beginerrmsg()
530{
531 fflush(stdout);
532 if (errfilename != nil(String)) {
533 fprintf(stderr, "%s: ", errfilename);
534 }
535 if (errlineno > 0) {
536 fprintf(stderr, "%d: ", errlineno);
537 }
538}
539
540public enderrmsg()
541{
542 putc('\n', stderr);
543 erecover();
544}
545
546/*
547 * The messages are listed in increasing order of seriousness.
548 *
549 * First are warnings.
550 */
551
552/* VARARGS1 */
553public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
554String s;
555{
556 nwarnings++;
557 errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
558}
559
560/*
561 * Errors are a little worse, they mean something is wrong,
562 * but not so bad that processing can't continue.
563 *
564 * The routine "erecover" is called to recover from the error,
565 * a default routine is provided that does nothing.
566 */
567
568/* VARARGS1 */
569public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
570String s;
571{
572 extern erecover();
573
574 nerrs++;
575 errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
576 erecover();
577}
578
579/*
580 * Non-recoverable user error.
581 */
582
583/* VARARGS1 */
584public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
585String s;
586{
587 errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
588}
589
590/*
591 * Panics indicate an internal program error.
592 */
593
594/* VARARGS1 */
595public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
596String s;
597{
598 errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
599}
600
601short numerrors()
602{
603 short r;
604
605 r = nerrs;
606 nerrs = 0;
607 return r;
608}
609
610short numwarnings()
611{
612 short r;
613
614 r = nwarnings;
615 nwarnings = 0;
616 return r;
617}
618
619/*
620 * Recover from an error.
621 *
622 * This is the default routine which we aren't using since we have our own.
623 *
624public erecover()
625{
626}
627 *
628 */
629
630/*
631 * Default way to quit from a program is just to exit.
632 *
633public quit(r)
634int r;
635{
636 exit(r);
637}
638 *
639 */
640
641/*
642 * Compare n-byte areas pointed to by s1 and s2
643 * if n is 0 then compare up until one has a null byte.
644 */
645
646public int cmp(s1, s2, n)
647register char *s1, *s2;
648register unsigned int n;
649{
650 if (s1 == nil(char *) || s2 == nil(char *)) {
651 panic("cmp: nil pointer");
652 }
653 if (n == 0) {
654 while (*s1 == *s2++) {
655 if (*s1++ == '\0') {
656 return(0);
657 }
658 }
659 return(*s1 - *(s2-1));
660 } else {
661 for (; n != 0; n--) {
662 if (*s1++ != *s2++) {
663 return(*(s1-1) - *(s2-1));
664 }
665 }
666 return(0);
667 }
668}
669
670/*
671 * Move n bytes from src to dest.
672 * If n is 0 move until a null is found.
673 */
674
675public mov(src, dest, n)
676register char *src, *dest;
677register unsigned int n;
678{
679 if (src == nil(char *))
680 panic("mov: nil source");
681 if (dest == nil(char *))
682 panic("mov: nil destination");
683 if (n != 0) {
684 for (; n != 0; n--) {
685 *dest++ = *src++;
686 }
687 } else {
688 while ((*dest++ = *src++) != '\0');
689 }
690}