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