/* VMSTPC Fast Tape Copy program VMS V4, native mode. VMSTPC uses multiple ast driven QIO's to get the tape drive streaming during copy operations. A sample copy of an ANSI D tape blocked at 8192 with serveral hundred files (a Columbia U Kermit tape) took 21 CPU seconds with the TU80 streaming about 95% of the time (done on an 11/785). 22-MAY-1986 09:15 Brian Nelson ( BRIAN@UOFT02.BITNET ) Files: VMSTPC.C VMSTPC.COM VMSTPC.CLD File TC.CLD define verb tc image "sys$sysroot:[brian.c]vmstpc" parameter P1,label=inputarg,prompt="From",value(REQUIRED) parameter P2,label=outputarg,prompt="To",value(REQUIRED) qualifier ANSI qualifier APPEND qualifier BACKUP qualifier REWIND qualifier RT11 qualifier DOS11 qualifier VERIFY qualifier DENSITY value(REQUIRED,TYPE=$NUMBER) qualifier ALLOCATION value(REQUIRED,TYPE=$NUMBER) qualifier EXTENDSIZE value(REQUIRED,TYPE=$NUMBER) qualifier BLOCKSIZE value(REQUIRED,TYPE=$NUMBER) qualifier BUFFERS value(REQUIRED,TYPE=$NUMBER) qualifier DIRECTORY, syntax=DIRECTORY disallow DENSITY and NEG REWIND disallow VERIFY and NEG REWIND disallow ANSI and DOS11 disallow BACKUP and DOS11 qualifier JUNK,label=outputarg define syntax DIRECTORY image "sys$sysroot:[brian.c]vmstpc" parameter P1,label=inputarg,prompt="From",value(REQUIRED) The qualifier /DENSITY=nnnn MAY not work. I can't test it. Check the function SET_DENS(LUN,DENSITY) Usage: $ set command vmstpc ! Define the TC command $ mou msa0:/for ! The drive must be mounted $ tc msa0: tape.con ! Copy the tape to TAPE.CON $ tc tape.con msa0: ! Copy TAPE.CON to a new tape $ tc/ansi msa0: ansi.con ! Allow NULL length ANSI files. $ tc/ver msa0: tape.con ! Copy from tape and verify it. $ tc/dir container.file ! Get directory of tape image $ tc/buf=30/blo=512 msa0: t.t ! Optimize for a DOS format tape $ tc/dos msa0: t.t ! Optimize for a DOS format tape The /ANSI qualifier is used to allow VMSTPC to avoid stopping when it finds a NULL length file on an ANSI tape. A null file is simply TWO eof marks following the last HDR label. Since VMSTPC normally thinks that two EOF marks in a row signify the end of the tape, this qualifier enables special checking for corresponding HDR2/EOF2 marks before deciding about EOT. The /VERIFY qualifier will force VMSTPC to rewind the tape and verify that was was read from (or written to) the tape is identical to the copy in the disk container file. The /VERIFY option is SLOW; no attempt is made during the verification pass to optimize throughput. The /APPEND qualifier is a bit unusual; if you do NOT speficy any other qualifiers it will simply use the MTAACP IO$_SKIPFILE call to find the end of the tape. Additionally, if the tape happends to be ANSI, the files appended may not show up later because the SEQUENCE fields of all the files will be incorrect. This is not a problem for VMSBACKUP, which bypassed RMS, but COPY and DIRECTORY will fail. Thus, for ANSI tapes, either the /ANSI or /BACKUP qualifier should be used with /APPEND. This will cause VMSTPC to look for HDR1 and EOF1 records and modify the four character SEQUENCE field. As you can imagine, this is a bit risky. Also note that the /BACKUP qualifier also uses the IO$_SKIPFILE call, whereas the /ANSI qualifier reads records until it gets a correct END of TAPE; ie it keeps track of HDR2 and EOF2 counts so it can detect a NULL length ANSI file, which is a file composed of HDRn records, followed by two tape marks and the EOFn records followed by a tape mark. The /[NO]REWIND qualifier is dangerous when used with /APPEND on ANSI tapes, as the file sequence fields (described above) will be incorrect. edits: 12-JUN-1986 12:54 BDN Add /DIR, fix status checking for disk file open and creates. 25-JUN-1986 11:52 BDN Add /BUFFERS=nnn/BLOCKSIZE=nnn to optimize when reading/writing tapes with small blocks, like RT tapes. Also, /RT11 implies /BUF=30/BLOCK=512 if those qualifiers were not present. 07-JUL-1986 11:55 BDN /APPEND and /BACKUP 17-JUL-1986 13:50 BDN Wait for event flag after start_tape_dump to fix getting the VOL1 out of sequence on 8600's (ie, faster CPU's 11-NOV-1986 11:13 BDN Fix (hopefully) event flag wait bugs that show up on the faster cpus like 86xx's. 02-DEC-1986 09:30 BDN Really fix it this time. 15-OCT-1987 12:56 BDN Raise default buffer count */ #include #include #include #include #include #include #include #include #include #define then #define RMS$_EOF 0x1827a union pointer { int *intbuf ; char *charbuf ; } ; struct itmlst { unsigned short int bufferlength ; unsigned short int item_code ; union pointer addr ; int *retlength ; } ; struct dsc { int len ; char *addr ; } ; struct devdsc { int dev_class ; int dev_type ; int dev_lun ; int dev_char ; char dev_name[NAM$C_DVI] ; char dev_spec[128] ; } in_dev, out_dev ; struct FAB parse_fab ; struct NAM parse_nam ; #define ANSI_NULLFILE 1 #define EOF_MARK 0 #define ANSI_HDR_SIZE 80 #define DOS_HDR_SIZE 14 #define BUFFER_SIZE 32768 #define NBUFFERS 12 #define RT_BUFFER_SIZE 512 #define RT_NBUFFERS 30 #define DOS_BUFFER_SIZE 512 #define DOS_NBUFFERS 30 #define MAX_NBUFFERS RT_NBUFFERS char *buffer_list[MAX_NBUFFERS+1] ; char *altbuf_list[MAX_NBUFFERS+1] ; short int iosblist[MAX_NBUFFERS+1][4] ; int eventf[MAX_NBUFFERS+1], wakeup_ef ; int eov, eof_count ,n_files_save, n_files_skipped, rec_count ; /* Internal tasking dispatch table */ struct dispatch { int state ; char *bufaddr ; char *altaddr ; int (*readproc) () ; int (*writeproc) () ; int iodone ; int iopending ; int efn ; int param ; int endoflist ; } proc_header[MAX_NBUFFERS+1] ; char vol_id[7] ; char *getmsg(), *getcpu() ; int tape_dump() , read_qio_ast() ,tape_write() ; extern char *strcpy() , *strcat() , *malloc() ; #define DEF_ANSI 0 #define DEF_RT11 0 #define DEF_DOS11 0 #define DEF_DENSITY 1600 #define DEF_ALLOCATION 2000 #define DEF_EXTENDSIZE 250 #define DEF_VERIFY 0 #define DEF_DIR 0 #define DEF_BUFFERS NBUFFERS #define DEF_BLOCKSIZE BUFFER_SIZE #define DEF_APPEND 0 /* This default should ALWAYS be 0 */ #define DEF_BACKUP 0 /* This default should ALWAYS be 0 */ #define DEF_REWIND 1 /* This default should ALWAYS be 1 */ int qual_ansi = DEF_ANSI ; int qual_rt11 = DEF_RT11 ; int qual_density = DEF_DENSITY ; int qual_allocation = DEF_ALLOCATION ; int qual_extendsize = DEF_EXTENDSIZE ; int qual_verify = DEF_VERIFY ; int qual_dir = DEF_DIR ; int qual_buffers = NBUFFERS ; int qual_blocksize = BUFFER_SIZE ; int qual_dos11 = DEF_DOS11 ; int qual_append = DEF_APPEND ; int qual_backup = DEF_BACKUP ; int qual_rewind = DEF_REWIND ; #define SET_ANSI 1 #define SET_RT11 2 #define SET_DENSITY 4 #define SET_ALLOCATION 8 #define SET_EXTENDSIZE 16 #define SET_VERIFY 32 #define SET_DOS11 64 #define SET_DIR 128 #define SET_BUFFERS 256 #define SET_BLOCKSIZE 512 #define SET_APPEND 1024 #define SET_BACKUP 2048 #define SET_REWIND 4096 int set_flags = 0 ; int hdr_count = 0 ; main() { $DESCRIPTOR(inputarg, "INPUTARG") ; $DESCRIPTOR(outputarg, "OUTPUTARG") ; $DESCRIPTOR(density, "DENSITY") ; $DESCRIPTOR(allocation,"ALLOCATION") ; $DESCRIPTOR(extendsize,"EXTENDSIZE") ; $DESCRIPTOR(ansitape, "ANSI") ; $DESCRIPTOR(rt11tape, "RT11") ; $DESCRIPTOR(verify, "VERIFY") ; $DESCRIPTOR(directory, "DIRECTORY") ; $DESCRIPTOR(outputfile,"OUTPUT") ; $DESCRIPTOR(buffers, "BUFFERS") ; $DESCRIPTOR(blocksize, "BLOCKSIZE") ; $DESCRIPTOR(dos11tape, "DOS11") ; $DESCRIPTOR(append, "APPEND") ; $DESCRIPTOR(backup, "BACKUP") ; $DESCRIPTOR(rewind, "REWIND") ; int retlength , status ,temp ; char inarg[128], outarg[128] ; getparam(inarg,&inputarg) ; getparam(outarg,&outputarg) ; qual_ansi = setqual(&ansitape,DEF_ANSI) ; qual_dos11= setqual(&dos11tape, DEF_DOS11 ) ; qual_rt11 = setqual(&rt11tape,DEF_RT11) ; qual_append = setqual(&append,DEF_APPEND) ; qual_backup = setqual(&append,DEF_BACKUP) ; qual_verify = setqual(&verify,DEF_VERIFY) ; qual_dir = setqual(&directory,DEF_DIR) ; qual_rewind = setqual(&rewind,DEF_REWIND) ; if ( status = getqual_value(&allocation) ) { qual_allocation = status ; set_flags |= SET_ALLOCATION ; } ; if ( status = getqual_value(&extendsize) ) { qual_extendsize = status ; set_flags |= SET_EXTENDSIZE ; } ; if ( status = getqual_value(&density) ) { qual_density = status ; set_flags |= SET_DENSITY ; } ; if ( status = getqual_value(&buffers) ) { if ( status >= 1 && status <= MAX_NBUFFERS ) { qual_buffers = status ; set_flags |= SET_BUFFERS ; } else printf("/BUFFERS out of range 1 to %d, qualifier ignored\n", MAX_NBUFFERS) ; } ; if ( status = getqual_value(&blocksize) ) { if ( status >= 512 && status <= BUFFER_SIZE ) { qual_blocksize = status ; set_flags |= SET_BLOCKSIZE ; } else printf("/BLOCK out of range 512 to %d, qualifier ignored\n", BUFFER_SIZE) ; } ; if ( qual_rt11 ) if ( ( set_flags & SET_BUFFERS ) == 0 && ( set_flags & SET_BLOCKSIZE ) == 0 ) { printf("RT11 buffer count raised to %d\n",RT_NBUFFERS) ; qual_buffers = RT_NBUFFERS ; qual_blocksize = RT_BUFFER_SIZE ; } ; if ( qual_dos11 ) if ( ( set_flags & SET_BUFFERS ) == 0 && ( set_flags & SET_BLOCKSIZE ) == 0 ) { printf("DOS11 buffer count raised to %d\n",DOS_NBUFFERS) ; qual_buffers = DOS_NBUFFERS ; qual_blocksize = DOS_BUFFER_SIZE ; } ; if ( init() == 0 ) exit() ; switch ( qual_density ) { case 800: case 1600: case 6250: break ; default: printf("Unknown density %d\n",qual_density) ; exit() ; break ; } ; if ( qual_rewind == 0 && qual_append && (qual_ansi || qual_backup)) printf("Ansi HDR1 and EOF1 labels may not be accessable\n\n") ; status = process(inarg,outarg) ; exit(status) ; } process(in,out) char *in,*out ; { int in_chan,out_chan,status ; if ( qual_dir ) { if ( *in == 0 ) return( SS$_INSFARG ) ; if ( ((status=parse(&in_dev,in)) & 1) == 0 ) return(status) ; } else { if ( *in == 0 || *out == 0 ) return( SS$_INSFARG ) ; if ( ((status=parse(&in_dev,in)) & 1) == 0 || ((status=parse(&out_dev,out)) & 1) == 0 ) then return(status) ; } ; eov = 0 ; eof_count = 0 ; switch ( in_dev.dev_class ) { /* Case */ case DC$_TAPE: if ( qual_dir ) { printf("The /DIR qualifier is only for containers\n"); return(0) ; } ; if ( out_dev.dev_class == DC$_TAPE && strcmp(out_dev.dev_name,in_dev.dev_name) != 0 ) then status = tape_to_tape() ; else if ( out_dev.dev_class == DC$_DISK ) then status = tape_to_disk() ; else status = SS$_IVDEVNAM ; break ; case DC$_DISK: if ( qual_dir ) then return(container_dir(in_dev.dev_spec)); if ( out_dev.dev_class == DC$_TAPE ) then status = disk_to_tape() ; else status = SS$_IVDEVNAM ; break ; default: status = SS$_IVDEVNAM ; break ; } ; /* end Case */ sys$dassgn( in_dev.dev_lun ) ; sys$dassgn(out_dev.dev_lun ) ; return(status) ; } container_dir(f) char *f ; { int block_count = 0,size,status, total_block = 0 ; char *cp, dosname[20] , *r50toa() ; int found_dos = 0 , found_ansi = 0 ; if ( (( status = open_disk(f) ) & 1 ) == 0 ) return( status ) ; if ( ( cp = malloc( qual_blocksize ) ) == 0 ) return( 0 ) ; if ( (( status = read_disk(cp,&size) ) & 1 ) == 0 ) return( status ) ; if ( (found_dos = qual_dos11) == 0 && (found_ansi = qual_ansi) == 0 ) switch( size ) { case ANSI_HDR_SIZE: if ( strncmp(cp,"VOL1",4) == 0 ) { printf("Container set appears to be ANSI labeled\n") ; found_ansi++ ; } ; break ; case DOS_HDR_SIZE: printf("Container set appears to be DOS-11 labeled\n"); found_dos++ ; break ; default: printf("Container does not seem to be a know format\n"); return(0) ; break ; } ; printf("\n") ; block_count = -1 ; while ( status & 1 ) { switch ( size ) { case ANSI_HDR_SIZE: eof_count = 0 ; if ( found_ansi && strncmp(cp,"HDR1",4) == 0 ) { if ( block_count != -1 ) printf(" %d\n",block_count); block_count = 0 ; *(cp+21) = 0 ; printf("%s ",cp+4) ; } ; break ; case DOS_HDR_SIZE: eof_count = 0 ; if ( block_count != -1 ) printf(" %d\n",block_count); r50toa(&dosname[0],cp) ; r50toa(&dosname[3],cp+2) ; dosname[6] = '.' ; r50toa(&dosname[7],cp+4) ; printf("%s ",dosname) ; block_count = 0 ; break ; case EOF_MARK: if ( ++eof_count > 1 ) { printf(" %d\n",block_count); total_block += block_count ; } ; break ; case ANSI_NULLFILE: eof_count = 0 ; break ; default: eof_count = 0 ; block_count++ ; break ; } ; status = read_disk(cp,&size) ; } ; close_disk() ; if ( status == RMS$_EOF ) return(1) ; else return( status ) ; } /* VERIFY( tape_lun ) VERIFY makes absolutly NO attempt to optimize transfer rates, as it will tend to be cpu bound anyway comparing data, as well as infrequently used. It is called with the tape channel number passed; all other needed information is global already. It can be called from Tape_to_Disk or Disk_to_Tape. */ verify(tape_lun,disk_file) int tape_lun; char *disk_file ; { char *cp, *tp ; short int iosb[4] ; int i,r_num,size,status,waiting ; if ( qual_verify == 0 ) return(1) ; else { r_num = 0 ; printf("Starting verification pass\n\n") ; close_disk() ; if (((status=open_disk(disk_file)) & 1) == 0) return(status); sys$qiow(0,tape_lun,IO$_REWIND,0,0,0,0,0,0,0,0,0) ; cp = malloc(qual_blocksize) ; tp = malloc(qual_blocksize) ; status = read_disk(cp,&size) ; while ( status & 1 ) { status = sys$qiow(0,tape_lun,IO$_READLBLK,&iosb,0,0, tp,qual_blocksize,0,0,0,0) ; if ( ( status & 1 ) == 0 ) break ; switch (iosb[0]) { case SS$_ENDOFFILE: if ( size > ANSI_NULLFILE ) printf("End of file mark mismatch\n"); break ; case SS$_NORMAL: r_num++ ; if ( size != iosb[1] ) { printf("Block size mismatch #%6d, ",r_num) ; printf("Expected: %5d, Got: %5d\n", size,iosb[1]) ; } else if ( strncmp(cp,tp,size) != 0 ) printf("Data compare error\n") ; break ; default: printmsg(iosb[0]) ; break ; } ; if ( status & 1 ) status = read_disk(cp,&size) ; } ; if ( status == RMS$_EOF ) status = 1 ; } ; sys$qiow(0,tape_lun,IO$_REWIND+IO$M_NOWAIT,0,0,0,0,0,0,0,0,0) ; close_disk() ; return(status) ; } tape_to_tape() { printf("Tape to Tape called %s %s\n",in_dev.dev_name,out_dev.dev_name); inistats() ; return(1) ; } /* This is the real work of Tape_to_Disk It functions by setting up a dispatch table for processing to be done AFTER I/O completetion. Ie, the AST completion routine simply 'schedules' a 'task' to be run which will process the result of the tape read. Thus the copy operation is done basically done via internal multitasking. When the ast completion routine is entered it simply takes the ast parameter and uses that to index into the process list to make a process eligible for execution. It then sets an event flag to get the scheduler to wake up and scan the process table for someone runnable. In the interests of generality, the address of the process to call is placed into the process table by INIT(), though in reality we always call the same routine and pass it the process number, which thus specifies the buffer, IOSB, and so on that it should access. */ tape_to_disk() { short int iosb[4] ; int current,i,status ; if (((status=create_disk(out_dev.dev_spec)) & 1) == 0) return(status); sys$qiow(0,in_dev.dev_lun,IO$_REWIND,0,0,0,0,0,0,0,0,0) ; inistats() ; printf("Tape dump starting to %s\n",out_dev.dev_spec) ; current = 0 ; rec_count = 0 ; eof_count = 0 ; eov = 0 ; sys$clref( wakeup_ef ) ; if ( ((status=start_tape_dump()) & 1) == 0 ) return( status ) ; sys$waitfr( wakeup_ef ) ; while ( !eov && ( status & 1 ) ) { while ( proc_header[current].state == 0 ) { sys$clref( wakeup_ef ) ; sys$setast(1) ; sys$waitfr(wakeup_ef ) ; } ; status = (*proc_header[current].readproc) (current) ; current = ++current % qual_buffers ; sys$clref( wakeup_ef ) ; sys$setast(1) ; } ; printstats(rec_count) ; if ( (status & 1 ) == 0 ) printmsg(status) ; if ( status & 1 ) status = verify(in_dev.dev_lun,out_dev.dev_spec) ; sys$qiow(0,in_dev.dev_lun,IO$_REWIND+IO$M_NOWAIT,0,0,0,0,0,0,0,0,0) ; close_disk() ; return(1) ; } tape_dump(procnum) { int size , status ; char null_buffer[] = "" ; char *cp ; proc_header[procnum].state = 0 ; size = iosblist[procnum][1] ; switch ( iosblist[procnum][0] ) { case SS$_ABORT: case SS$_CANCEL: status = 1 ; break ; case SS$_ENDOFFILE: status = 1 ; if (hdr_count == 0) eov = ( ++eof_count >= 2 ) ; if ( !eov ) status=sys$qio(eventf[procnum],in_dev.dev_lun,IO$_READLBLK, &iosblist[procnum],&read_qio_ast,procnum+1, proc_header[procnum].bufaddr,qual_blocksize, 0,0,0,0) ; size = ( hdr_count ) ? ANSI_NULLFILE:EOF_MARK ; if ( status & 1 ) status=write_disk(&null_buffer,size) ; break ; case SS$_ENDOFTAPE: status = 1 ; eov = 1 ; break ; case SS$_NORMAL: eof_count = 0 ; rec_count++ ; cp = proc_header[procnum].bufaddr ; proc_header[procnum].bufaddr = proc_header[procnum].altaddr ; proc_header[procnum].altaddr = cp ; status = sys$qio(eventf[procnum],in_dev.dev_lun,IO$_READLBLK, &iosblist[procnum],&read_qio_ast,procnum+1, proc_header[procnum].bufaddr,qual_blocksize, 0,0,0,0) ; if ( status & 1 ) status = write_disk(cp,size) ; if ( qual_rt11 || (qual_ansi && size == ANSI_HDR_SIZE )) then { if ( strncmp(cp,"HDR2",4) == 0 ) then hdr_count++ ; else if ( strncmp(cp,"EOF2",4) == 0 && hdr_count > 0 ) then hdr_count-- ; else if ( strncmp(cp,"EOV",3) == 0 ) eov = 1 ; } ; break ; default: status = iosblist[procnum][0] ; eov = 1 ; break ; } ; if ( eov ) { sys$setast(0) ; sys$cancel( in_dev.dev_lun ) ; } ; return( status ) ; } start_tape_dump() { int nqio , status ; for ( nqio = 0; nqio < qual_buffers; nqio++ ) { status = sys$qio(eventf[nqio],in_dev.dev_lun,IO$_READLBLK, &iosblist[nqio],&read_qio_ast,nqio+1, proc_header[nqio].bufaddr,qual_blocksize, 0,0,0,0) ; if ( ( status & 1 ) == 0 ) break ; } ; return( status ) ; } /* AST Completion, used for both tape reads and tape writes. Enter with the QIO number (+1) that completed. We disable AST delivery, mark the task table STATE entry to flag that we have something to process, and then set the event flag to wake up the copy routine. The copy routine then clears the event flag and enables further ast delivery. */ read_qio_ast(param) int param ; { sys$setast(0) ; proc_header[param-1].iopending = 0 ; proc_header[param-1].state = 1 ; sys$setef(wakeup_ef) ; } disk_to_tape() { char *cp, *tp ; short int iosb[4] ; int i,size,save_skipped,status,waiting ; if (((status=open_disk(in_dev.dev_spec)) & 1) == 0) return(status); if ( qual_append == 0 || qual_rewind ) sys$qiow(0,out_dev.dev_lun,IO$_REWIND,0,0,0,0,0,0,0,0,0) ; if (set_flags & SET_DENSITY) set_dens(out_dev.dev_lun,qual_density) ; n_files_skipped = 0 ; if ( qual_append && qual_rewind ) if ( (n_files_skipped = position_eot(out_dev.dev_lun)) == 0 ) { printf("?Failure to position tape to logical EOT\n"); return(0) ; } ; save_skipped = n_files_skipped ; rec_count = 0 ; eof_count = 0 ; eov = 0 ; printf("Tape dump starting to %s\n",out_dev.dev_spec) ; inistats() ; if ( ((status=start_tape_write()) & 1) == 0 ) return( status ) ; while ( !eov && ( status & 1 ) ) { for ( i=0; i < qual_buffers; i++ ) if ( proc_header[i].state ) status = (*proc_header[i].writeproc) (i) ; if ( !eov && ( status & 1 ) ) { sys$waitfr(wakeup_ef) ; sys$clref( wakeup_ef) ; sys$setast(1) ; } ; } ; while (1) { waiting = 0 ; for ( i=0; i < qual_buffers; i++ ) waiting = waiting | proc_header[i].iopending ; if ( waiting == 0 ) break ; sys$clref( wakeup_ef) ; sys$setast(1) ; sys$waitfr(wakeup_ef) ; } ; if ( save_skipped ) printf("%d HDR1 and EOF1 label record SEQUENCE fields modified\n", n_files_skipped-save_skipped) ; printstats(rec_count) ; if ( (status & 1 ) == 0 ) printmsg(status) ; sys$qio(0,out_dev.dev_lun,IO$_WRITEOF,0,0,0,0,0,0,0,0,0) ; if ( qual_rewind == 0 ) sys$qiow(0,out_dev.dev_lun,IO$_SKIPFILE,&iosb,0,0,-2,0,0,0,0,0) ; else { if ( status & 1 ) status = verify(out_dev.dev_lun,in_dev.dev_spec) ; sys$qiow(0,out_dev.dev_lun,IO$_REWIND+IO$M_NOWAIT,0,0,0,0,0,0,0,0,0) ; } ; close_disk() ; return(status) ; } #define HDR1_SEQ 32-1 tape_write(procnum) int procnum ; { int i, param , size , status ; int eof1, hdr1 ; char *cp ,seq[5] ; proc_header[procnum].state = 0 ; if ( eov ) return(1) ; if ( (status=iosblist[procnum][0]) != SS$_NORMAL ) return(status); cp = proc_header[procnum].bufaddr ; status = read_disk(cp,&size) ; if ( rec_count == 0 && qual_append ) then if ( ansi_check(cp,"VOL1",size) ) status = read_disk(cp,&size) ; if ( status & 1 ) switch ( size ) { case EOF_MARK: eov = ( ++eof_count >= 2 ) ; status = sys$qio(eventf[procnum],out_dev.dev_lun,IO$_WRITEOF, &iosblist[procnum],&read_qio_ast, procnum+1,0,0,0,0,0,0) ; break ; case ANSI_NULLFILE: eof_count = 0 ; status = sys$qio(eventf[procnum],out_dev.dev_lun,IO$_WRITEOF, &iosblist[procnum],&read_qio_ast, procnum+1,0,0,0,0,0,0) ; break ; default: eof1 = 0 ; hdr1 = 0 ; eof_count = 0 ; rec_count++ ; if ( qual_append && qual_rewind ) if ( (hdr1 = ansi_check(cp,"HDR1",size)) || (eof1 = ansi_check(cp,"EOF1",size)) ) { sprintf(seq,"%04d",n_files_skipped+1) ; for (i=0; i<4; i++) *(cp+HDR1_SEQ+i)=seq[i] ; if ( eof1 ) n_files_skipped++ ; } ; status = sys$qio(eventf[procnum],out_dev.dev_lun,IO$_WRITELBLK, &iosblist[procnum],&read_qio_ast, procnum+1,proc_header[procnum].bufaddr, size,0,0,0,0) ; break ; } ; if ( status & 1 ) proc_header[procnum].iopending = 1 ; return( status ) ; } start_tape_write() { int i , status ; sys$setast(0) ; for ( i = 0; i < qual_buffers; i++ ) { iosblist[i][0] = SS$_NORMAL ; status = tape_write(i) ; if ( ( status & 1 ) == 0 || eov ) break ; } ; sys$setast(1) ; return( status ) ; } ansi_check(cp,s,n) char *cp,*s ; int n ; { if ( qual_rt11 || ((qual_ansi || qual_backup) && n==ANSI_HDR_SIZE) ) return( strncmp(cp,s,strlen(s)) == 0 ) ; else return(0) ; } #define SKIPCOUNT 32766 position_eot(lun) int lun ; { short int iosb[4] ; int eov, i, n_files, status ; char *cp ; eov = 0 ; n_files = 0 ; hdr_count = 0 ; eof_count = 0 ; cp = malloc(qual_blocksize) ; vol_id[0] = 0 ; if ( qual_ansi || qual_backup ) { sys$qiow(0,lun,IO$_READLBLK,&iosb,0,0,cp,qual_blocksize, 0,0,0,0) ; if ( iosb[0] == SS$_NORMAL && iosb[1] == ANSI_HDR_SIZE ) { for (i=0; i<6; i++) vol_id[i] = *(cp+4+i) ; vol_id[6] = 0 ; } ; } ; if ( qual_ansi == 0 && qual_rt11 == 0 ) { while ( 1 ) { sys$qiow(0,lun,IO$_SKIPFILE,&iosb,0,0,SKIPCOUNT,0,0,0,0,0) ; switch ( iosb[0] ) { case SS$_NORMAL: case SS$_ENDOFFILE: n_files += iosb[1] ; break ; case SS$_ENDOFVOLUME: n_files += iosb[1] ; if ( qual_backup ) return(n_files/3) ; else return(n_files) ; break ; default: return(0) ; break ; } ; } ; } else { while ( !eov ) { sys$qiow(0,lun,IO$_READLBLK,&iosb,0,0,cp,qual_blocksize, 0,0,0,0) ; switch ( iosb[0] ) { case SS$_ENDOFFILE: if (hdr_count == 0) { eov = ( ++eof_count >= 2 ) ; n_files++ ; } ; break ; case SS$_ENDOFTAPE: eov = 1 ; break ; case SS$_NORMAL: eof_count = 0 ; if ( ansi_check(cp,"HDR2",iosb[1]) ) hdr_count++ ; else if ( ansi_check(cp,"EOF2",iosb[1]) ) hdr_count-- ; else if (ansi_check(cp,"EOV",iosb[1])) eov = 1 ; break ; default: return(0) ; break ; } ; } ; } ; sys$qiow(0,lun,IO$_SKIPFILE,&iosb,0,0,-1,0,0,0,0,0) ; return(n_files-1) ; } /* Someone else will have to test this. My TU80 is 1600 only, and my CDC 92185's density is set via the drive control panel. The code should work. */ /* Since SYS$LIBRARY:MTDEF.H does not exist, the following are taken from STARLET.MLB. */ #define MT$M_DENSITY 7936 #define MT$K_NRZI_800 3 #define MT$K_PE_1600 4 #define MT$K_GCR_6250 5 #define MT$S_DENSITY 5 #define MT$V_DENSITY 8 set_dens(lun,density) int lun ; { struct char_buffer_type { unsigned short int dummy ; unsigned short int size ; unsigned long int tchars ; } tape_chars, sense_chars ; short int iosb[4] ; int dens,field_pos,field_size,status ; status = sys$qiow(0,lun,IO$_SENSEMODE,&sense_chars,0,0,0,0,0,0,0,0) ; switch (density) { case 800: dens = MT$K_NRZI_800 ; break ; case 1600: dens = MT$K_PE_1600 ; break ; case 6250: dens = MT$K_GCR_6250 ; break ; default: return(0) ; break ; } ; field_pos = MT$V_DENSITY ; field_size = MT$S_DENSITY ; lib$insv(&dens,&field_pos,&field_size,&tape_chars.tchars) ; tape_chars.dummy = 0 ; tape_chars.size = qual_blocksize ; status = sys$qiow(0,lun,IO$_SETMODE,0,0,0,&tape_chars,0,0,0,0,0) ; printf("Status from IO$_SETMODE for density: %x\n",status) ; return( status ) ; } parse(dev,s,def_string) struct devdsc *dev ; char *s ; { char *cp,*dp ; int i,status,temp1,temp2,temp3,tempchan ; struct dsc devname ; struct itmlst dvilist[4] ; int devtype, devclass ; parse_fab = cc$rms_fab ; parse_nam = cc$rms_nam ; dp = dev->dev_spec ; parse_fab.fab$l_nam = &parse_nam ; parse_fab.fab$l_fna = s ; parse_fab.fab$b_fns = strlen(s) ; parse_nam.nam$l_esa = dp ; parse_nam.nam$b_ess = 127 ; parse_nam.nam$b_nop = NAM$M_NOCONCEAL ; if ( ((status=sys$parse(&parse_fab)) & 1) == 0 ) return( status ) ; *(dp + (parse_nam.nam$b_esl&0377) ) = 0 ; cp = &parse_nam.nam$t_dvi ; devname.len = *cp++ ; devname.addr = cp ; for (dp=dev->dev_name,i=0; idev_lun = tempchan ; dvilist[0].bufferlength = 4 ; dvilist[0].item_code = DVI$_DEVCLASS ; dvilist[0].addr.intbuf = &dev->dev_class ; dvilist[0].retlength = &temp1 ; dvilist[1].bufferlength = 4 ; dvilist[1].item_code = DVI$_DEVTYPE ; dvilist[1].addr.intbuf = &dev->dev_type ; dvilist[1].retlength = &temp2 ; dvilist[2].bufferlength = 4 ; dvilist[2].item_code = DVI$_DEVCHAR ; dvilist[2].addr.intbuf = &dev->dev_char ; dvilist[2].retlength = &temp3 ; dvilist[3].bufferlength = 0 ; dvilist[3].item_code = 0 ; status = sys$getdviw(0,tempchan,0,&dvilist,0,0,0,0) ; if ( ( status & 1 ) && dev->dev_class == DC$_TAPE ) then if ((dev->dev_char & DEV$M_MNT) == 0 ) then status = SS$_DEVNOTMOUNT ; else if ((dev->dev_char & DEV$M_FOR) == 0 ) then { status = 0 ; printf("%Tape device must be mounted foreign\n") ; } ; return( status ) ; } printmsg(n) int n ; { printf("%s\n",getmsg(n)) ; } char *getmsg(n) int n ; { struct dsc msgd ; int mlen ; char junk[4] ; mlen = 0 ; msgd.len = 256 ; msgd.addr = malloc(256) ; sys$getmsg(n,&mlen,&msgd,0,&junk) ; *(msgd.addr + (mlen&0377)) = 0 ; return( msgd.addr ) ; } init() { int i ; lib$get_ef( &wakeup_ef ) ; for (i=0; ilen &= 0377 ; strncpy(s,arg->addr,arg->len) ; *(s+arg->len) = 0 ; } printstats(n) int n; { char s[80] ; int code = 2 ; lib$show_timer(&handler,&code,&cpuformat,s) ; printf("Records processed: %d %s\n",n,s) ; } accstats() { lib$show_timer(&handler) ; lib$init_timer(&handler) ; } /* CLI interfacing */ getparam(s,arg) char *s; struct dsc$descriptor_s *arg ; { struct dsc out ; int retlength ; out.len = 128 ; out.addr = s ; *s = 0 ; if ( (cli$present( arg ) & 1) && (cli$get_value( arg,&out,&retlength ) & 1 )) then { *(s+(retlength&0377)) = 0 ; return(1) ; } else return(0) ; } setqual(arg,def) struct dsc$descriptor_s *arg ; int def ; { int status ; status = cli$present(arg) ; if ( status == CLI$_PRESENT || status == CLI$_LOCPRES ) return(1) ; else if ( status == CLI$_NEGATED || status == CLI$_LOCNEG ) return(0) ; else return(def) ; } getqual_value(arg) struct dsc$descriptor_s *arg ; { char valbuf[128] ; struct dsc out = { 128,&valbuf } ; int retlength,val ; *valbuf = 0 ; if ( (cli$present( arg ) & 1) && (cli$get_value( arg,&out,&retlength ) & 1 )) then { *(valbuf+(retlength&0377)) = 0 ; return( (sscanf(valbuf,"%d",&val)) ? val:0 ) ; } else return(0) ; } /* Disk Input and Output */ /* The disk image file */ struct FAB disk_image_fab ; struct RAB disk_image_rab ; int inisiz = 2000 ; int deqsiz = 256 ; char default_outputname[] = "SYS$LOGIN:VAX_TPC.DATA" ; init_fab(fname) char *fname ; { disk_image_fab = cc$rms_fab ; disk_image_fab.fab$l_alq = (qual_allocation) ?qual_allocation:inisiz ; disk_image_fab.fab$w_deq = (qual_extendsize) ?qual_extendsize:deqsiz ; disk_image_fab.fab$l_dna = &default_outputname ; disk_image_fab.fab$b_dns = strlen(default_outputname) ; disk_image_fab.fab$l_fna = ( disk_image_fab.fab$b_fns = strlen(fname) ) ? fname:0 ; disk_image_fab.fab$w_mrs = qual_blocksize ; disk_image_fab.fab$b_org = FAB$C_SEQ ; disk_image_fab.fab$b_rfm = FAB$C_VAR ; disk_image_fab.fab$b_shr = FAB$M_NIL ; disk_image_rab = cc$rms_rab ; disk_image_rab.rab$l_fab = &disk_image_fab ; disk_image_rab.rab$b_mbc = 32 ; disk_image_rab.rab$b_mbf = 8 ; return(1) ; } create_disk(s) char *s ; { int sts ; init_fab(s) ; if ( ( (sts = sys$create(&disk_image_fab)) & 1 ) == 0 ) return(sts); return( sys$connect(&disk_image_rab) ) ; } open_disk(s) char *s ; { int sts ; init_fab(s) ; disk_image_fab.fab$b_shr = FAB$M_SHRGET ; if ( ( (sts = sys$open(&disk_image_fab)) & 1 ) == 0 ) return(sts); return( sys$connect(&disk_image_rab) ) ; } read_disk(buffer,size) char *buffer ; int *size ; { int sts ; disk_image_rab.rab$l_ubf = buffer ; disk_image_rab.rab$w_usz = qual_blocksize ; sts = sys$get( &disk_image_rab ) ; *size = ( sts & 1 ) ? disk_image_rab.rab$w_rsz : 0 ; return( sts ) ; } write_disk(buffer,size) char *buffer ; int size ; { int sts ; disk_image_rab.rab$l_rbf = buffer ; disk_image_rab.rab$w_rsz = size ; return( sys$put( &disk_image_rab ) ) ; } close_disk() { int sts ; if ( (sts = sys$disconnect( &disk_image_rab )) & 1 ) then sts = sys$close( &disk_image_fab ) ; return( sts ) ; } char *r50toa(dst,r50val) char *dst ; unsigned short int *r50val ; { char *cp ; char rlist[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.?0123456789 " ; unsigned short int val ; val = *r50val ; cp = dst ; *cp++ = rlist[ val/03100 ] ; val = val % 03100 ; *cp++ = rlist[ val/050 ] ; *cp++ = rlist[ val % 050 ] ; *cp++ = 0 ; return(dst) ; }