Newsgroups: vmsnet.misc
Subject: Re: Rookie Tape Question (was Re: Info-VAX: "Basic" Common Questions)
From: jeh@cmkrnl.com
Date: 27 Sep 92 16:22:07 PDT
Followup-To: vmsnet.misc
Organization: Kernel Mode Consulting, San Diego, CA

In article <1992Sep25.152650.1@ducvax.auburn.edu>,
 swanger@ducvax.auburn.edu writes:
> Ok, here is my rookie tape question.  How do you copy all of the files in a
> directory structure to an ANSI tape that preserves the subdirectory
> organization?  

The short answer is "You can't".  The ANSI tape label spec just doesn't provide
for anyplace to store the directory information -- just file names and a few
file attributes.  

> Do I need to use a command procedure?

It is possible to write a command procedure which will copy the files in a
directory tree to tape, and also write the "directory" -- a list of full file
specs (with subdirectory info) -- to the same tape.  To restore, you first read
the "directory" off the tape and then restore each file to where the directory
tells you to.  

I wrote a pair of command procs like this, way back in the days
before VMS Backup.  THey are appended after my signature.  Please note that 
they were written about ten years ago, when VMS lacked not only Backup but also
some of the niftier DCL lexical functions, IF-THEN-ELSE, and many other things,
so comments about "crude DCL programming style" will not be appreciated.:-)
There may also be assumptions in this code about 9-character file names and
3-character extensions -- I haven't checked.  

Please note that COPYing a file to tape and back to disk will not preserve all
of the VMS file attributes.  I suppose these procedures could be modified to
use Joe Meadows's FILE utility to store the file attributes of each file on 
the tape, and restore them to the target disk files... 

...Why don't you want to put a saveset on the tape, anyway?  

	--- Jamie Hanrahan, Kernel Mode Consulting, San Diego CA
uucp 'g' protocol guru and release coordinator, VMSnet (DECUS uucp) W.G., and 
Chair, VMS Programming and Internals Working Group, U.S. DECUS VAX Systems SIG 
Internet:  jeh@cmkrnl.com, hanrahan@eisner.decus.org, or jeh@crash.cts.com
Uucp:  ...{crash,eisner,uunet}!cmkrnl!jeh

$ ! TREE2TAPE.COM
$ !
$ ! Copy files from one or more directory trees to tape, preceded by
$ ! (1) TAPE2TREE.COM, a command file which will recover the files 
$ ! from the tape into their proper subdirectories and
$ ! (2) a directory file to be used by the command file.
$ ! An error log file will also be written to the end of the tape.
$ ! 
$ ! This procedure will delete any files in the current directory
$ ! with the names .CT1 or .CT2 .
$ !
$ DSPL:="WRITE SYS$OUTPUT"
$ ASK:="INQUIRE/NOPUNC"
$ !
$ ! Find out what tape drive is to be used.  
$ ! Initialize, mount, etc., as requested.
$ !
$ T:='f$logi("TAPE")'
$ IF T.EQS."" THEN $T:="_MTA0:"
$ ASK TAPE "Enter the magtape device name (<CR> for ''T') : "
$ IF TAPE.EQS."" THEN $TAPE:='T'
$ ASSIGN 'TAPE' TAPE
$ ASK NULL "Ready the tape drive and press <CR>: "
$ SET NOON
$ DISMOUNT/NOUNLOAD TAPE
$ SET ON
$ ASK YES "Do you want the tape initialized? "
$ IF YES THEN $GOTO INIT_TAPE
$ MOUNT/OVER=ID TAPE
$ GOTO OPEN_DIR
$INIT_TAPE:
$ ASK LABEL "Enter the desired volume label: "
$ ASK DENS "Specify density (<CR> for 1600): "
$ IF DENS.EQS."" THEN $DENS:="1600"
$ ASK OWNER "Specify owner UIC (<CR> for ''f$user()' (your current UIC)): "
$ IF OWNER.EQS."" THEN $OWNER:='f$user()'
$ ASK PROT "Specify protection code (<CR> for (S:RW,O:RW,G:RW,W:RW)): "
$ IF PROT.EQS."" THEN $PROT:="(S:RW,O:RW,G:RW,W:RW)"
$ INIT/DENS='DENS'/OWNER='OWNER'/PROT='PROT' TAPE 'LABEL'
$ MOUNT TAPE 'LABEL'
$ !
$ ! Open the directory file
$ !
$OPEN_DIR:
$ OPEN/READ/ERRO=GOOD_DIR DRCT .CT1	! a failure is good; says the file isn't there
$ CLOSE DRCT
$ DELETE .CT1;*
$ GOTO OPEN_DIR
$GOOD_DIR:
$ OPEN/WRITE/ERRO=FGLITCH DRCT .CT1	! open it for writing
$ DSPEC:='f$logi("SYS$DISK")''f$dire()'.CT1;1
$ !
$ ! Open the error log file
$ !
$OPEN_ERR:
$ OPEN/READ/ERRO=GOOD_ERR ERLG .CT2	! again, a 'failure' is good
$ CLOSE ERLG
$ DELETE .CT2;*
$ GOTO OPEN_ERR
$GOOD_ERR:
$ OPEN/WRITE/ERRO=FGLITCH ERLG .CT2
$ ESPEC:='f$logi("SYS$DISK")''f$dire()'.CT2;1
$ !
$ ! Now find out what files to do
$ !
$BUILD_DIR_FILE:
$ DSPL ""
$ ASK SRCDIR "Copy files to tape from what directory? (end with <CR>) "
$ IF SRCDIR.EQS."" THEN $GOTO END_DIR_FILE
$ ON WARNING THEN $GOTO BAD_READ
$ DIRECTORY 'SRCDIR'/NOHEAD/NOTRAIL/OUT=DRCT
$ GOTO BUILD_DIR_FILE
$BAD_READ:
$ DSPL "Error in reading directory ''SRCDIR'"
$ GOTO BUILD_DIR_FILE
$END_DIR_FILE:
$ CLOSE DRCT
$ !
$ ! Copy the recover command procedure and the directory file to tape
$ !
$ COPY COM:TAPE2TREE.COM TAPE:
$ COPY .CT1 TAPE:DISKDIREC.LIS;1
$ !
$ ! Open the directory file for reading
$ !
$ OPEN/READ/ERROR=FILE_GLITCH DRCT .CT1
$ !
$ ! Read the directory file, and copy the indicated files to tape.
$ !
$READ_FILE_LIST:
$ READ/END=END_LIST DRCT FSPEC
$ LEN='f$leng(FSPEC)'			! skip over "No files found" messages
$ IF 'f$loca("No files found.",FSPEC)'.NE.LEN THEN $GOTO READ_FILE_LIST
$ FNAME_OFF='f$loca("]",FSPEC)'+1	! find the file name
$ FNAME_LEN='f$loca(" ",FSPEC)'-FNAME_OFF	! and its length
$ FNAME:='f$extr(FNAME_OFF,FNAME_LEN,FSPEC)'	! extract it
$ IF 'f$loca(DSPEC,FSPEC)'.NE.LEN THEN $GOTO READ_FILE_LIST	! skip known
$ IF 'f$loca(ESPEC,FSPEC)'.NE.LEN THEN $GOTO READ_FILE_LIST	! files
$ ON WARNING THEN $GOTO BAD_COPY	! arm error handler
$ COPY 'FSPEC' TAPE:'FNAME'		! copy file to tape
$ GOTO READ_FILE_LIST			! go get another file
$
$BAD_COPY:				! report errors
$ WRITE ERLG FSPEC
$ GOTO READ_FILE_LIST
$
$END_LIST:				! we're finished
$ CLOSE DRCT				! close
$ CLOSE ERLG				!   work files
$ COPY .CT2 TAPE:.CT2;1			! copy the error log to the tape
$ OPT:="/NOUNLOAD"
$ ASK YES "Do you want the tape unloaded? "
$ IF YES THEN $OPT:=""
$ DISM'OPT' TAPE			! rewind the tape
$ DELETE .CT1;*,.CT2;*			! delete work files
$ EXIT					! exit
$
$FILE_GLITCH:				! unexpected file error
$ DSPL "Unexpected file error"
$ CLOSE ERLG
$ CLOSE DRCT

$ ! TAPE2TREE.COM
$ !
$ ! Restores directory trees that were written to tape via TREE2TAPE.COM .
$ ! 
$ ! This procedure will delete any files in the current directory
$ ! with the names T2T.CT1 or T2T.CT2 .
$ !
$ DSPL:="WRITE SYS$OUTPUT"
$ ASK:="INQUIRE/NOPUNC"
$ !
$ ! Find out what tape drive is to be used.  
$ !
$ T:='f$logi("TAPE")'
$ IF T.EQS."" THEN $T:="_MTA0:"
$ ASK TAPE "Enter the magtape device name (<CR> for ''T') : "
$ IF TAPE.EQS."" THEN $TAPE:='T'
$ ASSIGN 'TAPE' TAPE
$ ASK NULL "Ready the tape drive and press <CR>: "
$ SET NOON
$ DISMOUNT/NOUNLOAD TAPE
$ SET ON
$ MOUNT/OVER=ID TAPE
$ !
$ ! Get a directory listing from the tape and put it in a file
$ !
$ DELETE T2T.CT1;*			! get rid of old versions
$ DIRE TAPE/NOHEAD/NOTRAIL/OUT=T2T.CT1
$ !
$ ! Copy the disk directory file from the tape.  
$ ! This should be the second file on the tape, and should
$ ! contain the complete filespecs of all files that were
$ ! supposed to be copied to it.
$ !
$ DELETE T2T.CT2;*			! get rid of old versions
$ COPY TAPE:DISKDIREC.LIS T2T.CT2
$ !
$ ! Open the files
$ !
$ OPEN/READ/ERRO=FILE_GLITCH TDIR T2T.CT1
$ OPEN/READ/ERRO=FILE_GLITCH DDIR T2T.CT2
$ !
$ ! Skip over the first two files in the tape directory
$ !
$ READ TDIR NULL 
$ READ TDIR NULL
$ !
$ ! Initialize the loop
$ !
$ READ/END=TDIR_END TDIR TFILE		! read the tape directory file
$ OLD_SRC_DVC:=""			! set previous device and
$ OLD_SRC_UFD:=""			!   directory names
$ OLD_DST_DVC_DIR:=""			!     to nulls
$ !
$ ! Loop head
$ !
$LOOP:
$ !
$ ! Read a filespec from the disk directory, extract important parts
$ !
$ READ/END=DDIR_END DDIR DFILE
$ LB='f$locate("[",DFILE)'
$ SRC_DVC:='f$extr(0,LB,DFILE)'		! device name of source file
$ UF=LB+1
$ RB='f$locate("]",DFILE)'
$ PR='f$locate(".",DFILE)'
$ DS=RB
$ IF PR.LT.RB THEN $DS=PR
$ DS_UF=DS-UF
$ SRC_UFD:='f$extr(UF,DS_UF,DFILE)'	! ufd of source file
$ RB_DS=RB-DS
$ SRC_SFD:='f$extr(DS,RB_DS,DFILE)'	! sfd of source file
$ FNTV=RB+1
$ SRC_FNTV:='f$extr(FNTV,99,DFILE)'	! source file name, type, and ver
$ PR='f$loca(".",SRC_FNTV)'		! source file name
$ SRC_FNM:='f$extr(0,PR,SRC_FNTV)'
$ !
$ ! Has the source device or ufd changed?  If so, give the user a 
$ ! chance to change the destination device or ufd.
$ !
$ IF (SRC_DVC.EQS.OLD_SRC_DVC).AND.(SRC_UFD.EQS.OLD_SRC_UFD) THEN -
	$ GOTO BUILD_DST_DVC_DIR
$  OLD_SRC_DVC:='SRC_DVC'
$  OLD_SRC_UFD:='SRC_UFD'
$  DSPL ""
$  DSPL "The next files on the tape came from ''SRC_DVC'[''SRC_UFD'...] ."
$  DSPL "What is the desired destination disk?  (Enter <CR> for"
$  CDD:='f$logi("SYS$DISK")'
$  ASK DST_DVC "''CDD', your current default device): "
$  IF DST_DVC.EQS."" THEN $DST_DVC:='CDD'
$  DSPL "What is the desired destination UFD?  (Enter <CR> for"
$  CDD:='f$dire()'
$  ASK DST_UFD "''CDD', your current default directory): "
$  IF DST_UFD.EQS."" THEN $DST_UFD:='CDD'
$  ! strip off brackets if they were included in the input
$  IF "''f$extr(0,1,DST_UFD)'".EQS."[" THEN $DST_UFD:='f$extr(1,99,DST_UFD)'
$  L='f$leng(DST_UFD)'
$  L_1=L-1
$  IF 'f$loca("]",DST_UFD)'.NE.L THEN $DST_UFD:='f$extr(0,L_1,DST_UFD)'
$ !
$ ! Build destination device/directory specification.  
$ ! If it's changed, create the directory.
$ !
$BUILD_DST_DVC_DIR:
$ DST_DVC_DIR:='DST_DVC'['DST_UFD''SRC_SFD']
$ IF OLD_DST_DVC_DIR.EQS.DST_DVC_DIR THEN $GOTO CHECK_TAPE
$ CREATE/DIRE 'DST_DVC_DIR'
$ OLD_DST_DVC_DIR:='DST_DVC_DIR'
$ !
$ ! Look at the current record in the tape directory and see if we can
$ ! expect to find the file on the tape.
$ !
$CHECK_TAPE:
$ CL='f$loca(":",TFILE)'+1
$ TAP_FNTV:='f$extr(CL,99,TFILE)'
$ IF TAP_FNTV.EQS.SRC_FNTV THEN $ GOTO CHECK_DIREC
$ !
$ ! It's not on the tape for some reason.  
$ ! Report the error and return to loop head.
$ !
$ IF 'f$loca(".CT1;1",DFILE)'.NE.'f$leng(DFILE)' THEN $GOTO LOOP
$ IF 'f$loca(".CT2;1",DFILE)'.NE.'f$leng(DFILE)' THEN $GOTO LOOP
$ DSPL "File ''DFILE' not on tape"
$ GOTO LOOP
$ !
$ ! Now check to see if it's a directory file.  If it is, skip the
$ ! file; otherwise, copy the file.
$ !
$CHECK_DIREC:
$ IF 'f$loca(".DIR;",DFILE)'.EQ.'f$leng(DFILE)' THEN $GOTO COPY_FILE
$ COPY TAPE:'TAP_FNTV' NL:		! skip over the file on the tape
$ READ/END=TDIR_END TDIR TFILE		! read the tape directory file
$ GOTO LOOP
$ !
$ ! Copy the file from the tape to the disk.
$ !
$COPY_FILE:
$ COPY TAPE:'TAP_FNTV' 'DST_DVC_DIR''SRC_FNTV'
$ READ/END=TDIR_END TDIR TFILE		! read the tape directory file
$ GOTO LOOP
$ !
$ ! This should never happen.
$ !
$TDIR_END:
$ DSPL "UNEXPECTED ERROR:  End of tape directory encountered"
$ DSPL "before end of disk directory."
$ CLOSE DDIR
$ CLOSE TDIR
$ EXIT
$ !
$ ! This is where we're supposed to come when finished.
$ !
$DDIR_END:
$ CLOSE DDIR
$ CLOSE TDIR
$ IF 'f$loca(".CT2;",TFILE)'.NE.'f$leng(TFILE)' THEN $GOTO DSPLY_ERR_LOG
$ DSPL "UNEXPECTED ERROR:  Error log file .CT2;1 wasn't on the tape"
$ EXIT
$DSPLY_ERR_LOG:
$ DELETE T2T.CT%;*
$ DELETE T2T.ERR;*
$ COPY TAPE:.CT2;1 T2T.ERR;1
$ DSPL "The following error messages were generated when the tape was written:"
$ TYPE T2T.ERR
$ ASK YES "Do you want to delete the error log file (T2T.ERR) ? "
$ IF YES THEN $DELETE T2T.ERR;*
$ ASK YES "Do you want the tape unloaded? "
$ OPT:="/NOUNLOAD"
$ IF YES THEN $OPT:=""
$ DISMOUNT'OPT' TAPE
$ EXIT
$FILE_GLITCH:
$ DSPL "Unexpected file error"
$ CLOSE DDIR
$ CLOSE TDIR