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