|
|
1.1 ! root 1: /* Copyright (c) 1981 Regents of the University of California */ ! 2: static char *sccsid = "@(#)exrecover.c 7.6 7/3/83"; ! 3: #include <stdio.h> /* mjm: BUFSIZ: stdio = 512, VMUNIX = 1024 */ ! 4: #undef BUFSIZ /* mjm: BUFSIZ different */ ! 5: #undef EOF /* mjm: EOF and NULL effectively the same */ ! 6: #undef NULL ! 7: ! 8: #include "ex.h" ! 9: #include "ex_temp.h" ! 10: #include "ex_tty.h" ! 11: #include <sys/dir.h> ! 12: #include "uparm.h" ! 13: ! 14: char xstr[1]; /* make loader happy */ ! 15: short tfile = -1; /* ditto */ ! 16: ! 17: /* ! 18: * ! 19: * This program searches through the specified directory and then ! 20: * the directory usrpath(preserve) looking for an instance of the specified ! 21: * file from a crashed editor or a crashed system. ! 22: * If this file is found, it is unscrambled and written to ! 23: * the standard output. ! 24: * ! 25: * If this program terminates without a "broken pipe" diagnostic ! 26: * (i.e. the editor doesn't die right away) then the buffer we are ! 27: * writing from is removed when we finish. This is potentially a mistake ! 28: * as there is not enough handshaking to guarantee that the file has actually ! 29: * been recovered, but should suffice for most cases. ! 30: */ ! 31: ! 32: /* ! 33: * For lint's sake... ! 34: */ ! 35: #ifndef lint ! 36: #define ignorl(a) a ! 37: #endif ! 38: ! 39: /* ! 40: * This directory definition also appears (obviously) in expreserve.c. ! 41: * Change both if you change either. ! 42: */ ! 43: char mydir[] = usrpath(preserve); ! 44: ! 45: /* ! 46: * Limit on the number of printed entries ! 47: * when an, e.g. ``ex -r'' command is given. ! 48: */ ! 49: #define NENTRY 50 ! 50: ! 51: char *ctime(); ! 52: char nb[BUFSIZ]; ! 53: int vercnt; /* Count number of versions of file found */ ! 54: ! 55: main(argc, argv) ! 56: int argc; ! 57: char *argv[]; ! 58: { ! 59: register char *cp; ! 60: register int b, i; ! 61: ! 62: /* ! 63: * Initialize as though the editor had just started. ! 64: */ ! 65: fendcore = (line *) sbrk(0); ! 66: dot = zero = dol = fendcore; ! 67: one = zero + 1; ! 68: endcore = fendcore - 2; ! 69: iblock = oblock = -1; ! 70: ! 71: /* ! 72: * If given only a -r argument, then list the saved files. ! 73: */ ! 74: if (argc == 2 && eq(argv[1], "-r")) { ! 75: listfiles(mydir); ! 76: exit(0); ! 77: } ! 78: if (argc != 3) ! 79: error(" Wrong number of arguments to exrecover", 0); ! 80: ! 81: CP(file, argv[2]); ! 82: ! 83: /* ! 84: * Search for this file. ! 85: */ ! 86: findtmp(argv[1]); ! 87: ! 88: /* ! 89: * Got (one of the versions of) it, write it back to the editor. ! 90: */ ! 91: cp = ctime(&H.Time); ! 92: cp[19] = 0; ! 93: fprintf(stderr, " [Dated: %s", cp); ! 94: fprintf(stderr, vercnt > 1 ? ", newest of %d saved]" : "]", vercnt); ! 95: H.Flines++; ! 96: ! 97: /* ! 98: * Allocate space for the line pointers from the temp file. ! 99: */ ! 100: if ((int) sbrk((int) (H.Flines * sizeof (line))) == -1) ! 101: /* ! 102: * Good grief. ! 103: */ ! 104: error(" Not enough core for lines", 0); ! 105: #ifdef DEBUG ! 106: fprintf(stderr, "%d lines\n", H.Flines); ! 107: #endif ! 108: ! 109: /* ! 110: * Now go get the blocks of seek pointers which are scattered ! 111: * throughout the temp file, reconstructing the incore ! 112: * line pointers at point of crash. ! 113: */ ! 114: b = 0; ! 115: while (H.Flines > 0) { ! 116: ignorl(lseek(tfile, (long) blocks[b] * BUFSIZ, 0)); ! 117: i = H.Flines < BUFSIZ / sizeof (line) ? ! 118: H.Flines * sizeof (line) : BUFSIZ; ! 119: if (read(tfile, (char *) dot, i) != i) { ! 120: perror(nb); ! 121: exit(1); ! 122: } ! 123: dot += i / sizeof (line); ! 124: H.Flines -= i / sizeof (line); ! 125: b++; ! 126: } ! 127: dot--; dol = dot; ! 128: ! 129: /* ! 130: * Sigh... due to sandbagging some lines may really not be there. ! 131: * Find and discard such. This shouldn't happen much. ! 132: */ ! 133: scrapbad(); ! 134: ! 135: /* ! 136: * Now if there were any lines in the recovered file ! 137: * write them to the standard output. ! 138: */ ! 139: if (dol > zero) { ! 140: addr1 = one; addr2 = dol; io = 1; ! 141: putfile(0); ! 142: } ! 143: ! 144: /* ! 145: * Trash the saved buffer. ! 146: * Hopefully the system won't crash before the editor ! 147: * syncs the new recovered buffer; i.e. for an instant here ! 148: * you may lose if the system crashes because this file ! 149: * is gone, but the editor hasn't completed reading the recovered ! 150: * file from the pipe from us to it. ! 151: * ! 152: * This doesn't work if we are coming from an non-absolute path ! 153: * name since we may have chdir'ed but what the hay, noone really ! 154: * ever edits with temporaries in "." anyways. ! 155: */ ! 156: if (nb[0] == '/') ! 157: ignore(unlink(nb)); ! 158: ! 159: /* ! 160: * Adieu. ! 161: */ ! 162: exit(0); ! 163: } ! 164: ! 165: /* ! 166: * Print an error message (notably not in error ! 167: * message file). If terminal is in RAW mode, then ! 168: * we should be writing output for "vi", so don't print ! 169: * a newline which would screw up the screen. ! 170: */ ! 171: /*VARARGS2*/ ! 172: error(str, inf) ! 173: char *str; ! 174: int inf; ! 175: { ! 176: ! 177: fprintf(stderr, str, inf); ! 178: #ifndef USG3TTY ! 179: gtty(2, &tty); ! 180: if ((tty.sg_flags & RAW) == 0) ! 181: #else ! 182: ioctl(2, TCGETA, &tty); ! 183: if (tty.c_lflag & ICANON) ! 184: #endif ! 185: fprintf(stderr, "\n"); ! 186: exit(1); ! 187: } ! 188: ! 189: /* ! 190: * Here we save the information about files, when ! 191: * you ask us what files we have saved for you. ! 192: * We buffer file name, number of lines, and the time ! 193: * at which the file was saved. ! 194: */ ! 195: struct svfile { ! 196: char sf_name[FNSIZE + 1]; ! 197: int sf_lines; ! 198: char sf_entry[MAXNAMLEN + 1]; ! 199: time_t sf_time; ! 200: }; ! 201: ! 202: listfiles(dirname) ! 203: char *dirname; ! 204: { ! 205: register DIR *dir; ! 206: struct direct *dirent; ! 207: int ecount, qucmp(); ! 208: register int f; ! 209: char *cp; ! 210: struct svfile *fp, svbuf[NENTRY]; ! 211: ! 212: /* ! 213: * Open usrpath(preserve), and go there to make things quick. ! 214: */ ! 215: dir = opendir(dirname); ! 216: if (dir == NULL) { ! 217: perror(dirname); ! 218: return; ! 219: } ! 220: if (chdir(dirname) < 0) { ! 221: perror(dirname); ! 222: return; ! 223: } ! 224: ! 225: /* ! 226: * Look at the candidate files in usrpath(preserve). ! 227: */ ! 228: fp = &svbuf[0]; ! 229: ecount = 0; ! 230: while ((dirent = readdir(dir)) != NULL) { ! 231: if (dirent->d_name[0] != 'E') ! 232: continue; ! 233: #ifdef DEBUG ! 234: fprintf(stderr, "considering %s\n", dirent->d_name); ! 235: #endif ! 236: /* ! 237: * Name begins with E; open it and ! 238: * make sure the uid in the header is our uid. ! 239: * If not, then don't bother with this file, it can't ! 240: * be ours. ! 241: */ ! 242: f = open(dirent->d_name, 0); ! 243: if (f < 0) { ! 244: #ifdef DEBUG ! 245: fprintf(stderr, "open failed\n"); ! 246: #endif ! 247: continue; ! 248: } ! 249: if (read(f, (char *) &H, sizeof H) != sizeof H) { ! 250: #ifdef DEBUG ! 251: fprintf(stderr, "culdnt read hedr\n"); ! 252: #endif ! 253: ignore(close(f)); ! 254: continue; ! 255: } ! 256: ignore(close(f)); ! 257: if (getuid() != H.Uid) { ! 258: #ifdef DEBUG ! 259: fprintf(stderr, "uid wrong\n"); ! 260: #endif ! 261: continue; ! 262: } ! 263: ! 264: /* ! 265: * Saved the day! ! 266: */ ! 267: enter(fp++, dirent->d_name, ecount); ! 268: ecount++; ! 269: #ifdef DEBUG ! 270: fprintf(stderr, "entered file %s\n", dirent->d_name); ! 271: #endif ! 272: } ! 273: ignore(closedir(dir)); ! 274: ! 275: /* ! 276: * If any files were saved, then sort them and print ! 277: * them out. ! 278: */ ! 279: if (ecount == 0) { ! 280: fprintf(stderr, "No files saved.\n"); ! 281: return; ! 282: } ! 283: qsort(&svbuf[0], ecount, sizeof svbuf[0], qucmp); ! 284: for (fp = &svbuf[0]; fp < &svbuf[ecount]; fp++) { ! 285: cp = ctime(&fp->sf_time); ! 286: cp[10] = 0; ! 287: fprintf(stderr, "On %s at ", cp); ! 288: cp[16] = 0; ! 289: fprintf(stderr, &cp[11]); ! 290: fprintf(stderr, " saved %d lines of file \"%s\"\n", ! 291: fp->sf_lines, fp->sf_name); ! 292: } ! 293: } ! 294: ! 295: /* ! 296: * Enter a new file into the saved file information. ! 297: */ ! 298: enter(fp, fname, count) ! 299: struct svfile *fp; ! 300: char *fname; ! 301: { ! 302: register char *cp, *cp2; ! 303: register struct svfile *f, *fl; ! 304: time_t curtime, itol(); ! 305: ! 306: f = 0; ! 307: if (count >= NENTRY) { ! 308: /* ! 309: * My god, a huge number of saved files. ! 310: * Would you work on a system that crashed this ! 311: * often? Hope not. So lets trash the oldest ! 312: * as the most useless. ! 313: * ! 314: * (I wonder if this code has ever run?) ! 315: */ ! 316: fl = fp - count + NENTRY - 1; ! 317: curtime = fl->sf_time; ! 318: for (f = fl; --f > fp-count; ) ! 319: if (f->sf_time < curtime) ! 320: curtime = f->sf_time; ! 321: for (f = fl; --f > fp-count; ) ! 322: if (f->sf_time == curtime) ! 323: break; ! 324: fp = f; ! 325: } ! 326: ! 327: /* ! 328: * Gotcha. ! 329: */ ! 330: fp->sf_time = H.Time; ! 331: fp->sf_lines = H.Flines; ! 332: for (cp2 = fp->sf_name, cp = savedfile; *cp;) ! 333: *cp2++ = *cp++; ! 334: for (cp2 = fp->sf_entry, cp = fname; *cp && cp-fname < 14;) ! 335: *cp2++ = *cp++; ! 336: *cp2++ = 0; ! 337: } ! 338: ! 339: /* ! 340: * Do the qsort compare to sort the entries first by file name, ! 341: * then by modify time. ! 342: */ ! 343: qucmp(p1, p2) ! 344: struct svfile *p1, *p2; ! 345: { ! 346: register int t; ! 347: ! 348: if (t = strcmp(p1->sf_name, p2->sf_name)) ! 349: return(t); ! 350: if (p1->sf_time > p2->sf_time) ! 351: return(-1); ! 352: return(p1->sf_time < p2->sf_time); ! 353: } ! 354: ! 355: /* ! 356: * Scratch for search. ! 357: */ ! 358: char bestnb[BUFSIZ]; /* Name of the best one */ ! 359: long besttime; /* Time at which the best file was saved */ ! 360: int bestfd; /* Keep best file open so it dont vanish */ ! 361: ! 362: /* ! 363: * Look for a file, both in the users directory option value ! 364: * (i.e. usually /tmp) and in usrpath(preserve). ! 365: * Want to find the newest so we search on and on. ! 366: */ ! 367: findtmp(dir) ! 368: char *dir; ! 369: { ! 370: ! 371: /* ! 372: * No name or file so far. ! 373: */ ! 374: bestnb[0] = 0; ! 375: bestfd = -1; ! 376: ! 377: /* ! 378: * Search usrpath(preserve) and, if we can get there, /tmp ! 379: * (actually the users "directory" option). ! 380: */ ! 381: searchdir(dir); ! 382: if (chdir(mydir) == 0) ! 383: searchdir(mydir); ! 384: if (bestfd != -1) { ! 385: /* ! 386: * Gotcha. ! 387: * Put the file (which is already open) in the file ! 388: * used by the temp file routines, and save its ! 389: * name for later unlinking. ! 390: */ ! 391: tfile = bestfd; ! 392: CP(nb, bestnb); ! 393: ignorl(lseek(tfile, 0l, 0)); ! 394: ! 395: /* ! 396: * Gotta be able to read the header or fall through ! 397: * to lossage. ! 398: */ ! 399: if (read(tfile, (char *) &H, sizeof H) == sizeof H) ! 400: return; ! 401: } ! 402: ! 403: /* ! 404: * Extreme lossage... ! 405: */ ! 406: error(" File not found", 0); ! 407: } ! 408: ! 409: /* ! 410: * Search for the file in directory dirname. ! 411: * ! 412: * Don't chdir here, because the users directory ! 413: * may be ".", and we would move away before we searched it. ! 414: * Note that we actually chdir elsewhere (because it is too slow ! 415: * to look around in usrpath(preserve) without chdir'ing there) so we ! 416: * can't win, because we don't know the name of '.' and if the path ! 417: * name of the file we want to unlink is relative, rather than absolute ! 418: * we won't be able to find it again. ! 419: */ ! 420: searchdir(dirname) ! 421: char *dirname; ! 422: { ! 423: struct direct *dirent; ! 424: register DIR *dir; ! 425: char dbuf[BUFSIZ]; ! 426: ! 427: dir = opendir(dirname); ! 428: if (dir == NULL) ! 429: return; ! 430: while ((dirent = readdir(dir)) != NULL) { ! 431: if (dirent->d_name[0] != 'E') ! 432: continue; ! 433: /* ! 434: * Got a file in the directory starting with E... ! 435: * Save a consed up name for the file to unlink ! 436: * later, and check that this is really a file ! 437: * we are looking for. ! 438: */ ! 439: ignore(strcat(strcat(strcpy(nb, dirname), "/"), dirent->d_name)); ! 440: if (yeah(nb)) { ! 441: /* ! 442: * Well, it is the file we are looking for. ! 443: * Is it more recent than any version we found before? ! 444: */ ! 445: if (H.Time > besttime) { ! 446: /* ! 447: * A winner. ! 448: */ ! 449: ignore(close(bestfd)); ! 450: bestfd = dup(tfile); ! 451: besttime = H.Time; ! 452: CP(bestnb, nb); ! 453: } ! 454: /* ! 455: * Count versions so user can be told there are ! 456: * ``yet more pages to be turned''. ! 457: */ ! 458: vercnt++; ! 459: } ! 460: ignore(close(tfile)); ! 461: } ! 462: ignore(closedir(dir)); ! 463: } ! 464: ! 465: /* ! 466: * Given a candidate file to be recovered, see ! 467: * if its really an editor temporary and of this ! 468: * user and the file specified. ! 469: */ ! 470: yeah(name) ! 471: char *name; ! 472: { ! 473: ! 474: tfile = open(name, 2); ! 475: if (tfile < 0) ! 476: return (0); ! 477: if (read(tfile, (char *) &H, sizeof H) != sizeof H) { ! 478: nope: ! 479: ignore(close(tfile)); ! 480: return (0); ! 481: } ! 482: if (!eq(savedfile, file)) ! 483: goto nope; ! 484: if (getuid() != H.Uid) ! 485: goto nope; ! 486: /* ! 487: * This is old and stupid code, which ! 488: * puts a word LOST in the header block, so that lost lines ! 489: * can be made to point at it. ! 490: */ ! 491: ignorl(lseek(tfile, (long)(BUFSIZ*HBLKS-8), 0)); ! 492: ignore(write(tfile, "LOST", 5)); ! 493: return (1); ! 494: } ! 495: ! 496: preserve() ! 497: { ! 498: ! 499: } ! 500: ! 501: /* ! 502: * Find the true end of the scratch file, and ``LOSE'' ! 503: * lines which point into thin air. This lossage occurs ! 504: * due to the sandbagging of i/o which can cause blocks to ! 505: * be written in a non-obvious order, different from the order ! 506: * in which the editor tried to write them. ! 507: * ! 508: * Lines which are lost are replaced with the text LOST so ! 509: * they are easy to find. We work hard at pretty formatting here ! 510: * as lines tend to be lost in blocks. ! 511: * ! 512: * This only seems to happen on very heavily loaded systems, and ! 513: * not very often. ! 514: */ ! 515: scrapbad() ! 516: { ! 517: register line *ip; ! 518: struct stat stbuf; ! 519: off_t size, maxt; ! 520: int bno, cnt, bad, was; ! 521: char bk[BUFSIZ]; ! 522: ! 523: ignore(fstat(tfile, &stbuf)); ! 524: size = stbuf.st_size; ! 525: maxt = (size >> SHFT) | (BNDRY-1); ! 526: bno = (maxt >> OFFBTS) & BLKMSK; ! 527: #ifdef DEBUG ! 528: fprintf(stderr, "size %ld, maxt %o, bno %d\n", size, maxt, bno); ! 529: #endif ! 530: ! 531: /* ! 532: * Look for a null separating two lines in the temp file; ! 533: * if last line was split across blocks, then it is lost ! 534: * if the last block is. ! 535: */ ! 536: while (bno > 0) { ! 537: ignorl(lseek(tfile, (long) BUFSIZ * bno, 0)); ! 538: cnt = read(tfile, (char *) bk, BUFSIZ); ! 539: while (cnt > 0) ! 540: if (bk[--cnt] == 0) ! 541: goto null; ! 542: bno--; ! 543: } ! 544: null: ! 545: ! 546: /* ! 547: * Magically calculate the largest valid pointer in the temp file, ! 548: * consing it up from the block number and the count. ! 549: */ ! 550: maxt = ((bno << OFFBTS) | (cnt >> SHFT)) & ~1; ! 551: #ifdef DEBUG ! 552: fprintf(stderr, "bno %d, cnt %d, maxt %o\n", bno, cnt, maxt); ! 553: #endif ! 554: ! 555: /* ! 556: * Now cycle through the line pointers, ! 557: * trashing the Lusers. ! 558: */ ! 559: was = bad = 0; ! 560: for (ip = one; ip <= dol; ip++) ! 561: if (*ip > maxt) { ! 562: #ifdef DEBUG ! 563: fprintf(stderr, "%d bad, %o > %o\n", ip - zero, *ip, maxt); ! 564: #endif ! 565: if (was == 0) ! 566: was = ip - zero; ! 567: *ip = ((HBLKS*BUFSIZ)-8) >> SHFT; ! 568: } else if (was) { ! 569: if (bad == 0) ! 570: fprintf(stderr, " [Lost line(s):"); ! 571: fprintf(stderr, " %d", was); ! 572: if ((ip - 1) - zero > was) ! 573: fprintf(stderr, "-%d", (ip - 1) - zero); ! 574: bad++; ! 575: was = 0; ! 576: } ! 577: if (was != 0) { ! 578: if (bad == 0) ! 579: fprintf(stderr, " [Lost line(s):"); ! 580: fprintf(stderr, " %d", was); ! 581: if (dol - zero != was) ! 582: fprintf(stderr, "-%d", dol - zero); ! 583: bad++; ! 584: } ! 585: if (bad) ! 586: fprintf(stderr, "]"); ! 587: } ! 588: ! 589: /* ! 590: * Aw shucks, if we only had a (void) cast. ! 591: */ ! 592: #ifdef lint ! 593: Ignorl(a) ! 594: long a; ! 595: { ! 596: ! 597: a = a; ! 598: } ! 599: ! 600: Ignore(a) ! 601: char *a; ! 602: { ! 603: ! 604: a = a; ! 605: } ! 606: ! 607: Ignorf(a) ! 608: int (*a)(); ! 609: { ! 610: ! 611: a = a; ! 612: } ! 613: ! 614: ignorl(a) ! 615: long a; ! 616: { ! 617: ! 618: a = a; ! 619: } ! 620: #endif ! 621: ! 622: int cntch, cntln, cntodd, cntnull; ! 623: /* ! 624: * Following routines stolen mercilessly from ex. ! 625: */ ! 626: putfile() ! 627: { ! 628: line *a1; ! 629: register char *fp, *lp; ! 630: register int nib; ! 631: ! 632: a1 = addr1; ! 633: clrstats(); ! 634: cntln = addr2 - a1 + 1; ! 635: if (cntln == 0) ! 636: return; ! 637: nib = BUFSIZ; ! 638: fp = genbuf; ! 639: do { ! 640: getline(*a1++); ! 641: lp = linebuf; ! 642: for (;;) { ! 643: if (--nib < 0) { ! 644: nib = fp - genbuf; ! 645: if (write(io, genbuf, nib) != nib) ! 646: wrerror(); ! 647: cntch += nib; ! 648: nib = 511; ! 649: fp = genbuf; ! 650: } ! 651: if ((*fp++ = *lp++) == 0) { ! 652: fp[-1] = '\n'; ! 653: break; ! 654: } ! 655: } ! 656: } while (a1 <= addr2); ! 657: nib = fp - genbuf; ! 658: if (write(io, genbuf, nib) != nib) ! 659: wrerror(); ! 660: cntch += nib; ! 661: } ! 662: ! 663: wrerror() ! 664: { ! 665: ! 666: syserror(); ! 667: } ! 668: ! 669: clrstats() ! 670: { ! 671: ! 672: ninbuf = 0; ! 673: cntch = 0; ! 674: cntln = 0; ! 675: cntnull = 0; ! 676: cntodd = 0; ! 677: } ! 678: ! 679: #define READ 0 ! 680: #define WRITE 1 ! 681: ! 682: getline(tl) ! 683: line tl; ! 684: { ! 685: register char *bp, *lp; ! 686: register int nl; ! 687: ! 688: lp = linebuf; ! 689: bp = getblock(tl, READ); ! 690: nl = nleft; ! 691: tl &= ~OFFMSK; ! 692: while (*lp++ = *bp++) ! 693: if (--nl == 0) { ! 694: bp = getblock(tl += INCRMT, READ); ! 695: nl = nleft; ! 696: } ! 697: } ! 698: ! 699: int read(); ! 700: int write(); ! 701: ! 702: char * ! 703: getblock(atl, iof) ! 704: line atl; ! 705: int iof; ! 706: { ! 707: register int bno, off; ! 708: ! 709: bno = (atl >> OFFBTS) & BLKMSK; ! 710: off = (atl << SHFT) & LBTMSK; ! 711: if (bno >= NMBLKS) ! 712: error(" Tmp file too large"); ! 713: nleft = BUFSIZ - off; ! 714: if (bno == iblock) { ! 715: ichanged |= iof; ! 716: return (ibuff + off); ! 717: } ! 718: if (bno == oblock) ! 719: return (obuff + off); ! 720: if (iof == READ) { ! 721: if (ichanged) ! 722: blkio(iblock, ibuff, write); ! 723: ichanged = 0; ! 724: iblock = bno; ! 725: blkio(bno, ibuff, read); ! 726: return (ibuff + off); ! 727: } ! 728: if (oblock >= 0) ! 729: blkio(oblock, obuff, write); ! 730: oblock = bno; ! 731: return (obuff + off); ! 732: } ! 733: ! 734: blkio(b, buf, iofcn) ! 735: short b; ! 736: char *buf; ! 737: int (*iofcn)(); ! 738: { ! 739: ! 740: lseek(tfile, (long) (unsigned) b * BUFSIZ, 0); ! 741: if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ) ! 742: syserror(); ! 743: } ! 744: ! 745: syserror() ! 746: { ! 747: extern int sys_nerr; ! 748: extern char *sys_errlist[]; ! 749: ! 750: dirtcnt = 0; ! 751: write(2, " ", 1); ! 752: if (errno >= 0 && errno <= sys_nerr) ! 753: error(sys_errlist[errno]); ! 754: else ! 755: error("System error %d", errno); ! 756: exit(1); ! 757: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.