Commit | Line | Data |
---|---|---|
f6f0d0bf KM |
1 | /* |
2 | * Copyright (c) 1982 Regents of the University of California. | |
3 | * All rights reserved. The Berkeley software License Agreement | |
4 | * specifies the terms and conditions for redistribution. | |
5 | */ | |
5bbc6432 | 6 | |
f6f0d0bf KM |
7 | #ifndef lint |
8 | static char sccsid[] = "@(#)library.c 5.1 (Berkeley) %G%"; | |
9 | #endif not lint | |
5bbc6432 ML |
10 | |
11 | /* | |
12 | * General purpose routines. | |
13 | */ | |
14 | ||
15 | #include <stdio.h> | |
16 | #include <errno.h> | |
17 | ||
18 | #define public | |
19 | #define private static | |
20 | #define and && | |
21 | #define or || | |
22 | #define not ! | |
23 | #define ord(enumcon) ((int) enumcon) | |
24 | #define nil(type) ((type) 0) | |
25 | ||
26 | typedef enum { FALSE, TRUE } Boolean; | |
27 | typedef char *String; | |
28 | typedef FILE *File; | |
29 | typedef String Filename; | |
30 | ||
31 | #undef FILE | |
32 | ||
33 | /* | |
34 | * Definitions of standard C library routines that aren't in the | |
35 | * standard I/O library, but which are generally useful. | |
36 | */ | |
37 | ||
38 | extern long atol(); /* ascii to long */ | |
39 | extern double atof(); /* ascii to floating point */ | |
40 | extern char *mktemp(); /* make a temporary file name */ | |
41 | ||
42 | String cmdname; /* name of command for error messages */ | |
43 | Filename errfilename; /* current file associated with error */ | |
44 | short errlineno; /* line number associated with error */ | |
45 | ||
46 | /* | |
47 | * Definitions for doing memory allocation. | |
48 | */ | |
49 | ||
50 | extern char *malloc(); | |
51 | ||
52 | #define alloc(n, type) ((type *) malloc((unsigned) (n) * sizeof(type))) | |
53 | #define dispose(p) { free((char *) p); p = 0; } | |
54 | ||
55 | /* | |
56 | * Macros for doing freads + fwrites. | |
57 | */ | |
58 | ||
59 | #define get(fp, var) fread((char *) &(var), sizeof(var), 1, fp) | |
60 | #define put(fp, var) fwrite((char *) &(var), sizeof(var), 1, fp) | |
61 | ||
62 | /* | |
63 | * String definitions. | |
64 | */ | |
65 | ||
66 | extern String strcpy(), index(), rindex(); | |
67 | extern int strlen(); | |
68 | ||
69 | #define strdup(s) strcpy(malloc((unsigned) strlen(s) + 1), s) | |
70 | #define streq(s1, s2) (strcmp(s1, s2) == 0) | |
71 | ||
72 | typedef int INTFUNC(); | |
73 | ||
74 | typedef struct { | |
75 | INTFUNC *func; | |
76 | } ERRINFO; | |
77 | ||
78 | #define ERR_IGNORE ((INTFUNC *) 0) | |
79 | #define ERR_CATCH ((INTFUNC *) 1) | |
80 | ||
81 | /* | |
82 | * Call a program. | |
83 | * | |
84 | * Four entries: | |
85 | * | |
86 | * call, callv - call a program and wait for it, returning status | |
87 | * back, backv - call a program and don't wait, returning process id | |
88 | * | |
89 | * The command's standard input and output are passed as FILE's. | |
90 | */ | |
91 | ||
92 | ||
93 | #define MAXNARGS 100 /* unchecked upper limit on max num of arguments */ | |
94 | #define BADEXEC 127 /* exec fails */ | |
95 | ||
96 | #define ischild(pid) ((pid) == 0) | |
97 | ||
98 | /* VARARGS3 */ | |
99 | public int call(name, in, out, args) | |
100 | String name; | |
101 | File in; | |
102 | File out; | |
103 | String args; | |
104 | { | |
105 | String *ap, *argp; | |
106 | String argv[MAXNARGS]; | |
107 | ||
108 | argp = &argv[0]; | |
109 | *argp++ = name; | |
110 | ap = &args; | |
111 | while (*ap != nil(String)) { | |
112 | *argp++ = *ap++; | |
113 | } | |
114 | *argp = nil(String); | |
115 | return callv(name, in, out, argv); | |
116 | } | |
117 | ||
118 | /* VARARGS3 */ | |
119 | public int back(name, in, out, args) | |
120 | String name; | |
121 | File in; | |
122 | File out; | |
123 | String args; | |
124 | { | |
125 | String *ap, *argp; | |
126 | String argv[MAXNARGS]; | |
127 | ||
128 | argp = &argv[0]; | |
129 | *argp++ = name; | |
130 | ap = &args; | |
131 | while (*ap != nil(String)) { | |
132 | *argp++ = *ap++; | |
133 | } | |
134 | *argp = nil(String); | |
135 | return backv(name, in, out, argv); | |
136 | } | |
137 | ||
138 | public int callv(name, in, out, argv) | |
139 | String name; | |
140 | File in; | |
141 | File out; | |
142 | String *argv; | |
143 | { | |
144 | int pid, status; | |
145 | ||
146 | pid = backv(name, in, out, argv); | |
147 | pwait(pid, &status); | |
148 | return status; | |
149 | } | |
150 | ||
151 | public int backv(name, in, out, argv) | |
152 | String name; | |
153 | File in; | |
154 | File out; | |
155 | String *argv; | |
156 | { | |
157 | int pid; | |
158 | ||
159 | fflush(stdout); | |
160 | if (ischild(pid = fork())) { | |
161 | fswap(0, fileno(in)); | |
162 | fswap(1, fileno(out)); | |
163 | onsyserr(EACCES, ERR_IGNORE); | |
164 | execvp(name, argv); | |
165 | _exit(BADEXEC); | |
166 | } | |
167 | return pid; | |
168 | } | |
169 | ||
170 | /* | |
171 | * Swap file numbers so as to redirect standard input and output. | |
172 | */ | |
173 | ||
174 | private fswap(oldfd, newfd) | |
175 | int oldfd; | |
176 | int newfd; | |
177 | { | |
178 | if (oldfd != newfd) { | |
179 | close(oldfd); | |
180 | dup(newfd); | |
181 | close(newfd); | |
182 | } | |
183 | } | |
184 | ||
185 | /* | |
186 | * Invoke a shell on a command line. | |
187 | */ | |
188 | ||
189 | #define DEF_SHELL "csh" | |
190 | ||
191 | public shell(s) | |
192 | String s; | |
193 | { | |
194 | extern String getenv(); | |
195 | String sh; | |
196 | ||
197 | if ((sh = getenv("SHELL")) == nil(String)) { | |
198 | sh = DEF_SHELL; | |
199 | } | |
200 | call(sh, stdin, stdout, "-c", s, 0); | |
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 | ||
212 | typedef struct pidlist { | |
213 | int pid; | |
214 | int status; | |
215 | struct pidlist *next; | |
216 | } Pidlist; | |
217 | ||
218 | private Pidlist *pidlist, *pfind(); | |
219 | ||
220 | public pwait(pid, statusp) | |
221 | int pid, *statusp; | |
222 | { | |
223 | Pidlist *p; | |
224 | int pnum, status; | |
225 | ||
226 | p = pfind(pid); | |
227 | if (p != nil(Pidlist *)) { | |
228 | *statusp = p->status; | |
229 | dispose(p); | |
230 | return; | |
231 | } | |
232 | while ((pnum = wait(&status)) != pid && pnum >= 0) { | |
233 | p = alloc(1, Pidlist); | |
234 | p->pid = pnum; | |
235 | p->status = status; | |
236 | p->next = pidlist; | |
237 | pidlist = p; | |
238 | } | |
239 | if (pnum < 0) { | |
240 | p = pfind(pid); | |
241 | if (p == nil(Pidlist *)) { | |
242 | panic("pwait: pid %d not found", pid); | |
243 | } | |
244 | *statusp = p->status; | |
245 | dispose(p); | |
246 | } else { | |
247 | *statusp = status; | |
248 | } | |
249 | } | |
250 | ||
251 | /* | |
252 | * Look for the given process id on the pidlist. | |
253 | * | |
254 | * Unlink it from list if found. | |
255 | */ | |
256 | ||
257 | private Pidlist *pfind(pid) | |
258 | int pid; | |
259 | { | |
260 | register Pidlist *p, *prev; | |
261 | ||
262 | prev = nil(Pidlist *); | |
263 | for (p = pidlist; p != nil(Pidlist *); p = p->next) { | |
264 | if (p->pid == pid) { | |
265 | break; | |
266 | } | |
267 | prev = p; | |
268 | } | |
269 | if (p != nil(Pidlist *)) { | |
270 | if (prev == nil(Pidlist *)) { | |
271 | pidlist = p->next; | |
272 | } else { | |
273 | prev->next = p->next; | |
274 | } | |
275 | } | |
276 | return p; | |
277 | } | |
278 | ||
279 | /* | |
280 | * System call error handler. | |
281 | * | |
282 | * The syserr routine is called when a system call is about to | |
283 | * set the c-bit to report an error. Certain errors are caught | |
284 | * and cause the process to print a message and immediately exit. | |
285 | */ | |
286 | ||
287 | extern int sys_nerr; | |
288 | extern char *sys_errlist[]; | |
289 | ||
290 | /* | |
291 | * Before calling syserr, the integer errno is set to contain the | |
292 | * number of the error. The routine "_mycerror" is a dummy which | |
293 | * is used to force the loader to get my version of cerror rather | |
294 | * than the usual one. | |
295 | */ | |
296 | ||
297 | extern int errno; | |
298 | extern _mycerror(); | |
299 | ||
300 | /* | |
301 | * default error handling | |
302 | */ | |
303 | ||
304 | private ERRINFO errinfo[] ={ | |
305 | /* no error */ ERR_IGNORE, | |
306 | /* EPERM */ ERR_IGNORE, | |
307 | /* ENOENT */ ERR_IGNORE, | |
308 | /* ESRCH */ ERR_IGNORE, | |
309 | /* EINTR */ ERR_CATCH, | |
310 | /* EIO */ ERR_CATCH, | |
311 | /* ENXIO */ ERR_CATCH, | |
312 | /* E2BIG */ ERR_CATCH, | |
313 | /* ENOEXEC */ ERR_CATCH, | |
314 | /* EBADF */ ERR_IGNORE, | |
315 | /* ECHILD */ ERR_CATCH, | |
316 | /* EAGAIN */ ERR_CATCH, | |
317 | /* ENOMEM */ ERR_CATCH, | |
318 | /* EACCES */ ERR_CATCH, | |
319 | /* EFAULT */ ERR_CATCH, | |
320 | /* ENOTBLK */ ERR_CATCH, | |
321 | /* EBUSY */ ERR_CATCH, | |
322 | /* EEXIST */ ERR_CATCH, | |
323 | /* EXDEV */ ERR_CATCH, | |
324 | /* ENODEV */ ERR_CATCH, | |
325 | /* ENOTDIR */ ERR_CATCH, | |
326 | /* EISDIR */ ERR_CATCH, | |
327 | /* EINVAL */ ERR_CATCH, | |
328 | /* ENFILE */ ERR_CATCH, | |
329 | /* EMFILE */ ERR_CATCH, | |
330 | /* ENOTTY */ ERR_IGNORE, | |
331 | /* ETXTBSY */ ERR_CATCH, | |
332 | /* EFBIG */ ERR_CATCH, | |
333 | /* ENOSPC */ ERR_CATCH, | |
334 | /* ESPIPE */ ERR_CATCH, | |
335 | /* EROFS */ ERR_CATCH, | |
336 | /* EMLINK */ ERR_CATCH, | |
337 | /* EPIPE */ ERR_CATCH, | |
338 | /* EDOM */ ERR_CATCH, | |
339 | /* ERANGE */ ERR_CATCH, | |
340 | /* EQUOT */ ERR_CATCH, | |
341 | }; | |
342 | ||
343 | public syserr() | |
344 | { | |
345 | ERRINFO *e; | |
346 | ||
347 | e = &errinfo[errno]; | |
348 | if (e->func == ERR_CATCH) { | |
349 | if (errno < sys_nerr) { | |
350 | panic(sys_errlist[errno]); | |
351 | } else { | |
352 | panic("errno %d", errno); | |
353 | } | |
354 | } else if (e->func != ERR_IGNORE) { | |
355 | (*e->func)(); | |
356 | } | |
357 | } | |
358 | ||
359 | /* | |
360 | * Catcherrs only purpose is to get this module loaded and make | |
361 | * sure my cerror is loaded (only applicable when this is in a library). | |
362 | */ | |
363 | ||
364 | public catcherrs() | |
365 | { | |
366 | _mycerror(); | |
367 | } | |
368 | ||
369 | /* | |
370 | * Change the action on receipt of an error. | |
371 | */ | |
372 | ||
373 | public onsyserr(n, f) | |
374 | int n; | |
375 | INTFUNC *f; | |
376 | { | |
377 | errinfo[n].func = f; | |
378 | } | |
379 | ||
380 | /* | |
381 | * Standard error handling routines. | |
382 | */ | |
383 | ||
384 | private short nerrs; | |
385 | private short nwarnings; | |
386 | ||
387 | /* | |
388 | * Main driver of error message reporting. | |
389 | */ | |
390 | ||
391 | /* VARARGS2 */ | |
392 | private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
393 | String errname; | |
394 | Boolean shouldquit; | |
395 | String s; | |
396 | { | |
397 | fflush(stdout); | |
398 | if (shouldquit and cmdname != nil(String)) { | |
399 | fprintf(stderr, "%s: ", cmdname); | |
400 | } | |
401 | if (errfilename != nil(Filename)) { | |
402 | fprintf(stderr, "%s: ", errfilename); | |
403 | } | |
404 | if (errlineno > 0) { | |
405 | fprintf(stderr, "%d: ", errlineno); | |
406 | } | |
407 | if (errname != nil(String)) { | |
408 | fprintf(stderr, "%s: ", errname); | |
409 | } | |
410 | fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
411 | putc('\n', stderr); | |
412 | if (shouldquit) { | |
413 | quit(1); | |
414 | } | |
415 | } | |
416 | ||
417 | /* | |
418 | * The messages are listed in increasing order of seriousness. | |
419 | * | |
420 | * First are warnings. | |
421 | */ | |
422 | ||
423 | /* VARARGS1 */ | |
424 | public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
425 | String s; | |
426 | { | |
427 | nwarnings++; | |
428 | errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
429 | } | |
430 | ||
431 | /* | |
432 | * Errors are a little worse, they mean something is wrong, | |
433 | * but not so bad that processing can't continue. | |
434 | * | |
435 | * The routine "erecover" is called to recover from the error, | |
436 | * a default routine is provided that does nothing. | |
437 | */ | |
438 | ||
439 | /* VARARGS1 */ | |
440 | public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
441 | String s; | |
442 | { | |
443 | extern erecover(); | |
444 | ||
445 | nerrs++; | |
446 | errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
447 | erecover(); | |
448 | } | |
449 | ||
450 | /* | |
451 | * Non-recoverable user error. | |
452 | */ | |
453 | ||
454 | /* VARARGS1 */ | |
455 | public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
456 | String s; | |
457 | { | |
458 | errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
459 | } | |
460 | ||
461 | /* | |
462 | * Panics indicate an internal program error. | |
463 | */ | |
464 | ||
465 | /* VARARGS1 */ | |
466 | public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) | |
467 | String s; | |
468 | { | |
469 | errmsg("panic", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); | |
470 | } | |
471 | ||
472 | short numerrors() | |
473 | { | |
474 | short r; | |
475 | ||
476 | r = nerrs; | |
477 | nerrs = 0; | |
478 | return r; | |
479 | } | |
480 | ||
481 | short numwarnings() | |
482 | { | |
483 | short r; | |
484 | ||
485 | r = nwarnings; | |
486 | nwarnings = 0; | |
487 | return r; | |
488 | } | |
489 | ||
490 | /* | |
491 | * Recover from an error. | |
492 | * | |
493 | * This is the default routine which we aren't using since we have our own. | |
494 | * | |
495 | public erecover() | |
496 | { | |
497 | } | |
498 | * | |
499 | */ | |
500 | ||
501 | /* | |
502 | * Default way to quit from a program is just to exit. | |
503 | * | |
504 | public quit(r) | |
505 | int r; | |
506 | { | |
507 | exit(r); | |
508 | } | |
509 | * | |
510 | */ | |
511 | ||
512 | /* | |
513 | * Compare n-byte areas pointed to by s1 and s2 | |
514 | * if n is 0 then compare up until one has a null byte. | |
515 | */ | |
516 | ||
517 | public int cmp(s1, s2, n) | |
518 | register char *s1, *s2; | |
519 | register unsigned int n; | |
520 | { | |
521 | if (s1 == nil(char *) || s2 == nil(char *)) { | |
522 | panic("cmp: nil pointer"); | |
523 | } | |
524 | if (n == 0) { | |
525 | while (*s1 == *s2++) { | |
526 | if (*s1++ == '\0') { | |
527 | return(0); | |
528 | } | |
529 | } | |
530 | return(*s1 - *(s2-1)); | |
531 | } else { | |
532 | for (; n != 0; n--) { | |
533 | if (*s1++ != *s2++) { | |
534 | return(*(s1-1) - *(s2-1)); | |
535 | } | |
536 | } | |
537 | return(0); | |
538 | } | |
539 | } | |
540 | ||
541 | /* | |
542 | * Move n bytes from src to dest. | |
543 | * If n is 0 move until a null is found. | |
544 | */ | |
545 | ||
546 | public mov(src, dest, n) | |
547 | register char *src, *dest; | |
548 | register unsigned int n; | |
549 | { | |
550 | if (src == nil(char *)) | |
551 | panic("mov: nil source"); | |
552 | if (dest == nil(char *)) | |
553 | panic("mov: nil destination"); | |
554 | if (n > 0) { | |
555 | for (; n != 0; n--) { | |
556 | *dest++ = *src++; | |
557 | } | |
558 | } else { | |
559 | while ((*dest++ = *src++) != '\0'); | |
560 | } | |
561 | } |