1 /* 2 * Copyright (c) 1983 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)library.c 5.4 (Berkeley) 06/01/90"; 10 #endif /* not lint */ 11 12 /* 13 * General purpose routines. 14 */ 15 16 #include <stdio.h> 17 #include <errno.h> 18 #include <signal.h> 19 20 #define public 21 #define private static 22 #define and && 23 #define or || 24 #define not ! 25 #define ord(enumcon) ((int) enumcon) 26 #define nil(type) ((type) 0) 27 28 typedef int integer; 29 typedef enum { FALSE, TRUE } boolean; 30 typedef char *String; 31 typedef FILE *File; 32 typedef String Filename; 33 34 #undef FILE 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 IntFunc *onsyserr(); 69 70 typedef struct { 71 IntFunc *func; 72 } ErrInfo; 73 74 #define ERR_IGNORE ((IntFunc *) 0) 75 #define ERR_CATCH ((IntFunc *) 1) 76 77 /* 78 * Call a program. 79 * 80 * Four entries: 81 * 82 * call, callv - call a program and wait for it, returning status 83 * back, backv - call a program and don't wait, returning process id 84 * 85 * The command's standard input and output are passed as FILE's. 86 */ 87 88 89 #define MAXNARGS 1000 /* unchecked upper limit on max num of arguments */ 90 #define BADEXEC 127 /* exec fails */ 91 92 #define ischild(pid) ((pid) == 0) 93 94 /* VARARGS3 */ 95 public int call(name, in, out, args) 96 String name; 97 File in; 98 File out; 99 String args; 100 { 101 String *ap, *argp; 102 String argv[MAXNARGS]; 103 104 argp = &argv[0]; 105 *argp++ = name; 106 ap = &args; 107 while (*ap != nil(String)) { 108 *argp++ = *ap++; 109 } 110 *argp = nil(String); 111 return callv(name, in, out, argv); 112 } 113 114 /* VARARGS3 */ 115 public int back(name, in, out, args) 116 String name; 117 File in; 118 File out; 119 String args; 120 { 121 String *ap, *argp; 122 String argv[MAXNARGS]; 123 124 argp = &argv[0]; 125 *argp++ = name; 126 ap = &args; 127 while (*ap != nil(String)) { 128 *argp++ = *ap++; 129 } 130 *argp = nil(String); 131 return backv(name, in, out, argv); 132 } 133 134 public int callv(name, in, out, argv) 135 String name; 136 File in; 137 File out; 138 String *argv; 139 { 140 int pid, status; 141 142 pid = backv(name, in, out, argv); 143 pwait(pid, &status); 144 return status; 145 } 146 147 public int backv(name, in, out, argv) 148 String name; 149 File in; 150 File out; 151 String *argv; 152 { 153 int pid; 154 155 fflush(stdout); 156 if (ischild(pid = fork())) { 157 fswap(0, fileno(in)); 158 fswap(1, fileno(out)); 159 onsyserr(EACCES, ERR_IGNORE); 160 execvp(name, argv); 161 _exit(BADEXEC); 162 } 163 return pid; 164 } 165 166 /* 167 * Swap file numbers so as to redirect standard input and output. 168 */ 169 170 private fswap(oldfd, newfd) 171 int oldfd; 172 int newfd; 173 { 174 if (oldfd != newfd) { 175 close(oldfd); 176 dup(newfd); 177 close(newfd); 178 } 179 } 180 181 /* 182 * Invoke a shell on a command line. 183 */ 184 185 #define DEF_SHELL "csh" 186 187 public shell(s) 188 String s; 189 { 190 extern String getenv(); 191 String sh; 192 193 if ((sh = getenv("SHELL")) == nil(String)) { 194 sh = DEF_SHELL; 195 } 196 if (s != nil(String) and *s != '\0') { 197 call(sh, stdin, stdout, "-c", s, 0); 198 } else { 199 call(sh, stdin, stdout, 0); 200 } 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 * To deal with a kernel idiosyncrasy, we keep a list on the side 212 * of "traced" processes, and do not notice them when waiting for 213 * another process. 214 */ 215 216 typedef struct pidlist { 217 int pid; 218 int status; 219 struct pidlist *next; 220 } Pidlist; 221 222 private Pidlist *pidlist, *ptrclist, *pfind(); 223 224 public ptraced(pid) 225 int pid; 226 { 227 Pidlist *p; 228 229 p = alloc(1, Pidlist); 230 p->pid = pid; 231 p->next = ptrclist; 232 ptrclist = p; 233 } 234 235 public unptraced(pid) 236 int pid; 237 { 238 register Pidlist *p, *prev; 239 240 prev = nil(Pidlist *); 241 p = ptrclist; 242 while (p != nil(Pidlist *) and p->pid != pid) { 243 prev = p; 244 p = p->next; 245 } 246 if (p != nil(Pidlist *)) { 247 if (prev == nil(Pidlist *)) { 248 ptrclist = p->next; 249 } else { 250 prev->next = p->next; 251 } 252 dispose(p); 253 } 254 } 255 256 private boolean isptraced(pid) 257 int pid; 258 { 259 register Pidlist *p; 260 261 p = ptrclist; 262 while (p != nil(Pidlist *) and p->pid != pid) { 263 p = p->next; 264 } 265 return (boolean) (p != nil(Pidlist *)); 266 } 267 268 public pwait(pid, statusp) 269 int pid, *statusp; 270 { 271 Pidlist *p; 272 int pnum, status; 273 274 p = pfind(pid); 275 if (p != nil(Pidlist *)) { 276 *statusp = p->status; 277 dispose(p); 278 } else { 279 pnum = wait(&status); 280 while (pnum != pid and pnum >= 0) { 281 if (not isptraced(pnum)) { 282 p = alloc(1, Pidlist); 283 p->pid = pnum; 284 p->status = status; 285 p->next = pidlist; 286 pidlist = p; 287 } 288 pnum = wait(&status); 289 } 290 if (pnum < 0) { 291 p = pfind(pid); 292 if (p == nil(Pidlist *)) { 293 panic("pwait: pid %d not found", pid); 294 } 295 *statusp = p->status; 296 dispose(p); 297 } else { 298 *statusp = status; 299 } 300 } 301 } 302 303 /* 304 * Look for the given process id on the pidlist. 305 * 306 * Unlink it from list if found. 307 */ 308 309 private Pidlist *pfind(pid) 310 int pid; 311 { 312 register Pidlist *p, *prev; 313 314 prev = nil(Pidlist *); 315 for (p = pidlist; p != nil(Pidlist *); p = p->next) { 316 if (p->pid == pid) { 317 break; 318 } 319 prev = p; 320 } 321 if (p != nil(Pidlist *)) { 322 if (prev == nil(Pidlist *)) { 323 pidlist = p->next; 324 } else { 325 prev->next = p->next; 326 } 327 } 328 return p; 329 } 330 331 /* 332 * System call error handler. 333 * 334 * The syserr routine is called when a system call is about to 335 * set the c-bit to report an error. Certain errors are caught 336 * and cause the process to print a message and immediately exit. 337 */ 338 339 extern int sys_nerr; 340 extern char *sys_errlist[]; 341 342 /* 343 * Before calling syserr, the integer errno is set to contain the 344 * number of the error. The routine "_mycerror" is a dummy which 345 * is used to force the loader to get my version of cerror rather 346 * than the usual one. 347 */ 348 349 extern int errno; 350 extern _mycerror(); 351 352 /* 353 * Initialize error information, setting defaults for handling errors. 354 */ 355 356 private ErrInfo *errinfo; 357 358 private initErrInfo () 359 { 360 integer i; 361 362 errinfo = alloc(sys_nerr, ErrInfo); 363 for (i = 0; i < sys_nerr; i++) { 364 errinfo[i].func = ERR_CATCH; 365 } 366 errinfo[0].func = ERR_IGNORE; 367 errinfo[EPERM].func = ERR_IGNORE; 368 errinfo[ENOENT].func = ERR_IGNORE; 369 errinfo[ESRCH].func = ERR_IGNORE; 370 errinfo[EBADF].func = ERR_IGNORE; 371 errinfo[ENOTTY].func = ERR_IGNORE; 372 errinfo[EOPNOTSUPP].func = ERR_IGNORE; 373 } 374 375 public syserr() 376 { 377 register ErrInfo *e; 378 379 if (errno < 0 or errno > sys_nerr) { 380 fatal("errno %d", errno); 381 } else { 382 if (errinfo == nil(ErrInfo *)) { 383 initErrInfo(); 384 } 385 e = &(errinfo[errno]); 386 if (e->func == ERR_CATCH) { 387 fatal(sys_errlist[errno]); 388 } else if (e->func != ERR_IGNORE) { 389 (*e->func)(); 390 } 391 } 392 } 393 394 /* 395 * Catcherrs' purpose is to initialize the errinfo table, get this module 396 * loaded, and make sure my cerror is loaded (only applicable when this is 397 * in a library). 398 */ 399 400 public catcherrs() 401 { 402 _mycerror(); 403 initErrInfo(); 404 } 405 406 /* 407 * Turn off the error catching mechanism completely by having all errors 408 * ignored. This is most useful between a fork and an exec. 409 */ 410 411 public nocatcherrs() 412 { 413 integer i; 414 415 for (i = 0; i < sys_nerr; i++) { 416 errinfo[i].func = ERR_IGNORE; 417 } 418 } 419 420 /* 421 * Change the action on receipt of an error, returning the previous action. 422 */ 423 424 public IntFunc *onsyserr(n, f) 425 int n; 426 IntFunc *f; 427 { 428 IntFunc *oldf; 429 430 if (errinfo == nil(ErrInfo *)) { 431 initErrInfo(); 432 } 433 oldf = errinfo[n].func; 434 errinfo[n].func = f; 435 return oldf; 436 } 437 438 /* 439 * Print the message associated with the given signal. 440 * Like a "perror" for signals. 441 */ 442 443 #ifdef SIGWINCH 444 public int sys_nsig = NSIG; 445 #else not 4.3 BSD 446 /* 447 * This table is correct for 4.2-like systems but will 448 * be inadequate for System V (which is the sort of 449 * Unix that needs it!). 450 */ 451 public String sys_siglist[] = { 452 "no signal", 453 "hangup", 454 "interrupt", 455 "quit", 456 "illegal instruction", 457 "trace trap", 458 "IOT instruction", 459 "EMT instruction", 460 "floating point exception", 461 "kill", 462 "bus error", 463 "segmentation violation", 464 "bad argument to system call", 465 "broken pipe", 466 "alarm clock", 467 "soft kill", 468 "urgent I/O condition", 469 "stop signal not from tty", 470 "stop signal from tty", 471 "continue", 472 "child termination", 473 "stop (tty input)", 474 "stop (tty output)", 475 "possible input/output", 476 "exceeded CPU time limit", 477 "exceeded file size limit" 478 }; 479 public int sys_nsig = sizeof sys_siglist / sizeof sys_siglist[0]; 480 #endif 481 482 public psignal(s, n) 483 String s; 484 integer n; 485 { 486 String msg; 487 integer len; 488 extern String sys_siglist[]; 489 490 if (n >= 0 and n < sys_nsig) { 491 msg = sys_siglist[n]; 492 } else { 493 msg = "Unknown signal"; 494 } 495 len = strlen(s); 496 if (len > 0) { 497 write(2, s, len); 498 write(2, ": ", 2); 499 } 500 write(2, msg, strlen(msg)); 501 write(2, "\n", 1); 502 } 503 504 /* 505 * Standard error handling routines. 506 */ 507 508 private short nerrs; 509 private short nwarnings; 510 511 /* 512 * Main driver of error message reporting. 513 */ 514 515 /* VARARGS2 */ 516 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) 517 String errname; 518 boolean shouldquit; 519 String s; 520 { 521 fflush(stdout); 522 if (shouldquit and cmdname != nil(String)) { 523 fprintf(stderr, "%s: ", cmdname); 524 } 525 if (errfilename != nil(Filename)) { 526 fprintf(stderr, "%s: ", errfilename); 527 } 528 if (errlineno > 0) { 529 fprintf(stderr, "%d: ", errlineno); 530 } 531 if (errname != nil(String)) { 532 fprintf(stderr, "%s: ", errname); 533 } 534 fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 535 putc('\n', stderr); 536 fflush(stderr); 537 if (shouldquit) { 538 quit(1); 539 } 540 } 541 542 /* 543 * For when printf isn't sufficient for printing the error message ... 544 */ 545 546 public beginerrmsg() 547 { 548 fflush(stdout); 549 if (errfilename != nil(String)) { 550 fprintf(stderr, "%s: ", errfilename); 551 } 552 if (errlineno > 0) { 553 fprintf(stderr, "%d: ", errlineno); 554 } 555 } 556 557 public enderrmsg() 558 { 559 putc('\n', stderr); 560 fflush(stderr); 561 erecover(); 562 } 563 564 /* 565 * The messages are listed in increasing order of seriousness. 566 * 567 * First are warnings. 568 */ 569 570 /* VARARGS1 */ 571 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 572 String s; 573 { 574 nwarnings++; 575 errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 576 } 577 578 /* 579 * Errors are a little worse, they mean something is wrong, 580 * but not so bad that processing can't continue. 581 * 582 * The routine "erecover" is called to recover from the error, 583 * a default routine is provided that does nothing. 584 */ 585 586 /* VARARGS1 */ 587 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 588 String s; 589 { 590 extern erecover(); 591 592 nerrs++; 593 errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 594 erecover(); 595 } 596 597 /* 598 * Non-recoverable user error. 599 */ 600 601 /* VARARGS1 */ 602 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 603 String s; 604 { 605 errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 606 } 607 608 /* 609 * Panics indicate an internal program error. 610 */ 611 612 /* VARARGS1 */ 613 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 614 String s; 615 { 616 errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 617 } 618 619 short numerrors() 620 { 621 short r; 622 623 r = nerrs; 624 nerrs = 0; 625 return r; 626 } 627 628 short numwarnings() 629 { 630 short r; 631 632 r = nwarnings; 633 nwarnings = 0; 634 return r; 635 } 636 637 /* 638 * Recover from an error. 639 * 640 * This is the default routine which we aren't using since we have our own. 641 * 642 public erecover() 643 { 644 } 645 * 646 */ 647 648 /* 649 * Default way to quit from a program is just to exit. 650 * 651 public quit(r) 652 int r; 653 { 654 exit(r); 655 } 656 * 657 */ 658 659 /* 660 * Compare n-byte areas pointed to by s1 and s2 661 * if n is 0 then compare up until one has a null byte. 662 */ 663 664 public int cmp(s1, s2, n) 665 register char *s1, *s2; 666 register unsigned int n; 667 { 668 if (s1 == nil(char *) || s2 == nil(char *)) { 669 panic("cmp: nil pointer"); 670 } 671 if (n == 0) { 672 while (*s1 == *s2++) { 673 if (*s1++ == '\0') { 674 return(0); 675 } 676 } 677 return(*s1 - *(s2-1)); 678 } else { 679 for (; n != 0; n--) { 680 if (*s1++ != *s2++) { 681 return(*(s1-1) - *(s2-1)); 682 } 683 } 684 return(0); 685 } 686 } 687 688 /* 689 * Move n bytes from src to dest. 690 * If n is 0 move until a null is found. 691 */ 692 693 public mov(src, dest, n) 694 register char *src, *dest; 695 register unsigned int n; 696 { 697 if (src == nil(char *)) 698 panic("mov: nil source"); 699 if (dest == nil(char *)) 700 panic("mov: nil destination"); 701 if (n != 0) { 702 for (; n != 0; n--) { 703 *dest++ = *src++; 704 } 705 } else { 706 while ((*dest++ = *src++) != '\0'); 707 } 708 } 709 710 #ifdef IRIS /* or in general for 4.2 - System V C library interface */ 711 712 public bcopy (fromaddr, toaddr, n) 713 char *fromaddr, *toaddr; 714 int n; 715 { 716 blt(toaddr, fromaddr, n); 717 } 718 719 public bzero (addr, n) 720 char *addr; 721 int n; 722 { 723 register char *p, *q; 724 725 p = addr; 726 q = p + n; 727 while (p < q) { 728 *p++ = '\0'; 729 } 730 } 731 732 #include <string.h> 733 734 public char *index (s, c) 735 char *s, c; 736 { 737 return strchr(s, c); 738 } 739 740 public char *rindex (s, c) 741 char *s, c; 742 { 743 return strrchr(s, c); 744 } 745 746 #endif 747