home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume6 / vol < prev    next >
Text File  |  1986-11-30  |  16KB  |  643 lines

  1. /* Written  9:18 am  Jun 18, 1986 by sources-request@mirror.UUCP in mirror:mod.sources */
  2. /* ---------- "v06i004:  vol: create volume header" ---------- */
  3. Submitted by: cca!harvard!clyde!infopro!bty!yost (Brian Host)
  4. Mod.sources: Volume 6, Issue 4
  5. Archive-name: vol
  6.  
  7. [ There was some discussion on the net a few weeks ago about the
  8.   abilities of cpio and tar to handle multi-volume dumps.  With
  9.   this front-end, both programs can.  Synchronicity.  --r$]
  10.  
  11. ----- Cut here -------------------------------------------------
  12. #!/bin/sh
  13. # shar:    Shell Archiver
  14. #    Run the following text with /bin/sh to create:
  15. #    README
  16. #    vol.1
  17. #    vol.c
  18. #    voltar
  19. echo x - extracting README
  20. sed 's/^X//' << 'SHAR_EOF' > README
  21. X
  22. XThis distribution contains the first release of the `vol' command,
  23. Xwhich creates volume header files for multivolume `tar' archives.
  24. XA man page is included.
  25. X
  26. XThe following items may be of interest for porting:
  27. X
  28. X    1.  It was compiled undex XENIX 3.0 originally.
  29. X    2.  My compiler has the 8-character limit on names.
  30. X    3.  I used upper and lowercase in names.
  31. X    4.  It reads directories and uses stat(2) and time(2).
  32. X
  33. XHere's my copy of /etc/default/vol:
  34. X# tape/disk volume sizes
  35. XVOLSIZE=1400
  36. XDISKSIZE=1400
  37. XTAPESIZE=60000
  38. X
  39. XI'd be very interested in hearing any comments, and of course I'd
  40. Xappreciate receiving any modifications, bug fixes, ports, etc.
  41. X
  42. XBrian Yost     {clyde,topaz}!infopro!bty!yost       14 June 1986
  43. SHAR_EOF
  44. echo x - extracting vol.1
  45. sed 's/^X//' << 'SHAR_EOF' > vol.1
  46. X.nh
  47. X.TH VOL 1 
  48. X.SH NAME
  49. Xvol  \-  create volume header files for
  50. X.IR tar (1)
  51. X.SH SYNOPSIS
  52. X.B vol
  53. X[ -ptd ] [ -s
  54. X.B "maxblocks"
  55. X] pathname...
  56. X.SH DESCRIPTION
  57. XBecause multivolume
  58. X.IR tar (1)
  59. Xarchives are sequential in nature and cannot be accessed individually,
  60. Xa number of difficulties can arise.
  61. XThe most common problem is that one must search through ten floppies
  62. Xin order to retrieve a file from the eleventh.
  63. XSearching through two cartridge tapes to get at something on the third
  64. Xis no fun, either,
  65. Xespecially when it takes 30 minutes or more per tape.
  66. XA much more devastating problem occurs when the third of fifteen floppy
  67. Xdisks gets trashed or lost,
  68. Xrendering disks 4 through 15 effectively inaccessible.
  69. X.PP
  70. XThe use of
  71. X.I vol
  72. Xeliminates these problems by keeping the amount of data written
  73. Xto each volume within the capacity of the medium being used.
  74. X.I Vol
  75. Xtakes a maximum capacity value (in blocks) and a list of pathnames,
  76. Xand creates a sequence of volume header files of the form
  77. X.nf
  78. X.sp 1
  79. X             Vol_1ofN, Vol_2ofN, ..., Vol_NofN
  80. X.sp 1
  81. X.fi
  82. XEach of these files contains
  83. X1) its own name,
  84. X2) a subset of the pathnames specified, and
  85. X3) a dummy file `TOTAL-NNNNN-Blocks-MM/DD/YY'.
  86. XThe total space in blocks required by this subset of files should
  87. Xbe some value NNNNN which is less than or equal to the maximum
  88. Xcapacity value.  Here's a sample volume header file for a
  89. Xdiskette that has a 1400-block capacity:
  90. X.nf
  91. X.sp 1
  92. X             $ cat Vol_1of3
  93. X             ./Vol_1of3
  94. X             /usr2/yost/c
  95. X             /usr2/yost/news
  96. X             /usr2/yost/calendar
  97. X             TOTAL-1387-Blocks-06/02/86
  98. X             $
  99. X.sp 1
  100. X.fi
  101. X.PP
  102. XThese volume header files may then be used in conjunction with the
  103. Xcommand evaluation capability of
  104. X.IR sh (1),
  105. Xas in the following examples:
  106. X.nf
  107. X.sp 1
  108. X             $ tar cv `cat Vol_1of3`
  109. X             $ ls -ls `cat Vol_1of3`
  110. X             $ rm -rf `cat Vol_1of3`
  111. X.sp 1
  112. X.fi
  113. X.PP
  114. X.I Vol
  115. Xruns
  116. X.IR du (1)
  117. Xon each pathname supplied on the command line to determine how
  118. Xmuch space it requires.
  119. XIf any pathname is larger than the maximum capacity,
  120. Xand it is a directory,
  121. X.I vol
  122. Xprocesses each of the files within that directory separately,
  123. Xrather than the directory as a whole.
  124. XFor example, if the directory /usr is too big to fit on a single magtape,
  125. X.I vol
  126. Xwill split it up into tape-sized chunks.
  127. XIf a regular file is larger than the maximum capacity,
  128. X.I vol
  129. Xwill print an error message and skip the file.
  130. X.PP
  131. XOnce all of the pathnames on the command line have been processed
  132. Xin this way,
  133. X.I vol
  134. Xbegins arranging them into volume header files.
  135. XThe default algorithm takes great liberties in rearranging the
  136. Xsequence of the pathnames in order to minimize the amount of
  137. Xwasted space on each volume.
  138. XThe -p option tells
  139. X.I vol
  140. Xto preserve the original sequence of pathnames,
  141. Xregardless of space considerations.
  142. X.PP
  143. XThe maximum capacity value defaults to the value of `VOLSIZE' in
  144. Xthe file /etc/default/vol,
  145. Xor the environment variable `VOLSIZE'.
  146. XThe -t option to use `TAPESIZE' rather than `VOLSIZE';
  147. Xsimilarly, -d means use `TAPESIZE'.
  148. XCommand-line options take priority over environment variables,
  149. Xand variables take priority over the values in /etc/default/vol.
  150. X.PP
  151. X.I Vol
  152. Xwill run much faster if you do some of its work for it.
  153. XFor example, if you wish to split up /usr into magtape-sized
  154. Xvolumes, and you know it will take more than one, use the
  155. Xcommand
  156. X.nf
  157. X
  158. X             $ vol -t /usr/*
  159. X
  160. Xrather than
  161. X
  162. X             $ vol -t /usr
  163. X
  164. X.fi
  165. XThis will save
  166. X.I vol
  167. Xthe trouble of doing the initial
  168. X.IR du (1)
  169. Xon /usr, only to find out that it's too big.
  170. X.SH DIAGNOSTICS
  171. X.I Vol
  172. Xexits with status 1 if any errors are encountered.
  173. X.SH FILES
  174. X/etc/default/vol /tmp/vol* ./Vol_*of*
  175. X.SH "SEE ALSO"
  176. Xtar(1)
  177. Xdu(1)
  178. Xsh(1)
  179. X.SH BUGS
  180. X.I Vol
  181. Xneglects to consider the size of the header files in its calculations.
  182. X.PP
  183. XMultiple links to a file are treated as separate files.
  184. X.SH AUTHOR
  185. XBrian Yost (bty!yost)
  186. SHAR_EOF
  187. echo x - extracting vol.c
  188. sed 's/^X//' << 'SHAR_EOF' > vol.c
  189. X
  190. X/*
  191. X**  vol.c - create volume header files for `tar'
  192. X**
  193. X**  I hereby release this source code into the public domain.  Anyone
  194. X**  may use, copy, or modify this source code, as long as:
  195. X**
  196. X**    1) it is not sold for profit;
  197. X**    2) modifications are clearly identified with the date,
  198. X**         author's name, and e-mail address; and
  199. X**    3) the entire text of this comment remains intact.
  200. X**
  201. X**  I would also appreciate it if ports, modifications and/or flames
  202. X**  were sent to me at the address below.
  203. X**
  204. X**  Brian Yost    {topaz,clyde}!infopro!bty!yost    14 June 1986
  205. X*/
  206. X
  207. X#include <stdio.h>
  208. X#include <string.h>
  209. X#include <signal.h>
  210. X#include <time.h>
  211. X#include <sys/types.h>
  212. X#include <sys/stat.h>
  213. X#include <sys/dir.h>
  214. X
  215. X#define OKAY        0
  216. X#define ERROR        1
  217. X#define    TOLERANCE    10    /* how close to actually come to limit */
  218. X#define MAX_HEADER    5000L    /* maximum size of header file (bytes) */
  219. X#define UNDER_10    "./Vol_%dof%d"
  220. X#define OVER_9        "./Vol_%02dof%02d"
  221. X
  222. X#define SIGNAL(S,A)    if (signal((S),SIG_IGN) != SIG_IGN) signal((S),(A))
  223. X
  224. Xtypedef enum {FALSE, TRUE} boolean;
  225. X
  226. Xchar *ProgName;
  227. Xchar *Default = "/etc/default/vol";
  228. Xchar *Usage = "Usage: %s [-ptd] [-s maxblocks] pathname...\n";
  229. Xchar *Size = "VOLSIZE";
  230. X
  231. Xboolean Preserve = FALSE;    /* ok to rearrange file sequence */
  232. X
  233. XFILE *TempFile;
  234. Xchar *TempName = "/tmp/volXXXXXX";
  235. X
  236. Xlong MaxBlocks = 0L;
  237. Xlong N_Records = 0L;
  238. X
  239. Xextern int CreateVol();        /* top level */
  240. Xextern void GetSize();        /* determines size of a pathname */
  241. Xextern void ProcessDir();   /* run GetSize() on directory contents */
  242. Xextern void AddList();        /* adds to the list of processed pathnames */
  243. Xextern char *GetDate();        /* returns today's date in MM/DD/YY format */
  244. Xextern int rm_temps();        /* interrupt handler */
  245. X
  246. Xmain(ac, av)
  247. Xint ac;
  248. Xchar *av[];
  249. X{
  250. X    int status, option;
  251. X    FILE *deflt;
  252. X    extern char *optarg;
  253. X    extern int optind;
  254. X    extern long atol();
  255. X    extern char *getenv(), *fgets();
  256. X
  257. X    ProgName = av[0];
  258. X    while ((option = getopt(ac, av, "tds:ph")) != EOF)
  259. X        switch (option) {
  260. X        case 't':    /* tape */
  261. X            Size = "TAPESIZE";
  262. X            break;
  263. X        case 'd':    /* disk */
  264. X            Size = "DISKSIZE";
  265. X            break;
  266. X        case 's':    /* size */
  267. X            MaxBlocks = atol(optarg);
  268. X            break;
  269. X        case 'p':    /* preserve file sequence */
  270. X            Preserve = TRUE;
  271. X            break;
  272. X        case 'h':    /* help */
  273. X        case '?':    /* error */
  274. X        default:
  275. X            fprintf(stderr, Usage, ProgName);
  276. X            exit(ERROR);
  277. X            break;
  278. X        }
  279. X    
  280. X    if (ac - optind < 1) {    /* not enough args */
  281. X        fprintf(stderr, Usage, ProgName);
  282. X        exit(ERROR);
  283. X    }
  284. X
  285. X    /* Look for environment variable */
  286. X    if (!MaxBlocks) {
  287. X        char *s;
  288. X
  289. X        if ((s = getenv(Size)) != NULL)
  290. X            MaxBlocks = atol(s);
  291. X    }
  292. X    
  293. X    /* Check DEFAULT file */
  294. X    if (!MaxBlocks)
  295. X        if ((deflt = fopen(Default, "r")) != NULL) {
  296. X            int len = strlen(Size);
  297. X            char buf[BUFSIZ];
  298. X
  299. X            while (fgets(buf, BUFSIZ, deflt))
  300. X              if (strncmp(buf, Size, len) == 0)
  301. X                if (buf[len] == '=')
  302. X                  if (MaxBlocks = atol(buf+len+1))
  303. X                    break;
  304. X            fclose(deflt);
  305. X        }
  306. X    
  307. X
  308. X    if (!MaxBlocks) {    /* just give up */
  309. X        fprintf(stderr, "%s: you must specify block size\n", ProgName);
  310. X        fprintf(stderr, Usage, ProgName);
  311. X        exit(ERROR);
  312. X    } else if (MaxBlocks < 0L) {    /* no way */
  313. X        fprintf(stderr, "%s: invalid block size %ld\n",
  314. X            ProgName, MaxBlocks);
  315. X        exit(ERROR);
  316. X    }
  317. X    printf("Volume size = %ld blocks\n", MaxBlocks);
  318. X
  319. X    SIGNAL(SIGHUP, rm_temps);
  320. X    SIGNAL(SIGINT, rm_temps);
  321. X    SIGNAL(SIGQUIT, rm_temps);
  322. X    SIGNAL(SIGTERM, rm_temps);
  323. X
  324. X    mktemp(TempName);
  325. X    if ((TempFile = fopen(TempName, "w+")) == NULL) {
  326. X        fprintf(stderr, "%s: can't open %s\n", ProgName, TempName);
  327. X        exit(ERROR);
  328. X    } else {
  329. X        setbuf(TempFile, (char*)NULL);
  330. X        status = CreateVol(ac-optind, av+optind);
  331. X        fclose(TempFile);
  332. X        exit(status);
  333. X    }
  334. X}
  335. X
  336. Xint
  337. XCreateVol(n, pathname)
  338. Xint n;
  339. Xchar *pathname[];
  340. X{
  341. X    int i, c, volume;
  342. X    long total, grandtotal, blocks, filepos, filesize;
  343. X    char volfile[BUFSIZ], oldfile[BUFSIZ], name[BUFSIZ], buf[BUFSIZ];
  344. X    FILE *vol, *old;
  345. X    extern long ftell();
  346. X
  347. X    for (i = 0; i < n; i++)
  348. X        GetSize(pathname[i]);
  349. X    
  350. X    if (Preserve != TRUE) {
  351. X        /*
  352. X        ** Since we only make one pass through the list, sort
  353. X        ** it by blocksize descending.  This should get us as
  354. X        ** close to MaxBlocks as possible.
  355. X        */
  356. X        sprintf(buf, "sort -n -r %s -o %s", TempName, TempName);
  357. X        if (system(buf) != 0)
  358. X            fprintf(stderr, "%s: error sorting %s\n",
  359. X                ProgName, TempName);
  360. X    }
  361. X    unlink(TempName);    /* (after last close) */
  362. X
  363. X    /*
  364. X    ** Move items from the list into the volume header files
  365. X    */
  366. X    volume = 0;
  367. X    grandtotal = 0L;
  368. X    while (N_Records) {
  369. X        volume++;
  370. X        total = 0L;
  371. X
  372. X        sprintf(volfile, "%s-%d", TempName, volume);
  373. X        if ((vol = fopen(volfile, "w")) == NULL) {
  374. X            fprintf(stderr, "%s: fatal error -- can't open %s\n",
  375. X                ProgName, volfile);
  376. X            return ERROR;
  377. X        }
  378. X
  379. X        rewind(TempFile);
  380. X        filepos = ftell(TempFile);
  381. X        filesize = 0L;
  382. X        while (fgets(buf, BUFSIZ, TempFile)) {
  383. X            if (sscanf(buf, "%ld %s", &blocks, name) != 2) {
  384. X                fprintf(stderr, "%s: error reading %s\n",
  385. X                    ProgName, TempName);
  386. X                return ERROR;
  387. X            }
  388. X
  389. X            if (blocks >= 0L)    /* hasn't been deleted */
  390. X                if ((total + blocks) <= (MaxBlocks - TOLERANCE)
  391. X                && (filesize + strlen(name)+1) < MAX_HEADER) {
  392. X                   total += blocks;
  393. X                   filesize += strlen(name) + 1;
  394. X                   fprintf(vol, "%s\n", name);
  395. X   
  396. X                   /* delete from tempfile */
  397. X                   fseek(TempFile, filepos, 0);
  398. X                   fprintf(TempFile, " -1    ");
  399. X                   fgets(buf, BUFSIZ, TempFile);
  400. X                   N_Records--;
  401. X                } else if (Preserve) /* can't rearrange files */
  402. X                   break;
  403. X            
  404. X            filepos = ftell(TempFile);
  405. X        }
  406. X
  407. X        fprintf(vol, "TOTAL-%ld-blocks-%s\n", total, GetDate());
  408. X        fclose(vol);
  409. X        printf("Volume %d: %ld blocks\n", volume, total);
  410. X        grandtotal += total;
  411. X    }
  412. X
  413. X    /*
  414. X    ** Now we know how many were needed altogether, so
  415. X    ** we can create the proper volume header file names.
  416. X    */
  417. X    for (i = 1; i <= volume; i++) {
  418. X        sprintf(oldfile, "%s-%d", TempName, i);
  419. X        if (volume < 10)
  420. X            sprintf(volfile, UNDER_10, i, volume);
  421. X        else
  422. X            sprintf(volfile, OVER_9, i, volume);
  423. X        
  424. X        if ((vol = fopen(volfile, "w")) == NULL) {
  425. X            fprintf(stderr, "%s: fatal error -- can't open %s\n",
  426. X                ProgName, volfile);
  427. X            return ERROR;
  428. X        } else if ((old = fopen(oldfile, "r")) == NULL) {
  429. X            fprintf(stderr, "%s: fatal error -- can't open %s\n",
  430. X                ProgName, volfile);
  431. X            return ERROR;
  432. X        }
  433. X        
  434. X        fprintf(vol, "%s\n", volfile);
  435. X        while ((c = getc(old)) != EOF)
  436. X            putc(c, vol);
  437. X        
  438. X        fclose(old);
  439. X        fclose(vol);
  440. X        unlink(oldfile);
  441. X    }
  442. X
  443. X    printf("Total %ld blocks in %d volume%c\n",
  444. X        grandtotal, volume, (volume == 1? ' ' : 's'));
  445. X
  446. X    return OKAY;
  447. X}
  448. X
  449. Xvoid
  450. XGetSize(pathname)
  451. Xchar *pathname;
  452. X{
  453. X    struct stat stat_buf;
  454. X    char du_pipe[BUFSIZ], buf[BUFSIZ];
  455. X    FILE *du, *popen();
  456. X
  457. X    if (stat(pathname, &stat_buf) != 0) {
  458. X        fprintf(stderr, "%s: can't stat %s\n",
  459. X            ProgName, pathname);
  460. X        return;
  461. X    } else if ((stat_buf.st_mode & S_IFDIR) == 0
  462. X           && (stat_buf.st_mode & S_IFREG) == 0) {
  463. X        fprintf(stderr, "%s: %s not a file or directory\n",
  464. X            ProgName, pathname);
  465. X        return;
  466. X    }
  467. X
  468. X    sprintf(du_pipe, "du -s %s", pathname);
  469. X    if ((du = popen(du_pipe, "r")) == NULL)
  470. X        fprintf(stderr, "%s: can't popen du\n", ProgName);
  471. X    else {
  472. X        if (fgets(buf, BUFSIZ, du)) {
  473. X            pclose(du);    /* before recursion */
  474. X            if (atol(buf) > (MaxBlocks - TOLERANCE))
  475. X                if (stat_buf.st_mode & S_IFDIR)
  476. X                    ProcessDir(pathname);
  477. X                else
  478. X                    fprintf(stderr, "%s: %s too big\n", 
  479. X                        ProgName, pathname);
  480. X            else
  481. X                AddList(buf);
  482. X        } else {
  483. X            pclose(du);
  484. X            fprintf(stderr, "%s: du not cooperating\n",
  485. X                ProgName);
  486. X        }
  487. X    }
  488. X
  489. X    return;
  490. X}
  491. X
  492. Xvoid
  493. XProcessDir(pathname)
  494. Xchar *pathname;
  495. X{
  496. X    FILE *dir;
  497. X    char buf[BUFSIZ];
  498. X    struct direct dir_buf;
  499. X
  500. X    if ((dir = fopen(pathname, "r")) == NULL)
  501. X        fprintf(stderr, "%s: can't read directory %s\n",
  502. X            ProgName, pathname);
  503. X    else {
  504. X        while (fread((char*)&dir_buf, sizeof(dir_buf), 1, dir) == 1)
  505. X            if (dir_buf.d_ino
  506. X                && strcmp(dir_buf.d_name, ".") != 0
  507. X                && strcmp(dir_buf.d_name, "..") != 0) {
  508. X                if (pathname[strlen(pathname)-1] == '/')
  509. X                    sprintf(buf, "%s%.14s",
  510. X                        pathname, dir_buf.d_name);
  511. X                else
  512. X                    sprintf(buf, "%s/%.14s",
  513. X                        pathname, dir_buf.d_name);
  514. X                GetSize(buf);
  515. X            }
  516. X        fclose(dir);
  517. X    }
  518. X    return;
  519. X}
  520. X
  521. Xvoid
  522. XAddList(buf)
  523. Xchar *buf;
  524. X{
  525. X    long blocks;
  526. X    char pathname[BUFSIZ];
  527. X
  528. X    if (sscanf(buf, "%ld %s", &blocks, pathname) != 2)
  529. X        fprintf(stderr, "%s: unexpected data from du - \"%s\"\n",
  530. X            ProgName, buf);
  531. X    else if (blocks >= 0) {
  532. X        fprintf(TempFile, "%07ld %s\n", blocks, pathname);
  533. X        N_Records++;
  534. X    }
  535. X
  536. X    return;
  537. X}
  538. X
  539. Xchar *
  540. XGetDate()
  541. X{
  542. X    long now, time();
  543. X    struct tm *tm, *localtime();
  544. X    static char date[9];
  545. X
  546. X    if (!(*date)) {
  547. X        now = time((long *)0);
  548. X        tm = localtime(&now);
  549. X        sprintf(date, "%02d/%02d/%02d",
  550. X            tm->tm_mon+1, tm->tm_mday, tm->tm_year);
  551. X    }
  552. X    return date;
  553. X}
  554. X
  555. X
  556. Xint
  557. Xrm_temps(signum)
  558. Xint signum;
  559. X{
  560. X    char buf[BUFSIZ];
  561. X
  562. X    signal(signum, SIG_IGN);
  563. X    sprintf(buf, "rm -f %s*", TempName);
  564. X    system(buf);
  565. X
  566. X    switch (signum) {
  567. X    case SIGHUP:
  568. X        fprintf(stderr, "%s: line gone\n", ProgName);
  569. X        exit(ERROR);
  570. X        break;
  571. X    case SIGINT:
  572. X        fprintf(stderr, "%s: terminated by user\n", ProgName);
  573. X        exit(ERROR);
  574. X        break;
  575. X    case SIGQUIT:
  576. X        signal(SIGQUIT, SIG_DFL);
  577. X        kill(getpid(), SIGQUIT);
  578. X        break;
  579. X    case SIGTERM:
  580. X        fprintf(stderr, "%s: killed\n", ProgName);
  581. X        exit(ERROR);
  582. X        break;
  583. X    default:
  584. X        return;
  585. X    }
  586. X}
  587. SHAR_EOF
  588. echo x - extracting voltar.sh
  589. sed 's/^X//' << 'SHAR_EOF' > voltar.sh
  590. X: voltar.sh
  591. X#
  592. X# Run vol to create volume header files.
  593. X# Run tar with the volume header files.
  594. X#
  595. X
  596. XBEEP=""
  597. X
  598. XTAPECMD="tar cvbf 126 /dev/rct"
  599. XDISKCMD="tar cv"
  600. X
  601. XTAR_CMD=$DISKCMD
  602. XOPTIONS="-d"
  603. XPROMPT="floppy diskette"
  604. X
  605. XFILES=
  606. X
  607. Xif test $# -eq 0
  608. Xthen echo "Usage: voltar [ -dt ] pathnames..."; exit
  609. Xfi
  610. X
  611. Xfor i
  612. Xdo
  613. X    case $i in
  614. X
  615. X        -d)    TAR_CMD=$DISKCMD
  616. X            OPTIONS="-d"
  617. X            PROMPT="floppy diskette"
  618. X            ;;
  619. X
  620. X        -t)    TAR_CMD=$TAPECMD
  621. X            OPTIONS="-t"
  622. X            PROMPT="magnetic tape"
  623. X            ;;
  624. X
  625. X        *)    FILES="$FILES $i"
  626. X            ;;
  627. X    esac
  628. Xdone
  629. X
  630. Xrm -f Vol_*
  631. Xvol $OPTIONS $FILES
  632. X
  633. Xfor i in Vol_*
  634. Xdo
  635. X    echo $BEEP
  636. X    echo -n "$i: Insert $PROMPT, then type <return>: "
  637. X    read line
  638. X    $TAR_CMD `cat $i`
  639. Xdone
  640. SHAR_EOF
  641. exit 0
  642. /* End of text from mirror:mod.sources */
  643.