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