|
|
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 <types.h> ! 9: #include <discbuf.h> ! 10: #include <dumptape.h> ! 11: #include <canon.h> ! 12: #include <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 %D ", 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: %D inodes, ", ninodes); ! 139: fprintf(stderr, "%D real blocks, ", nblocks); ! 140: fprintf(stderr, "%D 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: /* ! 269: * Normally pipes stay in core, ! 270: * and are not in directories. Just to be ! 271: * safe we pretend any pipe is a clear ! 272: * inode. ! 273: */ ! 274: if ((dinode.di_mode&IFMT) == IFPIPE) ! 275: return (0); ! 276: map[ino-1] |= DD_BUSY; ! 277: if ((dinode.di_mode&IFMT) != IFDIR) { ! 278: if (level==0 || dinode.di_ctime>ddate) { ! 279: map[ino-1] |= DD_DUMP; ! 280: return (1); ! 281: } ! 282: return (0); ! 283: } ! 284: map[ino-1] |= DD_DEJA|DD_DIR; ! 285: flag = dirb = 0; ! 286: cansize(dinode.di_size); ! 287: while (dinode.di_size != 0) { ! 288: if ((pb = imap(&dinode, dirb++)) == 0) { ! 289: dinode.di_size -= BUFSIZ; ! 290: continue; ! 291: } ! 292: /* ! 293: * Writing the code is this ! 294: * strange fashon makes it require 1 cache ! 295: * buffer, not 1 per level. ! 296: */ ! 297: dbp = dbread(pb); ! 298: nent = 0; ! 299: ndir = BUFSIZ / sizeof(struct direct); ! 300: if (dinode.di_size < BUFSIZ) ! 301: ndir = dinode.di_size / sizeof(struct direct); ! 302: dirp = (struct direct *) (dbp->db_data); ! 303: while (ndir--) { ! 304: if (dirp->d_ino != 0) { ! 305: ent[nent] = dirp->d_ino; ! 306: canino(ent[nent]); ! 307: ++nent; ! 308: } ! 309: ++dirp; ! 310: dinode.di_size -= sizeof(struct direct); ! 311: } ! 312: dbfree(dbp, 0); ! 313: while (nent--) { ! 314: if (walk(ent[nent])) ! 315: flag = 1; ! 316: } ! 317: } ! 318: if (flag!=0 || level==0 || dinode.di_ctime>ddate) ! 319: map[ino-1] |= DD_DUMP; ! 320: map[ino-1] &= ~DD_DEJA; ! 321: return (flag); ! 322: } ! 323: ! 324: /* ! 325: * Read in an inode. ! 326: * Leave it in the buffer in disc ! 327: * (that is, cannonical) format. ! 328: */ ! 329: ireadin(diop, ino) ! 330: register struct dinode *diop; ! 331: ino_t ino; ! 332: { ! 333: register struct dinode *diip; ! 334: DISCBUF *dbp; ! 335: ! 336: dbp = dbread((long)((ino-1)/INOPB + INODEI)); ! 337: diip = (struct dinode *)(dbp->db_data) + (ino-1)%INOPB; ! 338: *diop = *diip; /* Structure assignment */ ! 339: dbfree(dbp, 0); ! 340: } ! 341: ! 342: /* ! 343: * Map logical block `lb' in the ! 344: * file whose in core inode is pointed to ! 345: * by `ip' to the associated physical ! 346: * block. Return 0 if the block will not ! 347: * map. ! 348: */ ! 349: daddr_t ! 350: imap(ip, lb) ! 351: register struct dinode *ip; ! 352: daddr_t lb; ! 353: { ! 354: register il; ! 355: daddr_t bpos, pb; ! 356: DISCBUF *dbp; ! 357: register daddr_t *bp; ! 358: register daddr_t addrs[NADDR]; ! 359: ! 360: l3tol(addrs, ip->di_addr, NADDR); ! 361: for (il=0; il<4; il++) ! 362: if (lb < ranges[il]) { ! 363: if (il != 0) ! 364: lb -= ranges[il-1]; ! 365: bpos = lb/coeff[il]; ! 366: lb %= coeff[il]; ! 367: bp = &addrs[(int)bpos + offsets[il]]; ! 368: if ((pb = *bp) != 0) { ! 369: while (il-- > 0) { ! 370: dbp = dbread((long) pb); ! 371: bpos = lb/coeff[il]; ! 372: lb %= coeff[il]; ! 373: bp = (daddr_t *)(dbp->db_data) + bpos; ! 374: dbfree(dbp, 0); ! 375: if ((pb = *bp) == 0) ! 376: break; ! 377: candaddr(pb); ! 378: } ! 379: } ! 380: return (pb); ! 381: } ! 382: return (0); ! 383: } ! 384: ! 385: ! 386: /* ! 387: * Dump out the map. ! 388: * The map is packed into ordinary ! 389: * tape blocks. ! 390: */ ! 391: dumpinomap() ! 392: { ! 393: register ino_t ibase; ! 394: register char *imapp; ! 395: register nmap; ! 396: union dumpdata *ddp; ! 397: ! 398: ibase = 0; ! 399: while ((nmap = isize-ibase) > 0) { ! 400: ddp = ddnextbuf(); ! 401: ddp->dd_type = DD_MAP; ! 402: ddp->dd_ino = ibase+1; ! 403: if (nmap > BUFSIZ) ! 404: nmap = BUFSIZ; ! 405: ddp->dd_nmap = nmap; ! 406: canint(ddp->dd_type); ! 407: canino(ddp->dd_ino); ! 408: canint(ddp->dd_nmap); ! 409: imapp = &ddp->dd_map[0]; ! 410: do { ! 411: *imapp++ = map[ibase++]; ! 412: } while (--nmap); ! 413: } ! 414: } ! 415: ! 416: /* ! 417: * Fabricate a dump header and ! 418: * write it to the dump file. All the ! 419: * stuff in the header is in the ! 420: * usual canonical format. This makes it ! 421: * a little easier to move tapes from ! 422: * one machine to another. ! 423: */ ! 424: dumpheader() ! 425: { ! 426: register char *p; ! 427: register checksum; ! 428: ! 429: dh.dh_magic = DH_MAG; ! 430: dh.dh_nino = isize; ! 431: dh.dh_bdate = bdate; ! 432: dh.dh_ddate = ddate; ! 433: dh.dh_level = level; ! 434: dh.dh_reel = reel++; ! 435: dh.dh_blocking = block; ! 436: dh.dh_nbyte = Sflag ? length : 0; ! 437: strncpy(dh.dh_dev, dev, DIRSIZ); ! 438: strncpy(dh.dh_fname, fname, 6); ! 439: strncpy(dh.dh_fpack, fpack, 6); ! 440: p = (char *) &dh; ! 441: checksum = 0; ! 442: while (p < (char *) &dh.dh_checksum) ! 443: checksum += (*p++) & 0377; ! 444: dh.dh_checksum = checksum; ! 445: canint(dh.dh_magic); ! 446: canino(dh.dh_nino); ! 447: cantime(dh.dh_bdate); ! 448: cantime(dh.dh_ddate); ! 449: canint(dh.dh_level); ! 450: canint(dh.dh_reel); ! 451: canint(dh.dh_blocking); ! 452: cansize(dh.dh_nbyte); ! 453: canint(dh.dh_checksum); ! 454: if (write(fileno(dtp), &dh, sizeof dh) != sizeof dh) ! 455: fatal("header write error"); ! 456: } ! 457: ! 458: /* ! 459: * Put out the end of tape item ! 460: * and the very end of the very last ! 461: * dump tape. ! 462: */ ! 463: dumpfinish() ! 464: { ! 465: register union dumpdata *ddp; ! 466: ! 467: ddp = ddnextbuf(); ! 468: ddp->dd_type = DD_EOT; ! 469: canint(ddp->dd_type); ! 470: ddflush(); ! 471: } ! 472: ! 473: /* ! 474: * Dump out all inodes with ! 475: * the appropriate flags set in the ! 476: * map. ! 477: */ ! 478: dump(flag) ! 479: register flag; ! 480: { ! 481: register ino_t i; ! 482: register mapentry; ! 483: ! 484: for (i=0; i<isize; ++i) { ! 485: mapentry = map[i]; ! 486: if ((mapentry&DD_DUMP)!=0 && (mapentry&DD_DIR)==flag) { ! 487: dumpi(i+1); ! 488: map[i] = 0; ! 489: } ! 490: } ! 491: } ! 492: ! 493: /* ! 494: * Dump an inode. ! 495: */ ! 496: dumpi(ino) ! 497: ino_t ino; ! 498: { ! 499: struct dinode dinode; ! 500: register union dumpdata *ddp; ! 501: register DISCBUF *dbp; ! 502: daddr_t lb, pb; ! 503: int size; ! 504: ! 505: ++ninodes; ! 506: ireadin(&dinode, ino); ! 507: ddp = ddnextbuf(); ! 508: ddp->dd_type = DD_INO; ! 509: ddp->dd_ino = ino; ! 510: canint(ddp->dd_type); ! 511: canino(ddp->dd_ino); ! 512: copyb(&ddp->dd_dinode, &dinode, sizeof(struct dinode)); ! 513: lb = 0; ! 514: cansize(dinode.di_size); ! 515: while (dinode.di_size != 0) { ! 516: pb = imap(&dinode, lb); ! 517: size = dinode.di_size<BUFSIZ ? dinode.di_size : BUFSIZ; ! 518: if (pb != 0) { ! 519: dbp = dbread(pb); ! 520: if (allzeros(dbp->db_data, size) == 0) { ! 521: ++nblocks; ! 522: ddp = ddnextbuf(); ! 523: ddp->dd_type = DD_DATA; ! 524: ddp->dd_ino = ino; ! 525: ddp->dd_block = lb; ! 526: ddp->dd_size = size; ! 527: canint(ddp->dd_type); ! 528: canino(ddp->dd_ino); ! 529: candaddr(ddp->dd_block); ! 530: canint(ddp->dd_size); ! 531: copyb(ddp->dd_data, dbp->db_data, size); ! 532: } else ! 533: ++nsparse; ! 534: dbfree(dbp, 0); ! 535: } ! 536: dinode.di_size -= size; ! 537: ++lb; ! 538: } ! 539: } ! 540: ! 541: /* ! 542: * Check if a disc block is all ! 543: * zero bytes. True return if it is. ! 544: */ ! 545: allzeros(cp, nb) ! 546: register char *cp; ! 547: register int nb; ! 548: { ! 549: do { ! 550: if (*cp++ != 0) ! 551: return (0); ! 552: } while (--nb); ! 553: return (1); ! 554: } ! 555: ! 556: /* ! 557: * Copy a block of bytes. ! 558: */ ! 559: copyb(atp, afp, anb) ! 560: char *atp, *afp; ! 561: { ! 562: register char *tp, *fp; ! 563: register nb; ! 564: ! 565: if ((nb = anb) != 0) { ! 566: tp = atp; ! 567: fp = afp; ! 568: do { ! 569: *tp++ = *fp++; ! 570: } while (--nb); ! 571: } ! 572: } ! 573: ! 574: /* ! 575: * Return a pointer to the ! 576: * next data buffer. ! 577: */ ! 578: union dumpdata * ! 579: ddnextbuf() ! 580: { ! 581: register char *p; ! 582: register n; ! 583: ! 584: if (ddbuf == NULL) { ! 585: ddnbuf = block * sizeof(union dumpdata); ! 586: if ((ddbuf = malloc(ddnbuf)) == NULL) ! 587: fatal("out of memory (buffer)"); ! 588: ddend = &ddbuf[ddnbuf]; ! 589: ddptr = (union dumpdata *) ddbuf; ! 590: } ! 591: if ((char *) ddptr == ddend) { ! 592: ddflush(); ! 593: ddptr = (union dumpdata *) ddbuf; ! 594: } ! 595: if ((char *) ddptr == ddbuf) { ! 596: p = ddbuf; ! 597: n = ddnbuf; ! 598: do { ! 599: *p++ = 0; ! 600: } while (--n); ! 601: } ! 602: return (ddptr++); ! 603: } ! 604: ! 605: /* ! 606: * Write out the big data buffer. ! 607: */ ! 608: ddflush() ! 609: { ! 610: used += ddlen(ddnbuf); ! 611: if (used >= length) { ! 612: nextvol(); ! 613: used += ddlen(ddnbuf); ! 614: } ! 615: if (write(fileno(dtp), ddbuf, ddnbuf) != ddnbuf) ! 616: fatal("dump write error"); ! 617: } ! 618: ! 619: /* ! 620: * Request next dump volume. ! 621: */ ! 622: nextvol() ! 623: { ! 624: char buf[40]; ! 625: ! 626: if (Sflag != 0 || reel != 1) { ! 627: fclose(dtp); ! 628: fprintf(stderr, "dump: mount %s %d, type return key ...", ! 629: Sflag ? "volume" : "reel", reel); ! 630: if (gets(buf) == NULL) ! 631: exit(1); ! 632: if ((dtp = fopen(dtn, "w")) == NULL) ! 633: fatal("%s: cannot open dump file", dtn); ! 634: } ! 635: dumpheader(); ! 636: used = ddlen(sizeof(dh)); ! 637: } ! 638: ! 639: /* ! 640: * Given a record size in bytes, ! 641: * return the number of inches/100 of tape ! 642: * that will get used. ! 643: * For blocked devices, the granularity is ! 644: * actually in bytes. ! 645: */ ! 646: long ! 647: ddlen(nbytes) ! 648: { ! 649: if (Sflag) ! 650: return (nbytes); ! 651: return (100L*((nbytes+density-1)/density) + 75); ! 652: } ! 653: ! 654: /* ! 655: * Update the dump date in the ! 656: * dump date file. The date used is the ! 657: * date at the beginning of the dump. ! 658: */ ! 659: putddate() ! 660: { ! 661: register FILE *ddfp; ! 662: struct idates id; ! 663: ! 664: if ((ddfp = fopen(DDATE, "r+w")) == NULL) { ! 665: fprintf(stderr, "dump: cannot update dump date\n"); ! 666: return; ! 667: } ! 668: while (fread(&id, sizeof(id), 1, ddfp) == 1) { ! 669: if (strncmp(id.id_name, dev, DIRSIZ) == 0 ! 670: && id.id_incno==level) { ! 671: fseek(ddfp, (long)-sizeof(id), 1); ! 672: break; ! 673: } ! 674: } ! 675: strncpy(id.id_name, dev, DIRSIZ); ! 676: id.id_incno = level; ! 677: id.id_ddate = bdate; ! 678: if (fwrite(&id, sizeof(id), 1, ddfp) != 1) ! 679: fprintf(stderr, "dump: error updating date file\n"); ! 680: fclose(ddfp); ! 681: } ! 682: ! 683: /* ! 684: * Get dump date. ! 685: */ ! 686: long ! 687: getddate() ! 688: { ! 689: register FILE *ddfp; ! 690: register long ddate; ! 691: struct idates id; ! 692: ! 693: ddate = 0; ! 694: if ((ddfp = fopen(DDATE, "r")) != NULL) { ! 695: while (fread(&id, sizeof(id), 1, ddfp) == 1) { ! 696: if (strncmp(id.id_name, dev, DIRSIZ)==0 ! 697: && id.id_incno<level ! 698: && id.id_ddate>ddate) ! 699: ddate = id.id_ddate; ! 700: } ! 701: fclose(ddfp); ! 702: } ! 703: return (ddate); ! 704: } ! 705: ! 706: /* ! 707: * Print out a fatal diagnostic ! 708: * and die. ! 709: */ ! 710: fatal(a) ! 711: { ! 712: fprintf(stderr, "dump: %r", &a); ! 713: fprintf(stderr, "\n"); ! 714: exit(1); ! 715: } ! 716: ! 717: /* ! 718: * Print out a usage message and ! 719: * exit. If you can figure out how to use ! 720: * dump from the usage message you are ! 721: * a better man than I. ! 722: */ ! 723: usage() ! 724: { ! 725: fprintf(stderr, helpmessage); ! 726: exit(1); ! 727: } ! 728: ! 729: /* ! 730: * Squash off any structure ! 731: * before the last part of a file ! 732: * name and put it in the supplied ! 733: * buffer. ! 734: */ ! 735: squash(tp, fp) ! 736: register char *tp, *fp; ! 737: { ! 738: register char *np; ! 739: ! 740: np = fp; ! 741: while (*fp != '\0') { ! 742: if (*fp == '/') ! 743: np = fp+1; ! 744: ++fp; ! 745: } ! 746: while (*tp++ = *np++) ! 747: ; ! 748: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.