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