(********************************************************) (* *) (* PROJECT 4/PHASE II - TERENCE CHANG *) (* *) (* This whopping program is merely a file copier with *) (* 3 functions: Copy, Log, and Verify. It now uses *) (* real tape drives with help of TAPE$SUBS/LIB *) (* and can generate hardcopy. It is hoped that the *) (* routines will work with no new compatibility *) (* problems. *) (* *) (********************************************************) PROGRAM TAPECOPY (INPUT, OUTPUT); LABEL 9999; CONST LL = '---------------------------------------------------------------'; GET_AFTER_EOF = 118; (* These are Pascal codes *) ERR_DUR_REW = 106; ERR_DUR_RES = 105; FILE_NOT_FOUND = 29; (* These are now Fortran error codes *) ERR_DUR_CLO = 28; ERR_DUR_WRI = 38; END_DUR_REA = 24; ERR_DUR_REA = 39; (* this one is actually = 1 in routines *) DEF_LIMIT = 6 ; (* Number of errors per file before query *) FNSIZE = 100 ; (* Max. length of filename *) LO = 1; HI = 65535; MAX_LEN = 65535; TYPE LINE = PACKED ARRAY[1..MAX_LEN] OF CHAR; TEXTLINE = PACKED ARRAY[1..255] OF CHAR; MODE = (None, Copy, Log, Verify, Copy_plus); FILENAME = VARYING[FNSIZE] OF CHAR; (* Note: if fn : filename, then fn.length = length of string, fn.body = array[1..len] of CHAR *) FOR_NAME = PACKED ARRAY[1..FNSIZE] OF CHAR; VAR X_MODE : MODE (* Program operation mode *); FN, FN_NEW, FN2 : FILENAME (* Input filename *); LOG_FLAG : BOOLEAN (* If true, produce hardcopy *); DEF_LOG : FILENAME (* Default logical name *); FOR_LOG_NAME : FOR_NAME (* Converted logical name *); INFO : TEXT (* Logged info file_var *); ITEM, ITEM2 : LINE (* Current record *); REC_LEN, OLD_LEN : INTEGER (* Length of record *); LAST_REC : INTEGER (* Number of records in last file *); FILE_NUM : INTEGER (* Total files counter *); ERR_LIMIT : INTEGER (* Number of errors before query *); TOTAL_VER : INTEGER (* Total files verified OK *); ABORTED : INTEGER (* Number of files cancelled by user *); VMS_SET : SET OF CHAR (* Legal filename characters *); NUM_SET : SET OF CHAR (* Legal numeric input characters *); TODAYS_DATE, CURRENT_TIME : PACKED ARRAY[1..11] OF CHAR; OLD_CLOCK : INTEGER (* CPU time since init *); CONT_STATUS : CHAR (* If 'Y' or 'y', user wants copy aborted *); IOSTAT : INTEGER (* Error code for main block *); ERR_FLAG : BOOLEAN (* If true, halt procedure *); ERR_COUNT, TEMP_ERR : INTEGER (* Number errors during transfer *); ASTERISKS : LINE (* Line of 255 asterisks *); REC, TEMP_REC : INTEGER (* Current record number *); (* (* A note about [External] declarations: the directive that constitutes (* the routine body is either EXTERN, EXTERNAL, or FORTRAN. All have (* the same meaning to the compiler. I use them for self-documenting (* my declarations. (* *) [EXTERNAL] PROCEDURE STR$UPCASE (%STDESCR DEST_STR, SRCE_STR : PACKED ARRAY[LO..HI : INTEGER] OF CHAR); EXTERN; (* This declaration almost comes straight out of the R-T Lib Ref *) (****************************************************************) (* *) (* STR$UPCASE : Capitalize a packed string array *) (* *) (****************************************************************) (*******************************************************************) (* *) (* TAPE$SUBS/LIB : foreign tape subroutines *) (* *) (* SETUPFT (LUN,CDEV,ISTAT) ! Open drive *) (* RCTAPE (LUN,CBUFF,NBYTE,IBYTE,ISTAT) ! Reads a record. *) (* WCTAPE (LUN,CBUFF,NBYTE,IBYTE,ISTAT) ! Writes a record. *) (* TAPE_EOF (LUN,ISTAT) ! Writes an EOF/EOV. *) (* REWTAPE (LUN,ISTAT) ! Rewinds the file. *) (* SKIPFILE (LUN,NUMB,NSKIP,ISTAT) ! Skips to the next file. *) (* *) (*******************************************************************) [EXTERNAL(SETUPFT)] PROCEDURE T$OPEN (%REF UNIT : INTEGER; %STDESCR FN : FOR_NAME; %REF ERROR : INTEGER); FORTRAN; [EXTERNAL(RCTAPE)] PROCEDURE T$READ (%REF UNIT : INTEGER; %STDESCR BUFFER : LINE; %REF LENGTH : INTEGER; %REF IBYTE : INTEGER; %REF ERROR : INTEGER; MODIF : INTEGER); FORTRAN; [EXTERNAL(WCTAPE)] PROCEDURE T$WRITE (%REF UNIT : INTEGER; %STDESCR BUFFER : LINE; %REF LENGTH : INTEGER; %REF IBYTE : INTEGER; %REF ERROR : INTEGER; MODIF : INTEGER); FORTRAN; [EXTERNAL(TAPE_EOF)] PROCEDURE T$WEOF (%REF UNIT : INTEGER; %REF ERROR : INTEGER); FORTRAN; [EXTERNAL(REWTAPE)] PROCEDURE T$REWIND (%REF UNIT : INTEGER; %REF ERROR : INTEGER; NOWAIT : INTEGER := %IMMED 1); FORTRAN; [EXTERNAL(SKIPFILE)] PROCEDURE T$SKIP (%REF UNIT : INTEGER; %REF NUM : INTEGER; %REF ISKIP : INTEGER; %REF ERROR : INTEGER); FORTRAN; PROCEDURE CAP_VAR (VAR S : FILENAME); (****************************************************************) (* *) (* CAP_VAR : Capitalize a varying string array *) (* *) (* S : a varying array[fnsize] which is modified *) (* *) (* Since Str$UpCase has already been declared using packed *) (* conformant array schema, this procedure converts varying *) (* arrays into the packed array TEMP, which is passed on to *) (* Str$UpCase. *) (* *) (****************************************************************) TYPE CON_VAR = PACKED ARRAY[1..FNSIZE] OF CHAR; VAR TEMP : CON_VAR (* Converted Varying array *); X : INTEGER; BEGIN FOR X := 1 TO S.LENGTH DO TEMP[X] := S[X]; STR$UPCASE (TEMP, TEMP); FOR X := 1 TO S.LENGTH DO S[X] := TEMP[X]; END (* Cap_Var *); PROCEDURE LOG_LINE (TEMP_REC, LEN : INTEGER); (****************************************************************) (* *) (* LOG_LINE : print a line of logging *) (* *) (* Temp_Rec : number of records this line *) (* Len : length in bytes this line *) (* *) (* Writes 'xxxx records of xxxx bytes' to OUTPUT and INFO. *) (* *) (****************************************************************) BEGIN WRITE (' ', TEMP_REC:4, ' record'); WRITE (INFO, ' ', TEMP_REC:4, ' record'); IF TEMP_REC > 1 THEN BEGIN WRITE ('s'); WRITE (INFO, 's'); END ELSE BEGIN WRITE (' '); WRITE (INFO, ' '); END; WRITE (' of ', OLD_LEN:4, ' byte'); WRITE (INFO, ' of ', OLD_LEN:4, ' byte'); IF OLD_LEN > 1 THEN BEGIN WRITE ('s'); WRITE (INFO, 's'); END; WRITELN; WRITELN (INFO); END (* Log_Line *); PROCEDURE CONVERT_FN (FN : FILENAME; VAR FOR_FN : FOR_NAME); (****************************************************************) (* *) (* CONVERT_FN : convert a filename to Class_S array *) (* *) (* I almost broke tradition and made this a Function. This *) (* Procedure converts a filename, which is a Varying[] of CHAR *) (* (%DESCR Class_VS), into a Packed Array (%STDESCR Class_S). *) (* *) (****************************************************************) VAR X : INTEGER (* Temporary counter *); BEGIN FOR X := 1 TO FNSIZE DO FOR_FN[X] := ' '; FOR X := 1 TO FN.LENGTH DO FOR_FN[X] := FN[X]; END (* Convert_FN *); PROCEDURE INITIALIZE; (****************************************************************) (* *) (* INITIALIZE : set up miscellaneous variables, etc. *) (* *) (****************************************************************) VAR X : INTEGER (* Loop counter *); BEGIN VMS_SET := ['A'..'Z', '0'..'9', ' ', '_']; (* Legal characters in VMS 4.1 extension *) FOR X := 1 TO 255 DO ASTERISKS[X] := '*'; ERR_LIMIT := DEF_LIMIT; NUM_SET := ['0'..'9']; OLD_CLOCK := CLOCK; ERR_FLAG := FALSE; TOTAL_VER := 0; LAST_REC := -1; FILE_NUM := 0; X_MODE := Log; ABORTED := 0; END (* Initialize *); PROCEDURE GET_MODE; (****************************************************************) (* *) (* GET_MODE : Does the user want Copy, Log, or Verify? *) (* *) (* This procedure sets the global variable X_Mode (Xerox Mode) *) (* as the user requests. If 'None' is returned, the user has *) (* typed CTRL-Z. *) (* *) (****************************************************************) LABEL 1; VAR TEMP : TEXTLINE; BEGIN X_MODE := None; WRITELN; WRITELN (LL); REPEAT WRITELN; WRITE (' Select mode of operation [C/L/V or ?/H = Help, ^Z = Exit]: '); RESET (INPUT); READ (TEMP, ERROR := CONTINUE); IF STATUS (INPUT) = GET_AFTER_EOF THEN GOTO 1 (* ^Z input *); STR$UPCASE (TEMP, TEMP); WRITELN; STR$UPCASE (TEMP, TEMP); CASE TEMP[1] OF 'C' : BEGIN X_MODE := Copy; WRITE (' Would you like automatic Verify after Copy (Y/N) [N] ? '); RESET (INPUT); READ (TEMP, ERROR := CONTINUE); IF STATUS (INPUT) = GET_AFTER_EOF THEN GOTO 1; STR$UPCASE (TEMP, TEMP); IF TEMP[1] = 'Y' THEN X_MODE := Copy_plus; WRITELN; END; 'L' : X_MODE := Log; 'V' : X_MODE := Verify; '?', '/', 'H' : BEGIN WRITELN; WRITELN (' This program is a tape copier which has 3 modes of operation:'); WRITELN (' C)opy, L)og, and V)erify. This program cannot destroy data,'); WRITELN (' but its use is not recommended for entertainment purposes.'); WRITELN; WRITELN (' Copy : copies a tape '); WRITELN (' Log : gives a description of tape '); WRITELN (' Verify : compares two tapes and reports discrepancies'); WRITELN; WRITELN (' Default drive names are given within a pair of < >.'); END; END (* Case *); UNTIL (X_MODE <> None); 1:END (* Get_Mode *); PROCEDURE GET_OUTPUT; (****************************************************************) (* *) (* GET_OUTPUT : get specifications for information output *) (* *) (* This procedure determines where the informational output *) (* should go. All of the Writes not concerning the tapes *) (* are directed to file_variable INFO, which has a disk *) (* filename TAPE.LOG. This is printed if so requested by the *) (* user and deleted. Sets global variable Log_Flag. *) (* *) (****************************************************************) LABEL 1; VAR TEMP : FILENAME (* Temporary string *); BEGIN WRITE (' Produce hardcopy of TAPECOPY session (Y/N) [N] ? '); RESET (INPUT); READ (TEMP, ERROR := CONTINUE); CAP_VAR (TEMP); IF TEMP.LENGTH = 0 THEN GOTO 1; LOG_FLAG := (TEMP[1] = 'Y'); 1: (* Ready TAPE.LOG for writing *) IF LOG_FLAG THEN OPEN (FILE_VARIABLE := INFO, FILE_NAME := 'TAPE.LOG', HISTORY := NEW, DISPOSITION := PRINT) ELSE OPEN (FILE_VARIABLE := INFO, (* Open virtual file *) DISPOSITION := DELETE); REWRITE (INFO); END (* Get_Output *); PROCEDURE GET_FN (PROMPT : FILENAME; VAR FN : FILENAME); (****************************************************************) (* *) (* GET_FN : Get a FileName *) (* *) (* Prompt : A string written before requesting input *) (* FN : The varying array that the filename is returned to *) (* *) (* This is a straight-forward input routine. If "Exit" or *) (* Ctrl-Z is entered, global variable X_Mode is set to "None". *) (* Uses implicit parameter DEF_LOG. *) (****************************************************************) (* X_Mode is set to "None" if a Ctrl-Z or "Exit" has *) (* been entered in response to the prompt *) LABEL 1,2; VAR OLDLEN : INTEGER (* Temporary FN.Length holder *); BEGIN WRITELN; WRITELN (LL); WRITELN; 2:WRITE (PROMPT,' <', DEF_LOG, '> [^Z = Quit] : '); RESET (INPUT); READ (FN, ERROR := CONTINUE); WRITELN; CAP_VAR (FN); OLDLEN := FN.LENGTH; IF (OLDLEN = 0) AND (STATUS (INPUT) = 0) THEN BEGIN FN := DEF_LOG; GOTO 1; END; FN.LENGTH := 4; IF (STATUS (INPUT) = GET_AFTER_EOF) OR (* Is there a better way? *) (FN[1]+FN[2]+FN[3]+FN[4] = 'EXIT') THEN BEGIN X_MODE := None; GOTO 1; END (* IF *); IF STATUS (INPUT) > 0 THEN WRITELN ('Status(INPUT) = ',STATUS(INPUT):3); FN.LENGTH := OLDLEN; 1 : END (* GET_FN *); PROCEDURE BYE; (****************************************************************) (* *) (* BYE : Give farewell message *) (* *) (****************************************************************) VAR X : INTEGER; ET : REAL (* Elapsed time *); BEGIN DATE (TODAYS_DATE); TIME (CURRENT_TIME); ET := CLOCK - OLD_CLOCK (* Calculate elapsed Cpu time *); ET := ET/1000; WRITELN; WRITELN (LL); WRITELN; WRITE (' TAPECOPY session completed on ',TODAYS_DATE,' at '); FOR X := 1 TO 5 DO WRITE (CURRENT_TIME[X]); WRITELN; WRITE (' Mode selected : '); CASE X_MODE OF Log : WRITELN ('Log'); Copy : WRITELN ('Copy'); None : WRITELN ('----'); Verify : WRITELN ('Verify'); Copy_plus : WRITELN ('Copy with Verify'); END (* Case *); WRITELN (' Files aborted : ', ABORTED:1); WRITELN (' Cpu time (s) : ', ET:1:2); WRITE (' Hardcopy log : '); CASE LOG_FLAG OF True : WRITELN ('Enabled'); False : WRITELN ('Not enabled'); END (* Case *); WRITELN; WRITELN (LL); WRITELN; IF LOG_FLAG THEN BEGIN WRITELN (INFO); WRITELN (INFO, LL); WRITELN (INFO); WRITE (INFO, ' TAPECOPY session completed on ',TODAYS_DATE,' at '); FOR X := 1 TO 5 DO WRITE (INFO, CURRENT_TIME[X]); WRITELN (INFO); WRITE (INFO, ' Mode selected : '); CASE X_MODE OF Log : WRITELN (INFO, 'Log'); Copy : WRITELN (INFO, 'Copy'); None : WRITELN (INFO, '----'); Verify : WRITELN (INFO, 'Verify'); Copy_plus : WRITELN (INFO, 'Copy with Verify'); END (* Case *); WRITELN (INFO, ' Files aborted : ', ABORTED:1); WRITELN (INFO, ' Cpu time (s) : ', ET:1:2); WRITE (INFO, ' Hardcopy log : '); CASE LOG_FLAG OF True : WRITELN (INFO, 'Enabled'); False : WRITELN (INFO, 'Not enabled'); END (* Case *); WRITELN (INFO); WRITELN (INFO, LL); WRITELN (INFO); END (* If *); END; PROCEDURE PHOTOCOPY; (****************************************************************) (* *) (* PHOTOCOPY - copy or log a tape file *) (* *) (* This procedure could not be parameterized because Pascal, *) (* it appears, will not pass file variables (there may be a *) (* way around it, but it's not worth finding). *) (* *) (****************************************************************) LABEL 1,2; VAR LEN : INTEGER (* Length of record *); NUM : INTEGER (* Number of bytes actually written *); X : INTEGER (* Temporary counter *); STAT : INTEGER (* IOSTAT result on tape routines *); ERROR : INTEGER (* IOSTAT's that are ignored *); R_STAT : BOOLEAN (* Replaces EOF function *); BEGIN ERR_FLAG := FALSE; STAT := 0; (* No error condition *); R_STAT := FALSE; (* Not at EOF *); OLD_LEN := -1 (* No previous length *); REC := 0; TEMP_REC := REC (* No records read yet *); TEMP_ERR := 0; (* No errors this file *); CONT_STATUS := 'N' (* Don't abort file *); (****************************************************************) (* *) (* Main Photocopy loop begins here. *) (* *) (****************************************************************) WHILE NOT R_STAT DO BEGIN T$READ (42, ITEM, MAX_LEN, LEN, STAT, 0) (* Get a record *); (* Enter error-handling block? *) R_STAT := (STAT = 2) OR (STAT = 4) (* R_Stat emulates the EOF function *); IF STAT = 6 THEN BEGIN ERR_COUNT := ERR_COUNT + 1; TEMP_ERR := TEMP_ERR + 1; WRITELN (' *** Unrecoverable I/O error, number ', STAT:1, ' at record ', REC+1:3, ' ***'); WRITELN (INFO, ' *** Unrecoverable I/O error, number ', STAT:1, ' at record ', REC+1:3, ' ***'); IF X_MODE <> Log THEN T$WRITE (43, ASTERISKS, OLD_LEN, NUM, ERROR, 0); (* Report error count to user? *) IF TEMP_ERR MOD DEF_LIMIT = 0 THEN BEGIN WRITE (' ', TEMP_ERR:1, ' errors so far. Abort this file (Y/N) [N] ? '); RESET (INPUT); READ (CONT_STATUS); WRITELN; STR$UPCASE (CONT_STATUS, CONT_STATUS); IF CONT_STATUS = 'Y' THEN BEGIN IF X_MODE <> Log THEN T$WEOF (43, STAT) (* Mark end of this file *); WRITELN (INFO); WRITELN (INFO, ' <<< File aborted >>>'); ABORTED := ABORTED + 1; ERR_FLAG := TRUE; GOTO 1; END; END (* If temp_err ... *); END ELSE BEGIN (* No, record read successfully *) (* Determine whether or not new length *) REC := REC + 1; TEMP_REC := TEMP_REC + 1; IF (X_MODE <> Log) AND (NOT R_STAT) THEN BEGIN T$WRITE (43, ITEM, LEN, NUM, ERROR, 0); if error > 1 then writeln ('**** ERROR : T$WRITE ',ERROR:1); END; IF (REC = 1) AND (NOT R_STAT) THEN BEGIN (* Yes, this is a file *) FILE_NUM := FILE_NUM + 1; WRITELN; WRITELN (' File : ',FILE_NUM:1); WRITELN; WRITELN (INFO); WRITELN (INFO, ' File : ',FILE_NUM:1); WRITELN (INFO); END; IF LEN <> OLD_LEN THEN BEGIN IF OLD_LEN > -1 THEN LOG_LINE (TEMP_REC, LEN); (* New previous length, no temp_rec's read *) OLD_LEN := LEN; TEMP_REC := 0; END (* If Len <> Old_Len *); END (* Else *); END (* While NOT EOF *); (****************************************************************) (* *) (* The copying loop ends here. The remaining code prints *) (* a final report and writes the last EOF. *) (* *) (****************************************************************) (* Print final "xxxx records of xxx bytes" *) REC := REC - 1 (* Ignore the EOF record *); IF REC = 0 THEN GOTO 2; (*LOG_LINE (TEMP_REC, LEN);*) (* Print file summary *) WRITELN (' ----'); WRITELN (INFO, ' ----'); WRITELN (' ', REC:4, ' total records'); WRITELN (INFO, ' ', REC:4, ' total records'); IF TEMP_ERR > 0 THEN BEGIN WRITE (' ', TEMP_ERR:4, ' unrecoverable error'); WRITE (INFO, ' ', TEMP_ERR:4, ' unrecoverable error'); IF TEMP_ERR > 1 THEN BEGIN WRITELN ('s'); WRITELN (INFO, 's'); END ELSE BEGIN WRITELN; WRITELN (INFO); END; END (* If Temp_Err *); IF X_MODE <> Log THEN T$WEOF (43, STAT); 2: WRITELN (' >> EOF <<'); WRITELN (INFO, ' >> EOF <<'); LAST_REC := REC (* Will equal zero if EOF reached with no records read *); 1: END (* COPY *); PROCEDURE GIVE_TOTALS; (****************************************************************) (* *) (* Give_Totals : give final copy volume results *) (* *) (****************************************************************) BEGIN WRITELN; WRITELN (INFO); WRITELN (' TOTALS :'); WRITELN (INFO, ' TOTALS :'); WRITE (' ', FILE_NUM:2, ' file'); WRITE (INFO, ' ', FILE_NUM:2, ' file'); IF FILE_NUM <> 1 THEN BEGIN WRITE ('s'); WRITE (INFO, 's'); END; WRITELN (' processed'); WRITELN (INFO, ' processed'); IF ERR_COUNT > 0 THEN BEGIN WRITE (' ', ERR_COUNT:2, ' unrecoverable I/O error'); WRITE (INFO, ' ', ERR_COUNT:2, ' unrecoverable I/O error'); IF ERR_COUNT > 1 THEN BEGIN WRITELN ('s'); WRITELN (INFO, 's'); END ELSE BEGIN WRITELN; WRITELN (INFO); END; END (* If *); END (* Give_Totals *); PROCEDURE PHOTOCOPY_MCP; (****************************************************************) (* *) (* PHOTOCOPY_MCP - copy or log a tape volume *) (* *) (* This procedure calls Photocopy, counts the files, and *) (* checks for the end of the volume. *) (* *) (****************************************************************) LABEL 1; VAR STAT : INTEGER (* Temporary error code holder *); NSKIP : INTEGER (* number of files actually skipped *); BEGIN ERR_FLAG := FALSE; STAT := 0; T$REWIND (42, STAT, 0); IF X_MODE <> Log THEN T$REWIND (43, STAT, 0); (* (* All the files needed for the operation have been initialized (* and readied. Now for a control loop that calls Photocopy. (* *) WRITELN; WRITELN (INFO); WRITELN (' Drive name : ', FN); WRITELN (INFO, ' Drive name : ', FN); WHILE (LAST_REC <> 0) DO BEGIN PHOTOCOPY; IF (ERR_FLAG = TRUE) AND (CONT_STATUS = 'Y') THEN BEGIN T$SKIP (42, 1, NSKIP, STAT); IF X_MODE <> Log THEN T$SKIP (43, 1, NSKIP, STAT); END; END; GIVE_TOTALS; 1:END (* PHOTOCOPY_MCP *); PROCEDURE PHOTOVERIFY; (****************************************************************) (* *) (* PHOTOVERIFY - verify two files (FN and FN2) *) (* *) (* Since Verify mode is so different from the other two, *) (* I have recopied the Photocopy procedure and altered it *) (* to fit the specifications. *) (* *) (* Uses global variables REC, ITEM, ITEM2, ERR_LIMIT, *) (* CONT_STATUS, ERR_COUNT. *) (****************************************************************) LABEL 1; VAR LEN, LEN2 : INTEGER (* Length of record *); STAT, STAT2 : INTEGER (* IOSTAT results *); R_STAT, R_STAT2 : BOOLEAN (* Replaces Pascal EOF function *); V_STAT : BOOLEAN (* Verify contents status *); REC_LEFT : INTEGER (* Remaining records on still open file *); X : INTEGER (* Temporary counter *); TEMP_E : INTEGER (* Number of errors this cycle *); TEMP : FILENAME (* Temporary string *); BEGIN ERR_FLAG := FALSE; STAT := 0; STAT2 := 0; REC := 0 (* No records read yet *); ERR_COUNT := 0 (* No errors *); TEMP_E := ERR_LIMIT (* No errors *); R_STAT := FALSE; R_STAT2 := FALSE; (* Not at EOF's yet *) CONT_STATUS := 'N'; (****************************************************************) (* *) (* Main PhotoVerify loop begins here. *) (* *) (****************************************************************) WHILE (NOT R_STAT) AND (NOT R_STAT2) DO BEGIN T$READ (42, ITEM, MAX_LEN, LEN, STAT, 0) (* Get a pair of records *); T$READ (43, ITEM2,MAX_LEN, LEN2, STAT2,0); R_STAT := (STAT = 2) OR (STAT = 4); R_STAT2 := (STAT2 = 2) OR (STAT = 4); (* Enter error-handling block? *) IF (STAT = 6) OR (STAT2 = 6) THEN BEGIN (* Keep logic simple, be redundant *) IF STAT = 6 THEN BEGIN WRITELN (' *** Unrecoverable I/O error on input, at record ',REC+1:4, ' ***'); WRITELN (INFO, ' *** Unrecoverable I/O error on input, at record ',REC+1:4, ' ***'); ERR_COUNT := ERR_COUNT + 1; TEMP_E := TEMP_E - 1; END; IF STAT2 = 6 THEN BEGIN WRITELN (' *** Unrecoverable I/O error on output, at record ',REC+1:4, ' ***'); WRITELN (INFO, ' *** Unrecoverable I/O error on output, at record ',REC+1:4, ' ***'); ERR_COUNT := ERR_COUNT + 1; TEMP_E := TEMP_E - 1; END END ELSE BEGIN (* Records read OK, check 'em out *) REC := REC + 1; IF (REC = 1) AND ((NOT R_STAT) OR (NOT R_STAT2)) THEN BEGIN FILE_NUM := FILE_NUM + 1; WRITELN; WRITELN (INFO); WRITELN (' File ',FILE_NUM:3); WRITELN (INFO, ' File ',FILE_NUM:3); END; (* Check records for errors in size, content *) IF LEN <> LEN2 THEN BEGIN (* Length of item doesn't match its counterpart *) WRITE (' *** Record ', REC:4, ' : Non-matching record size. '); WRITE (INFO, ' *** Record ', REC:4, ' : Non-matching record size. '); WRITELN (LEN:3, ' - ', LEN2:3); WRITELN (INFO, LEN:3, ' - ', LEN2:3); ERR_COUNT := ERR_COUNT + 1; TEMP_E := TEMP_E - 1; END; IF (LEN = LEN2) THEN BEGIN (* Smart compare *) V_STAT := FALSE; FOR X := 1 TO LEN DO IF ITEM [X] <> ITEM2 [X] THEN V_STAT := TRUE; (* Note: V_Stat must be set only if true *) IF V_STAT THEN BEGIN WRITELN (' *** Record ', REC:4, ' : Non-matching data.'); WRITELN (INFO, ' *** Record ', REC:4, ' : Non-matching data.'); ERR_COUNT := ERR_COUNT + 1; TEMP_E := TEMP_E - 1; END; END (* If (Len = Len2)... *); END (* ELSE BEGIN *); IF TEMP_E <= 0 THEN BEGIN WRITE (' ', ERR_COUNT:1, ' errors so far. Abort this file (Y/N) [N] ? '); RESET (INPUT); READ (CONT_STATUS); WRITELN; STR$UPCASE (CONT_STATUS, CONT_STATUS); IF CONT_STATUS = 'Y' THEN BEGIN ABORTED := ABORTED + 1; ERR_FLAG := TRUE; GOTO 1; END ELSE BEGIN WRITE (' How many more errors before re-prompting [', ERR_LIMIT:1, '] ? '); RESET (INPUT); READ (TEMP, ERROR := CONTINUE); IF TEMP.LENGTH > 0 THEN BEGIN ERR_LIMIT := 0; FOR X := 1 TO TEMP.LENGTH DO IF TEMP[X] IN NUM_SET THEN ERR_LIMIT := ERR_LIMIT + (ORD (TEMP[X]) - 48) * 10 ** (TEMP.LENGTH - X); IF ERR_LIMIT <= 0 THEN ERR_LIMIT := DEF_LIMIT; END; TEMP_E := ERR_LIMIT; WRITELN; END; END (* If Err_Count ... *); END (* While NOT EOF *); (****************************************************************) (* *) (* Main loop of Photoverify ends here. Some code that is not *) (* in Photocopy is necessary if an unexpected EOF is found. *) (* *) (****************************************************************) IF R_STAT <> R_STAT2 THEN BEGIN (* Oops, records still remaining on a file *) WRITE (' *** Record ', REC:4, ' : Unexpected EOF on '); WRITE (INFO, ' *** Record ', REC:4, ' : Unexpected EOF on '); ERR_COUNT := ERR_COUNT + 1; REC_LEFT := 0; IF R_STAT = TRUE THEN BEGIN (* Count records on FN *) WRITELN (FN); WRITELN (INFO, FN); WHILE NOT R_STAT2 DO BEGIN T$READ (43, ITEM2, MAX_LEN, LEN2, STAT2, 0); R_STAT2 := (STAT2 = -1); REC_LEFT := REC_LEFT + 1; END (* While *); WRITELN (' : ', FN2,' has ', REC_LEFT:4,' more records.'); WRITELN (INFO, ' : ', FN2,' has ', REC_LEFT:4,' more records.'); END ELSE IF R_STAT2 = TRUE THEN BEGIN (* Count records on FN2 *) WRITELN (FN2); WRITELN (INFO, FN2); WHILE NOT R_STAT DO BEGIN T$READ (42, ITEM, MAX_LEN, LEN, STAT, 0); R_STAT := (STAT = -1); REC_LEFT := REC_LEFT + 1; END (* While *); WRITELN (' : ', FN,' has ', REC_LEFT:4,' more records.'); WRITELN (INFO, ' : ', FN,' has ', REC_LEFT:4,' more records.'); END (* ELSE *); END (* If *); LAST_REC := REC - 1 (* Don't count EOF as record *); IF (ERR_COUNT = 0) AND (LAST_REC > 0) THEN BEGIN WRITELN (' >>> Verified <<<'); WRITELN (INFO, ' >>> Verified <<<'); TOTAL_VER := TOTAL_VER + 1; END ELSE IF LAST_REC > 0 THEN BEGIN WRITELN (' >>> NOT Verified <<<'); WRITELN (INFO, ' >>> NOT Verified <<<'); END; 1: END (* PhotoVerify *); PROCEDURE PHOTOVERIFY_MCP; (****************************************************************) (* *) (* PHOTOVERIFY_MCP - verify a tape volume *) (* *) (* This procedure calls PhotoVerify, counts the files, and *) (* checks for the end of the volume. *) (* *) (* Uses globals FOR_HIST, ERR_FLAG, FN, FN2, FILE_NUM, *) (* TOTAL_VER *) (****************************************************************) LABEL 1; VAR STAT2 : INTEGER (* IOSTAT result *); NSKIP : INTEGER (* number of files actually skipped *); BEGIN ERR_FLAG := FALSE; FILE_NUM := 0; LAST_REC := -1; T$REWIND (42, STAT2, 1) (* Don't wait for this one to rewind *); T$REWIND (43, STAT2, 0); WRITELN; WRITELN (INFO); WRITELN (' Drives : ', FN, ' --- ', FN2); WRITELN (INFO, ' Drives : ', FN, ' --- ', FN2); WHILE (LAST_REC <> 0) DO BEGIN PHOTOVERIFY; IF (ERR_FLAG = TRUE) AND (CONT_STATUS = 'Y') THEN BEGIN T$SKIP (42, 1, NSKIP, STAT2); T$SKIP (43, 1, NSKIP, STAT2); END; END; WRITELN; WRITELN (INFO); WRITELN (' Total ',TOTAL_VER:1,' out of ',FILE_NUM:1,' files verified.'); WRITELN (INFO,' Total ',TOTAL_VER:1,' out of ',FILE_NUM:1,' files verified.'); 1:END (* Photoverify_MCP *); (****************************************************************) (* *) (* MAIN BLOCK *) (* *) (* Calls all the above procedures. The GOTO 9999's indicate *) (* a jump to halt program execution. *) (* *) (****************************************************************) BEGIN INITIALIZE; IOSTAT := 0; DEF_LOG := 'MT0'; GET_FN (' Enter source drive name', FN); IF X_MODE = None THEN GOTO 9999; GET_MODE (* Which mode of operation? *); IF X_MODE = None THEN GOTO 9999; GET_OUTPUT (* Where shall the output go? *); CONVERT_FN (FN, FOR_LOG_NAME); T$OPEN (42, FOR_LOG_NAME, IOSTAT); IF X_MODE <> Log THEN BEGIN IF ERR_FLAG = TRUE THEN GOTO 9999; WRITELN; WRITELN (INFO); DEF_LOG := 'MT1'; GET_FN (' Enter second drive name', FN2); IF X_MODE = None THEN GOTO 9999; CONVERT_FN (FN2, FOR_LOG_NAME); T$OPEN (43, FOR_LOG_NAME, IOSTAT); if iostat > 1 then writeln ('**** ERROR T$OPEN ',iostat:1); END; WRITELN; WRITELN (INFO); CASE X_MODE OF Copy, Log : PHOTOCOPY_MCP; (* MCP = Master Control Procedure *) Verify : PHOTOVERIFY_MCP; Copy_plus : BEGIN PHOTOCOPY_MCP; WRITELN; WRITELN (INFO); WRITELN (' >> Beginning Verification Pass <<'); WRITELN (INFO, ' >> Beginning Verification Pass <<'); PHOTOVERIFY_MCP; END; END (* CASE *); 9999 : BYE; END.