Commit | Line | Data |
---|---|---|
d842e291 | 1 | #ifndef lint |
0f4556f1 | 2 | static 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 | ||
23 | typedef enum { FALSE, TRUE } Boolean; | |
24 | typedef char *String; | |
25 | typedef FILE *File; | |
26 | typedef 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 | ||
35 | extern long atol(); /* ascii to long */ | |
36 | extern double atof(); /* ascii to floating point */ | |
37 | extern char *mktemp(); /* make a temporary file name */ | |
38 | ||
39 | String cmdname; /* name of command for error messages */ | |
40 | Filename errfilename; /* current file associated with error */ | |
41 | short errlineno; /* line number associated with error */ | |
42 | ||
43 | /* | |
44 | * Definitions for doing memory allocation. | |
45 | */ | |
46 | ||
47 | extern 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 | ||
63 | extern String strcpy(), index(), rindex(); | |
64 | extern int strlen(); | |
65 | ||
66 | #define strdup(s) strcpy(malloc((unsigned) strlen(s) + 1), s) | |
67 | #define streq(s1, s2) (strcmp(s1, s2) == 0) | |
68 | ||
69 | typedef int INTFUNC(); | |
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 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 */ | |
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. | |
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 | ||
217 | typedef struct pidlist { | |
218 | int pid; | |
219 | int status; | |
220 | struct pidlist *next; | |
221 | } Pidlist; | |
222 | ||
ec4f7c5c ML |
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 | } | |
536150a4 ML |
268 | |
269 | public pwait(pid, statusp) | |
270 | int 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 | ||
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 | * Default error handling. | |
355 | */ | |
356 | ||
357 | private 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 | ||
396 | public 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 | ||
417 | public catcherrs() | |
418 | { | |
419 | _mycerror(); | |
420 | } | |
421 | ||
422 | /* | |
423 | * Change the action on receipt of an error. | |
424 | */ | |
425 | ||
426 | public onsyserr(n, f) | |
427 | int n; | |
428 | INTFUNC *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 | ||
438 | public int sys_nsig = NSIG; | |
439 | public 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 | ||
469 | public psig(s) | |
470 | String 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 | ||
492 | private short nerrs; | |
493 | private short nwarnings; | |
494 | ||
495 | /* | |
496 | * Main driver of error message reporting. | |
497 | */ | |
498 | ||
499 | /* VARARGS2 */ | |
500 | private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
501 | String errname; | |
502 | Boolean shouldquit; | |
503 | String 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 | ||
529 | public 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 | ||
540 | public 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 */ | |
553 | public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
554 | String 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 */ | |
569 | public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
570 | String 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 */ | |
584 | public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
585 | String 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 */ | |
595 | public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
596 | String s; | |
597 | { | |
598 | errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
599 | } | |
600 | ||
601 | short numerrors() | |
602 | { | |
603 | short r; | |
604 | ||
605 | r = nerrs; | |
606 | nerrs = 0; | |
607 | return r; | |
608 | } | |
609 | ||
610 | short 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 | * | |
624 | public erecover() | |
625 | { | |
626 | } | |
627 | * | |
628 | */ | |
629 | ||
630 | /* | |
631 | * Default way to quit from a program is just to exit. | |
632 | * | |
633 | public quit(r) | |
634 | int 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 | ||
646 | public int cmp(s1, s2, n) | |
647 | register char *s1, *s2; | |
648 | register 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 | ||
675 | public mov(src, dest, n) | |
676 | register char *src, *dest; | |
677 | register 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 | } |