1 /*-
2 * Copyright (c) 1982, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * %sccs.include.redist.c%
6 */
7
8 #ifndef lint
9 static char sccsid[] = "@(#)library.c 8.2 (Berkeley) 05/27/94";
10 #endif /* not lint */
11
12 /*
13 * General purpose routines.
14 */
15
16 #include <stdio.h>
17 #include <errno.h>
18 #include "defs.h"
19
20 #define public
21 #define private static
22 #define and &&
23 #define nil(type) ((type) 0)
24
25 typedef char *String;
26 typedef FILE *File;
27 typedef String Filename;
28 typedef char Boolean;
29
30 #undef FILE
31
32 String cmdname; /* name of command for error messages */
33 Filename errfilename; /* current file associated with error */
34 short errlineno; /* line number associated with error */
35
36 typedef int INTFUNC();
37
38 typedef struct {
39 INTFUNC *func;
40 } ERRINFO;
41
42 #define ERR_IGNORE ((INTFUNC *) 0)
43 #define ERR_CATCH ((INTFUNC *) 1)
44
45 public INTFUNC *onsyserr();
46
47 /*
48 * Call a program.
49 *
50 * Three entries:
51 *
52 * call, callv - call a program and wait for it, returning status
53 * backv - call a program and don't wait, returning process id
54 *
55 * The command's standard input and output are passed as FILE's.
56 */
57
58
59 #define MAXNARGS 100 /* unchecked upper limit on max num of arguments */
60 #define BADEXEC 127 /* exec fails */
61
62 #define ischild(pid) ((pid) == 0)
63
64 /* VARARGS3 */
call(name,in,out,args)65 public int call(name, in, out, args)
66 String name;
67 File in;
68 File out;
69 String args;
70 {
71 String *ap, *argp;
72 String argv[MAXNARGS];
73
74 argp = &argv[0];
75 *argp++ = name;
76 ap = &args;
77 while (*ap != nil(String)) {
78 *argp++ = *ap++;
79 }
80 *argp = nil(String);
81 return callv(name, in, out, argv);
82 }
83
callv(name,in,out,argv)84 public int callv(name, in, out, argv)
85 String name;
86 File in;
87 File out;
88 String *argv;
89 {
90 int pid, status;
91
92 pid = backv(name, in, out, argv);
93 pwait(pid, &status);
94 return status;
95 }
96
backv(name,in,out,argv)97 public int backv(name, in, out, argv)
98 String name;
99 File in;
100 File out;
101 String *argv;
102 {
103 int pid;
104
105 fflush(stdout);
106 if (ischild(pid = fork())) {
107 fswap(0, fileno(in));
108 fswap(1, fileno(out));
109 (void) onsyserr(EACCES, ERR_IGNORE);
110 execvp(name, argv);
111 _exit(BADEXEC);
112 }
113 return pid;
114 }
115
116 /*
117 * Swap file numbers so as to redirect standard input and output.
118 */
119
fswap(oldfd,newfd)120 private fswap(oldfd, newfd)
121 int oldfd;
122 int newfd;
123 {
124 if (oldfd != newfd) {
125 close(oldfd);
126 dup(newfd);
127 close(newfd);
128 }
129 }
130
131 /*
132 * Invoke a shell on a command line.
133 */
134
135 #define DEF_SHELL "csh"
136
shell(s)137 public shell(s)
138 String s;
139 {
140 extern String getenv();
141 String sh;
142
143 if ((sh = getenv("SHELL")) == nil(String)) {
144 sh = DEF_SHELL;
145 }
146 call(sh, stdin, stdout, "-c", s, 0);
147 }
148
149 /*
150 * Wait for a process the right way. We wait for a particular
151 * process and if any others come along in between, we remember them
152 * in case they are eventually waited for.
153 *
154 * This routine is not very efficient when the number of processes
155 * to be remembered is large.
156 */
157
158 typedef struct pidlist {
159 int pid;
160 int status;
161 struct pidlist *next;
162 } Pidlist;
163
164 private Pidlist *pidlist, *pfind();
165
pwait(pid,statusp)166 public pwait(pid, statusp)
167 int pid, *statusp;
168 {
169 Pidlist *p;
170 int pnum, status;
171
172 p = pfind(pid);
173 if (p != nil(Pidlist *)) {
174 *statusp = p->status;
175 dispose(p);
176 return;
177 }
178 while ((pnum = wait(&status)) != pid && pnum >= 0) {
179 p = alloc(1, Pidlist);
180 p->pid = pnum;
181 p->status = status;
182 p->next = pidlist;
183 pidlist = p;
184 }
185 if (pnum < 0) {
186 p = pfind(pid);
187 if (p == nil(Pidlist *)) {
188 panic("pwait: pid %d not found", pid);
189 }
190 *statusp = p->status;
191 dispose(p);
192 } else {
193 *statusp = status;
194 }
195 #ifdef tahoe
196 chkret(p, status);
197 #endif
198 }
199
200 /*
201 * Look for the given process id on the pidlist.
202 *
203 * Unlink it from list if found.
204 */
205
pfind(pid)206 private Pidlist *pfind(pid)
207 int pid;
208 {
209 register Pidlist *p, *prev;
210
211 prev = nil(Pidlist *);
212 for (p = pidlist; p != nil(Pidlist *); p = p->next) {
213 if (p->pid == pid) {
214 break;
215 }
216 prev = p;
217 }
218 if (p != nil(Pidlist *)) {
219 if (prev == nil(Pidlist *)) {
220 pidlist = p->next;
221 } else {
222 prev->next = p->next;
223 }
224 }
225 return p;
226 }
227
228 /*
229 * System call error handler.
230 *
231 * The syserr routine is called when a system call is about to
232 * set the c-bit to report an error. Certain errors are caught
233 * and cause the process to print a message and immediately exit.
234 */
235
236 extern int sys_nerr;
237
238 /*
239 * Before calling syserr, the integer errno is set to contain the
240 * number of the error.
241 */
242
243 extern int errno;
244
245 /*
246 * default error handling
247 */
248
249 private ERRINFO errinfo[] ={
250 /* no error */ ERR_IGNORE,
251 /* EPERM */ ERR_IGNORE,
252 /* ENOENT */ ERR_IGNORE,
253 /* ESRCH */ ERR_IGNORE,
254 /* EINTR */ ERR_CATCH,
255 /* EIO */ ERR_CATCH,
256 /* ENXIO */ ERR_CATCH,
257 /* E2BIG */ ERR_CATCH,
258 /* ENOEXEC */ ERR_CATCH,
259 /* EBADF */ ERR_IGNORE,
260 /* ECHILD */ ERR_CATCH,
261 /* EAGAIN */ ERR_CATCH,
262 /* ENOMEM */ ERR_CATCH,
263 /* EACCES */ ERR_CATCH,
264 /* EFAULT */ ERR_CATCH,
265 /* ENOTBLK */ ERR_CATCH,
266 /* EBUSY */ ERR_CATCH,
267 /* EEXIST */ ERR_CATCH,
268 /* EXDEV */ ERR_CATCH,
269 /* ENODEV */ ERR_CATCH,
270 /* ENOTDIR */ ERR_CATCH,
271 /* EISDIR */ ERR_CATCH,
272 /* EINVAL */ ERR_CATCH,
273 /* ENFILE */ ERR_CATCH,
274 /* EMFILE */ ERR_CATCH,
275 /* ENOTTY */ ERR_IGNORE,
276 /* ETXTBSY */ ERR_CATCH,
277 /* EFBIG */ ERR_CATCH,
278 /* ENOSPC */ ERR_CATCH,
279 /* ESPIPE */ ERR_CATCH,
280 /* EROFS */ ERR_CATCH,
281 /* EMLINK */ ERR_CATCH,
282 /* EPIPE */ ERR_CATCH,
283 /* EDOM */ ERR_CATCH,
284 /* ERANGE */ ERR_CATCH,
285 /* EQUOT */ ERR_CATCH,
286 };
287
syserr()288 public syserr()
289 {
290 ERRINFO *e;
291
292 e = &errinfo[errno];
293 if (e->func == ERR_CATCH) {
294 if (errno < sys_nerr) {
295 panic(sys_errlist[errno]);
296 } else {
297 panic("errno %d", errno);
298 }
299 } else if (e->func != ERR_IGNORE) {
300 (*e->func)();
301 }
302 }
303
304 /*
305 * Change the action on receipt of an error.
306 */
307
onsyserr(n,f)308 public INTFUNC *onsyserr(n, f)
309 int n;
310 INTFUNC *f;
311 {
312 INTFUNC *g = errinfo[n].func;
313
314 errinfo[n].func = f;
315 return(g);
316 }
317
318 /*
319 * Main driver of error message reporting.
320 */
321
322 /* VARARGS2 */
errmsg(errname,shouldquit,s,a,b,c,d,e,f,g,h,i,j,k,l,m)323 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m)
324 String errname;
325 Boolean shouldquit;
326 String s;
327 {
328 fflush(stdout);
329 if (shouldquit and cmdname != nil(String)) {
330 fprintf(stderr, "%s: ", cmdname);
331 }
332 if (errfilename != nil(Filename)) {
333 fprintf(stderr, "%s: ", errfilename);
334 }
335 if (errlineno > 0) {
336 fprintf(stderr, "%d: ", errlineno);
337 }
338 if (errname != nil(String)) {
339 fprintf(stderr, "%s: ", errname);
340 }
341 fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
342 putc('\n', stderr);
343 if (shouldquit) {
344 quit(1);
345 }
346 }
347
348 /*
349 * Errors are a little worse, they mean something is wrong,
350 * but not so bad that processing can't continue.
351 *
352 * The routine "erecover" is called to recover from the error,
353 * a default routine is provided that does nothing.
354 */
355
356 /* VARARGS1 */
error(s,a,b,c,d,e,f,g,h,i,j,k,l,m)357 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
358 String s;
359 {
360 extern erecover();
361
362 errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
363 erecover();
364 }
365
366 /*
367 * Non-recoverable user error.
368 */
369
370 /* VARARGS1 */
fatal(s,a,b,c,d,e,f,g,h,i,j,k,l,m)371 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
372 String s;
373 {
374 errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
375 }
376
377 /*
378 * Panics indicate an internal program error.
379 */
380
381 /* VARARGS1 */
panic(s,a,b,c,d,e,f,g,h,i,j,k,l,m)382 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m)
383 String s;
384 {
385 errmsg("panic", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m);
386 }
387
388 /*
389 * Compare n-byte areas pointed to by s1 and s2
390 * if n is 0 then compare up until one has a null byte.
391 */
392
cmp(s1,s2,n)393 public int cmp(s1, s2, n)
394 register char *s1, *s2;
395 register unsigned int n;
396 {
397 if (s1 == nil(char *) || s2 == nil(char *)) {
398 panic("cmp: nil pointer");
399 }
400 if (n == 0) {
401 while (*s1 == *s2++) {
402 if (*s1++ == '\0') {
403 return(0);
404 }
405 }
406 return(*s1 - *(s2-1));
407 } else {
408 for (; n != 0; n--) {
409 if (*s1++ != *s2++) {
410 return(*(s1-1) - *(s2-1));
411 }
412 }
413 return(0);
414 }
415 }
416
417 /*
418 * Move n bytes from src to dest.
419 * If n is 0 move until a null is found.
420 */
421
mov(src,dest,n)422 public mov(src, dest, n)
423 register char *src, *dest;
424 register int n;
425 {
426 if (src == nil(char *)) {
427 panic("mov: nil source");
428 }
429 if (dest == nil(char *)) {
430 panic("mov: nil destination");
431 }
432 if (n > 0) {
433 for (; n != 0; n--) {
434 *dest++ = *src++;
435 }
436 } else {
437 while ((*dest++ = *src++) != '\0');
438 }
439 }
440