|
|
1.1 ! root 1: /* ! 2: * Dump a filesystem onto a ! 3: * tape. Dump only those files that ! 4: * have checked since the last ! 5: * dump. ! 6: */ ! 7: #include <stdio.h> ! 8: #include <sys/types.h> ! 9: #include <discbuf.h> ! 10: #include <dumptape.h> ! 11: #include <canon.h> ! 12: #include <sys/filsys.h> ! 13: ! 14: char *dtn = DTAPE; /* Tape name */ ! 15: FILE *dtp; /* Tape file pointer */ ! 16: char *fsn = DFSYS; /* File system name */ ! 17: int block = 20; /* Blocking factor */ ! 18: int density = 1600; /* Tape density, bpi */ ! 19: long length = 2300L*12L*100L; /* Length of tape, inches/100 */ ! 20: long used; /* Tape used, inches/100 */ ! 21: struct dumpheader dh; /* Dump header */ ! 22: int reel = 1; /* Reel # */ ! 23: int uflag = 1; /* Update dates file flag */ ! 24: int vflag; /* Verbose flag */ ! 25: int Sflag; /* Set when sizes are in blocks */ ! 26: int level = 9; /* Dump level */ ! 27: time_t bdate; /* Date at beginning of dump */ ! 28: time_t ddate; /* Dump since date */ ! 29: char *map; /* Map pointer */ ! 30: char *ddbuf; /* Dump data buffer */ ! 31: char *ddend; /* Dump data buffer end pointer */ ! 32: int ddnbuf; /* Buffer size */ ! 33: union dumpdata *ddptr; /* Pointer to current item */ ! 34: ino_t isize; /* # of inodes */ ! 35: char dev[DIRSIZ+1]; /* Name of device, squashed */ ! 36: char fname[6]; /* File system name */ ! 37: char fpack[6]; /* File system pack name */ ! 38: long ninodes; /* # of inodes dumped */ ! 39: long nblocks; /* # of blocks of data */ ! 40: long nsparse; /* # of sparse blocks deleted */ ! 41: char helpmessage[] = "\ ! 42: Usage: dump [bdfsuvS0123456789 [args]] filesystem ...\n\ ! 43: "; ! 44: ! 45: /* ! 46: * Tables used by imap. ! 47: * This effectively implements ! 48: * the access polynomial for the indirect ! 49: * blocks. ! 50: */ ! 51: daddr_t ranges[] = { ! 52: ND, ! 53: ND+(daddr_t)1*NBN, ! 54: ND+(daddr_t)1*NBN + (daddr_t)1*NBN*NBN, ! 55: ND+(daddr_t)1*NBN + (daddr_t)1*NBN*NBN + (daddr_t)1*NBN*NBN*NBN ! 56: }; ! 57: ! 58: char offsets[] = { ! 59: 0, ! 60: ND, ! 61: ND+1, ! 62: ND+1+1 ! 63: }; ! 64: ! 65: daddr_t coeff[] = { ! 66: 1, ! 67: (daddr_t) NBN, ! 68: (daddr_t) NBN*NBN, ! 69: (daddr_t) NBN*NBN*NBN ! 70: }; ! 71: ! 72: /* ! 73: * One pass compilers are ! 74: * such fun. ! 75: */ ! 76: long time(); ! 77: long ddlen(); ! 78: union dumpdata *ddnextbuf(); ! 79: long getddate(); ! 80: char *ctime(); ! 81: daddr_t imap(); ! 82: char *calloc(); ! 83: ! 84: main(argc, argv) ! 85: char *argv[]; ! 86: { ! 87: register DISCBUF *dbp; ! 88: register struct filsys *fsp; ! 89: unsigned fi; ! 90: ! 91: init(argc, argv); ! 92: if (*fsn == 0) ! 93: fatal("no filesystem specified"); ! 94: if ((dbfp=fopen(fsn, "r")) == NULL) ! 95: fatal("%s: cannot open filesystem", fsn); ! 96: if ((dtp=fopen(dtn, "w")) == NULL) ! 97: fatal("%s: cannot open dump file", dtn); ! 98: squash(dev, fsn); ! 99: bdate = time((long *) 0); ! 100: ddate = getddate(); ! 101: if (vflag) { ! 102: if (ddate == 0) ! 103: fprintf(stderr, "dump: since epoch\n"); ! 104: else ! 105: fprintf(stderr, "dump: since %s", ctime(&ddate)); ! 106: } ! 107: dbclaim(5); ! 108: sync(); ! 109: dbp = dbread((long) SUPERI); ! 110: fsp = (struct filsys *) (dbp->db_data); ! 111: fi = fsp->s_isize; ! 112: canshort(fi); ! 113: isize = (fi-INODEI)*INOPB; ! 114: strncpy(fname, fsp->s_fname, sizeof(fname)); ! 115: strncpy(fpack, fsp->s_fpack, sizeof(fpack)); ! 116: dbfree(dbp, 0); ! 117: if ((map=calloc(isize, sizeof(char))) == NULL) ! 118: fatal("out of memory (map)"); ! 119: walk((ino_t) ROOTIN); ! 120: nextvol(); ! 121: dumpinomap(); ! 122: dump(DD_DIR); /* Directories */ ! 123: dump(0); /* Files */ ! 124: dumpfinish(); ! 125: if (uflag) ! 126: putddate(); ! 127: if (vflag) { ! 128: if (Sflag) ! 129: used /= 512; else ! 130: used /= 100*12; ! 131: fprintf(stderr, "dump: length is %ld ", used); ! 132: if (Sflag) ! 133: fprintf(stderr, "blocks\n"); ! 134: else if (used == 1) ! 135: fprintf(stderr, "foot\n"); ! 136: else ! 137: fprintf(stderr, "feet\n"); ! 138: fprintf(stderr, "dump: %ld inodes, ", ninodes); ! 139: fprintf(stderr, "%ld real blocks, ", nblocks); ! 140: fprintf(stderr, "%ld sparse blocks\n", nsparse); ! 141: } ! 142: exit(0); ! 143: } ! 144: ! 145: /* ! 146: * Scan the command line and ! 147: * set options. Perhaps reset the name ! 148: * of the filesystem, the dump tape ! 149: * and the dump origin. ! 150: */ ! 151: init(argc, argv) ! 152: char *argv[]; ! 153: { ! 154: register char *p; ! 155: register c, i; ! 156: ! 157: if (argc > 1) { ! 158: uflag = 0; ! 159: i = 1; ! 160: p = argv[1]; ! 161: while ((c = *p++) != '\0') { ! 162: switch (c) { ! 163: ! 164: case 'f': ! 165: if (++i >= argc) ! 166: usage(); ! 167: dtn = argv[i]; ! 168: break; ! 169: ! 170: case 'u': ! 171: uflag = 1; ! 172: break; ! 173: ! 174: case 'v': ! 175: vflag = 1; ! 176: break; ! 177: ! 178: case 's': ! 179: if (++i >= argc) ! 180: usage(); ! 181: length = (long)12*100*num(argv[i], "length", ! 182: 600, 4800); ! 183: break; ! 184: ! 185: case 'S': ! 186: if (++i >= argc) ! 187: usage(); ! 188: Sflag++; ! 189: length = (long)512 * num(argv[i], "block size", ! 190: 0, (unsigned)65535L); ! 191: break; ! 192: ! 193: case 'b': ! 194: if (++i >= argc) ! 195: usage(); ! 196: block = num(argv[i], "blocking", 1, 20); ! 197: break; ! 198: ! 199: case 'd': ! 200: if (++i >= argc) ! 201: usage(); ! 202: density = num(argv[i], "density", 800, 6250); ! 203: break; ! 204: ! 205: case '0': ! 206: case '1': ! 207: case '2': ! 208: case '3': ! 209: case '4': ! 210: case '5': ! 211: case '6': ! 212: case '7': ! 213: case '8': ! 214: case '9': ! 215: level = c-'0'; ! 216: break; ! 217: ! 218: default: ! 219: usage(); ! 220: } ! 221: } ! 222: if (++i < argc) ! 223: fsn = argv[i]; ! 224: } ! 225: } ! 226: ! 227: /* ! 228: * Collect and range check ! 229: * numeric arguments. ! 230: */ ! 231: num(s, msg, ll, hl) ! 232: register char *s; ! 233: char *msg; ! 234: unsigned ll, hl; ! 235: { ! 236: register c, n; ! 237: ! 238: n = 0; ! 239: while ((c=*s++)>='0' && c<='9') ! 240: n = 10*n + c - '0'; ! 241: if (c!='\0' || n<ll || n>hl) ! 242: fatal("bad %s specification", msg); ! 243: return (n); ! 244: } ! 245: ! 246: /* ! 247: * Walk down the file system, ! 248: * looking for files that should be ! 249: * dumped. Mark them in the map. ! 250: * All directories that lead to changed ! 251: * inodes are forced to be dumped. ! 252: */ ! 253: walk(ino) ! 254: ino_t ino; ! 255: { ! 256: register DISCBUF *dbp; ! 257: register struct direct *dirp; ! 258: int flag, nent, ndir; ! 259: long pb, dirb; ! 260: struct dinode dinode; ! 261: ino_t ent[BUFSIZ/sizeof(struct direct)]; ! 262: ! 263: if ((map[ino-1]&DD_DEJA) != 0) ! 264: return (0); ! 265: ireadin(&dinode, ino); ! 266: canshort(dinode.di_mode); ! 267: cantime(dinode.di_ctime); ! 268: cantime(dinode.di_mtime); ! 269: /* ! 270: * Normally pipes stay in core, ! 271: * and are not in directories. Just to be ! 272: * safe we pretend any pipe is a clear ! 273: * inode. ! 274: */ ! 275: if ((dinode.di_mode&IFMT) == IFPIPE) ! 276: return (0); ! 277: map[ino-1] |= DD_BUSY; ! 278: if ((dinode.di_mode&IFMT) != IFDIR) { ! 279: if (level==0||dinode.di_mtime>ddate||dinode.di_ctime>ddate) { ! 280: map[ino-1] |= DD_DUMP; ! 281: return (1); ! 282: } ! 283: return (0); ! 284: } ! 285: map[ino-1] |= DD_DEJA|DD_DIR; ! 286: flag = dirb = 0; ! 287: cansize(dinode.di_size); ! 288: while (dinode.di_size != 0) { ! 289: if ((pb = imap(&dinode, dirb++)) == 0) { ! 290: dinode.di_size -= BUFSIZ; ! 291: continue; ! 292: } ! 293: /* ! 294: * Writing the code is this ! 295: * strange fashon makes it require 1 cache ! 296: * buffer, not 1 per level. ! 297: */ ! 298: dbp = dbread(pb); ! 299: nent = 0; ! 300: ndir = BUFSIZ / sizeof(struct direct); ! 301: if (dinode.di_size < BUFSIZ) ! 302: ndir = dinode.di_size / sizeof(struct direct); ! 303: dirp = (struct direct *) (dbp->db_data); ! 304: while (ndir--) { ! 305: if (dirp->d_ino != 0) { ! 306: ent[nent] = dirp->d_ino; ! 307: canino(ent[nent]); ! 308: ++nent; ! 309: } ! 310: ++dirp; ! 311: dinode.di_size -= sizeof(struct direct); ! 312: } ! 313: dbfree(dbp, 0); ! 314: while (nent--) { ! 315: if (walk(ent[nent])) ! 316: flag = 1; ! 317: } ! 318: } ! 319: if (flag!=0 || level==0 || dinode.di_ctime>ddate) ! 320: map[ino-1] |= DD_DUMP; ! 321: map[ino-1] &= ~DD_DEJA; ! 322: return (flag); ! 323: } ! 324: ! 325: /* ! 326: * Read in an inode. ! 327: * Leave it in the buffer in disc ! 328: * (that is, cannonical) format. ! 329: */ ! 330: ireadin(diop, ino) ! 331: register struct dinode *diop; ! 332: ino_t ino; ! 333: { ! 334: register struct dinode *diip; ! 335: DISCBUF *dbp; ! 336: ! 337: dbp = dbread((long)((ino-1)/INOPB + INODEI)); ! 338: diip = (struct dinode *)(dbp->db_data) + (ino-1)%INOPB; ! 339: *diop = *diip; /* Structure assignment */ ! 340: dbfree(dbp, 0); ! 341: } ! 342: ! 343: /* ! 344: * Map logical block `lb' in the ! 345: * file whose in core inode is pointed to ! 346: * by `ip' to the associated physical ! 347: * block. Return 0 if the block will not ! 348: * map. ! 349: */ ! 350: daddr_t ! 351: imap(ip, lb) ! 352: register struct dinode *ip; ! 353: daddr_t lb; ! 354: { ! 355: register il; ! 356: daddr_t bpos, pb; ! 357: DISCBUF *dbp; ! 358: register daddr_t *bp; ! 359: register daddr_t addrs[NADDR]; ! 360: ! 361: l3tol(addrs, ip->di_addr, NADDR); ! 362: for (il=0; il<4; il++) ! 363: if (lb < ranges[il]) { ! 364: if (il != 0) ! 365: lb -= ranges[il-1]; ! 366: bpos = lb/coeff[il]; ! 367: lb %= coeff[il]; ! 368: bp = &addrs[(int)bpos + offsets[il]]; ! 369: if ((pb = *bp) != 0) { ! 370: while (il-- > 0) { ! 371: dbp = dbread((long) pb); ! 372: bpos = lb/coeff[il]; ! 373: lb %= coeff[il]; ! 374: bp = (daddr_t *)(dbp->db_data) + bpos; ! 375: dbfree(dbp, 0); ! 376: if ((pb = *bp) == 0) ! 377: break; ! 378: candaddr(pb); ! 379: } ! 380: } ! 381: return (pb); ! 382: } ! 383: return (0); ! 384: } ! 385: ! 386: ! 387: /* ! 388: * Dump out the map. ! 389: * The map is packed into ordinary ! 390: * tape blocks. ! 391: */ ! 392: dumpinomap() ! 393: { ! 394: register ino_t ibase; ! 395: register char *imapp; ! 396: register nmap; ! 397: union dumpdata *ddp; ! 398: ! 399: ibase = 0; ! 400: while ((nmap = isize-ibase) > 0) { ! 401: ddp = ddnextbuf(); ! 402: ddp->dd_type = DD_MAP; ! 403: ddp->dd_ino = ibase+1; ! 404: if (nmap > BUFSIZ) ! 405: nmap = BUFSIZ; ! 406: ddp->dd_nmap = nmap; ! 407: canint(ddp->dd_type); ! 408: canino(ddp->dd_ino); ! 409: canint(ddp->dd_nmap); ! 410: imapp = &ddp->dd_map[0]; ! 411: do { ! 412: *imapp++ = map[ibase++]; ! 413: } while (--nmap); ! 414: } ! 415: } ! 416: ! 417: /* ! 418: * Fabricate a dump header and ! 419: * write it to the dump file. All the ! 420: * stuff in the header is in the ! 421: * usual canonical format. This makes it ! 422: * a little easier to move tapes from ! 423: * one machine to another. ! 424: */ ! 425: dumpheader() ! 426: { ! 427: register char *p; ! 428: register checksum; ! 429: ! 430: dh.dh_magic = DH_MAG; ! 431: dh.dh_nino = isize; ! 432: dh.dh_bdate = bdate; ! 433: dh.dh_ddate = ddate; ! 434: dh.dh_level = level; ! 435: dh.dh_reel = reel++; ! 436: dh.dh_blocking = block; ! 437: dh.dh_nbyte = Sflag ? length : 0; ! 438: strncpy(dh.dh_dev, dev, DIRSIZ); ! 439: strncpy(dh.dh_fname, fname, 6); ! 440: strncpy(dh.dh_fpack, fpack, 6); ! 441: p = (char *) &dh; ! 442: checksum = 0; ! 443: while (p < (char *) &dh.dh_checksum) ! 444: checksum += (*p++) & 0377; ! 445: dh.dh_checksum = checksum; ! 446: canint(dh.dh_magic); ! 447: canino(dh.dh_nino); ! 448: cantime(dh.dh_bdate); ! 449: cantime(dh.dh_ddate); ! 450: canint(dh.dh_level); ! 451: canint(dh.dh_reel); ! 452: canint(dh.dh_blocking); ! 453: cansize(dh.dh_nbyte); ! 454: canint(dh.dh_checksum); ! 455: if (write(fileno(dtp), &dh, sizeof dh) != sizeof dh) ! 456: fatal("header write error"); ! 457: } ! 458: ! 459: /* ! 460: * Put out the end of tape item ! 461: * and the very end of the very last ! 462: * dump tape. ! 463: */ ! 464: dumpfinish() ! 465: { ! 466: register union dumpdata *ddp; ! 467: ! 468: ddp = ddnextbuf(); ! 469: ddp->dd_type = DD_EOT; ! 470: canint(ddp->dd_type); ! 471: ddflush(); ! 472: } ! 473: ! 474: /* ! 475: * Dump out all inodes with ! 476: * the appropriate flags set in the ! 477: * map. ! 478: */ ! 479: dump(flag) ! 480: register flag; ! 481: { ! 482: register ino_t i; ! 483: register mapentry; ! 484: ! 485: for (i=0; i<isize; ++i) { ! 486: mapentry = map[i]; ! 487: if ((mapentry&DD_DUMP)!=0 && (mapentry&DD_DIR)==flag) { ! 488: dumpi(i+1); ! 489: map[i] = 0; ! 490: } ! 491: } ! 492: } ! 493: ! 494: /* ! 495: * Dump an inode. ! 496: */ ! 497: dumpi(ino) ! 498: ino_t ino; ! 499: { ! 500: struct dinode dinode; ! 501: register union dumpdata *ddp; ! 502: register DISCBUF *dbp; ! 503: daddr_t lb, pb; ! 504: int size; ! 505: ! 506: ++ninodes; ! 507: ireadin(&dinode, ino); ! 508: ddp = ddnextbuf(); ! 509: ddp->dd_type = DD_INO; ! 510: ddp->dd_ino = ino; ! 511: canint(ddp->dd_type); ! 512: canino(ddp->dd_ino); ! 513: copyb(&ddp->dd_dinode, &dinode, sizeof(struct dinode)); ! 514: lb = 0; ! 515: cansize(dinode.di_size); ! 516: while (dinode.di_size != 0) { ! 517: pb = imap(&dinode, lb); ! 518: size = dinode.di_size<BUFSIZ ? dinode.di_size : BUFSIZ; ! 519: if (pb != 0) { ! 520: dbp = dbread(pb); ! 521: if (allzeros(dbp->db_data, size) == 0) { ! 522: ++nblocks; ! 523: ddp = ddnextbuf(); ! 524: ddp->dd_type = DD_DATA; ! 525: ddp->dd_ino = ino; ! 526: ddp->dd_block = lb; ! 527: ddp->dd_size = size; ! 528: canint(ddp->dd_type); ! 529: canino(ddp->dd_ino); ! 530: candaddr(ddp->dd_block); ! 531: canint(ddp->dd_size); ! 532: copyb(ddp->dd_data, dbp->db_data, size); ! 533: } else ! 534: ++nsparse; ! 535: dbfree(dbp, 0); ! 536: } ! 537: dinode.di_size -= size; ! 538: ++lb; ! 539: } ! 540: } ! 541: ! 542: /* ! 543: * Check if a disc block is all ! 544: * zero bytes. True return if it is. ! 545: */ ! 546: allzeros(cp, nb) ! 547: register char *cp; ! 548: register int nb; ! 549: { ! 550: do { ! 551: if (*cp++ != 0) ! 552: return (0); ! 553: } while (--nb); ! 554: return (1); ! 555: } ! 556: ! 557: /* ! 558: * Copy a block of bytes. ! 559: */ ! 560: copyb(atp, afp, anb) ! 561: char *atp, *afp; ! 562: { ! 563: register char *tp, *fp; ! 564: register nb; ! 565: ! 566: if ((nb = anb) != 0) { ! 567: tp = atp; ! 568: fp = afp; ! 569: do { ! 570: *tp++ = *fp++; ! 571: } while (--nb); ! 572: } ! 573: } ! 574: ! 575: /* ! 576: * Return a pointer to the ! 577: * next data buffer. ! 578: */ ! 579: union dumpdata * ! 580: ddnextbuf() ! 581: { ! 582: register char *p; ! 583: register n; ! 584: ! 585: if (ddbuf == NULL) { ! 586: ddnbuf = block * sizeof(union dumpdata); ! 587: if ((ddbuf = malloc(ddnbuf)) == NULL) ! 588: fatal("out of memory (buffer)"); ! 589: ddend = &ddbuf[ddnbuf]; ! 590: ddptr = (union dumpdata *) ddbuf; ! 591: } ! 592: if ((char *) ddptr == ddend) { ! 593: ddflush(); ! 594: ddptr = (union dumpdata *) ddbuf; ! 595: } ! 596: if ((char *) ddptr == ddbuf) { ! 597: p = ddbuf; ! 598: n = ddnbuf; ! 599: do { ! 600: *p++ = 0; ! 601: } while (--n); ! 602: } ! 603: return (ddptr++); ! 604: } ! 605: ! 606: /* ! 607: * Write out the big data buffer. ! 608: */ ! 609: ddflush() ! 610: { ! 611: used += ddlen(ddnbuf); ! 612: if (used >= length) { ! 613: nextvol(); ! 614: used += ddlen(ddnbuf); ! 615: } ! 616: if (write(fileno(dtp), ddbuf, ddnbuf) != ddnbuf) ! 617: fatal("dump write error"); ! 618: } ! 619: ! 620: /* ! 621: * Request next dump volume. ! 622: */ ! 623: nextvol() ! 624: { ! 625: char buf[40]; ! 626: ! 627: if (Sflag != 0 || reel != 1) { ! 628: fclose(dtp); ! 629: fprintf(stderr, "dump: mount %s %d, type return key ...", ! 630: Sflag ? "volume" : "reel", reel); ! 631: if (gets(buf) == NULL) ! 632: exit(1); ! 633: if ((dtp = fopen(dtn, "w")) == NULL) ! 634: fatal("%s: cannot open dump file", dtn); ! 635: } ! 636: dumpheader(); ! 637: used = ddlen(sizeof(dh)); ! 638: } ! 639: ! 640: /* ! 641: * Given a record size in bytes, ! 642: * return the number of inches/100 of tape ! 643: * that will get used. ! 644: * For blocked devices, the granularity is ! 645: * actually in bytes. ! 646: */ ! 647: long ! 648: ddlen(nbytes) ! 649: { ! 650: if (Sflag) ! 651: return (nbytes); ! 652: return (100L*((nbytes+density-1)/density) + 75); ! 653: } ! 654: ! 655: /* ! 656: * Update the dump date in the ! 657: * dump date file. The date used is the ! 658: * date at the beginning of the dump. ! 659: */ ! 660: putddate() ! 661: { ! 662: register FILE *ddfp; ! 663: struct idates id; ! 664: ! 665: if ((ddfp = fopen(DDATE, "r+w")) == NULL) { ! 666: fprintf(stderr, "dump: cannot update dump date\n"); ! 667: return; ! 668: } ! 669: while (fread(&id, sizeof(id), 1, ddfp) == 1) { ! 670: if (strncmp(id.id_name, dev, DIRSIZ) == 0 ! 671: && id.id_incno==level) { ! 672: fseek(ddfp, (long)-sizeof(id), 1); ! 673: break; ! 674: } ! 675: } ! 676: strncpy(id.id_name, dev, DIRSIZ); ! 677: id.id_incno = level; ! 678: id.id_ddate = bdate; ! 679: if (fwrite(&id, sizeof(id), 1, ddfp) != 1) ! 680: fprintf(stderr, "dump: error updating date file\n"); ! 681: fclose(ddfp); ! 682: } ! 683: ! 684: /* ! 685: * Get dump date. ! 686: */ ! 687: long ! 688: getddate() ! 689: { ! 690: register FILE *ddfp; ! 691: register long ddate; ! 692: struct idates id; ! 693: ! 694: ddate = 0; ! 695: if ((ddfp = fopen(DDATE, "r")) != NULL) { ! 696: while (fread(&id, sizeof(id), 1, ddfp) == 1) { ! 697: if (strncmp(id.id_name, dev, DIRSIZ)==0 ! 698: && id.id_incno<level ! 699: && id.id_ddate>ddate) ! 700: ddate = id.id_ddate; ! 701: } ! 702: fclose(ddfp); ! 703: } ! 704: return (ddate); ! 705: } ! 706: ! 707: /* ! 708: * Print out a fatal diagnostic ! 709: * and die. ! 710: */ ! 711: fatal(a) ! 712: { ! 713: fprintf(stderr, "dump: %r", &a); ! 714: fprintf(stderr, "\n"); ! 715: exit(1); ! 716: } ! 717: ! 718: /* ! 719: * Print out a usage message and ! 720: * exit. If you can figure out how to use ! 721: * dump from the usage message you are ! 722: * a better man than I. ! 723: */ ! 724: usage() ! 725: { ! 726: fprintf(stderr, helpmessage); ! 727: exit(1); ! 728: } ! 729: ! 730: /* ! 731: * Squash off any structure ! 732: * before the last part of a file ! 733: * name and put it in the supplied ! 734: * buffer. ! 735: */ ! 736: squash(tp, fp) ! 737: register char *tp, *fp; ! 738: { ! 739: register char *np; ! 740: ! 741: np = fp; ! 742: while (*fp != '\0') { ! 743: if (*fp == '/') ! 744: np = fp+1; ! 745: ++fp; ! 746: } ! 747: while (*tp++ = *np++) ! 748: ; ! 749: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.