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