Commit | Line | Data |
---|---|---|
2a24676e DF |
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 | */ | |
536150a4 | 6 | |
2a24676e DF |
7 | #ifndef lint |
8 | static char sccsid[] = "@(#)library.c 5.1 (Berkeley) %G%"; | |
9 | #endif not lint | |
0022c355 ML |
10 | |
11 | static char rcsid[] = "$Header: library.c,v 1.5 84/12/26 10:39:52 linton Exp $"; | |
2fd0f574 | 12 | |
536150a4 ML |
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 | ||
2fd0f574 SL |
29 | typedef int integer; |
30 | typedef enum { FALSE, TRUE } boolean; | |
536150a4 ML |
31 | typedef char *String; |
32 | typedef FILE *File; | |
33 | typedef String Filename; | |
34 | ||
35 | #undef FILE | |
36 | ||
536150a4 ML |
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 | ||
2fd0f574 | 88 | #define MAXNARGS 1000 /* unchecked upper limit on max num of arguments */ |
536150a4 ML |
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. | |
ec4f7c5c ML |
209 | * |
210 | * To deal with a kernel idiosyncrasy, we keep a list on the side | |
211 | * of "traced" processes, and do not notice them when waiting for | |
212 | * another process. | |
536150a4 ML |
213 | */ |
214 | ||
215 | typedef struct pidlist { | |
216 | int pid; | |
217 | int status; | |
218 | struct pidlist *next; | |
219 | } Pidlist; | |
220 | ||
ec4f7c5c ML |
221 | private Pidlist *pidlist, *ptrclist, *pfind(); |
222 | ||
223 | public ptraced(pid) | |
224 | int pid; | |
225 | { | |
226 | Pidlist *p; | |
227 | ||
228 | p = alloc(1, Pidlist); | |
229 | p->pid = pid; | |
230 | p->next = ptrclist; | |
231 | ptrclist = p; | |
232 | } | |
233 | ||
234 | public unptraced(pid) | |
235 | int pid; | |
236 | { | |
237 | register Pidlist *p, *prev; | |
238 | ||
239 | prev = nil(Pidlist *); | |
240 | p = ptrclist; | |
241 | while (p != nil(Pidlist *) and p->pid != pid) { | |
242 | prev = p; | |
243 | p = p->next; | |
244 | } | |
245 | if (p != nil(Pidlist *)) { | |
246 | if (prev == nil(Pidlist *)) { | |
247 | ptrclist = p->next; | |
248 | } else { | |
249 | prev->next = p->next; | |
250 | } | |
251 | dispose(p); | |
252 | } | |
253 | } | |
254 | ||
2fd0f574 | 255 | private boolean isptraced(pid) |
ec4f7c5c ML |
256 | int pid; |
257 | { | |
258 | register Pidlist *p; | |
259 | ||
260 | p = ptrclist; | |
261 | while (p != nil(Pidlist *) and p->pid != pid) { | |
262 | p = p->next; | |
263 | } | |
2fd0f574 | 264 | return (boolean) (p != nil(Pidlist *)); |
ec4f7c5c | 265 | } |
536150a4 ML |
266 | |
267 | public pwait(pid, statusp) | |
268 | int pid, *statusp; | |
269 | { | |
b3738d36 ML |
270 | Pidlist *p; |
271 | int pnum, status; | |
536150a4 | 272 | |
b3738d36 ML |
273 | p = pfind(pid); |
274 | if (p != nil(Pidlist *)) { | |
275 | *statusp = p->status; | |
276 | dispose(p); | |
b3738d36 | 277 | } else { |
ec4f7c5c ML |
278 | pnum = wait(&status); |
279 | while (pnum != pid and pnum >= 0) { | |
280 | if (not isptraced(pnum)) { | |
281 | p = alloc(1, Pidlist); | |
282 | p->pid = pnum; | |
283 | p->status = status; | |
284 | p->next = pidlist; | |
285 | pidlist = p; | |
286 | } | |
287 | pnum = wait(&status); | |
288 | } | |
289 | if (pnum < 0) { | |
290 | p = pfind(pid); | |
291 | if (p == nil(Pidlist *)) { | |
292 | panic("pwait: pid %d not found", pid); | |
293 | } | |
294 | *statusp = p->status; | |
295 | dispose(p); | |
296 | } else { | |
297 | *statusp = status; | |
298 | } | |
b3738d36 | 299 | } |
536150a4 ML |
300 | } |
301 | ||
302 | /* | |
303 | * Look for the given process id on the pidlist. | |
304 | * | |
305 | * Unlink it from list if found. | |
306 | */ | |
307 | ||
308 | private Pidlist *pfind(pid) | |
309 | int pid; | |
310 | { | |
311 | register Pidlist *p, *prev; | |
312 | ||
313 | prev = nil(Pidlist *); | |
314 | for (p = pidlist; p != nil(Pidlist *); p = p->next) { | |
315 | if (p->pid == pid) { | |
316 | break; | |
317 | } | |
318 | prev = p; | |
319 | } | |
320 | if (p != nil(Pidlist *)) { | |
321 | if (prev == nil(Pidlist *)) { | |
322 | pidlist = p->next; | |
323 | } else { | |
324 | prev->next = p->next; | |
325 | } | |
326 | } | |
327 | return p; | |
328 | } | |
329 | ||
330 | /* | |
331 | * System call error handler. | |
332 | * | |
333 | * The syserr routine is called when a system call is about to | |
334 | * set the c-bit to report an error. Certain errors are caught | |
335 | * and cause the process to print a message and immediately exit. | |
336 | */ | |
337 | ||
338 | extern int sys_nerr; | |
339 | extern char *sys_errlist[]; | |
340 | ||
341 | /* | |
342 | * Before calling syserr, the integer errno is set to contain the | |
343 | * number of the error. The routine "_mycerror" is a dummy which | |
344 | * is used to force the loader to get my version of cerror rather | |
345 | * than the usual one. | |
346 | */ | |
347 | ||
348 | extern int errno; | |
349 | extern _mycerror(); | |
350 | ||
351 | /* | |
2fd0f574 | 352 | * Initialize error information, setting defaults for handling errors. |
536150a4 ML |
353 | */ |
354 | ||
2fd0f574 SL |
355 | private ERRINFO *errinfo; |
356 | ||
357 | private initErrInfo () | |
358 | { | |
359 | integer i; | |
360 | ||
361 | errinfo = alloc(sys_nerr, ERRINFO); | |
362 | for (i = 0; i < sys_nerr; i++) { | |
363 | errinfo[i].func = ERR_CATCH; | |
364 | } | |
365 | errinfo[0].func = ERR_IGNORE; | |
366 | errinfo[EPERM].func = ERR_IGNORE; | |
367 | errinfo[ENOENT].func = ERR_IGNORE; | |
368 | errinfo[ESRCH].func = ERR_IGNORE; | |
369 | errinfo[EBADF].func = ERR_IGNORE; | |
370 | errinfo[ENOTTY].func = ERR_IGNORE; | |
371 | errinfo[EOPNOTSUPP].func = ERR_IGNORE; | |
372 | } | |
536150a4 ML |
373 | |
374 | public syserr() | |
375 | { | |
376 | ERRINFO *e; | |
377 | ||
2fd0f574 SL |
378 | if (errno < 0 or errno > sys_nerr) { |
379 | fatal("errno %d", errno); | |
380 | } else { | |
381 | if (errinfo == nil(ERRINFO *)) { | |
382 | initErrInfo(); | |
383 | } | |
384 | e = &(errinfo[errno]); | |
385 | if (e->func == ERR_CATCH) { | |
536150a4 | 386 | fatal(sys_errlist[errno]); |
2fd0f574 SL |
387 | } else if (e->func != ERR_IGNORE) { |
388 | (*e->func)(); | |
536150a4 | 389 | } |
536150a4 ML |
390 | } |
391 | } | |
392 | ||
393 | /* | |
2fd0f574 SL |
394 | * Catcherrs' purpose is to initialize the errinfo table, get this module |
395 | * loaded, and make sure my cerror is loaded (only applicable when this is | |
396 | * in a library). | |
536150a4 ML |
397 | */ |
398 | ||
399 | public catcherrs() | |
400 | { | |
401 | _mycerror(); | |
2fd0f574 | 402 | initErrInfo(); |
536150a4 ML |
403 | } |
404 | ||
0022c355 ML |
405 | /* |
406 | * Turn off the error catching mechanism completely by having all errors | |
407 | * ignored. This is most useful between a fork and an exec. | |
408 | */ | |
409 | ||
410 | public nocatcherrs() | |
411 | { | |
412 | integer i; | |
413 | ||
414 | for (i = 0; i < sys_nerr; i++) { | |
415 | errinfo[i].func = ERR_IGNORE; | |
416 | } | |
417 | } | |
418 | ||
536150a4 ML |
419 | /* |
420 | * Change the action on receipt of an error. | |
421 | */ | |
422 | ||
423 | public onsyserr(n, f) | |
424 | int n; | |
425 | INTFUNC *f; | |
426 | { | |
2fd0f574 SL |
427 | if (errinfo == nil(ERRINFO *)) { |
428 | initErrInfo(); | |
429 | } | |
536150a4 ML |
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; | |
536150a4 | 439 | |
2fd0f574 | 440 | public psignal(s, n) |
536150a4 | 441 | String s; |
2fd0f574 | 442 | integer n; |
536150a4 | 443 | { |
2fd0f574 SL |
444 | String msg; |
445 | integer len; | |
fe99b76d | 446 | extern String sys_siglist[]; |
536150a4 | 447 | |
2fd0f574 SL |
448 | if (n >= 0 and n < sys_nsig) { |
449 | msg = sys_siglist[n]; | |
450 | } else { | |
451 | msg = "Unknown signal"; | |
536150a4 | 452 | } |
2fd0f574 SL |
453 | len = strlen(s); |
454 | if (len > 0) { | |
455 | write(2, s, len); | |
536150a4 ML |
456 | write(2, ": ", 2); |
457 | } | |
2fd0f574 | 458 | write(2, msg, strlen(msg)); |
536150a4 ML |
459 | write(2, "\n", 1); |
460 | } | |
461 | ||
462 | /* | |
463 | * Standard error handling routines. | |
464 | */ | |
465 | ||
466 | private short nerrs; | |
467 | private short nwarnings; | |
468 | ||
469 | /* | |
470 | * Main driver of error message reporting. | |
471 | */ | |
472 | ||
473 | /* VARARGS2 */ | |
474 | private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
475 | String errname; | |
2fd0f574 | 476 | boolean shouldquit; |
536150a4 ML |
477 | String s; |
478 | { | |
479 | fflush(stdout); | |
480 | if (shouldquit and cmdname != nil(String)) { | |
481 | fprintf(stderr, "%s: ", cmdname); | |
482 | } | |
483 | if (errfilename != nil(Filename)) { | |
484 | fprintf(stderr, "%s: ", errfilename); | |
485 | } | |
486 | if (errlineno > 0) { | |
487 | fprintf(stderr, "%d: ", errlineno); | |
488 | } | |
489 | if (errname != nil(String)) { | |
490 | fprintf(stderr, "%s: ", errname); | |
491 | } | |
492 | fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
493 | putc('\n', stderr); | |
494 | if (shouldquit) { | |
495 | quit(1); | |
496 | } | |
497 | } | |
498 | ||
499 | /* | |
500 | * For when printf isn't sufficient for printing the error message ... | |
501 | */ | |
502 | ||
503 | public beginerrmsg() | |
504 | { | |
505 | fflush(stdout); | |
506 | if (errfilename != nil(String)) { | |
507 | fprintf(stderr, "%s: ", errfilename); | |
508 | } | |
509 | if (errlineno > 0) { | |
510 | fprintf(stderr, "%d: ", errlineno); | |
511 | } | |
512 | } | |
513 | ||
514 | public enderrmsg() | |
515 | { | |
516 | putc('\n', stderr); | |
517 | erecover(); | |
518 | } | |
519 | ||
520 | /* | |
521 | * The messages are listed in increasing order of seriousness. | |
522 | * | |
523 | * First are warnings. | |
524 | */ | |
525 | ||
526 | /* VARARGS1 */ | |
527 | public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
528 | String s; | |
529 | { | |
530 | nwarnings++; | |
531 | errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
532 | } | |
533 | ||
534 | /* | |
535 | * Errors are a little worse, they mean something is wrong, | |
536 | * but not so bad that processing can't continue. | |
537 | * | |
538 | * The routine "erecover" is called to recover from the error, | |
539 | * a default routine is provided that does nothing. | |
540 | */ | |
541 | ||
542 | /* VARARGS1 */ | |
543 | public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
544 | String s; | |
545 | { | |
546 | extern erecover(); | |
547 | ||
548 | nerrs++; | |
549 | errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
550 | erecover(); | |
551 | } | |
552 | ||
553 | /* | |
554 | * Non-recoverable user error. | |
555 | */ | |
556 | ||
557 | /* VARARGS1 */ | |
558 | public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
559 | String s; | |
560 | { | |
561 | errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
562 | } | |
563 | ||
564 | /* | |
565 | * Panics indicate an internal program error. | |
566 | */ | |
567 | ||
568 | /* VARARGS1 */ | |
569 | public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
570 | String s; | |
571 | { | |
572 | errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
573 | } | |
574 | ||
575 | short numerrors() | |
576 | { | |
577 | short r; | |
578 | ||
579 | r = nerrs; | |
580 | nerrs = 0; | |
581 | return r; | |
582 | } | |
583 | ||
584 | short numwarnings() | |
585 | { | |
586 | short r; | |
587 | ||
588 | r = nwarnings; | |
589 | nwarnings = 0; | |
590 | return r; | |
591 | } | |
592 | ||
593 | /* | |
594 | * Recover from an error. | |
595 | * | |
596 | * This is the default routine which we aren't using since we have our own. | |
597 | * | |
598 | public erecover() | |
599 | { | |
600 | } | |
601 | * | |
602 | */ | |
603 | ||
604 | /* | |
605 | * Default way to quit from a program is just to exit. | |
606 | * | |
607 | public quit(r) | |
608 | int r; | |
609 | { | |
610 | exit(r); | |
611 | } | |
612 | * | |
613 | */ | |
614 | ||
615 | /* | |
616 | * Compare n-byte areas pointed to by s1 and s2 | |
617 | * if n is 0 then compare up until one has a null byte. | |
618 | */ | |
619 | ||
620 | public int cmp(s1, s2, n) | |
621 | register char *s1, *s2; | |
622 | register unsigned int n; | |
623 | { | |
624 | if (s1 == nil(char *) || s2 == nil(char *)) { | |
625 | panic("cmp: nil pointer"); | |
626 | } | |
627 | if (n == 0) { | |
628 | while (*s1 == *s2++) { | |
629 | if (*s1++ == '\0') { | |
630 | return(0); | |
631 | } | |
632 | } | |
633 | return(*s1 - *(s2-1)); | |
634 | } else { | |
635 | for (; n != 0; n--) { | |
636 | if (*s1++ != *s2++) { | |
637 | return(*(s1-1) - *(s2-1)); | |
638 | } | |
639 | } | |
640 | return(0); | |
641 | } | |
642 | } | |
643 | ||
644 | /* | |
645 | * Move n bytes from src to dest. | |
646 | * If n is 0 move until a null is found. | |
647 | */ | |
648 | ||
649 | public mov(src, dest, n) | |
650 | register char *src, *dest; | |
651 | register unsigned int n; | |
652 | { | |
653 | if (src == nil(char *)) | |
654 | panic("mov: nil source"); | |
655 | if (dest == nil(char *)) | |
656 | panic("mov: nil destination"); | |
657 | if (n != 0) { | |
658 | for (; n != 0; n--) { | |
659 | *dest++ = *src++; | |
660 | } | |
661 | } else { | |
662 | while ((*dest++ = *src++) != '\0'); | |
663 | } | |
664 | } |