1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 static char sccsid[] = "@(#)library.c 1.3 8/7/83"; 4 5 static char rcsid[] = "$Header: library.c,v 1.3 84/03/27 10:21:12 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 * Change the action on receipt of an error. 401 */ 402 403 public onsyserr(n, f) 404 int n; 405 INTFUNC *f; 406 { 407 if (errinfo == nil(ERRINFO *)) { 408 initErrInfo(); 409 } 410 errinfo[n].func = f; 411 } 412 413 /* 414 * Print the message associated with the given signal. 415 * Like a "perror" for signals. 416 */ 417 418 public int sys_nsig = NSIG; 419 public String sys_siglist[] = { 420 "no signal", 421 "hangup", 422 "interrupt", 423 "quit", 424 "illegal instruction", 425 "trace trap", 426 "IOT instruction", 427 "EMT instruction", 428 "floating point exception", 429 "kill", 430 "bus error", 431 "segmentation violation", 432 "bad argument to system call", 433 "broken pipe", 434 "alarm clock", 435 "soft kill", 436 "urgent I/O condition", 437 "stop signal not from tty", 438 "stop signal from tty", 439 "continue", 440 "child termination", 441 "stop (tty input)", 442 "stop (tty output)", 443 "possible input/output", 444 "exceeded CPU time limit", 445 "exceeded file size limit", 446 nil(String) 447 }; 448 449 public psignal(s, n) 450 String s; 451 integer n; 452 { 453 String msg; 454 integer len; 455 456 if (n >= 0 and n < sys_nsig) { 457 msg = sys_siglist[n]; 458 } else { 459 msg = "Unknown signal"; 460 } 461 len = strlen(s); 462 if (len > 0) { 463 write(2, s, len); 464 write(2, ": ", 2); 465 } 466 write(2, msg, strlen(msg)); 467 write(2, "\n", 1); 468 } 469 470 /* 471 * Standard error handling routines. 472 */ 473 474 private short nerrs; 475 private short nwarnings; 476 477 /* 478 * Main driver of error message reporting. 479 */ 480 481 /* VARARGS2 */ 482 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) 483 String errname; 484 boolean shouldquit; 485 String s; 486 { 487 fflush(stdout); 488 if (shouldquit and cmdname != nil(String)) { 489 fprintf(stderr, "%s: ", cmdname); 490 } 491 if (errfilename != nil(Filename)) { 492 fprintf(stderr, "%s: ", errfilename); 493 } 494 if (errlineno > 0) { 495 fprintf(stderr, "%d: ", errlineno); 496 } 497 if (errname != nil(String)) { 498 fprintf(stderr, "%s: ", errname); 499 } 500 fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 501 putc('\n', stderr); 502 if (shouldquit) { 503 quit(1); 504 } 505 } 506 507 /* 508 * For when printf isn't sufficient for printing the error message ... 509 */ 510 511 public beginerrmsg() 512 { 513 fflush(stdout); 514 if (errfilename != nil(String)) { 515 fprintf(stderr, "%s: ", errfilename); 516 } 517 if (errlineno > 0) { 518 fprintf(stderr, "%d: ", errlineno); 519 } 520 } 521 522 public enderrmsg() 523 { 524 putc('\n', stderr); 525 erecover(); 526 } 527 528 /* 529 * The messages are listed in increasing order of seriousness. 530 * 531 * First are warnings. 532 */ 533 534 /* VARARGS1 */ 535 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 536 String s; 537 { 538 nwarnings++; 539 errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 540 } 541 542 /* 543 * Errors are a little worse, they mean something is wrong, 544 * but not so bad that processing can't continue. 545 * 546 * The routine "erecover" is called to recover from the error, 547 * a default routine is provided that does nothing. 548 */ 549 550 /* VARARGS1 */ 551 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 552 String s; 553 { 554 extern erecover(); 555 556 nerrs++; 557 errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 558 erecover(); 559 } 560 561 /* 562 * Non-recoverable user error. 563 */ 564 565 /* VARARGS1 */ 566 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 567 String s; 568 { 569 errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 570 } 571 572 /* 573 * Panics indicate an internal program error. 574 */ 575 576 /* VARARGS1 */ 577 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 578 String s; 579 { 580 errmsg("internal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 581 } 582 583 short numerrors() 584 { 585 short r; 586 587 r = nerrs; 588 nerrs = 0; 589 return r; 590 } 591 592 short numwarnings() 593 { 594 short r; 595 596 r = nwarnings; 597 nwarnings = 0; 598 return r; 599 } 600 601 /* 602 * Recover from an error. 603 * 604 * This is the default routine which we aren't using since we have our own. 605 * 606 public erecover() 607 { 608 } 609 * 610 */ 611 612 /* 613 * Default way to quit from a program is just to exit. 614 * 615 public quit(r) 616 int r; 617 { 618 exit(r); 619 } 620 * 621 */ 622 623 /* 624 * Compare n-byte areas pointed to by s1 and s2 625 * if n is 0 then compare up until one has a null byte. 626 */ 627 628 public int cmp(s1, s2, n) 629 register char *s1, *s2; 630 register unsigned int n; 631 { 632 if (s1 == nil(char *) || s2 == nil(char *)) { 633 panic("cmp: nil pointer"); 634 } 635 if (n == 0) { 636 while (*s1 == *s2++) { 637 if (*s1++ == '\0') { 638 return(0); 639 } 640 } 641 return(*s1 - *(s2-1)); 642 } else { 643 for (; n != 0; n--) { 644 if (*s1++ != *s2++) { 645 return(*(s1-1) - *(s2-1)); 646 } 647 } 648 return(0); 649 } 650 } 651 652 /* 653 * Move n bytes from src to dest. 654 * If n is 0 move until a null is found. 655 */ 656 657 public mov(src, dest, n) 658 register char *src, *dest; 659 register unsigned int n; 660 { 661 if (src == nil(char *)) 662 panic("mov: nil source"); 663 if (dest == nil(char *)) 664 panic("mov: nil destination"); 665 if (n != 0) { 666 for (; n != 0; n--) { 667 *dest++ = *src++; 668 } 669 } else { 670 while ((*dest++ = *src++) != '\0'); 671 } 672 } 673