1 /* Copyright (c) 1982 Regents of the University of California */ 2 3 /* static char sccsid[] = "@(#)library.c 1.1 09/02/82"; */ 4 5 /* 6 * General purpose routines. 7 */ 8 9 #include <stdio.h> 10 #include <errno.h> 11 12 #define public 13 #define private static 14 #define and && 15 #define or || 16 #define not ! 17 #define ord(enumcon) ((int) enumcon) 18 #define nil(type) ((type) 0) 19 20 typedef enum { FALSE, TRUE } Boolean; 21 typedef char *String; 22 typedef FILE *File; 23 typedef String Filename; 24 25 #undef FILE 26 27 /* 28 * Definitions of standard C library routines that aren't in the 29 * standard I/O library, but which are generally useful. 30 */ 31 32 extern long atol(); /* ascii to long */ 33 extern double atof(); /* ascii to floating point */ 34 extern char *mktemp(); /* make a temporary file name */ 35 36 String cmdname; /* name of command for error messages */ 37 Filename errfilename; /* current file associated with error */ 38 short errlineno; /* line number associated with error */ 39 40 /* 41 * Definitions for doing memory allocation. 42 */ 43 44 extern char *malloc(); 45 46 #define alloc(n, type) ((type *) malloc((unsigned) (n) * sizeof(type))) 47 #define dispose(p) { free((char *) p); p = 0; } 48 49 /* 50 * Macros for doing freads + fwrites. 51 */ 52 53 #define get(fp, var) fread((char *) &(var), sizeof(var), 1, fp) 54 #define put(fp, var) fwrite((char *) &(var), sizeof(var), 1, fp) 55 56 /* 57 * String definitions. 58 */ 59 60 extern String strcpy(), index(), rindex(); 61 extern int strlen(); 62 63 #define strdup(s) strcpy(malloc((unsigned) strlen(s) + 1), s) 64 #define streq(s1, s2) (strcmp(s1, s2) == 0) 65 66 typedef int INTFUNC(); 67 68 typedef struct { 69 INTFUNC *func; 70 } ERRINFO; 71 72 #define ERR_IGNORE ((INTFUNC *) 0) 73 #define ERR_CATCH ((INTFUNC *) 1) 74 75 /* 76 * Call a program. 77 * 78 * Four entries: 79 * 80 * call, callv - call a program and wait for it, returning status 81 * back, backv - call a program and don't wait, returning process id 82 * 83 * The command's standard input and output are passed as FILE's. 84 */ 85 86 87 #define MAXNARGS 100 /* unchecked upper limit on max num of arguments */ 88 #define BADEXEC 127 /* exec fails */ 89 90 #define ischild(pid) ((pid) == 0) 91 92 /* VARARGS3 */ 93 public int call(name, in, out, args) 94 String name; 95 File in; 96 File out; 97 String args; 98 { 99 String *ap, *argp; 100 String argv[MAXNARGS]; 101 102 argp = &argv[0]; 103 *argp++ = name; 104 ap = &args; 105 while (*ap != nil(String)) { 106 *argp++ = *ap++; 107 } 108 *argp = nil(String); 109 return callv(name, in, out, argv); 110 } 111 112 /* VARARGS3 */ 113 public int back(name, in, out, args) 114 String name; 115 File in; 116 File out; 117 String args; 118 { 119 String *ap, *argp; 120 String argv[MAXNARGS]; 121 122 argp = &argv[0]; 123 *argp++ = name; 124 ap = &args; 125 while (*ap != nil(String)) { 126 *argp++ = *ap++; 127 } 128 *argp = nil(String); 129 return backv(name, in, out, argv); 130 } 131 132 public int callv(name, in, out, argv) 133 String name; 134 File in; 135 File out; 136 String *argv; 137 { 138 int pid, status; 139 140 pid = backv(name, in, out, argv); 141 pwait(pid, &status); 142 return status; 143 } 144 145 public int backv(name, in, out, argv) 146 String name; 147 File in; 148 File out; 149 String *argv; 150 { 151 int pid; 152 153 fflush(stdout); 154 if (ischild(pid = fork())) { 155 fswap(0, fileno(in)); 156 fswap(1, fileno(out)); 157 onsyserr(EACCES, ERR_IGNORE); 158 execvp(name, argv); 159 _exit(BADEXEC); 160 } 161 return pid; 162 } 163 164 /* 165 * Swap file numbers so as to redirect standard input and output. 166 */ 167 168 private fswap(oldfd, newfd) 169 int oldfd; 170 int newfd; 171 { 172 if (oldfd != newfd) { 173 close(oldfd); 174 dup(newfd); 175 close(newfd); 176 } 177 } 178 179 /* 180 * Invoke a shell on a command line. 181 */ 182 183 #define DEF_SHELL "csh" 184 185 public shell(s) 186 String s; 187 { 188 extern String getenv(); 189 String sh; 190 191 if ((sh = getenv("SHELL")) == nil(String)) { 192 sh = DEF_SHELL; 193 } 194 call(sh, stdin, stdout, "-c", s, 0); 195 } 196 197 /* 198 * Wait for a process the right way. We wait for a particular 199 * process and if any others come along in between, we remember them 200 * in case they are eventually waited for. 201 * 202 * This routine is not very efficient when the number of processes 203 * to be remembered is large. 204 */ 205 206 typedef struct pidlist { 207 int pid; 208 int status; 209 struct pidlist *next; 210 } Pidlist; 211 212 private Pidlist *pidlist, *pfind(); 213 214 public pwait(pid, statusp) 215 int pid, *statusp; 216 { 217 Pidlist *p; 218 int pnum, status; 219 220 p = pfind(pid); 221 if (p != nil(Pidlist *)) { 222 *statusp = p->status; 223 dispose(p); 224 return; 225 } 226 while ((pnum = wait(&status)) != pid && pnum >= 0) { 227 p = alloc(1, Pidlist); 228 p->pid = pnum; 229 p->status = status; 230 p->next = pidlist; 231 pidlist = p; 232 } 233 if (pnum < 0) { 234 p = pfind(pid); 235 if (p == nil(Pidlist *)) { 236 panic("pwait: pid %d not found", pid); 237 } 238 *statusp = p->status; 239 dispose(p); 240 } else { 241 *statusp = status; 242 } 243 } 244 245 /* 246 * Look for the given process id on the pidlist. 247 * 248 * Unlink it from list if found. 249 */ 250 251 private Pidlist *pfind(pid) 252 int pid; 253 { 254 register Pidlist *p, *prev; 255 256 prev = nil(Pidlist *); 257 for (p = pidlist; p != nil(Pidlist *); p = p->next) { 258 if (p->pid == pid) { 259 break; 260 } 261 prev = p; 262 } 263 if (p != nil(Pidlist *)) { 264 if (prev == nil(Pidlist *)) { 265 pidlist = p->next; 266 } else { 267 prev->next = p->next; 268 } 269 } 270 return p; 271 } 272 273 /* 274 * System call error handler. 275 * 276 * The syserr routine is called when a system call is about to 277 * set the c-bit to report an error. Certain errors are caught 278 * and cause the process to print a message and immediately exit. 279 */ 280 281 extern int sys_nerr; 282 extern char *sys_errlist[]; 283 284 /* 285 * Before calling syserr, the integer errno is set to contain the 286 * number of the error. The routine "_mycerror" is a dummy which 287 * is used to force the loader to get my version of cerror rather 288 * than the usual one. 289 */ 290 291 extern int errno; 292 extern _mycerror(); 293 294 /* 295 * default error handling 296 */ 297 298 private ERRINFO errinfo[] ={ 299 /* no error */ ERR_IGNORE, 300 /* EPERM */ ERR_IGNORE, 301 /* ENOENT */ ERR_IGNORE, 302 /* ESRCH */ ERR_IGNORE, 303 /* EINTR */ ERR_CATCH, 304 /* EIO */ ERR_CATCH, 305 /* ENXIO */ ERR_CATCH, 306 /* E2BIG */ ERR_CATCH, 307 /* ENOEXEC */ ERR_CATCH, 308 /* EBADF */ ERR_IGNORE, 309 /* ECHILD */ ERR_CATCH, 310 /* EAGAIN */ ERR_CATCH, 311 /* ENOMEM */ ERR_CATCH, 312 /* EACCES */ ERR_CATCH, 313 /* EFAULT */ ERR_CATCH, 314 /* ENOTBLK */ ERR_CATCH, 315 /* EBUSY */ ERR_CATCH, 316 /* EEXIST */ ERR_CATCH, 317 /* EXDEV */ ERR_CATCH, 318 /* ENODEV */ ERR_CATCH, 319 /* ENOTDIR */ ERR_CATCH, 320 /* EISDIR */ ERR_CATCH, 321 /* EINVAL */ ERR_CATCH, 322 /* ENFILE */ ERR_CATCH, 323 /* EMFILE */ ERR_CATCH, 324 /* ENOTTY */ ERR_IGNORE, 325 /* ETXTBSY */ ERR_CATCH, 326 /* EFBIG */ ERR_CATCH, 327 /* ENOSPC */ ERR_CATCH, 328 /* ESPIPE */ ERR_CATCH, 329 /* EROFS */ ERR_CATCH, 330 /* EMLINK */ ERR_CATCH, 331 /* EPIPE */ ERR_CATCH, 332 /* EDOM */ ERR_CATCH, 333 /* ERANGE */ ERR_CATCH, 334 /* EQUOT */ ERR_CATCH, 335 }; 336 337 public syserr() 338 { 339 ERRINFO *e; 340 341 e = &errinfo[errno]; 342 if (e->func == ERR_CATCH) { 343 if (errno < sys_nerr) { 344 panic(sys_errlist[errno]); 345 } else { 346 panic("errno %d", errno); 347 } 348 } else if (e->func != ERR_IGNORE) { 349 (*e->func)(); 350 } 351 } 352 353 /* 354 * Catcherrs only purpose is to get this module loaded and make 355 * sure my cerror is loaded (only applicable when this is in a library). 356 */ 357 358 public catcherrs() 359 { 360 _mycerror(); 361 } 362 363 /* 364 * Change the action on receipt of an error. 365 */ 366 367 public onsyserr(n, f) 368 int n; 369 INTFUNC *f; 370 { 371 errinfo[n].func = f; 372 } 373 374 /* 375 * Standard error handling routines. 376 */ 377 378 private short nerrs; 379 private short nwarnings; 380 381 /* 382 * Main driver of error message reporting. 383 */ 384 385 /* VARARGS2 */ 386 private errmsg(errname, shouldquit, s, a, b, c, d, e, f, g, h, i, j, k, l, m) 387 String errname; 388 Boolean shouldquit; 389 String s; 390 { 391 fflush(stdout); 392 if (shouldquit and cmdname != nil(String)) { 393 fprintf(stderr, "%s: ", cmdname); 394 } 395 if (errfilename != nil(Filename)) { 396 fprintf(stderr, "%s: ", errfilename); 397 } 398 if (errlineno > 0) { 399 fprintf(stderr, "%d: ", errlineno); 400 } 401 if (errname != nil(String)) { 402 fprintf(stderr, "%s: ", errname); 403 } 404 fprintf(stderr, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 405 putc('\n', stderr); 406 if (shouldquit) { 407 quit(1); 408 } 409 } 410 411 /* 412 * The messages are listed in increasing order of seriousness. 413 * 414 * First are warnings. 415 */ 416 417 /* VARARGS1 */ 418 public warning(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 419 String s; 420 { 421 nwarnings++; 422 errmsg("warning", FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 423 } 424 425 /* 426 * Errors are a little worse, they mean something is wrong, 427 * but not so bad that processing can't continue. 428 * 429 * The routine "erecover" is called to recover from the error, 430 * a default routine is provided that does nothing. 431 */ 432 433 /* VARARGS1 */ 434 public error(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 435 String s; 436 { 437 extern erecover(); 438 439 nerrs++; 440 errmsg(nil(String), FALSE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 441 erecover(); 442 } 443 444 /* 445 * Non-recoverable user error. 446 */ 447 448 /* VARARGS1 */ 449 public fatal(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 450 String s; 451 { 452 errmsg("fatal error", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 453 } 454 455 /* 456 * Panics indicate an internal program error. 457 */ 458 459 /* VARARGS1 */ 460 public panic(s, a, b, c, d, e, f, g, h, i, j, k, l, m) 461 String s; 462 { 463 errmsg("panic", TRUE, s, a, b, c, d, e, f, g, h, i, j, k, l, m); 464 } 465 466 short numerrors() 467 { 468 short r; 469 470 r = nerrs; 471 nerrs = 0; 472 return r; 473 } 474 475 short numwarnings() 476 { 477 short r; 478 479 r = nwarnings; 480 nwarnings = 0; 481 return r; 482 } 483 484 /* 485 * Recover from an error. 486 * 487 * This is the default routine which we aren't using since we have our own. 488 * 489 public erecover() 490 { 491 } 492 * 493 */ 494 495 /* 496 * Default way to quit from a program is just to exit. 497 * 498 public quit(r) 499 int r; 500 { 501 exit(r); 502 } 503 * 504 */ 505 506 /* 507 * Compare n-byte areas pointed to by s1 and s2 508 * if n is 0 then compare up until one has a null byte. 509 */ 510 511 public int cmp(s1, s2, n) 512 register char *s1, *s2; 513 register unsigned int n; 514 { 515 if (s1 == nil(char *) || s2 == nil(char *)) { 516 panic("cmp: nil pointer"); 517 } 518 if (n == 0) { 519 while (*s1 == *s2++) { 520 if (*s1++ == '\0') { 521 return(0); 522 } 523 } 524 return(*s1 - *(s2-1)); 525 } else { 526 for (; n != 0; n--) { 527 if (*s1++ != *s2++) { 528 return(*(s1-1) - *(s2-1)); 529 } 530 } 531 return(0); 532 } 533 } 534 535 /* 536 * Move n bytes from src to dest. 537 * If n is 0 move until a null is found. 538 */ 539 540 public mov(src, dest, n) 541 register char *src, *dest; 542 register unsigned int n; 543 { 544 if (src == nil(char *)) 545 panic("mov: nil source"); 546 if (dest == nil(char *)) 547 panic("mov: nil destination"); 548 if (n > 0) { 549 for (; n != 0; n--) { 550 *dest++ = *src++; 551 } 552 } else { 553 while ((*dest++ = *src++) != '\0'); 554 } 555 } 556