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