1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 /* static char sccsid[] = "@(#)library.c 1.1 9/2/82"; */ 4 5 /* 6 * General purpose routines. 7 */ 8 9 #include <stdio.h> 10 #include <errno.h> 11 #include <signal.h> 12 13 #define public 14 #define private static 15 #define and && 16 #define or || 17 #define not ! 18 #define ord(enumcon) ((int) enumcon) 19 #define nil(type) ((type) 0) 20 21 typedef enum { FALSE, TRUE } Boolean; 22 typedef char *String; 23 typedef FILE *File; 24 typedef String Filename; 25 26 #undef FILE 27 28 /* 29 * Definitions of standard C library routines that aren't in the 30 * standard I/O library, but which are generally useful. 31 */ 32 33 extern long atol(); /* ascii to long */ 34 extern double atof(); /* ascii to floating point */ 35 extern char *mktemp(); /* make a temporary file name */ 36 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 88 #define MAXNARGS 100 /* unchecked upper limit on max num of arguments */ 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. 209 */ 210 211 typedef struct pidlist { 212 int pid; 213 int status; 214 struct pidlist *next; 215 } Pidlist; 216 217 private Pidlist *pidlist, *pfind(); 218 219 public pwait(pid, statusp) 220 int pid, *statusp; 221 { 222 Pidlist *p; 223 int pnum, status; 224 225 p = pfind(pid); 226 if (p != nil(Pidlist *)) { 227 *statusp = p->status; 228 dispose(p); 229 return; 230 } 231 while ((pnum = wait(&status)) != pid && pnum >= 0) { 232 p = alloc(1, Pidlist); 233 p->pid = pnum; 234 p->status = status; 235 p->next = pidlist; 236 pidlist = p; 237 } 238 if (pnum < 0) { 239 p = pfind(pid); 240 if (p == nil(Pidlist *)) { 241 panic("pwait: pid %d not found", pid); 242 } 243 *statusp = p->status; 244 dispose(p); 245 } else { 246 *statusp = status; 247 } 248 } 249 250 /* 251 * Look for the given process id on the pidlist. 252 * 253 * Unlink it from list if found. 254 */ 255 256 private Pidlist *pfind(pid) 257 int pid; 258 { 259 register Pidlist *p, *prev; 260 261 prev = nil(Pidlist *); 262 for (p = pidlist; p != nil(Pidlist *); p = p->next) { 263 if (p->pid == pid) { 264 break; 265 } 266 prev = p; 267 } 268 if (p != nil(Pidlist *)) { 269 if (prev == nil(Pidlist *)) { 270 pidlist = p->next; 271 } else { 272 prev->next = p->next; 273 } 274 } 275 return p; 276 } 277 278 /* 279 * System call error handler. 280 * 281 * The syserr routine is called when a system call is about to 282 * set the c-bit to report an error. Certain errors are caught 283 * and cause the process to print a message and immediately exit. 284 */ 285 286 extern int sys_nerr; 287 extern char *sys_errlist[]; 288 289 /* 290 * Before calling syserr, the integer errno is set to contain the 291 * number of the error. The routine "_mycerror" is a dummy which 292 * is used to force the loader to get my version of cerror rather 293 * than the usual one. 294 */ 295 296 extern int errno; 297 extern _mycerror(); 298 299 /* 300 * Default error handling. 301 */ 302 303 private ERRINFO errinfo[] ={ 304 /* no error */ ERR_IGNORE, 305 /* EPERM */ ERR_IGNORE, 306 /* ENOENT */ ERR_IGNORE, 307 /* ESRCH */ ERR_IGNORE, 308 /* EINTR */ ERR_CATCH, 309 /* EIO */ ERR_CATCH, 310 /* ENXIO */ ERR_CATCH, 311 /* E2BIG */ ERR_CATCH, 312 /* ENOEXEC */ ERR_CATCH, 313 /* EBADF */ ERR_IGNORE, 314 /* ECHILD */ ERR_CATCH, 315 /* EAGAIN */ ERR_CATCH, 316 /* ENOMEM */ ERR_CATCH, 317 /* EACCES */ ERR_CATCH, 318 /* EFAULT */ ERR_CATCH, 319 /* ENOTBLK */ ERR_CATCH, 320 /* EBUSY */ ERR_CATCH, 321 /* EEXIST */ ERR_CATCH, 322 /* EXDEV */ ERR_CATCH, 323 /* ENODEV */ ERR_CATCH, 324 /* ENOTDIR */ ERR_CATCH, 325 /* EISDIR */ ERR_CATCH, 326 /* EINVAL */ ERR_CATCH, 327 /* ENFILE */ ERR_CATCH, 328 /* EMFILE */ ERR_CATCH, 329 /* ENOTTY */ ERR_IGNORE, 330 /* ETXTBSY */ ERR_CATCH, 331 /* EFBIG */ ERR_CATCH, 332 /* ENOSPC */ ERR_CATCH, 333 /* ESPIPE */ ERR_CATCH, 334 /* EROFS */ ERR_CATCH, 335 /* EMLINK */ ERR_CATCH, 336 /* EPIPE */ ERR_CATCH, 337 /* EDOM */ ERR_CATCH, 338 /* ERANGE */ ERR_CATCH, 339 /* EQUOT */ ERR_CATCH, 340 }; 341 342 public syserr() 343 { 344 ERRINFO *e; 345 346 e = &errinfo[errno]; 347 if (e->func == ERR_CATCH) { 348 if (errno < sys_nerr) { 349 fatal(sys_errlist[errno]); 350 } else { 351 fatal("errno %d", errno); 352 } 353 } else if (e->func != ERR_IGNORE) { 354 (*e->func)(); 355 } 356 } 357 358 /* 359 * Catcherrs only purpose is to get this module loaded and make 360 * sure my cerror is loaded (only applicable when this is in a library). 361 */ 362 363 public catcherrs() 364 { 365 _mycerror(); 366 } 367 368 /* 369 * Change the action on receipt of an error. 370 */ 371 372 public onsyserr(n, f) 373 int n; 374 INTFUNC *f; 375 { 376 errinfo[n].func = f; 377 } 378 379 /* 380 * Print the message associated with the given signal. 381 * Like a "perror" for signals. 382 */ 383 384 public int sys_nsig = NSIG; 385 public String sys_siglist[] = { 386 "no signal", 387 "hangup", 388 "interrupt", 389 "quit", 390 "illegal instruction", 391 "trace trap", 392 "IOT instruction", 393 "EMT instruction", 394 "floating point exception", 395 "kill", 396 "bus error", 397 "segmentation violation", 398 "bad argument to system call", 399 "broken pipe", 400 "alarm clock", 401 "soft kill", 402 "urgent I/O condition", 403 "stop signal not from tty", 404 "stop signal from tty", 405 "continue", 406 "child termination", 407 "stop (tty input)", 408 "stop (tty output)", 409 "possible input/output", 410 "exceeded CPU time limit", 411 "exceeded file size limit", 412 nil(String) 413 }; 414 415 public psig(s) 416 String s; 417 { 418 String c; 419 int n; 420 421 c = "Unknown signal"; 422 if (errno < sys_nsig) { 423 c = sys_errlist[errno]; 424 } 425 n = strlen(s); 426 if (n > 0) { 427 write(2, s, n); 428 write(2, ": ", 2); 429 } 430 write(2, c, strlen(c)); 431 write(2, "\n", 1); 432 } 433 434 /* 435 * Standard error handling routines. 436 */ 437 438 private short nerrs; 439 private short nwarnings; 440 441 /* 442 * Main driver of error message reporting. 443 */ 444 445 /* VARARGS2 */ 446 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) 447 String errname; 448 Boolean shouldquit; 449 String s; 450 { 451 fflush(stdout); 452 if (shouldquit and cmdname != nil(String)) { 453 fprintf(stderr, "%s: ", cmdname); 454 } 455 if (errfilename != nil(Filename)) { 456 fprintf(stderr, "%s: ", errfilename); 457 } 458 if (errlineno > 0) { 459 fprintf(stderr, "%d: ", errlineno); 460 } 461 if (errname != nil(String)) { 462 fprintf(stderr, "%s: ", errname); 463 } 464 fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 465 putc('\n', stderr); 466 if (shouldquit) { 467 quit(1); 468 } 469 } 470 471 /* 472 * For when printf isn't sufficient for printing the error message ... 473 */ 474 475 public beginerrmsg() 476 { 477 fflush(stdout); 478 if (errfilename != nil(String)) { 479 fprintf(stderr, "%s: ", errfilename); 480 } 481 if (errlineno > 0) { 482 fprintf(stderr, "%d: ", errlineno); 483 } 484 } 485 486 public enderrmsg() 487 { 488 putc('\n', stderr); 489 erecover(); 490 } 491 492 /* 493 * The messages are listed in increasing order of seriousness. 494 * 495 * First are warnings. 496 */ 497 498 /* VARARGS1 */ 499 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 500 String s; 501 { 502 nwarnings++; 503 errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 504 } 505 506 /* 507 * Errors are a little worse, they mean something is wrong, 508 * but not so bad that processing can't continue. 509 * 510 * The routine "erecover" is called to recover from the error, 511 * a default routine is provided that does nothing. 512 */ 513 514 /* VARARGS1 */ 515 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 516 String s; 517 { 518 extern erecover(); 519 520 nerrs++; 521 errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 522 erecover(); 523 } 524 525 /* 526 * Non-recoverable user error. 527 */ 528 529 /* VARARGS1 */ 530 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 531 String s; 532 { 533 errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 534 } 535 536 /* 537 * Panics indicate an internal program error. 538 */ 539 540 /* VARARGS1 */ 541 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 542 String s; 543 { 544 errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 545 } 546 547 short numerrors() 548 { 549 short r; 550 551 r = nerrs; 552 nerrs = 0; 553 return r; 554 } 555 556 short numwarnings() 557 { 558 short r; 559 560 r = nwarnings; 561 nwarnings = 0; 562 return r; 563 } 564 565 /* 566 * Recover from an error. 567 * 568 * This is the default routine which we aren't using since we have our own. 569 * 570 public erecover() 571 { 572 } 573 * 574 */ 575 576 /* 577 * Default way to quit from a program is just to exit. 578 * 579 public quit(r) 580 int r; 581 { 582 exit(r); 583 } 584 * 585 */ 586 587 /* 588 * Compare n-byte areas pointed to by s1 and s2 589 * if n is 0 then compare up until one has a null byte. 590 */ 591 592 public int cmp(s1, s2, n) 593 register char *s1, *s2; 594 register unsigned int n; 595 { 596 if (s1 == nil(char *) || s2 == nil(char *)) { 597 panic("cmp: nil pointer"); 598 } 599 if (n == 0) { 600 while (*s1 == *s2++) { 601 if (*s1++ == '\0') { 602 return(0); 603 } 604 } 605 return(*s1 - *(s2-1)); 606 } else { 607 for (; n != 0; n--) { 608 if (*s1++ != *s2++) { 609 return(*(s1-1) - *(s2-1)); 610 } 611 } 612 return(0); 613 } 614 } 615 616 /* 617 * Move n bytes from src to dest. 618 * If n is 0 move until a null is found. 619 */ 620 621 public mov(src, dest, n) 622 register char *src, *dest; 623 register unsigned int n; 624 { 625 if (src == nil(char *)) 626 panic("mov: nil source"); 627 if (dest == nil(char *)) 628 panic("mov: nil destination"); 629 if (n != 0) { 630 for (; n != 0; n--) { 631 *dest++ = *src++; 632 } 633 } else { 634 while ((*dest++ = *src++) != '\0'); 635 } 636 } 637