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