name restore title restore.com 6.4 for MS DOS 2.0 - 7.0, and Windows DOS compatibility box ; Copyright: (c) 1989-2017 Roedy Green, Canadian Mind Products Comment | Written in Microsoft Assembler MASM 5.0 or OptAsm 1.61b Last Updated: 2009-03-09 Roedy Green Authors: Dan Wright and Roedy Green Please report bugs and problems to: Roedy Green Canadian Mind Products #101 - 2536 Wark Street Victoria, BC Canada V8T 4G8 tel:(250) 361-9093 mailto:roedyg@mindprod.com http://mindprod.com Purpose ======= To restore files backed up under any version of DOS to any other version of DOS, Roedy Green wrote the original the Canadian Mind Products RESTORE program. Versions 1 through 4.4 handled the Backup format used by MS and PC DOS 2.0 through 3.2. CMP Restore replaces the RESTORE program that comes with MS DOS or IBM PC DOS. Dan Wright wrote version 5 of Restore to further extend the capability to include the new DOS backup format introduced with DOS 3.3. With this version, either of the formats will be recognized automatically and restored to any version of DOS from 2.0 to 5.0. It cannot restore backups created with DOS 6.0 through DOS 7.0 restore, but it can run under them. CMP Restore corrects the 20 bugs in the official Restore. This will allow people to restore from backups where before they could not. Restore is a free program. Syntax ====== RESTORE A: [C:][path\][filespec] [/S] [/P] [/Q] Where: A: = source floppy drive, i.e. A:, B:, etc. C: = target hard drive, i.e. C:, D:, etc. path = the directory where the files will be placed. filespec = the specification of files to be restored. May include wildcard chars. * and ?. /S = restore subdirectories also. /P = prompt before restoring each file. /Q = quiet, suppress advertising banner. Samples of Use ============== RESTORE A: C:\*.* /S /P -- restores all files, letting you decide which ones to restore individually. RESTORE A: C:\*.BAT/S -- restores all BAT files in all subdirectories. RESTORE A: C:\MySub\MyFile.Ext RESTORE A: C:\MySub\*.*/P/S/Q RESTORE A: C:\MySub/P RESTORE A: C:\MySub RESTORE A: \MySub -- presumes default drive RESTORE A: C:*.* -- presumes default directory RESTORE A: C:\*.BAT/S -- restores all BAT files in all subdirectories. It is not as elaborate as the commercial versions, only the /S and /P switches are supported. About the Authors ================= Dan Wright is a legally blind programmer seeking work in the field either on a contract or full-time basis. He is presenting this version of RESTORE as an example of his work. This code was written, and parts of it adapted from Canadian Mind Products RESTORE version 4.4, using a speech synthesizer and a minimal amount of visual feedback from the screen (not enough to read). Information on DOS function calls, MASM, etc. was obtained using Norton Guides and various reference books which I have had read onto cassette tape. The integrity of restored files was verified by redirecting the output of DIR commands and the output of a CRC utility to files before and after restoring and then comparing these using the DOS COMP and FC commands. Roedy Green is the author of the public domain Abundance Database compiler and the 32 bit BBL Forth compiler as well as a suite of many small public domain utilities. He also writes for The Computer Paper - a Vancouver free advertising paper. WARNING TO HACKERS ================== If you modify this program to look for existing files on hard disk before restoring, beware the APPEND command. It will fool you into thinking the file already exists when in fact it does not, there is just a similarly named file in one of the APPEND directories. The guys who wrote FastBack got fooled by this one. Notes an How BACKUP Works ========================= BACKUP uses standard DOS-formatted disks (they must be pre-formatted, but not necessarily cleared of all files). Backup first erases all files in the root directory of the diskette -- even read only files. (I have heard unconfirmed reports that some versions of BACKUP have a bug and you have to erase the hidden, system and read-only files yourself with QDOS II first.) If they were not erased, they could confuse RESTORE no end! In addition BACKUP will not work if there are subdirectories on the floppies. Instead of erasing them, it terminates with a mysterious stack overflow error. Notes on DOS 3.2 Backup Format ============================== MS DOS and PC DOS 2.0 through 3.2 use this format. There is a slight difference between MS and PC DOS formats -- the \ verses the / in file names. Each backup disk begins with a file called BackupId.@@@, followed by one or more of the backed-up files. If two identically named files are backed up onto the same physical disk, the subsequent file extensions are listed as @01, @02, etc. A file may be split across more than one disk, and in this case the name of all subsequent chunks are the same as the first chunk's (regardless of whether a non-unique name which had to be modified on its initial disk is unique on a later disk). Backup puts a file called \BackupId.@@@ on each diskette. In it you can find the diskette sequence number and the date the backup was done. There is also an indicator flag to tell if this is the last diskette of the set. offset len 01..01 1 -1 if this is last diskette in the set. 02..03 2 Sequence no. of diskette. In 8 bit BCD 04..05 2 Year e.g. 1986 backup done. True 1900 form, not 1980 relative. 06..06 1 Day backup done, binary 1..31. 07..07 1 Month backup done, binary 1..12. Each file is backed up with a 128 byte preamble. In the preamble is the fully qualified name of the file. To make life interesting, the subdirectories may be separated with either / or \. This goofiness is 90% of the reason my version of RESTORE had to be written. If files are too big to fit on diskette, there are two other numbers of interest in the preamble: 1. The sequence number of this chunk 1, 2 etc 2. A flag that indicates this is the last chunk. offset len 01 .. 01 1 -1 if last fragment of file, 0 otherwise. 02 .. 03 2 Sequence no. of this fragment, 04 .. 05 2 Padding. 06 .. 69 64 Fully qualified filename on hard disk without drive specifier padded on right with nuls. May have / instead \. 70 .. 83 14 Padding. 84 .. 84 1 Length of fully qualified name including one trailing nul. 85 ..129 45 Padding. Note that the preamble does NOT contain any of the following information that you might expect. 1. attribute byte of the file. 2. date and time file was last modified. 3. Length in bytes of the chunk. 4. offset in file where this chunk fits. 5. Length in bytes of the entire file. 6. Count of how many chunks there are supposed to be in total. 7. Checksum To glean this information we must examine the date, time and attribute bytes of the backup file itself. To figure out where the chunk fits in, we simply have to start at the beginning and keep track. Thus it is impossible to restore a file unless you start at chunk 1 and work sequentially through. Bad news if one of the chunks is unreadable. Following the preamble is the file itself -- absolutely raw -- no compression of any kind. When restoring a file we have four versions of the filename: Fname -- from the floppy directory - part of the floppy file name missing A:\ e.g. XXXXXX.XXX InName -- the actual name of the floppy file e.g. A:\XXXXXXXX.XXX HardName -- the name in the floppy premamble missing C: e.g. \SUB1\SUB2\XXXXXXXX.XXX OutName -- the actual name of the file on the hard disk e.g. C:\SUB1\SUB2\XXXXXXXX.XXX Notes on DOS 3.3 Backup Format ============================== MS and PC DOS 3.3 through 5.0 use the following format. DOS 7.0 backup uses the proprietary Norton backup format. On each backup diskette are two files CONTROL.001 - list of files, subdirectories, sizes BACKUP.001 - actual data files, not compressed, crammed together end to end in a single file. volume id is BACKUP 001 The diskettes are numbered in three places: CONTROL.002 BACKUP.002 volume id is BACKUP 002 The Control file **************** The control file begins with a 139 byte header: offset len 00..00 1 8B=139 length of record 01..08 8 the word BACKUP signature padded on the right with spaces 09..0A 2 diskette number, simple binary starting at 1 Does not use the goofy BCD format of DOS 3.2 0B..89 127 zeroes 8A..8A 1 -1 if last diskette in set, 0 otherwise Then come two types of entries: subdirectories and files. There is no special marker for the end of file other than the file size recorded in the directory. Subdirectory entry ****************** A 70-byte subdirectory entry looks like this. 00..00 1 HEX 46=70 length of record 01..3F 63 subdirectory name, no drive, no lead \, with embedded \, no trail \, zero padded on right 40..41 2 count of files following on this diskette in this subdir 42..45 4 offset in the Control file of the next subdirectory record. If there is no further subdirectory on this diskette, contains -1. The offset is absolute, not relative to this record. This way if we are restoring only some subdirectories, we can rapidly get on with the next one. The date, time and attribute byte of the subdirectory are NOT recorded. Restore builds directories as needed with the current date and time with a default attribute byte. File entry ********** Following a subdirectory entry are multiple file entries for the files being backed up in that subdirectory. A 34 byte file entry looks like this: 00..00 1 HEX 22=34 length of record 01..0C 12 file name with embedded dot, padded with 0s. no embedded spaces. 0D..0D 1 02 = another fragment to come on another diskette after this one. 03 = this is the last (possibly only) fragment. 0E..11 4 total size of file in bytes 12..13 2 Sequence number of this fragment starting at 1. 14..17 4 offset into Backup.nnn of start of file. 18..1B 4 size of fragment of file in bytes on this diskette 1C..1C 1 attribute byte of file in directory format. usually the archive bit is on. 1D..1D 1 zeroes 1E..1F 2 time in DOS directory format 20..21 2 date in DOS directory format Note on register conventions ============================ Whenever we call a subroutine, the subroutine is permitted to trash all registers. Thus the caller must save any registers needed. The way the code is written, this is very rarely necessary as all high level routines do nothing but calls and comparisons of memory resident variables. Setting the carry flag is often used by a subroutine to inform its caller that trouble has occurred. Callers often cheat when calling very simple routines. They fail to save necessary registers knowing that the called routine will not mess them up. If you make changes to low level routines beware! /* Version History: 1.0 1985-11-01 not released to the public restored all files ignoring the filespec 2.0 1986-12-25 /P /Q /S options. allowed you to select files to be restored. 3.0 1987-01-28 released to Charware subscribers fixed bug when restore had to handle 10 or more diskettes. 4.0 1987-03-05 fixed bug RESTORE A: C:*.* /S in root directory did not work. was treated as RESTORE A: C:\\*.* /S instead of C:\*.* /S fixed bug. Originally all disks in the backup set were checked to makes sure they were created on the same day to make sure they were part of the same set. Because backups can be created with the /A option, it is possible each diskette in the set is part of a different set. Thus the check was removed. 4.1 1987-05-08 version number now prints as part of the "Screwed up" message RESTORE.TXT documention separated from ASM source code change mailing address 4.2 1987-07-09 beeps when wants disk 4.3 1988-04-09 change of mailing address in banner 4.4 1988-12-24 change of mailing address in banner 5.0 1989-03-03 very limited distribution added support for DOS 3.3 through DOS 4.01 this was a major rewrite that took many times longer to write than the original. For this reason Version 5.0 is not Charware, but rather ShareWare. 5.1 1989-05-24 more friendly, simpler, less technical error messages. fancier prompts. tidier source code. bug fixed that restored a file fragment when you inserted the wrong diskette. avoid reprompting entire prompt when input bad. Abort Retry Ignore keystrokes no longer echo. / options no longer picky about leading space. warning message if source not A: or B: warning message if target not C: through G: fancier banner, with pause 5.2 1989-07-26 add extra blank line ahead of insert disk prompt extra information on site licenses separate registering text. now assembled with Optasm 5.3 1990-02-05 there are bugs in DOS 2.0 and 2.1 that can interfere with Restore. changes to Default_Subdir now account for the DOS bug when DOS gives mixed upper and lower case for the current directory and when DOS sometimes give directory names that are too long. 5.4 1990-08-03 better explanation of registration in banner. 5.5 1990-10-13 new documentation warning that RESTORE cannot restore to a different directory. new docs on how to handle problem of a restoring a backup when you don't know what is on it. better heading in the docs now works on LANS, no more IOCTL calls on hard drives. ^ now accepted as valid character in a filename. You can now assemble RESTORE in a way that makes it more suitable as an install program for multi-disk software packages. Some versions of DOS and some cachers have a bug that they fail to notice when you change the diskette in a floppy drive and thus give the program data from the previous diskette instead of the current one. To bypass this bug I added a disk reset just prior to each diskette swap. The symptom is that the program claims diskette 1 is in the drive, and demands diskette 2, when in fact you have already inserted diskette 2. 5.6 1992-02-06 notes that works on DOS 5.0 6.0 1993-06-17 notes on DOS 5 and DOS 6 use. new address and phone number. new information on site licensing. 6.1 1996 10-29 embed Quathiaski address notes on DOS 7.0. 6.2 1998-11-08 embed Barker address 6.3 2007-07-21 now free 6.4 2009-03-09 bundled with ANT build script. */ | ; End of comment. ;============================================================== ; Generate either a restore program or an install program INSTALLING equ 0 ; 0FFh = true if generating an install program ; install program has smaller alternate banner, ; makes no references to "restore diskettes" ; 0 = false if generating a restore program if1 if INSTALLING %out generating an INSTALL-style version of RESTORE.COM else %out generating a RESTORE-style version of RESTORE.COM endif endif ;============================================================== ifdef DEBUG ;; Declared on MASM command line. if1 %out Debug version. endif .xlist .lall else if1 %out Production version. endif endif ;============================================================== ; E Q U A T E S CR equ 0DH LF equ 0AH BEL equ 7 STD_IN equ 0 ; Handle for standard input device. STD_OUT equ 1 ; Handle for standard output device. STD_ERR equ 2 ; Handle for standard error device. STACK_SIZE equ 256 ; Size of program stack in bytes. Used ; to check for sufficient memory. BUFF_SIZE equ 512*9*10 ; Size of buffer for disk I/O. Size = 10 ; tracks for 360K floppy. HDR_REC_LEN equ 139 ; Length of header record in ; CONTROL.XXX. SUBDIR_REC_LEN equ 70 ; Length of subdirectory records ; in CONTROL.XXX. FILE_REC_LEN equ 34 ; Length of file records in ; CONTROL.XXX. ;============================================================== dbln macro Message, Msg_Lbl ;; Define Message as one line of a message terminated by CR/LF. If the ;; message label Msg_Lbl is supplied, make sure it has been declared and use it ;; to calculate the length of the message after the current line is defined. ifnb ;; If the message label has been supplied ifndef Msg_Lbl ;; but not declared, declare it. even msg_lbl label byte else if $+1 eq Msg_Lbl ;; Ensure start address of message is even ;; the same on both passes. endif endif endif ifnb ;; Define text of message. db "&Message" endif db CR, LF ;; Terminate line. ifnb ;; Calculate current length of message. msg_lbl&_len = $ - Msg_Lbl endif endm ;; End of macro dbln. ;============================================================== dbpl macro Prompt, Prompt_Lbl ;; Define partial line Prompt as a prompt with no terminating CR/LF. If ;; the prompt label Prompt_Lbl is supplied, make sure it has been declared and ;; use it to calculate the length of the prompt after the current line is ;; defined. ifnb ;; If the label is supplied but not ifndef Prompt_Lbl ;; declared, declare it. even prompt_lbl label byte else if $+1 eq Prompt_Lbl ;; Ensure start address of prompt is even ;; the same for both passes. endif endif endif ifnb ;; Define text of prompt. db "&Prompt" endif ifnb ;; Calculate current length of prompt ;; if label supplied. prompt_lbl&_len = $ - Prompt_Lbl endif endm ;; End of macro dbpl ;============================================================== dbs macro Len ;; Len spaces db &Len DUP (" ") ;; define string of spaces endm ;============================================================== dbpr macro Prompt, Prompt_Lbl ;; Define Prompt as a prompt with no terminating CR/LF and one bell char. If ;; the prompt label Prompt_Lbl is supplied, make sure it has been declared and ;; use it to calculate the length of the prompt after the current line is ;; defined. ifnb ;; If the label is supplied but not ifndef Prompt_Lbl ;; declared, declare it. even prompt_lbl label byte else if $+1 eq Prompt_Lbl ;; Ensure start address of prompt is even ;; the same for both passes. endif endif endif ifnb ;; Define text of prompt. db "&Prompt" endif db BEL ;; Add one bell char. ifnb ;; Calculate current length of prompt ;; if label supplied. prompt_lbl&_len = $ - Prompt_Lbl endif endm ;; End of macro dbpr. ;============================================================== dbzs macro Z_String, Z_String_Lbl ;; Define the contents of Z_String as an ASCII Z string with the label ;; Z_String_Lbl. ifnb ;; Declare label for Z string. even z_string_lbl label byte else ;; Force error if no label. .err endif ifnb ;; Define Z string. db "&Z_String" endif db 0 ;; Add terminating nul. endm ;; End of macro dbzs. ;============================================================== Patch_Num macro Hex_Num, Num_Start, Num_Width local pn_more ;; Display the 16 bit quantity Hex_Num as a decimal integer right justified ;; in a field Num_Width characters wide with the right most digit at offset ;; Num_Start. It generally patches the number into an error message or prompt. ifdif , mov ax, Hex_Num ;; Value to display in ASCII decimal. endif lea di, Num_Start ;; Location of right most digit. mov cx, Num_Width ;; Width of field in which to place ;; Number. call Patch_Sub endm ;; End of macro Patch_Num. ;============================================================== Say_Err_Msg macro Err_Msg ;; Print Err_Msg&_Len characters beginning at the address of Err_Msg ;; to the standard error device. lea dx, Err_Msg mov cx, Err_Msg&_Len mov bx, STD_ERR mov ah, 40H int 21H ;; 40H=Write. endm ;; End of macro Say_Err_Msg. ;============================================================== Say_Msg macro Msg ;; Print Msg&_Len characters beginning at the address of Msg ;; to the standard output device. These can be redirected via Pipes. lea dx, Msg mov cx, Msg&_Len mov bx, STD_OUT mov ah, 40H int 21H ;; 40H=Write. endm ;; End of macro Say_Msg. ;============================================================== D_Count = 0 ;; Initialize count of dangerous files. Test_Count = 0 ;; Used to detect incorrect parameters. ;============================================================== Def_D_File macro D_Filename, DC ;; Define an ASCII Z string containing the filename passed in D_File and ;; calculate its length. ;; Keep track of the number of filenames processed in D_Count and use this ;; value to generate unique symbols for the address and length of each ;; Z string. ;; Only the first parameter, the filename of a file which could be dangerous ;; to restore to the root directory, should be supplied when calling this ;; macro. ifb ;; This part executes on initial call. d_count = D_Count + 1 ;; Increment count of dangerous files. ;; Now the macro calls itself passing the value of D_Count as a second ;; parameter. This enables the generation of symbol names made unique by ;; including the current value of D_Count for the address of the string and ;; its length. def_d_file , %D_Count else ;; This part executes when the macro ;; calls itself. if Test_Count eq D_Count ;; This occurs if the macro was .err ;; erroneously called with a second exitm ;; parameter supplied. endif even d_file&dc label byte ;; Generate label for string address. ifnb ;; The first parameter can't be blank. db "&D_Filename" ;; Define string. db 0 ;; Add terminating nul. else .err endif d_file&dc&_len = $ - D_File&DC ;; Generate symbol containing string ;; length. ;; Test_Count follows the value of D_Count to prohibit the execution of the ;; second part of the macro before the first part which would happen if a ;; second parameter was included in the initial call. D_Count is incremented ;; on execution of the first part allowing subsequent error free execution of ;; the second part. Incrementing Test_Count at the end of the second part ;; ensures that the first part must be executed before the second part may ;; be executed again. test_count = Test_Count + 1 endif endm ;; End of macro Def_D_File. ;============================================================== Blank_Extension macro Ext_Start ;; Patch a filename extension with wild card characters. Ext_Start points ;; at the beginning of the file extension. lea di, Ext_Start mov cx, 3 ;; Length of a file extension. mov al, '?' cld rep stosb endm ;; End of macro Blank_Extension. ;============================================================== DTA equ 80H ;; offset in PSP of default DTA. ; Template for DTa after call to dir. search functions 4EH and 4FH. Dir_Search struc DS_Reserved db 21 dup (?) ; Reserved for DOS. DS_Attr db ? ; File attribute in dir. format. DS_Time dw ? ; File time in dir. format. DS_Date dw ? ; File date in dir. format. DS_Size dd ? ; file size. DS_Name db 13 dup (?) ; Filename as ASCII Z string. Dir_Search ends ;============================================================== ; BACKUPID.@@@ file format in DOS 3.2 Bkp_ID_Rec struc ; 7 bytes of interesting info in ; \BACKUPID.@@@. BI_Last db ? ; -1 if this is last diskette in the set. BI_Seq dw ? ; Sequence no. of diskette. This is ; originally in 8 bit BCD form but ; will be converted to true binary. BI_YYYY dw ? ; Year e.g. 1986 backup done. ; True 1900 form, not 1980 relative. BI_DD db ? ; Day backup done, binary 1..31. BI_MM db ? ; Month backup done, binary 1..12. Bkp_ID_Rec ends ;============================================================== ; 128 byte header at the beginning of every DOS 3.2 backup file. Preamble struc P_Last db ? ; -1 if last fragment of file, 0 ; otherwise. P_Frag_Seq dw ? ; Sequence no. of this fragment, ; 1 = first, 2 = second, etc. dw ? ; Padding. P_Hard_Name db 64 dup (?) ; Fully qualified filename on hard disk ; without drive specifier, padded on ; right with nuls. May have / instead ; of \. db 14 dup (?) ; Padding. P_Hard_Len db ? ; Length of fully qualified name ; including one trailing nul. db 45 dup (?) ; Padding. Preamble ends ;============================================================== ; Header Record in DOS 3.3 Control.XXX file Hdr_Rec struc Hdr_Len db ? ; Length of header record. Hdr_Sig db "BACKUP " ; Backup signature. Hdr_Seq dw ? ; Disk number in set, 1=first, 2= ; second, etc. db 127 dup (?) ; Padding. HDR_Last db ? ; -1 if last diskette in set, 0 otherwise Hdr_Rec ends ;============================================================== ; Subdir Record in DOS 3.3 Control.XXX file Subdir_Rec struc Subdir_Len db ? ; Length of subdirectory record. Subdir_Name db 61 dup (?) ; Subdirectory name. No leading or ; trailing backslash, 0 padded on right. dw ? ; Padding. Subdir_Count dw ? ; Count of files following in this ; subdirectory on this diskette. Subdir_Next dd ? ; Absolute offset in CONTROL.XXX of next ; subdirectory record. (-1 if no more ; subdir entries) Subdir_Rec ends ;============================================================== ; File Record in DOS 3.3 Control.XXX file File_Rec struc File_Len db ? ; Length of file record. File_Name db 12 dup (?) ; file name with embedded dot, zero ; padded, no embedded spaces. File_More db ? ; 2=another fragment on next diskette, 3= ; last and possibly only fragment. File_Size dd ? ; Size of file in bytes. File_Frag_Seq dw ? ; Sequence number of this fragment, ; 1=first, 2=second, etc. File_Frag_Start dd ? ; offset in BACKUP.XXX where frag. ; starts. File_Frag_Size dd ? ; Size of fragment of file on this ; diskette. File_Attr db ? ; File attributes in dir. format. db ? ; Padding. File_Time dw ? ; File time in dir. format. File_Date dw ? ; File date in dir. format. File_Rec ends ;============================================================== Code segment para assume cs : Code, ds : Code, ss : Code, es : Code org 100H Start: jmp Restore_Main ;============================================================== ; V A R I A B L E S even Rest_Count dw 0 ; count of files restored. Want_Seq dw 1 ; Expected disk sequence no. Want_Frag_Seq dw 1 ; Expected fragment sequence no. Want_Size dd 0 ; Expected file size for multiple ; fragment files and for verifying copy. Bkp_Seq dw 1 ; Current Diskette sequence no. Ctl_Hndl dw -1 ; CONTROL.XXX or BACKUPID.@@@ handle. Bkp_Hndl dw -1 ; Handle for backup data files. Out_Hndl dw -1 ; Output file handle. Format_Flags db 0 ; Used to indicate format of backup ; diskettes being restored. DOS32 equ 1 ; Bit 0 set = DOS 3.2 format. DOS33 equ 2 ; Bit 1 set = DOS 3.3 format. Flags db 01H ; If bit set: LastFrag equ 01H ; bit 0 = last file fragment. LastDisk equ 02H ; bit 1 = last diskette in set. NewDisk equ 04H ; Bit 2 = New Diskette. WrongOk equ 08H ; Bit 3 = user OKed diskette out of seq. SkipFile equ 10H ; Bit 4 = Do not restore file. FileMatched equ 20H ; Bit 5 = filename was matched. WildCards equ 40H ; Bit 6 = wild card chars. in command ; line pathspec. WasPeriod equ 80H ; Bit 7 = period processed in command ; line pathspec. Cmd_Flags db 0 ; Flags for command line switches. Prompt_Needed equ 01h ; Bit 0 set = prompted restore (/p) RestoreSubs equ 04h ; Bit 2 set = restore subdirectories (/s) Quietly equ 08h ; Bit 3 set = quiet, suppress banner (/Q) Bkp_Sig db "BACKUP " ; Valid backup control file signature. Bkp_Sig_Len equ $ - Bkp_Sig ; Define filenames used in both backup schemes. X's will be patched later with ; correct values. ; this is strange MASM syntax. Symbol name is on right. dbzs Bkp_ID_Name ; Backup ID filename (DOS 3.2). dbzs All_Pattern ; Pattern to find files on floppy in ; DOS 3.2 backup scheme. dbzs Ctl_Filename ; Backup control file name (DOS 3.3). dbzs Bkp_Filename ; Backup data filename (DOS 3.3). ; allocate two more bytes so kp_Filename can be used to hold floppy filenames ; in DOS 3.2 scheme. db 2 dup (0) Rest_Name label byte ; Pathspec to be restored. Rest_Drive db 'X:' ; Hard disk drive letter. Patched later. db '\' ; Not supplied in subdir records. Rest_Path db 75 dup (0) Filespec_Ptr dw 0 ; Ptr. to start of filespec in Rest_Path. Rest_Name_Len dw 0 ; Length of drive and pathspec to be ; restored not including terminating nul. Match_File db "XXXXXXXX.XXX" ; Name of file proposed to be restored. ; Extracted from Rest_Path in format ; comparable with command line pattern. CL_Subdir_Pattern db 64 dup (0) ; Subdirectory pattern from command line ; of files to be restored. Subdir_Pat_Len dw 0 ; Length of subdirectory pattern ; including trailing backslash, but ; excluding leading backslash and ; trailing nul. CL_File_Pattern db "????????.???" ; Pattern from command line of files ; to be restored. ; Files dangerous to restore to the root directory. def_d_file def_d_file def_d_file def_d_file def_d_file ; Define prompts and informational and error messages. Wrong_Ver db "RESTORE requires DOS 2.0 or later.$" ; Not done with dbline since handles may not work. Part_X_Msg db " part " Part_X db "XXX" Part_X_Msg_Len equ $ - Part_X_Msg dbpl ,echochar db 08 ; allow keystroke echo dbpl ,echochar ; Embedded tabs are ok, since DOS will expand them again. ; Always leave RestBanner in code, even if not displayed. dbln < ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»>, RestBanner dbln < º RESTORE 6.3 for PC DOS and MS DOS 2.0 through 7.00 º> dbln < ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ> dbln dbln < Copyright: (c) 1986-2017 Roedy Green, Dan Wright, Canadian Mind Products> dbln dbln < °±²Û Canadian Mind Products Û²±°> dbln < #101 - 2536 Wark Street> dbln < Victoria, BC Canada V8T 4G8> dbln < tel:(250) 361-9093> dbln < mailto:roedyg@mindprod.com> dbln < http://mindprod.com> dbln dbln dbln dbln dbln dbln dbln dbln ,RestBanner if INSTALLING ;; Roll your own banner here!! dbln <ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»>, InstBanner dbln <º °±²Û Project Discovery Main Install Program Û²±° º> dbln <ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ> dbln dbln ,InstBanner endif dbln <úääääääääääääääääääääääääääääääääääääääääääääääääää¿> ,Help_Msg dbln <³ RESTORE A: [C:][\path\][filespec] [/S] [/P] [/Q] ³> dbln <ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ> dbln dbln dbln dbln dbln < May include wildcard chars. * and ?.> dbln dbln

dbln dbln dbln dbln dbln < Restore A: C:\MySub\MyFile.Ext> dbln < Restore A: C:\M???.Bat/P/S/Q> dbln < Restore A: C:\*.*/S> dbln < Restore A: C:\MySub\> dbln < RESTORE A: C:\MySub\MyFile.> ,Help_Msg if INSTALLING dbln ,insert dbln dbln <ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿> dbpl <³ Insert diskette > Insert_Seq db "XXX in drive " Insert_Drive db "X: ³" dbln dbln <ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ> dbln ,Insert else ; Restoring dbln ,insert dbln dbln <ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ¿> dbpl <³ Insert backup diskette > Insert_Seq db "XXX in drive " Insert_Drive db "X: ³" dbln dbln <ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÙ> dbln ,Insert endif dbpr ,strike_any dbln ,Strike_Any dbln ,announce_bkp_msg dbs 3 dbpl ABM_Seq db "XXX" dbpl < into drive > ABM_Drive db "X:" dbln dbs 3 dbpl ABM_MM db "XX-" ABM_DD db "XX-" ABM_YYYY db "XXXX" dbln ,Announce_Bkp_Msg dbpl ,old_bkp_format dbs 3 dbln dbln ,Old_Bkp_Format dbpl ,new_bkp_format dbs 3 dbln dbln ,New_Bkp_Format dbln <°±²û Usually the source floppy drive is A: or B: Are you sure? Û²±°> ,Strange_Source dbln <°±²û Usually the target hard drive is C: D: ... G: Are you sure? Û²±°> ,Strange_Target dbln <°±²û Your request to restore was ambiguous. Û²±°>, Ambiguous dbln dbln <ÚÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄ¿> dbln <³ Restore A: C:\Mysub\X\ /S /P ³ if X is a subirectory> dbln <³ Restore A: C:\Mysub\X. /S /P ³ if X is a file> dbln <ÀÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÁÄÄÄÄÄÄÄÙ> , Ambiguous dbln <°±²û Diskette is from another backup set. Û²±°> ,Wrong_Set dbln ,AR_Msg dbln dbln dbpr ,AR_Msg dbln ,out_of_seq dbln <°±²Û Diskette out of sequence. Û²±°> dbln dbln ,Out_Of_Seq dbln ,ARI_Msg dbln dbln dbln dbpr ,ARI_Msg dbpl ,prompt_user1 dbs 9 dbpl ,Prompt_User1 dbpl < (Y/N): > ,Prompt_User2 dbln < restored> ,Restored ; single file msg if INSTALLING dbln ,done_msg dbln dbpl DM_Count db "XXXXX file(s) installed on hard disk." dbln ,done_msg else dbln ,done_msg dbln dbpl DM_Count db "XXXXX file(s) restored." dbln ,done_msg endif if INSTALLING dbpr ,none_restored dbln ,None_Restored else dbpr ,none_restored dbln ,None_Restored endif dbln <°±²û Insufficient memory. Û²±°> ,Insufficient dbln ,Insufficient dbln <°±²û Invalid parameters. Û²±°> ,Bad_Params dbln <°±²û Invalid path. Û²±°> ,Bad_Path dbln <°±²û Invalid file specification. Û²±°> ,Bad_Filespec dbln <°±²û Invalid source drive specification. Û²±°> ,Bad_Source dbln <°±²û Invalid target drive specification. Û²±°> ,Bad_Target dbln <°±²û Target drive must not be same as source. Û²±°> ,Same dbln <°±²û Wildcard characters not allowed in path. Û²±°> ,Bad_Wildcards dbln <°±²û Unrecognized command line switch. Û²±°> ,Bad_Switch dbln <°±²û Input error -- possibly you removed the floppy too soon. Û²±°> ,In_IO_Err dbln <°±²û Output error -- possibly the hard disk is full. Û²±°> ,Out_IO_Err dbln <°±²û Possibly damaged backup diskette. Û²±°> ,Floppy_Trouble dbln <°±²û Hard disk trouble. Û²±°> ,Hard_Disk_Trouble dbln <°±²û Too many open files. Û²±°> ,Too_Many_Open dbln ,Too_Many_Open dbln <°±²û Mysterious error. Diskette possibly damaged. Û²±°> ,Unknown dbln ,crlf dbpr ,beep dbln , Cant_Restore dbln , Cant_Restore IF installing dbln ,not_bkp dbpl <°±²Û The wrong diskette is in drive > NB_Drive db "X: Û²±°" dbln ,Not_Bkp else dbln ,not_bkp dbpl <°±²Û Non-backup diskette in drive > NB_Drive db "X: Û²±°" dbln ,Not_Bkp endif dbln ,special_attr dbln <°±²Û This is a Read-only, Hidden, and/or System file. Û²±°> dbln dbln ,Special_Attr dbln ,aborted dbpl Aborted_Count db "XXXXX" dbln < files restored.> ,Aborted ; Define table of valid filename and pathname chars. Valid_Chars db 0,"!",0,"#$%&'()",0,0,0,"-",0,0, "0123456789", 6 dup (0) db "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_" db "`ABCDEFGHIJKLMNOPQRSTUVWXYZ{",0,"}",0,0 ;============================================================== Patch_Sub proc near ; DOS 3.2 and 3.3 ; on entry ax=num di=target addr cx=num width ; See Patch_Num macro for more details. mov bx, 10 std PN_More: xor dx, dx ; Zero out high word. div bx xchg dx, ax ; Get remainder in al. add al, '0' ; Convert to ASCII stosb ; and store it in the string. mov ax, dx or ax, ax ; All done? loopnz PN_More ; Drop out if so. mov al, '0' ; Pad on the left with zeros. rep stosb cld ret Patch_Sub endp ;============================================================== Parse proc near ; DOS 3.2 and 3.3 ; parse the command line tail for the source drive, target drive, ; pathspec pattern, and switches specified. ; ; Returns: ; ; CL_Subdir_Pattern contains the pattern against which the dirspecs. ; of files to be restored are compared. An empty pattern specifies ; the root directory; otherwise, the pattern is an absolute dirspec. ; stored with a trailing but no leading backslash. ; ; Subdir_Pat_Len contains the length of the subdir pattern against ; which the dirspecs. of files to be restored are matched. If the ; pattern specifies the root directory, this value is 0; otherwise, ; it contains the length of the pattern including a trailing but not ; a leading backslash. ; ; CL_File_Pattern contains a 12 byte fixed length file pattern which ; files to be restored must match. The pattern contains a period at ; the 9th position and the filename and extension are padded on the ; right to full length with blanks. Any asterisks are expanded to ; their question mark equivalents. ; ; Cmd_Flags bit 0 set if /P switch specified, clear otherwise . ; ; Cmd_Flags bit 2 set if /S switch specified, clear otherwise. ; ; Cmd_Flags bit 3 set if /Q switch specified, clear otherwise. ; xor ch, ch mov cl, ds:80H ; Get length of command line tail. jcxz P_Help mov di, 81H ; Point at command line tail. mov al, ' ' ; Skip leading spaces. cld repe scasb jz P_Help jcxz P_Err cmp byte ptr [di], ':' jnz P_Err push cx ; Save no. of remaining chars. call Get_Source_Drive pop cx inc di ; Bypass the colon. dec cx jnz P_Get_Target call Default_Drive call Default_Subdir ret P_Help: if INSTALLING say_err_msg InstBanner ; Just plain RESTORE no parms else say_err_msg RestBanner ; Just plain RESTORE no parms endif call Strike_Any_Key call Abort_With_Help P_Err: say_err_msg Bad_Params call Abort_With_Help P_Get_Target: cmp byte ptr [di], '/' ; Is target supplied? jz P_Use_Dflts ; No, use default drive and pathspec. mov ah, cl ; Should be at least one space dec ah ; between source and target. mov al, ' ' ; Skip spaces between source and repe scasb ; target specifications. jz P_Use_Dflts ; Rest of command line was spaces. cmp ah, cl ; At least one space? jz P_Err ; No, abort. jcxz P_Use_Dflt_Drv ; Use default drive for target if cmp byte ptr [di], ':' ; if none specified. jnz P_Use_Dflt_Drv push cx call Get_Target_Drive pop cx inc di ; Point to start of pathspec. dec cx jnz P_Get_Pathspec call Default_Subdir ; Use default subdir for pathspec ret ; if none specified. P_Use_Dflt_Drv: push cx call Default_Drive pop cx inc cx ; Back up to start of pathspec. dec di P_Get_Pathspec: cmp byte ptr [di], '/' ; Switches could be here. jz P_Get_Switches ; They are, skip to switch processing. cmp byte ptr [di], ' ' ; If space here, switches must follow. jz P_Get_Switches ; call Get_Pathspec jmp short P_Get_Switches P_Use_Dflts: push cx push di call Default_Drive call Default_Subdir pop di pop cx ; If any switches were specified, di points at the first slash. P_Get_Switches: mov al, '/' P_More: jcxz P_Done repne scasb jne P_No_More jcxz P_Err call Get_Switch inc di dec cx jmp short P_More P_No_More: mov al, ' ' ; Ignore trailing spaces on command line. cmp [di-1], al jnz P_Err jcxz P_Done repe scasb jnz P_Err P_Done: ret ; actual Parse EndP but contains nested procs. ;============================================================== Get_Source_Drive proc near ; DOS 3.2 and 3.3 ; nested in Parse ; Test the validity of the drive letter pointed to by [di-1]. Patch the ; names of the backup control and data files with it if it is valid, abort ; otherwise. Used by Parse and byte ptr [di-1], 0DFH ; Convert drive to upper case. ; determine if floppy exists using IOCTL 44 04 read control data ; cannot use 1C as that requires a mounted floppy mov bl, [di-1] sub bl, 'A'-1 ; 1 = drive A, 2 = drive B, etc. xor cx, cx ; Read 0 bytes. ; is returned. mov ax, 4404H ; Function = IOCTL Read from block int 21H ; device. jnc GSD_Check ; Drive is valid. cmp ax, 0FH ; Was drive invalid? jz GSD_Err ; Yes, abort GSD_Check: mov al, [di-1] ; get drive letter cmp al,'A' ; usually source is A: or B: jb GSD_Warn cmp al,'B' ja GSD_Warn GSD_OK: mov al, [di-1] ; Patch filenames with drive letter. mov Ctl_Filename, al mov Bkp_Filename, al mov Bkp_ID_Name, al mov All_Pattern, al mov Insert_Drive, al ; Patch insert message. mov ABM_Drive, al mov NB_Drive, al ret GSD_Warn: say_err_msg Strange_Source ; expect A: or B: as source. call Strike_Any_Key jmp GSD_OK GSD_Err: say_err_msg Bad_Source call Abort_With_Help Get_Source_Drive endp ;============================================================== Get_Target_Drive proc near ; DOS 3.2 and 3.3 ; nested in Parse ; Get the target drive specification pointed to by [di-1] and make sure that ; it is valid and that it is different from the ; source drive. If all is OK, use it to patch the restore name drive ; letter. Abort otherwise. Used by Parse. ; and byte ptr [di-1], 0DFH ; Convert drive to upper case. ;; OLD code using IOCTL - did not work on LAN drives ;; mov bl, [di-1] ;; sub bl, 'A'-1 ; 1 = drive A, 2 = drive B, 3 = C etc ;; xor cx, cx ; Read 0 bytes. ;; lea dx, Buffer ; Point at a safe place in case something ;; ; is returned. ;; mov ax, 4404H ; Function = IOCTL Read from block ;; int 21H ; device. ;; jnc GTD_Check ; Drive is valid. ;; cmp ax, 0FH ; Was drive invalid? ;; jz GTD_Err1 ; Yes, abort mov dl, [di-1] sub dl, 'A'-1 ; 1=drive A, 2= drive B etc. mov ah,1Ch ; Function 1Ch get disk characteristics xor cx, cx push DS int 21h pop DS jcxz GTD_Err1 ; cx = sector size GTD_Check: mov al, [di-1] cmp al, 'C' ; allow C: .. G: without comment jb GTD_Warn cmp al, 'G' ja GTD_Warn GTD_OK: mov al, [di-1] ; Make sure source and target drives cmp al, Ctl_Filename ; are different. jz GTD_Err2 ; Abort if not. mov Rest_Drive, al ; Patch restore name with drive letter. ret GTD_Warn: say_err_msg Strange_Target ; expect C: or D: as source. call Strike_Any_Key jmp GTD_OK GTD_Err1: say_err_msg Bad_Target call Abort_With_Help GTD_Err2: say_err_msg Same call Abort_With_Help Get_Target_Drive endp ;============================================================== Default_Drive proc near ; DOS 3.2 and 3.3 ; nested in Parse ; Get the default drive to use as the target drive. Make sure it is ; different from the source drive. Use it to patch the ; pathspec of files to be restored if its OK, abort otherwise. ; Used by Parse. mov ah, 19H ; Function = Get Current Disk. int 21H add al, 'A' ; Convert to ASCII. 0=A, 1=B, etc. cmp al, Ctl_Filename ; Make sure target is different from ; source. jz DD_Same mov Rest_Drive, al ; Patch restore pathspec with drive. ret DD_Same: say_err_msg Same call Abort_With_Help Default_Drive endp ;============================================================== Default_Subdir proc near ; DOS 3.2 and 3.3 ; nested in Parse ; Begin restore pattern with default subdir ; Calculate length of default subdir including trailing backslash but not ; including leading backslash or trailing nul and return it in Subdir_Pat_Len. ; used by Parse mov dl, Rest_Drive ; Get target drive letter and convert sub dl, 'A'-1 ; to code (1=A, 2=B, etc.). lea si, CL_Subdir_Pattern ; si points to buffer for returned ; subdir. mov ah, 47H ; Function = Get Current Subdir. int 21H jc DS_Err ; Calculate length of subdir name with trailing backslash which we will ; add, but without leading backslash or trailing nul. The returned subdir ; name is nul terminated without leading or trailing backslash. ; Because DOS 2.X does not necessarily return the subdir in upper case and ; because it also may return extra chars appended to the end of path name ; elements which are the maximum length, we must also convert the subdir to ; upper case and truncate each path element to the maximum length. mov di,si xor bx,bx ; Used to watch length of path elements. mov cx,size CL_Subdir_Pattern ; Maximum length of subdir. DS_Loop: lodsb ; Get next char in subdir. inc bx ; Update length of this path element. cmp al,'a' ; Convert letters to upper case. jb DS_Not_LC_Letter cmp al,'z' ja DS_Not_LC_Letter and al,0DFH DS_Not_LC_Letter: mov byte ptr [di],al ; Store this char. or al,al ; Terminating nul? jz DS_Get_Length ; Yes, get subdir length. cmp al,'\' ; Path separator? jne DS_Not_Sep ; No. xor bx,bx ; Yes, reset path element length ; counter. DS_Not_Sep: cmp al,'.' ; If period, can have 3 more chars ; before '\'. jne DS_Not_Dot ; Not period. mov bx,5 ; Allow 3 more chars in this element. DS_Not_Dot: cmp bx,8 ; Path element too long? ja DS_Too_Long ; Yes, let next char overwrite this one. inc di ; No, bump ptr. DS_Too_Long: loop DS_Loop jmp DS_Err ; Terminating nul missing. DS_Get_Length: mov si,di ; Save ptr to subdir end. sub di,offset CL_Subdir_Pattern ; Get subdir length. or di,di ; Was default subdir root? jz DS_Root ; Yes, don't need trailing '\'. mov byte ptr [si], '\' ; Add trailing backslash. inc di DS_Root: mov Subdir_Pat_Len, di ; Save length. ret DS_Err: say_err_msg Unknown call Abort_With_Help Default_Subdir endp ;============================================================== Get_Pathspec proc near ; DOS 3.2 and 3.3 ; nested in Parse ; Parse the pathspec entered on the command line inserting the current ; directory if it is relative. Perform edit checks on the pathspec and abort ; if it is invalid. Used by Parse ; ; Called with: ; ; di points to the character following the target drive on the ; command line or to the first non-blank character following the ; source drive if no target drive is specified. ; ; cx contains the number of characters of the command line tail ; remaining to be processed. ; ; Returns: ; ; di points to the first switch char (/) on the command line if ; there are any, otherwise it points 1 byte past the end of the line. ; ; cx Contains the number of characters in the command line tail ; remaining to be processed. ; ; CL_Subdir_Pattern contains the full absolute dirspec. against which ; files to be restored must be matched. This string includes a ; trailing but no leading backslash. If the resultant dirspec. ; is the root, its value is empty. ; ; Subdir_Pat_Len contains the length of the dirspec. contained in ; CL_Subdir_Pattern including a trailing but no leading backslash. If ; the resultant dirspec. is the root, it contains 0. ; ; CL_File_Pattern contains a 12 byte fixed length file pattern against ; which files to be restored must match. It contains a period in ; the 9th position with the filename and extension padded on the right ; to full length with blanks and any asterisks expanded to their ; question mark equivalent. ; cmp byte ptr [di], '\' ; absolute path specified? jz GP_Absolute ; Yes, don't get current dir. push cx push di call Default_Subdir pop di pop cx jmp short GP_Trailing ; Proceed to check for trailing spaces on ; command line. GP_Absolute: inc di ; Bypass leading backslash. dec cx jnz GP_Trailing ret GP_Trailing: cmp byte ptr [di], ' ' ; Check for trailing spaces on command ; line. jnz GP_Copy ; If none, proceed. ; allow optional spaces ret GP_Copy: mov si, di ; Save ptr. to start of pathspec. mov al, '/' ; Search for switches. repne scasb ; Find beginning of switches if any. jne GP_No_Switches ; If none, di points one past end of line dec di ; otherwise, two past end of pattern. inc cx ; allow optional space before switches GP_No_Switches: cmp byte ptr [di-1], ' ' ; Ignore trailing spaces on command line. jnz GP_Onward ; No more trailing spaces, proceed. dec di ; Skip trailing space. inc cx ; jmp short GP_No_Switches ; Look for more. GP_Err0: say_err_msg Bad_Wildcards call Abort_With_Help GP_Err1: say_err_msg Bad_Params call Abort_With_Help ; Now di points 1 past end of pattern. GP_Onward: push cx ; Save remaining char count push di ; and ptr. to switches. mov cx, di ; Get length of pathspec in cx. sub cx, si lea di, CL_Subdir_Pattern ; Copy pathspec. add di, Subdir_Pat_Len ; Move ptr. past default dir. if any. lea bx, Valid_Chars ; Table of valid chars for pathspecs. inc cx ; compensate for DEC CX inside loop. GP_More_Zero: test Flags, WasPeriod ; Did last path element end with a dot? jz GP_No_Dot ; No, proceed. dec di ; Yes, get rid of it. mov byte ptr [di-1], '\' GP_No_Dot: mov Subdir_Pat_Len, di ; Used to get length of subdir pattern. xor dx, dx ; Zero path element length counter. test Flags, WildCards ; Wild card char in path? jnz GP_Err0 ; Yes, abort. dec cx ; Decrement count of remaining chars. jcxz GP_Copied cmp byte ptr [si], '\' ; "\\" not allowed. jz GP_Err2 GP_More: cmp di, offset CL_Subdir_Pattern+62 ; Pattern too long? jae GP_Err1 lodsb call Edit_Pathspec ; Convert char in al to upper case and jc GP_Err2 ; set Carry if char is invalid. stosb ; copying. inc dx ; Increment counter for this path ; element. cmp al, '\' ; Is this a path separator? jz GP_More_Zero ; Yes, zero dx before re-entering loop. cmp dx, 12 ; Is this element too long? ja GP_Err2 ; Yes, abort. loop GP_More GP_Copied: cmp di, Subdir_Pat_Len ; Is this the nul file pattern? jz GP_Nul ; Yes, no need to decode file pattern. call Get_File_Pattern GP_Nul: sub Subdir_Pat_Len, offset CL_Subdir_Pattern ; Calculate subdir pattern length. pop di ; Retrieve ptr. to switches. pop cx ; Retrieve remaining char count. GP_Done: ret GP_Err2: mov di, si mov al, '\' ; Was this part of filespec repne scasb jnz GP_Err3 ; Yes, use next error message. say_err_msg Bad_Path call Abort_With_Help GP_Err3: say_err_msg Bad_Filespec call Abort_With_Help Get_Pathspec endp ;============================================================== Edit_Pathspec proc near ; DOS 3.2 and 3.3 ; Determine if the char in al is a valid filename or pathname ; char and if it is a wildcard char Used by Get_PathSpec ; ; Called with: ; ; al contains char to be validated. ; ; bx contains ptr. to table of valid filename and pathname chars. ; ; dx contains number of preceding chars for this pathspec element. ; ; Returns: ; ; Carry flag set if char was invalid, clear otherwise. ; ; Flags bit 6 set if char was a wildcard char, clear otherwise. ; ; Flags bit 7 set if the char was a period, clear if ; no period has been processed or if some character other than a ; backslash has been processed since the last period. The calling ; routine needs to know if a search pattern contains a period not ; followed by an extension in a path element since in this form ; the pattern will not match any dirspec's originating in the ; backup control file. ; cmp al, '.' jnz EP_Not_Period or dx, dx ; Is this the first char in a ; path element. jz EP_Err ; Yes, abort. cmp dx, 8 ; Has this element already got a '.'? ja EP_Err ; Yes, abort. mov dx, 8 ; Can only have 3 chars. after this. or Flags, WasPeriod ; Signal calling routine period ; processed. jmp short EP_OK EP_Not_Period: cmp al, '\' ; See Flags bit 7 description in ; this procedure's description. jz EP_OK and Flags,0ffh-WasPeriod ; 7fH cmp dx, 8 ; Should this have been a period? jz EP_Err ; Yes, abort. cmp al, '?' ; Set Flag if wildcard. jz EP_Wilds cmp al, '*' jz EP_Wilds sub al, 20H jl EP_Err ; al is out of range. xlat Valid_Chars ; Return upper case if letter, or al, al ; 0 if not a valid char. jz EP_Err EP_OK: clc ret EP_Wilds: or Flags, Wildcards ; Bit 6 set = wildcards found in clc ; command line pathspec. ret EP_Err: stc ret Edit_Pathspec endp ;============================================================== Get_File_Pattern proc near ; DOS 3.2 and 3.3 ; Convert the file pattern entered on the command line to a fixed format 12 ; chars. in length with a period in the 9th position. Pad the filename and ; extension on the right with blanks and expand any asterisks to their ; question mark equivalent. Used by Get_PathSpec ; ; Called with: ; ; di points to the byte following the last char copied from the ; command line to CL_Subdir_Pattern. This char is guaranteed to ; be either a filespec or part of a filespec. ; ; Subdir_Pat_Len contains the offset relative to ds of the filespec ; copied from the command line into CL_Subdir_Pattern. ; ; CL_Subdir_Pattern contains the pathspec entered from the command ; line. It is guaranteed to contain a filespec. ; ; CL_File_Pattern is a 12 byte string initialized to the nul file ; pattern "????????.???". ; ; Returns: ; ; CL_File_Pattern contains the file pattern as described used for ; deciding whether or not to restore a given file. ; ; If DX > 8 then filename contains explicit period. ; If BIT 6 in Flags is set we have a wildcard. ; Ambiguous case is C:\X ; Which should be C:\X\ for subdirectory or C:\X. for file cmp dx, 8 ja Notambiguous test Flags, Wildcards jnz Notambiguous say_err_msg Ambiguous call Strike_Any_Key call Abort_With_Help Notambiguous: mov cx, di ; Get length of file pattern in cx. sub cx, Subdir_Pat_Len mov si, Subdir_Pat_Len lea di, CL_File_Pattern GFP_More: lodsb ; Get a byte. cmp al, '*' jnz GFP_Not_Star cmp di, offset CL_File_Pattern+9 ; Is this the extension? jae GFP_Done ; Yes, all done. mov di, si mov al, '.' ; Scan for the period if any. dec cx repnz scasb mov si, di ; Point at first char of extension. lea di, CL_File_Pattern+9 ; Skip to file extension. jcxz GFP_Fill jmp short GFP_More GFP_Not_Star: cmp al, '.' jnz GFP_Copy GFP_Blank: cmp byte ptr [di], '.' jz GFP_Copy mov byte ptr [di], ' ' inc di jmp short GFP_Blank GFP_Copy: stosb ; Copy the char. loop GFP_More GFP_Fill: mov al, ' ' mov cx, offset CL_File_Pattern+12 sub cx, di rep stosb mov CL_File_Pattern+8, '.' GFP_Done: ret Get_File_Pattern endp ;============================================================== Get_Switch proc near ; DOS 3.2 and 3.3 ; nested in Parse ; di points to the letter following a switch char on the command line. ; Examine it and set the appropriate bit in Cmd_Flags or abort if it is ; not recognizable. Called by Parse. and byte ptr [di], 0DFH ; Convert to upper case. cmp byte ptr [di], 'P' ; Prompted restore? jnz GS_SlashS ; No or Cmd_Flags, Prompt_Needed ; Bit 0 set = prompted restore. ret GS_SlashS: cmp byte ptr [di], 'S' ; Restore subdirectories? jnz GS_SlashQ ; No. or Cmd_Flags, RestoreSubs ; Bit 3 set = restore subdirectories. ret GS_SlashQ: cmp byte ptr [di], 'Q' ; Quiet, suppress banner. jnz GS_Bad ; No. or Cmd_Flags, Quietly ; Bit 3 set = quiet. ret GS_Bad: say_err_msg Bad_Switch call Abort_With_Help Get_Switch endp ;============================================================== Abort_With_Help proc near ; called when syntax on command line is bad. We abort. ; We also give some help with the syntax. ; DOS 3.2 3.3 ; Print syntax help exit with return code of 4. say_err_msg Help_Msg mov ax, 4C04H ; Function = exit with return code of 4. int 21H Abort_With_Help endp ;============================================================== ;============================================================== Parse endp ;============================================================== Get_Next_Disk proc near ; DOS 3.2 and 3.3 ; Prompt the user for the next diskette in the set and continue to prompt until ; he inserts a valid backup diskette. If the diskette inserted is not in ; sequence prompt him to select either Abort or Retry. In addition if there ; is currently no partially restored file on the hard disk allow the option of ; Ignore. ; ; Returns: ; ; Flags bit 2 set to indicate a new diskette. ; ; Flags bit 3 set if the user accepted a diskette out of sequence. ; ; Format_Flags bit 0 set if diskettes were backed up with DOS 3.2 ; format. ; ; Format_Flags bit 1 set if diskettes were backed up using DOS 3.3 format. ; ; Patch insert prompt with sequence number. patch_num Want_Seq, Insert_Seq+2, 3 GND_Again: call Disk_Reset ; Force DOS/Cacher to look freshly ; at diskette about to be inserted say_err_msg Insert ; Force the message to the console call Strike_Any_Key ; no matter where the output is going. call Process_Bkp_ID ; Is diskette in old format? jc GND_Again ; Yes, but we were restoring from new. jnz GND_OK ; Yes, proceed. Sequence no. is in ax. call Process_Ctl_Filename ; Sequence no. returned in ax. jc GND_Again ; CY=invalid backup diskette. GND_OK: cmp ax, Want_Seq ; Check returned sequence no. jz GND_Done ; Continue if its what we expected. push ax say_err_msg Out_Of_Seq say_err_msg CrLf pop ax comment | If the user has inserted a valid backup diskette with the wrong sequence number, allow him to choose either to abort or to insert a different diskette (Retry). In addition if the last file restored from the previous diskette is complete allow the user to continue despite the wrong sequence number. | ; end of comment. test Flags, LastFrag ; Is last file restored complete? jnz GND_Can_Ignore ; Yes, ignore is an option. test Flags, SkipFile ; Was last file being skipped? jnz GND_Can_Ignore ; Yes, Ignore is an option. call Abort_Retry jmp short GND_Again GND_Can_Ignore: push ax ; Save seq. no. call Abort_Retry_Ignore pop ax ; Retrieve seq. no. in ax. jc GND_Again or Flags, WrongOk+SkipFile ; User has accepted a diskette ; out of sequence, and ; do not restore frag. GND_Done: mov Bkp_Seq, ax ; Save current sequence no. inc ax ; Compute and store next sequence no. mov Want_Seq, ax or Flags, NewDisk ; Set New Disk flag. ret ; actual Get_Next_Disk endp but contains nested procs ;============================================================== Process_Bkp_ID proc near ; DOS 3.2 ; nested in Get_Next_Disk ; If the current diskette is in DOS 3.2 backup format, process the backup ID ; file and announce the diskette sequence no. and date. ; ; Returns zero flag set if diskette is not in DOS 3.2 format, clear ; otherwise. ; ; Returns carry flag set if diskette is in DOS 3.2 format but the set ; currently being restored is in DOS 3.3 format, clear otherwise. ; ; Returns with bit 0 of Format_Flags set if the backup diskette is in ; DOS 3.2 format and this is acceptable, i.e. either this is the first ; diskette to be restored or the previous diskettes were in DOS 3.2 format. ; call Open_Bkp_ID call Get_Bkp_ID jnc PBI_Old ; BACKUPID.@@@ was read successfully. clc ; Return with ZF and NC meaning ret ; unrecognized format. PBI_Old: call Close_Ctl_File ; Close BACKUPID.@@@. ; Announce diskette. patch_num Buffer.BI_Seq, ABM_Seq+2, 3 patch_num Buffer.BI_YYYY, ABM_YYYY+3, 4 xor ax, AX mov al, Buffer.BI_MM patch_num ax, ABM_MM+1, 2 xor ax, AX mov al, Buffer.BI_DD patch_num ax, ABM_DD+1, 2 say_msg Announce_Bkp_Msg test Format_Flags, DOS33 ; Were we already restoring a new ; format set? jnz PBI_Err ; Yes. or Format_Flags, DOS32 ; No, set flag for old format. and Flags, 0ffh - LastDisk ; 0FDH ; Reset flag for last diskette. Could ; have been set prematurely if a user ; rejected a diskette out of sequence. cmp Buffer.BI_Last, -1 ; Is this last diskette in set? jne PBI_Done ; No, don't set flag. or Flags, LastDisk ; Yes, set flag for last diskette. PBI_Done: mov ax, Buffer.BI_Seq ; Return sequence no. in ax. or ax, ax ; Return NZ for valid diskette. clc ; Return NC for correct format. ret PBI_Err: say_err_msg Wrong_Set say_err_msg CrLf call Abort_Retry stc ; CY = diskette in wrong format. ret ; actual Process_Bkp_ID EndP but contains nested procs ;============================================================== Open_Bkp_ID proc near ; DOS 3.3 ; nested in Get_Next_Disk ; nested in Process_Bkp_ID ; Open BACKUPID.@@@ on current floppy and return handle in Ctl_Hndl. If file ; cannot be found, return with handle of -1. Abort if any other error occurs. lea dx, Bkp_ID_Name mov ax, 3D00H ; open read-only 3D:00 int 21H jc OBI_Err mov Ctl_Hndl, ax ret OBI_Err: cmp ax, 2 ; File not found? je OBI_Err1 cmp ax, 3 ; Invalid path or file missing? je OBI_Err1 push ax ; Save error code. say_err_msg Floppy_Trouble ; Cant_Open_BC pop ax ; Retrieve error code. cmp ax, 4 ; Too many files open? je OBI_Err2 say_err_msg Unknown ; Unknown error, abort. call Abort OBI_Err1: ret ; Return handle of -1. OBI_Err2: say_err_msg Too_Many_Open call Abort ; not enough handles, abort. Open_Bkp_ID endp ;============================================================== Get_Bkp_ID proc near ; DOS 3.3 ; nested in Get_Next_Disk ; nested in Process_Bkp_ID ; If BACKUPID.@@@ was successfully opened, read its first 7 bytes into the ; buffer and convert the sequence no. to true binary form. ; ; Called with: ; ; Ctl_Hndl contains the handle of BACKUPID.@@@ if it was successfully ; opened, -1 otherwise. ; ; Returns if successful: ; ; Buffer contains the first 7 bytes of BACKUP.@@@ with the sequence no. ; converted to true binary form. ; ; Carry Flag = clear. ; ; Returns carry flag set if BACKUPID.@@@ was not previously successfully opened. ; mov bx, Ctl_Hndl cmp bx, -1 jz GBI_Not_Bkp mov cx, 7 ; Read 7 bytes. lea dx, Buffer ; ds:dx points at buffer. mov ah, 3FH ; Function = Read. int 21H jc GBI_Err ; Abort if trouble. cmp ax, 7 jne GBI_Err ; Convert 8 bit BCD BI_Seq into true binary format. mov al, 10 mul byte ptr Buffer.BI_Seq+1 add al, byte ptr Buffer.BI_Seq adc ah, 0 mov Buffer.BI_Seq, ax clc ; NC means all is in order. ret GBI_Not_Bkp: ; Return CY if diskette is not stc ; DOS 3.2 backup. ret GBI_Err: say_err_msg Unknown call Abort Get_Bkp_ID endp ;============================================================== Process_Bkp_ID endp ;============================================================== Process_Ctl_Filename proc near ; DOS 3.3 ; nested in Get_Next_Disk ; Blank out the extension of the previous backup control filename with "?" ; wildcard characters and use it to find the backup control file on the new ; diskette. If a file is found, convert its sequence number contained in its ; extension to a hex value returned to the calling procedure. If no filename ; is matched, or if the extension of a matched filename does not contain three ; numeric digits, print an error message and signal the calling routine to prompt ; for another diskette. If some other error occurs, abort the program with an ; error message. Called by Get_Next_Disk ; ; Called with: ; ; Ctl_Filename contains the complete nul terminated drive and pathspec ; which when the extension is patched with the sequence no. of the ; current disk will refer to the backup control file on that disk. ; ; Bkp_Filename contains the same as Ctl_filename but with respect ; to the backup data filename. ; ; Returns if successful: ; ; ax contains the hex representation of the sequence no. for the ; current diskette. ; ; Ctl_filename and Bkp_Filename are as when called but patched with ; the correct extensions. ; blank_extension Ctl_Filename+11 lea dx, Ctl_Filename xor cx, cx ; Use normal attributes. or cx, 10H ; Turn on subdir bit as well. I've ; heard that MS DOS 3.2 may not return ; all normal files unless subdirs. are ; included. mov ah, 4EH ; Function = Search for First int 21H jnc PCF_Found cmp ax, 12H ; Matching file not found? jz PCF_Err1 ; Yes, not a backup diskette. jmp short PCF_Err2 ; No, unknown error. PCF_Found: cmp ds:DTA.DS_Name + 7, '.' jnz PCF_Err1 mov si, DTA.DS_Name+8 ; Point di to beginning of extension. mov cx, 3 ; cx = field length. call ASCII_Dec_To_Hex jc PCF_Err1 ; CY = non-numeric char in field. test ds:DTA.DS_Attr, 10H ; Was file a subdir? jnz PCF_Err1 ; Yes, non-backup diskette. ; si points at terminating nul following DTA.DS_Name. di points at terminating ; nul following Ctl_Filename. call Patch_Filename_Exts push ax ; Save sequence no. call Patch_Announce_Bkp_Msg say_msg Announce_Bkp_Msg pop ax ; Retrieve seq. no. in ax. test Format_Flags, DOS32 ; Were we already restoring files ; from a backup set in the old format? jnz PCF_Err3 ; Yes. or Format_Flags, DOS33 ; No, set flag for DOS 3.3 format. clc ret PCF_Err1: say_err_msg Not_Bkp say_err_msg CrLf call Abort_Retry stc ret PCF_Err2: say_err_msg Unknown call Abort PCF_Err3: say_err_msg Wrong_Set say_err_msg CrLf call Abort_Retry stc ret ; actual Process_Ctl_Filename EndP but contains nested procs. ;============================================================== Patch_Filename_Exts proc near ; DOS 3.3 ; nested in Get_Next_Disk ; nested in Process_Ctl_Filename ; Fill in the extensions for CONTROL.XXX and BACKUP.XXX. ; ; Called with: ; ; offset DTA.DS_Name contains filename returned from function 4EH ; Search For First. ; ; si points at the nul terminating the above name. ; ; di points at the nul terminating Ctl_Filename. ; ; Returns: ; ; Ctl_Filename and Bkp_Filename extensions filled in with the ; apparent sequence no. of the diskette according to the directory ; search for CONTROL.XXX in the procedure Process_Ctl_Filename. ; mov cx, 4 ; 3 chars. plus trailing nul. std rep movsb lea di, Bkp_Filename + 9 ; Point to "." before Bkp_filename ext. mov cx, 4 ; "." + 3 chars. file ext. cld rep movsb ret Patch_Filename_Exts endp ;============================================================== Process_Ctl_Filename endp ;============================================================== Patch_Announce_Bkp_Msg proc near ; DOS 3.3 ; Patch the message which announces the current diskette with its sequence no. ; and date. patch_num ax, ABM_Seq+2, 3 mov ax, ds:DTA.DS_Date ; Date of CONTROL.XXX file. push ax ; Save a copy. mov cl, 5 ; Isolate month and patch it into msg. shr ax, cl and ax, 000FH patch_num ax, ABM_MM+1, 2 pop ax push ax and ax, 001FH ; Isolate day and patch it into msg. patch_num ax, ABM_DD+1, 2 pop ax mov cl, 7 ; Isolate year and patch it into msg. rol ax, cl and ax, 07FH add ax, 1980 patch_num ax, ABM_YYYY+3, 4 ret Patch_Announce_Bkp_Msg endp ;============================================================== Get_Next_Disk endp ;============================================================== ASCII_Dec_To_Hex proc near ; DOS 3.3 ; used by Process_Control_FileName ; Convert ASCII string of decimal digits to a 16 bit hex value. ; ; Called with: ; ; si points to leftmost digit in string. ; ; cx contains field width. ; ; Returns if successful: ; ; ax contains 16 bit quantity. ; ; si points to char following last digit. ; ; carry flag = clear. ; ; Returns carry flag set if non-numeric char in string. ; xor ax, ax mov bl, 0AH ADTH_More: mul bl mov dl, [si] ; Fetch a digit. inc si cmp dl, '9' ja ADTH_Err sub dl, '0' ; Convert from ASCII and make sure jb ADTH_Err ; char is numeric. add al, dl adc ah, 0 loop ADTH_More clc ret ADTH_Err: stc ret ASCII_Dec_To_Hex endp ;============================================================== Strike_Any_Key proc near ; DOS 3.2 and 3.3 ; Prompt the user to strike spacebar and return to caller when any key detected. ; This is better than asking him to strike any key since there are at least ; 6 keys which wont be detected. say_err_msg Strike_Any ; Force message to print ; on console regardless of I/O ; redirection. mov ax, 0C08H ; Clear typeahead buffer and get one int 21H ; filtered char without echo. or al, al ; If extended code, get rid of it. jnz SAK_Done mov ah, 7 ; Function = Get Unfiltered Char. int 21H ;w/o echo. SAK_Done: ret Strike_Any_Key endp ;============================================================== Open_Ctl_File proc near ; DOS 3.3 ; OPEN CONTROL.XXX ; Open the current backup control file and store the returned handle in ; Ctl_Hndl. print error message and abort if operation failed. ; ; Returns: ; ; Ctl_Hndl contains the handle of the successfully opened backup ; control file with the file ptr. positioned at the beginning of the file. ; OCF_Retry: lea dx, Ctl_Filename mov ax, 3D00H ; Open file with read access. int 21H jc OCF_Err mov Ctl_Hndl, ax ; Save handle. ret ; File could not be opened, process condition. OCF_Err: push ax ; Would be trashed by Say_Err_Msg say_err_msg Floppy_Trouble ; Cant_Open_BC pop ax ; Restore error code to ax. cmp ax, 4 ; Too many files open? je OCF_Err1 say_err_msg Unknown call Abort OCF_Err1: say_err_msg Too_Many_Open call Abort ; not enough handles, abort. Open_Ctl_File endp ;============================================================== Open_Bkp_File proc near ; DOS 3.2 ; If restoring files backup up with DOS 3.2 format, copy the filespec of the ; backup data file from the DTA into Bkp_Filename. ; Open the current backup data file and store the returned handle in ; Bkp_Hndl. print error message and abort if operation failed. ; ; Returns: ; ; Bkp_Hndl contains the handle of the successfully opened backup ; data file with the file ptr. positioned at the beginning of the file. ; OBF_Retry: test Format_Flags, DOS33 ; Is this DOS 3.3 format? jnz OBF_New ; Yes, Bkp_Filename already contains ; filespec ; This is DOS 3.2 format. Copy the backup data filename from the DTA. mov cx, 13 ; Max. length of filename with nul. mov si, DTA.DS_Name lea di, Bkp_Filename+3 ; Bypass d:\. rep movsb ; Copy filename. OBF_New: lea dx, Bkp_Filename mov ax, 3D00H ; Open with read access. int 21H jc OBF_Err mov Bkp_Hndl, ax ret ; File could not be opened, process condition. OBF_Err: push ax ; Would be trashed by Say_Err_Msg say_err_msg Floppy_Trouble ; Bkp_File_Msg pop ax ; Restore error code to ax. cmp ax, 2 ; File not found? je OBF_Err1 cmp ax, 3 ; Invalid path or file missing? je OBF_Err1 cmp ax, 4 ; Too many files open? je OBF_Err2 say_err_msg Unknown ; Unknown error, abort. call Abort OBF_Err1: say_err_msg Floppy_Trouble ; File_Not_Found call Abort OBF_Err2: say_err_msg Too_Many_Open call Abort ; not enough handles, abort. Open_Bkp_File endp ;============================================================== Process_Hdr proc near ; DOS 3.3 ; in CONTROL.XXX file ; Read the header record from the current backup control file and confirm ; the validity of the file. ; Set a flag if this is the last diskette in the set. ; ; Called with: ; ; Ctl_Hndl contains the handle of the currently open control file ; with the file ptr. positioned at the beginning of the file. ; ; Returns: ; ; Buffer contains the successfully read header record beginning ; at offset zero. ; call Read_Hdr call Chk_Sig call Chk_Seq cmp Buffer.HDR_Last, 0ffh ; Is this last diskette in set? jnz PH_Done or Flags, LastDisk ; Set flag if it is. PH_Done: ret ;============================================================== Read_Hdr proc near ; DOS 3.3 ; nested in Process_Hdr ; Read the header record into the buffer from the currently open backup control ; file. Print error message and abort if operation failed. ; ; Called with: ; ; Ctl_Hndl contains the handle of the currently open backup control ; file with the file ptr. positioned at the beginning of the file. ; ; Returns: ; ; Buffer contains the successfully read header record beginning ; at offset buffer. ; lea dx, Buffer mov bx, Ctl_Hndl mov cx, HDR_REC_LEN mov ah, 3FH ; Function = Read. int 21H jc RH_Err cmp cx, ax ; Read o.k.? jne RH_Err ; Abort if not. ret RH_Err: say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort Read_Hdr endp ;============================================================== Chk_Seq proc near ; DOS 3.3 ; nested in Process_Hdr ; Make sure the sequence number in the control file header agrees with that ; of the volume label and file extensions. Abort if it does not. ; mov ax, Bkp_Seq cmp ax, Buffer.Hdr_Seq jnz CSeq_Err ret CSeq_Err: say_err_msg Floppy_Trouble ; Bad_Hdr_Seq call Abort Chk_Seq endp ;============================================================== Chk_Sig proc near ; DOS 3.3 ; nested in Process_Hdr ; Verify backup control file signature. ; lea si, Bkp_Sig lea di, Buffer.Hdr_Sig mov cx, Bkp_Sig_Len repe cmpsb jnz CS_Err ret CS_Err: say_err_msg Floppy_Trouble ; Bad_Sig call Abort Chk_Sig endp ;============================================================== Process_Hdr endp ;============================================================== Get_Subdir proc near ; DOS 3.3 ; Process all of the subdir records for the current diskette. ; GS_More: call Get_Subdir_Rec ; Get a subdir record. call Get_Dirspec ; Copy out the pathname. jc GS_Wrong_Set ; Save ptr. to next subdir record. push word ptr Buffer.Subdir_Next + 2 push word ptr Buffer.Subdir_Next call Select_Subdir ; Zero flag set means this dirspec. clc ; doesn't match the command line jnz GS_Bypass ; pattern, clear otherwise. mov cx, Buffer.Subdir_Count ; Pass file record count in cx. call Get_Files GS_Bypass: pop dx ; Pass offset of next subdir record ; low word in dx. pop cx ; Pass offset of next subdir record ; high word in cx. jc GS_Wrong_Set ; cy set by Get_Files. Needed to ; pop stack before test. call Seek_Next_Subdir ; NC = no more subdirs on this diskette. jc GS_More ; CY = go back for more. ret GS_Wrong_Set: say_err_msg Wrong_Set say_err_msg CrLf call Abort_Retry call Close_Bkp_File ; If Retry, go back for another diskette. call Close_Ctl_File ; after closing files on this diskette. dec Want_Seq stc ; CY signals to prompt for another disk. ret ;============================================================== Get_Subdir_Rec proc near ; DOS 3.3 ; Read the next subdir record into the buffer and verify it by checking ; its length field. Abort with error message if record proves invalid. ; ; Called with: ; ; Ctl_Hndl contains the handle of the currently open backup control ; file with the file ptr. positioned at the beginning of the next ; subdir record. ; ; Returns: ; ; Buffer contains the successfully read subdir record beginning ; at offset buffer. ; lea dx, Buffer mov cx, SUBDIR_REC_LEN mov bx, Ctl_Hndl mov ah, 3FH ; Function = Read. int 21H jc GSR_Err cmp ax, cx ; Read o.k.? jne GSR_Err ; Abort if not. cmp Buffer.Subdir_Len, cl ; If this record is valid, these jne GSR_Err ; should be equal. ret GSR_Err: say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort Get_Subdir_Rec endp ;============================================================== Get_Dirspec proc near ; DOS 3.3 ; If the current diskette is not in sequence and this is the first subdir ; record or if the last file was not to be continued, copy the dirspec from ; the current subdir record to the pathspec to be restored. Otherwise, make ; sure the dirspec in this subdir record is the same as that from the last. ; A discrepancy probably indicates the new diskette is from a different set ; so alert the user and prompt for the proper diskette. ; ; Called with: ; ; Buffer contains the last subdir record read from the backup control ; file. ; ; Flags bit 0 is set if the last file was not to be continued, clear ; otherwise. ; ; Flags bit 3 is set if the newly inserted diskette is out of ; sequence, clear otherwise. ; ; Returns if Successful: ; ; Filespec_Ptr points to the offset in Rest_Path where the filespec ; will begin. ; ; Rest_Path contains the dirspec. of the current subdir record ; left justified with a trailing backslash if required. The ; drive and leading backslash have already been filled in immediately ; before Rest_Path. ; ; Carry flag = clear. ; ; Returns carry flag set if the dirspec. for a continuation of a file did ; not match the previous dirspec. ; lea si, Buffer.Subdir_Name lea di, Rest_Path cld test Flags, WrongOk ; Diskette out of sequence? jnz GD_Copy ; Yes, copy dirspec. test Flags, LastFrag ; Last file continued? jnz GD_Copy ; No, copy new dirspec. mov cx, Filespec_Ptr ; = dirspec. length with trailing ; backslash jcxz GD_Nul ; or 0 if dirspec. is root. dec cx ; Don't count trailing backslash. repe cmpsb jnz GD_Err ; New dirspec. is different. GD_Nul: mov al, 0 ; Now make sure trailing nul cmp [si], al ; comes next. jnz GD_Err jmp short GD_Done GD_Copy: mov cx, size Subdir_Name GD_More: lodsb or al, al ; Watch for the trailing nul. jz GD_Copied ; Add backslash and set up ptr. stosb loop GD_More ; If we get this far there's something wrong. say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort GD_Copied: cmp di, offset Rest_Path ; If dirspec. is root, don't add je GD_Root ; a trailing backslash. mov al, '\' ; Add trailing backslash. stosb dec cx GD_Root: mov Filespec_Ptr, size Subdir_Name ; Set ptr. to start of ; filespec sub Filespec_Ptr, cx GD_Done: clc ; All is in order. ret GD_Err: stc ret Get_Dirspec endp ;============================================================== Seek_Next_Subdir proc near ; DOS 3.3 ; Set the backup control file ptr. to the next subdir record, or set the ; carry flag if there is none. Abort if the flags indicate a file with ; fragments pending if there are more subdir records on the disk. ; ; Called with: ; ; cx contains high word of absolute offset in backup control file of ; next subdir record or -1 if there is none. ; ; dx contains low word of absolute offset in backup control file of ; next subdir record or -1 if there is none. ; ; Ctl_Hndl contains the handle of the open backup control file. ; ; Flags contains bit 0 set if there are no pending file fragments for ; the file just restored, clear if there is. ; ; Returns: ; ; If there is another subdir record to process, the carry flag is set ; and the file ptr. of the backup control file is positioned at ; the beginning of the next subdir record. ; ; If there are no more subdir records for this diskette the carry flag ; is clear. ; cmp cx, 0ffffh ; Find out if this is the last record. jnz SNS_Not_Last ; If it is, cx and dx = -1. cmp dx, 0ffffh jnz SNS_Not_Last clc ret SNS_Not_Last: ; Check for invalid flag. test Flags, LastFrag jz SNS_Err1 ; Point to next subdir record. mov bx, Ctl_Hndl mov ax, 4200H ; Function 42 = Move File Ptr. int 21H ; al=0 for absolute offset. jc SNS_Err2 stc ret SNS_Err1: say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort SNS_Err2: say_err_msg Unknown call Abort Seek_Next_Subdir endp ;============================================================== Get_Subdir endp ;============================================================== Get_Files proc near ; DOS 3.3 ; Process the file records which follow a subdirectory record in the backup ; control file. Return with carry flag clear if all is in order or set if ; the current diskette appears to be from the wrong set. ; ; Called with: ; ; cx contains the number of file records in the backup control file ; remaining to be processed. ; ; Ctl_Hndl contains the handle of the open backup control file with the ; file ptr. positioned at the beginning of the next file record. ; ; Flags bit 0 set if the previous file fragment was not to be ; continued, clear otherwise. ; ; Flags bit 2 set if this is a new diskette, clear otherwise. ; GF_More: or cx, cx ; Any more file records? jz GtF_Done ; No, return to caller. Comment | I am assuming that a file record which indicates that there is another file fragment coming must always be the last file record in a backup control file. The following tests the validity of the backup control file based on this assumption. | ; End of comment. test Flags, LastFrag ; Was previous file to be continued? jnz GtF_OK ; No, proceed with next file. test Flags, NewDisk ; Yes, is this a new diskette? jz GtF_Err ; No, control file is invalid. GtF_OK: push cx ; Save count value. call Get_File_Rec call Get_Filespec jc GtF_Wrong_Set call Set_Checks ; Attempt to catch diskettes from jc GtF_Wrong_Set ; another set. mov dx, Buffer.File_Frag_Seq ; Pass file fragment no. in dx. call Select_File ; Decide if file should be restored. jc GF_Bypass ; CY = don't restore file. mov dx, Buffer.File_Frag_Seq ; Pass file frag. seq. no. in dx. mov bl, Buffer.File_Attr ; Pass file attribute in bl. call Announce_File jc GF_Bypass ; CY = User answered no to restore ; prompt. call Seek_Fragment ; Set backup data file ptr. to beginning ; of this fragment. ; Copy over one fragment. mov bl, Buffer.File_Attr ; Pass file attribute in bl. mov cx, Buffer.File_Time ; Pass file time in cx. mov dx, Buffer.File_Date ; File date in dx. ; Pass file fragment size in bp:si. mov si, word ptr Buffer.File_Frag_Size mov bp, word ptr Buffer.File_Frag_Size + 2 call Do_One_Chunk GF_Bypass: pop cx ; Restore remaining file record count ; to cx. dec cx jmp GF_More GtF_Done: clc ; NC means all is well. ret GtF_Wrong_Set: pop cx ; Clear the stack. ret ; Return to caller with carry flag set. GtF_Err: say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort ;============================================================== Get_File_Rec proc near ; DOS 3.3 ; Read a file record into the buffer from the backup control file and verify ; it by checking its length field. Abort if record is not valid. ; Set bit 0 of Flags if record is for last fragment of a file. ; Make sure the file fragment sequence number is correct and set the next ; expected fragment sequence no. based on whether or not this file is continued. ; ; Called with: ; ; Ctl_Hndl contains the handle of the open backup control file with ; the file ptr. positioned at the beginning of the file record to ; be read. ; ; Want_Frag_Seq contains the expected file fragment sequence number. ; ; Returns: ; ; Buffer contains the successfully read file record beginning at ; offset Buffer. ; ; Flags has bit 0 set if this record is for the last fragment of ; a file. ; ; Want_Frag_Seq contains the next expected fragment sequence number. ; lea dx, Buffer mov bx, Ctl_Hndl mov cx, FILE_REC_LEN mov ah, 3FH ; Function = Read. int 21H jc GFR_Err cmp cx, ax ; Read successful? jne GFR_Err ; Abort if not. cmp Buffer.File_Len, cl ; If this record is valid, these jne GFR_Err ; should be equal. ret GFR_Err: say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort Get_File_Rec endp ;============================================================== Get_Filespec proc near ; DOS 3.3 Comment | If this is the first file record on a diskette accepted out of sequence, or if the previous file was not to be continued: > Get filespec in current file record into target pathspec to be restored. > Calculate the length of the complete drive and pathspec not including the terminating nul. Otherwise, if this file record is for the continuation of a previous file: > Make sure the previous filespec in the pathspec to be restored matches the filespec in the current file record. If they do not match, this probably indicates that the new diskette is from another set. Called with: Buffer contains the current file record beginning at offset 0. Filespec_Ptr contains offset into rest_Path where the filespec of the file to be restored must begin. Rest_Path contains the dirspec. of the file to be restored. Flags bit 3 set if this is the first file record on a diskette accepted out of sequence. Flags bit 0 set if the previous file was to be continued. Returns: Rest_Name contains the complete nul terminated pathspec including the drive of the file being restored. Rest_Name_Len contains the length of the drive and pathspec not including the terminating nul. Carry Flag set if filespec comparison failed, clear otherwise. | ; End of comment. lea si, Buffer.File_Name ; Set up indexes to copy or compare. lea di, Rest_Path add di, Filespec_Ptr ; Point di to start of filespec. test Flags, WrongOk ; Is this the first file rec. on a ; diskette out of sequence? jnz GF_Copy ; Yes, copy the new filespec test Flags, LastFrag ; Is this a continuation? jnz GF_Copy ; No, copy the new filespec mov cx, Rest_Name_Len ; Get length of filespec not including sub cx, Filespec_Ptr ; nul in cx. sub cx, 3 ; Knock off 3 for the C:\ repe cmpsb jnz GF_Err2 ; filespec didn't match. Comment | In the case of a full length filespec in the file record there will be no trailing nul. A matching full length filespec is shown by si pointing to the byte following the filename field in the file record. For the other cases the trailing nul may be tested for. | ; End of comment. cmp si, offset Buffer + 13 ; Does si point past filename field? jz GF_Done ; Yes, name was a perfect match. cmp byte ptr [si], 0 ; Otherwise check that the next byte jz GF_Done ; is 0. jmp short GF_Err2 ; Filename did not match. GF_Copy: mov cx, size File_Name cld rep movsb mov byte ptr [di], 0 ; and make sure it's null terminated. mov cx, 64 ; Maximum length of pathspec. lea di, Rest_Path ; Calculate total length of drive mov al, 0 ; and pathspec not including trailing repne scasb ; nul. sub di, offset Rest_Name + 1 cmp di, 65 ; Abort if length illegal. ja GF_Err1 mov Rest_Name_Len, di GF_Done: clc ; All is in order. ret GF_Err1: say_err_msg Floppy_Trouble ; Bad_Ctl_File call Abort GF_Err2: stc ; CY = diskette is from another set. ret Get_Filespec endp ;============================================================== Set_Checks proc near ; DOS 3.3 ; Attempt to catch diskettes from the wrong backup set by checking for ; incorrect file fragment sequence numbers and file size fields in file records. ; Also keep partial files from being restored when diskettes are inserted out ; of sequence. ; ; Called with: ; ; Buffer contains the current file record. ; ; Want_Frag_Seq contains the expected file fragment sequence no. ; ; Want_Size contains the expected file size for subsequent fragments ; of multiple fragment files. ; ; Flags bit 0 set according to previous file record or to 1 if ; there was none. ; ; Flags bit 3 set if this is the first file on a diskette accepted out ; of sequence. ; ; Returns if no problem detected: ; ; Carry Flag = clear. ; ; Want_Frag_Seq contains the next expected file fragment sequence no. ; ; Want_Size contains the total file size of the current file. ; ; Flags bit 0 set according to the current file record. ; ; Flags bit 4 set if the current fragment is a file continuation on ; a diskette out of sequence. ; ; Flags bits 2 and 3 cleared. ; ; Returns if problem detected: ; ; Carry Flag = set. ; test Flags, WrongOk ; Is this the first file rec. from a ; diskette accepted out of sequence? jz SC_In_Seq ; No, test for validity. mov ax, Buffer.File_Frag_Seq ; Yes, re-initialize gragment sequence ; number from file rec. mov Want_Frag_Seq, ax cmp ax, 1 ; Signal that this fragment should not jz SC_Frag_Seq_OK ; be restored if the fragment seq. no. or Flags, SkipFile ; is not 1. jmp short SC_Frag_Seq_OK ; Bypass validity check. SC_In_Seq: mov ax, Want_Frag_Seq ; Make sure fragment sequence number ; is as expected. cmp ax, Buffer.File_Frag_Seq jnz SC_Wrong_Set SC_Frag_Seq_OK: ; Now for the file size check. lea si, Buffer.File_Size lea di, Want_Size cld mov cx, 2 ; cx = no. of words to compare or copy. test Flags, WrongOk ; Is this first file on diskette ; accepted out of sequence? jnz SC_Copy ; Yes, copy over new file size. test Flags, LastFrag ; Was last file to be continued? jnz SC_Copy ; No, copy over new file size. repe cmpsw ; File sizes for current and previous jnz SC_Wrong_Set ; file records should be equal. jmp short SC_Size_OK SC_Copy: rep movsw SC_Size_OK: and Flags, 0ffh-(LastFrag+WrongOk+NewDisk) ; 0F2H ; Clear last fragment, diskette out ; of sequence and new diskette ; indicators. and Buffer.File_More, 1 ; Set bit 0 in Flags if this is jz SC_Done ; the record for a file's last fragment. or Flags, LastFrag mov Want_Frag_Seq, 0 ; Reset fragment sequence no. SC_Done: inc Want_Frag_Seq ; Store next expected frag. seq. no. clc ret SC_Wrong_Set: stc ret Set_Checks endp ;============================================================== Seek_Fragment proc near ; DOS 3.3 ; Position BACKUP.XXX file ptr. to start of this fragment. The current ; file record is still in Buffer beginning at offset zero. ; mov dx, word ptr Buffer.File_Frag_Start mov cx, word ptr Buffer.File_Frag_Start + 2 mov bx, Bkp_Hndl mov ax, 4200H ; Function Move File Ptr. int 21H ; al=0 for absolute offset. jc SF_Err ret SF_Err: say_err_msg Unknown call Abort Seek_Fragment endp ;============================================================== Get_Files endp ;============================================================== Select_Subdir proc near ; DOS 3.2 ; Decide whether or not the dirspec. of the file proposed to be restored ; matches the pattern from the command line. ; ; Called with: ; ; Filespec_Ptr contains the length of the dirspec. portion of the ; file(s) proposed to be restored including a trailing but no leading ; backslash. If the dirspec. is the root directory its value is 0. ; ; Rest_Path contains the dirspec. of the file(s) proposed to be ; restored including a trailing but no leading backslash. An empty ; string indicates the root directory. ; ; Subdir_Pat_Len contains the length of the directory portion ; of the pattern entered on the command line including a trailing but ; no leading backslash. A value of zero indicates the root directory. ; ; CL_Subdir_Pattern contains the directory portion of the pattern ; entered from the command line including a trailing but no leading ; backslash. An empty string indicates the root directory. ; ; Cmd_Flags bit 2 set if subdirectories are to be restored, clear ; if only files are to be restored. ; ; Returns: ; ; Zero Flag set if directory is to be restored, clear if not. ; mov cx, Subdir_Pat_Len cmp cx, Filespec_Ptr ; If the pattern is longer, there ja SS_Done ; Cannot be a match. test Cmd_Flags, RestoreSubs ; /S switch specified? jnz SS_Compare ; Yes, proceed with compare. cmp cx, Filespec_Ptr ; No, lengths must equal to match. jne SS_Done SS_Compare: or cx, cx jz SS_Done ; File matched. lea si, CL_Subdir_Pattern lea di, Rest_Path repe cmpsb SS_Done: ret ; zR = matched, NZ = not matched. Select_Subdir endp ;============================================================== Select_File proc near ; DOS 3.2 and 3.3 ; Decide whether or not the current file should be restored as directed by ; the file pattern entered on the command line. ; ; Called with: ; ; Rest_Path contains pathspec of file proposed to be restored less ; the leading backslash. ; ; Filespec_ptr contains the offset of the filespec relative to ; Rest_Path. ; ; Rest_Name_Len contains the length of the complete pathspec proposed ; to be restored including the "C:\" but not the trailing nul. ; ; Flags bit 4 set if the previous file fragment was not to be ; restored, clear otherwise. ; ; Flags bit 5 set if the filespec of the previous file fragment ; matched the command line pattern and the current pathspec refers ; to the same file, clear otherwise. ; ; dx contains the sequence no. of the current file fragment. ; ; Returns if file fragment is to be restored: ; ; Carry Flag clear. ; ; Flags bit 4 clear. ; ; Flags bit 5 set. ; ; Returns if file fragment not to be restored: ; ; Carry Flag set. ; ; Flags bit 4 set. ; cmp dx, 1 ; Release Don't Restore flag and File jnz SF_Frag_Pending ; Matched flag for each first fragment. and Flags,0ffh-(SkipFile+FileMatched) ; 0CFH ; Don't restore a fragment for a file whose previous fragments were not ; restored. SF_Frag_Pending: test Flags, SkipFile ; Bit 4 set = Don't Restore File. jnz SF_Dont_Restore test Flags, FileMatched ; Filename matched on previous fragment? jnz SF_Restore ; Yes, no need to check again. call Match_Filespec ; Restore only files which match jc SF_Dont_Restore ; the command line filespec call Dangerous_Files ; Don't restore the system files. jc SF_Dont_Restore or Flags, FileMatched ; Signal for subsequent fragments to ; restore this pathspec. SF_Restore:clc ret SF_Dont_Restore: or Flags, SkipFile ; Bit 4 set = Don't Restore. stc ret ;============================================================== Match_Filespec proc near ; DOS 3.2 and 3.3 ; Decide if the filespec of the pathspec proposed to be restored matches that ; of the command line pattern. ; ; Called with: ; ; CL_File_Pattern contains the command line file pattern to be matched. ; It is in the format "MYFILE__.X__" with any '*' wildcard chars. ; expanded to the maximum possible number of '?' wildcard chars. ; ; Returns carry flag clear if a match was made, set if not. ; cld call Extract_Filespec lea si, CL_File_Pattern lea di, Match_File mov cx, 12 ; Filename length with embedded '.'. MF_More: jcxz MF_Matched repe cmpsb je MF_Matched cmp byte ptr [si-1], '?' je MF_More stc ret MF_Matched:clc ret ;============================================================== Extract_Filespec proc near ; DOS 3.2 and 3.3 ; Extract the filespec from the pathspec. proposed to be restored so that ; it is in the form "MYFile__.X__" so that it may be more easily compared ; with the pattern specified on the command line. lea di, Match_File ; Blank out string with spaces in case mov ax, 2020H ; some bytes don't get overwritten. mov cx, 6 rep stosw lea si, Rest_Path add si, Filespec_Ptr ; Point to start of filespec lea di, Match_File mov cx, Rest_Name_Len ; Get length of filespec in cx. sub cx, Filespec_Ptr sub cx, 3 ; Allow for "C:\" EF_More: lodsb cmp al, '.' ; Watch for the period. jz EF_Ext ; Copy it and the file extension. stosb loop EF_More mov Match_File + 8, '.' ; '.' needed to match string properly. ret EF_Ext: lea di, Match_File+8 stosb dec cx rep movsb ret Extract_Filespec endp ;============================================================== Match_Filespec endp ;============================================================== Dangerous_File macro DC local DF_Exit ;; Test the filename proposed to be restored against one of the dangerous ;; filenames. cmp cx, D_File&DC&_Len ;; Filenames can't match if lengths jnz DF_Exit ;; aren't equal. lea si, D_File&DC ;; Compare filenames. push cx call Dangerous ;; CY = file is dangerous. pop cx jc DF_Dangerous DF_Exit label near endm ;; End of macro Dangerous_File. ;============================================================== Dangerous_Files proc near ; DOS 3.2 and 3.3 ; Don't allow any files with possible operating system filenames to be ; restored to the root directory. Announce the names of any files skipped ; over. Return with the carry flag set if the file is to be skipped, clear ; otherwise. This rather intimidating code just compares the ; file we are about to restore with the names of 5 files known dangerous ; to restore, namely IBMBIO.COM IBMDOS.COM IO.SYS MSDOS.SYS and COMMAND.COM cld cmp Filespec_Ptr, 0 ; Is file headed for the root directory? jnz DF_OK ; No, File OK to restore. mov cx, Rest_Name_Len ; Get filename length in cx. sub cx, 2 ; Omit "C:\" but add 1 for trailing nul. ; Check the filename of the file proposed to be restored to the root directory ; to make sure it doesn't have the name of one of the system files. If it ; does, return to the calling procedure with carry flag set. dc = 0 ;; Keep track of which iteration were on. rept D_Count ;; D_Count = no. of dangerous files. dc = DC + 1 ;; Update iteration counter. dangerous_file %DC endm ;; End of repeat block. DF_OK: clc ; File is OK to restore. ret DF_Dangerous: stc ret ;============================================================== Dangerous proc near ; DOS 3.2 and 3.3 ; Compare the filename to which si points with the filename of the file proposed ; to be restored. Return to caller with carry flag set if they match, clear ; otherwise. The length of the filename to be checked including a trailing nul ; is passed to this routine in cx. lea di, Rest_Path repe cmpsb jz D_Dangerous clc ret D_Dangerous:stc ret Dangerous endp ;============================================================== Dangerous_Files endp ;============================================================== Select_File endp ;============================================================== Announce_File proc near ; DOS 3.2 and 3.3 ; If the /P switch is active and the current fragment is the first fragment ; in a file, prompt the user whether or not to restore the file. If the current ; file fragment is the first fragment in a file containing Read-only, Hidden, ; and/or System attributes prompt the user whether or not to ; restore the file. If the /P switch is not active and the file has normal ; attributes or if the user indicates to restore the file, announce on the ; standard output device the drive and full pathspec of the file being ; restored. If the file exists in more than one fragment, also announce the ; fragment or part number of this fragment. ; ; Called with: ; ; Flags bit 0 set if this fragment is the last for the file, clear ; otherwise. ; ; Cmd_Flags bit 0 set if /P switch is active, clear otherwise. ; ; dx contains the file fragment sequence no. of the current file. ; ; bl contains the attribute of the file fragment about to be restored. ; ; Returns: ; ; Carry Flag and Flags bit 4 set if user indicated not to restore ; a file, clear otherwise. ; push dx ; Save frag. seq. no. cmp dx, 1 ; Prompt only on first fragment. jnz AF_Restore test Cmd_Flags, Prompt_Needed ; Is /P switch in effect? jz AF_Attr ; No, skip the prompt. push bx ; Save file attribute. call Prompt_User ; Get user's decision on restoring file. pop bx ; Retrieve file attribute. jc AF_No ; NC = yes, CY = no. AF_Attr: test bl, 7 ; Is file Read-only and/or System and/or ; hidden? jz AF_Restore ; No, skip prompt. say_err_msg Special_Attr call Prompt_User jnc AF_Restore AF_No: pop dx ; Clear stack. or Flags, SkipFile ; Bit 4 = Don't restore file. stc ret AF_Restore: say_msg Rest_Name pop dx ; Retrieve file frag. seq. no. test Flags, LastFrag ; Is this a multi-fragment file? jz AF_Part_X ; Yes, announce part no. after name. cmp dx, 1 ; Maybe, is frag. no. > 1. ja AF_Part_X ; Yes, announce part no. clc ret AF_Part_X: patch_num dx, Part_X+2, 3 say_msg Part_X_Msg clc ret ;============================================================== GetUCChar proc near ; Gets one upper-case char from keyboard ; Lower case get converted to upper case ; punctuation passed unmolested. ; Echos printable chars as letter backspace. mov ax, 0C08H ; Clear typeahead buffer and get int 21H ; filtered no echo cmp AL,'a' jb FineAsIs cmp AL,'z' ja FineAsIs sub AL,20h ; convert a: to A: FineAsIs: ; check for displayable char cmp AL,32d ; space jb NoEcho cmp AL,126d ; tilde ~ ja NoEcho mov EchoChar,AL ; echo upper case chars push AX ; save char from destruction say_err_msg EchoChar pop AX NoEcho: ret GetUCChar endp ;============================================================== Prompt_User proc near ; DOS 3.2 and 3.3 ; Display on the standard error device the pathspec of the file to be restored ; and ask the user to indicate by answering y or n if it should be restored. ; Return with carry flag set if he answers N, clear if he answers Y. ; say_err_msg Prompt_User1 say_err_msg Rest_Name say_err_msg Prompt_User2 PU_Again: call GetUCChar ; get Y/N cmp al, 'Y' jne PU1 say_err_msg CRLF clc jmp short PU_Done PU1: cmp al, 'N' jne PU_Err say_err_msg CRLF stc PU_Done: ret PU_Err: say_err_msg Beep jmp short PU_Again Prompt_User endp ;============================================================== Announce_File endp ;============================================================== Do_One_Chunk proc near ; DOS 3.2 and 3.3 ; Copy the current file fragment to its pathname on the hard disk. If this is ; the first fragment of the file, create the pathspec making any needed but ; non-existent subdirectories and creating or truncating to zero length the ; specified filename. If this is the last fragment for the file, close the ; file on the hard disk after copying the fragment. ; ; Called with: ; ; Bkp_Hndl contains the handle of the open backup data file with the ; file ptr. positioned at the beginning of the file fragment to be ; restored, ie at the first byte of data to be copied. ; ; Out_Hndl contains the handle of the file being restored on the hard ; disk if the file has had previous fragments restored, -1 otherwise. ; ; bl contains the attributes of the file being restored. ; ; cx contains the file time in DOS dir. format. ; ; dx contains the file date in DOS directory format. ; ; bp:si contains the size of the current file fragment. ; ; Flags bit 0 set if this is the last fragment for the file, ; clear if not. ; ; Returns: ; ; Out_Hndl contains the handle of the open file being restored on the ; hard disk if there are still more fragments to go; otherwise, the ; output file has been closed and Out_Hndl contains -1. ; push cx ; Save file time. push dx ; Save file date. cmp Out_Hndl, -1 ; File will already be open if this is ; not a first fragment. jnz DOC_Opened call Open_Out_File ; Create output file on hard disk. bx, ; bp, and si may need to be saved if ; Open_OUt_File is modified. jnc DOC_Opened ; NC = file created successfully. ; CY = create failed, error code returned in ax. cmp ax, 5 ; Access denied? jne DOC_Not_RO ; No. bx, bp, and si may need to be ; saved if Rid_RO is modified. call Rid_RO ; Yes, if file with same pathspec as ; Rest_Name exists on hard disk, change ; its attributes to normal. call Open_Out_File jnc DOC_Opened call DOC_Unknown ; Unknown error, abort. DOC_Not_RO: cmp ax, 3 ; Path not found? jne DOC_Not_Path ; No, path seems OK. call Make_Dirs ; Yes, try to create it. May need to ; save bx, bp, and si if Make_Dirs ; is modified. call Open_Out_File jnc DOC_Opened DOC_Not_Path: call DOC_Unknown ; Unknown error, abort. DOC_Opened: call Copy_Fragment pop dx ; Retrieve file date. pop cx ; Retrieve file time. test Flags, LastFrag ; Only close output file if no more jz DOC_Done ; fragments. call Close_Out_File DOC_Done: ret ;============================================================== Rid_RO proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; If a file with the same pathspec as the one proposed to be restored already ; exists on the hard disk, change its attributes to normal. lea dx, Rest_Name xor cx, cx ; Use normal attributes. mov ax, 4301H ; Function = change file attributes. int 21H ret Rid_RO endp ;============================================================== Make_Dirs proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; Create the path elements necessary for the pathspec in Rest_Name to exist. ; IF a path element cannot be created, assume that a junk file with the same ; name as the element exists, attempt to delete it, and try again to create the ; path element. mov cx, Rest_Name_Len sub cx, 4 ; Skip d:\ and first char since first ; '\' delimiting a path element can't ; occur there. lea di, Rest_Name+4 ; Skip over d:\ and first char lea dx, Rest_Name ; dx points at path. mov al, '\' MD_More: jcxz MD_Done repne scasb ; Scan for a backslash. jne MD_Done ; No more path elements to create. mov byte ptr [di-1], 0 ; Temporarily change '\' to nul. mov ah, 39H ; Function = create subdirectory. int 21H ; May fail because of junk file with jnc MD_Success ; same name as path. If so, attempt to ; delete pathspec and try again. mov ah, 41H ; Function = Delete File. int 21H mov ah, 39H ; Try again. int 21H MD_Success: mov al, '\' ; Replace backslash. mov [di-1], al jmp short MD_More MD_Done: ret Make_Dirs endp ;============================================================== Open_Out_File proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; Attempt to open the file to be restored on the hard drive truncating any ; existing file to zero length or creating the file if it doesn't exist. ; Abort if operation fails because too many files are open. ; ; Called with: ; ; Rest_Name contains complete pathspec of file to be restored on ; the hard disk. ; ; bl contains attributes of file to be restored. ; ; Returns if successful: ; ; Out_Hndl contains the handle of the successfully opened file on ; the hard disk. ; ; Carry Flag = cleared. ; ; Returns If Not Successful: ; ; Carry Flag = set. ; ; ax contains error code. ; 3 = Path not found. ; 5 = Access denied. ; lea dx, Rest_Name ; Point dx to the pathspec of the ; file to be restored. xor ch, ch ; PUt file attributes in cx. mov cl, bl or cl, 20H ; Force archive bit on. mov ah, 3CH ; Function 3CH=Create or Truncate file. int 21H jc OOF_Err mov Out_Hndl, ax ret OOF_Err: cmp ax, 4 ; Too many open files? jne OOF_Err1 say_err_msg Hard_Disk_Trouble ; Cant_Open_OF say_err_msg Rest_Name say_err_msg Too_Many_Open call Abort OOF_Err1: stc ret Open_Out_File endp ;============================================================== Copy_Fragment proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; Copy the current file fragment from the backup data file to the hard disk. ; If the backup format is DOS 3.3 and this is the last fragment for a file, ; make sure the file size is correct. ; ; Called with: ; ; Bkp_Hndl contains handle of open backup data file with file ptr. ; positioned at the beginning of the data to be restored. ; ; Out_Hndl contains handle of open file on hard disk with file ; ptr. positioned at end-of-file. ; ; Flags bit 0 set if this is the last fragment for the file, clear ; otherwise. ; ; Format_Flags bit 0 set if the backup is in DOS 3.2 format, clear ; otherwise. ; ; Format_Flags bit 1 set if backup is in DOS 3.3 format, clear ; otherwise. ; ; Want_Size contains the total size of the file in bytes according ; to its file record(s) in CONTROL.XXX if the backup is in DOS 3.3 ; format. ; ; bp:si contains the size of the file fragment. ; lea dx, Buffer ; For subsequent reads and writes. CF_More: mov cx, BUFF_SIZE ; Assume at least one buffer full clc ; to go. cmp si, cx ; Check cx against low word of bytes ; remaining to be copied. jnc CF_Cpy ; If NC assumption is correct. ; Proceed with copy. or bp, bp ; If Hi byte isn't 0, assumption is jnz CF_Cpy ; correct. mov cx, si ; Otherwise, copy remaining bytes. CF_Cpy: mov bx, Bkp_Hndl mov ah, 3FH ; Function = Read. int 21H jc CF_Err_Unknown ; Abort if trouble. cmp cx, ax ; Read o.k.? jne CF_Err_In ; Abort if not. mov bx, Out_Hndl ; Write this chunk. mov ah, 40H ; Function 40H = Write. int 21H jc CF_Err_Unknown ; Abort if trouble. cmp cx, ax ; Write o.k.? jne CF_Err_Out ; Abort if not. sub si, cx ; Subtract bytes read from total sbb bp, 0 ; and go back if there's more. jnz CF_More or si, si jnz CF_More test Format_Flags, DOS32 ; Is backup DOS 3.2 format? jnz CF_Done ; Yes, can't check file size. test Flags, LastFrag ; Is this the last chunk. jz CF_Done ; No, return to caller. Comment | The file size may be determined by using the Move File Ptr. function (ah=42H) to seek an offset of 0 bytes relative to EOF. (al=2). Note that the file handle is already in bx. | ; End of comment. mov ax, 4202H ; Determine if file size is correct. xor dx, dx ; Low byte of offset. xor cx, cx ; High byte of offset. int 21H ; dx:ax = returned offset. jc CF_Err_Unknown cmp ax, word ptr Want_Size ; Correct size returned for low word? jnz CF_Wrong_Size ; No. cmp dx, word ptr Want_Size+2 ; Correct size returned for high word? jnz CF_Wrong_Size ; No. CF_Done: ret CF_Wrong_Size: call Say_Cant_Restore say_err_msg Floppy_Trouble ; Bad_Size call Abort CF_Err_In: call Say_Cant_Restore say_err_msg In_IO_Err call Abort CF_Err_Out: call Say_Cant_Restore say_err_msg Out_IO_Err call Abort CF_Err_Unknown: call Say_Cant_Restore say_err_msg Unknown call Abort ;============================================================== Say_Cant_Restore proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; Print common part of error messages including the pathspec of the ; unsuccessfully restored file. say_err_msg Cant_Restore say_err_msg Rest_Name say_err_msg CRLF ret Say_Cant_Restore endp ;============================================================== Copy_Fragment endp ;============================================================== Close_Out_File proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; Close the file which has been restored on the hard disk. Abort if not ; successful. Also set the time and date stamp to that stored in the backup. ; ; Called with: ; ; Out_Hndl contains handle of open output file. ; ; dx contains file date from backup in DOS dir. format. ; ; cx contains file time from backup in DOS dir. format. ; ; Returns: ; ; Out_Hndl contains -1. ; mov bx, Out_Hndl mov ax, 5701H ; Function 57H with al=1 = Set File int 21H ; time and date. mov ah, 3EH ; Function 3EH=Close File. int 21H jc COF_Err mov Out_Hndl, -1 ; Indicates that file is closed. say_msg Restored inc Rest_Count ; UPdate count of files restored. ret COF_Err: say_err_msg Hard_Disk_Trouble ; Cant_Close_OF say_err_msg Rest_Name call Abort Close_Out_File endp ;============================================================== DOC_Unknown proc near ; DOS 3.2 and 3.3 ; nested in Do_One_Chunk ; Restore failed for unknown reason, print error message and abort. say_err_msg Hard_Disk_Trouble ; Cant_Open_OF say_err_msg Rest_Name say_err_msg CRLF say_err_msg Unknown call Abort DOC_Unknown endp ;============================================================== Do_One_Chunk endp ;============================================================== Close_Bkp_File proc near ; DOS 3.3 ; CLOSE BACKUP.XXX ; Close the backup data file and reset its handle to a value of -1. Bkp_Hndl ; contains the handle of the open backup data file. mov bx, Bkp_Hndl mov ah, 3EH ; Function 3EH=Close File. int 21H jc CBF_Err mov Bkp_Hndl, -1 ; Indicates that file is closed. ret CBF_Err: say_err_msg Floppy_Trouble ; Cant_Close_BF call Abort Close_Bkp_File endp ;============================================================== Close_Ctl_File proc near ; DOS 3.3 ; CLOSE CONTROL.XXX ; Close the backup control or ID file and set its handle to -1. Abort if error. ; Ctl_Hndl contains the handle of the open backup control or ID file. mov bx, Ctl_Hndl mov ah, 3EH ; Function 3EH=Close File. int 21H jc CCF_Err mov Ctl_Hndl, -1 ; Indicates that file is closed. ret CCF_Err: say_err_msg Floppy_Trouble ; Cant_Close_CF call Abort Close_Ctl_File endp ;============================================================== Disk_Reset proc near ; DOS 3.2 and 3.3 ; flush all writes to all disks, and clear all DOS buffers or cache. ; We do this prior to inserting a new floppy so buggy versions of DOS ; or buggy caches will not erroneously give us data from the old diskette. ; In particular Lightning is suspect. mov ah,0dh ; Function 0dH=disk reset int 21H ; don't bother with status. ret Disk_Reset endp ;============================================================== Abort_Retry proc near ; DOS 3.2 and 3.3 ; Prompt user to enter A for abort or R for retry. Terminate if abort or ; return to caller if retry. say_err_msg AR_Msg ; Prompt on standard error device. AR_Again: call GetUcChar ; get A/R cmp al, 'A' jne AR1 call Abort AR1: cmp al, 'R' jne AR_Err say_err_msg CRLF ret AR_Err: say_err_msg Beep jmp short AR_Again Abort_Retry endp ;============================================================== Abort_Retry_Ignore proc near ; DOS 3.2 and 3.3 ; Prompt user to enter A for abort, R for retry, or I for Ignore. Terminate ; if abort or return to caller with carry flag set if retry, clear if ignore. say_err_msg ARI_Msg ; Prompt on standard error device. ARI_Again: call GetUCChar ; get A/R/I cmp al, 'A' jne ARI1 call Abort ARI1: cmp al, 'I' jne ARI2 say_err_msg CRLF clc jmp short ARI_Done ARI2: cmp al, 'R' jne ARI_Err say_err_msg CRLF stc ARI_Done: ret ARI_Err: say_err_msg Beep jmp short ARI_Again Abort_Retry_Ignore endp ;============================================================== Abort proc near ; DOS 3.2 3.3 ; Print message with count of successfully restored files and exit with return ; code of 4. patch_num Rest_Count, Aborted_Count+4, 5 say_msg Aborted mov ax, 4C04H ; Function = exit with return code of 4. int 21H Abort endp ;============================================================== Bye proc near ; DOS 3.2 and 3.3 ; Print sign-off message with count of files restored. ; Terminate with return code of 0. If no files were restored, print warning ; message and terminate with return code of 1. call Disk_Reset ; extra insurance that all hard disk ; and cache buffers are cleared ; so DOS will not use data from current ; floppy for next program without reading ; it physically. patch_num Rest_Count, DM_Count+4, 5 say_msg Done_Msg cmp Rest_Count, 0 ; No files restored? je B_None ; Yes, print warning and exit. mov ax, 4C00H ; No, files were restored. int 21H ; Function = exit with return code of 0. B_None: say_err_msg None_Restored mov ax, 4C01H ; Function = exit with return code of 1. int 21H Bye endp ;============================================================== Restore_File proc near ; DOS 3.2 ; Decide if the file currently described in the DTA is a valid file fragment and ; it should be restored. Restore it if it should. Return with carry flag set ; if the diskette is determined to have a scrambled directory or to be from ; another set, clear otherwise. ; call Bypass_Bkp_ID ; Don't restore BACKUPID.@@@ jc RF_Skip ; CY = current file is BACKUPID.@@@. call Open_Bkp_File call Get_Preamble call Copy_Pathspec jc RF_Wrong_Set call Select_Subdir ; Does subdir match command line? jnz RF_Bypass ; NO, skip this pathspec. call Scramble_Check jc RF_Wrong_Set mov dx, Buffer.P_Frag_Seq ; Pass file fragment no. in dx. call Select_File ; Decide if file should be restored. jc RF_Bypass ; CY = don't restore file. mov dx, Buffer.P_Frag_Seq ; Pass file frag. seq. no. in dx. mov bl, ds:DTA.DS_Attr ; Pass file attribute in bl. call Announce_File jc RF_Bypass ; CY = User answered no to restore ; prompt. ; Copy over one fragment. mov bl, ds:DTA.DS_Attr ; Pass file attribute in bl. mov cx, ds:DTA.DS_Time ; Pass file time in cx. mov dx, ds:DTA.DS_Date ; File date in dx. ; Pass file fragment size in bp:si. mov si, word ptr ds:DTA.DS_Size mov bp, word ptr ds:DTA.DS_Size+2 sub si, 128 ; Allow for preamble. sbb bp, 0 call Do_One_Chunk RF_Bypass: call Close_Bkp_File RF_Skip: clc ; NC = all is in order. ret RF_Wrong_Set: ; Diskette is either from another set or its dir. is scrambled. call Close_Bkp_File say_err_msg Floppy_Trouble ; Scrambled call Abort_Retry and Flags, 0ffh - LastDisk ; 0FDH ; Clear last diskette flag so OM_Main ; will prompt for another diskette. dec Want_Seq stc ret ; actual Restore_File endp contains nested procs ;============================================================== Bypass_Bkp_ID proc near ; DOS 3.2 ; nested in Restore_File ; Return CY if file currently in DTA is BACKUPID.@@@ or a subdirectory, NC ; otherwise. We don't want to touch a DOS 3.3 diskette in ; middle of DOS 3.3 restore. test ds:DTA.DS_Attr, 10H ; Is current file a subdir? jnz BBI_Bypass ; Yes, skip it. mov cx, 12 ; Length of BACKUPID.@@@. lea si, Bkp_ID_Name+3 ; Bypass d:\. lea di, ds:DTA.DS_Name ; Point at filename in DTA. repe cmpsb ; Is this BACKUPID.@@@? jz BBI_Bypass ; Yes, skip this file. clc ; No, return NC. ret BBI_Bypass: stc ; Return CY so file will be skipped. ret Bypass_Bkp_ID endp ;============================================================== Get_Preamble proc near ; DOS 3.2 ; Read 128 byte preamble from the open backup file proposed to be restored. mov bx, Bkp_Hndl ; Handle of backup file currently ; being considered. mov cx, 128 ; Length of preamble. lea dx, Buffer ; ds:dx points at buffer. mov ah, 3FH ; Function = read. int 21H jc GP_Err cmp cx, ax ; Correct no. of bytes read? jnz GP_Err ret GP_Err: say_err_msg Unknown call Abort Get_Preamble endp ;============================================================== Copy_Pathspec proc near ; DOS 3.2 ; nested in Restore_File ; Copy the pathspec proposed to be restored out of the buffer into Rest_Name. ; Calculate the total length of the pathspec including the drive but not the ; terminating nul and the offset of the filename relative to Rest_Path. If the ; new fragment is a continuation of the last file, do not copy its pathspec but ; make sure it matches the old one exactly and return CY if not. ; ; Called with: ; ; Buffer contains the preamble of the file proposed to be restored. ; ; Flags bit 1 set if this fragment should be the beginning of a new ; file, clear otherwise. ; ; Flags bit 3 set if this is the first fragment on a diskette accepted ; out of sequence, clear otherwise. ; ; Rest_Name contains the complete nul terminated pathspec including ; drive specification of the last fragment processed if any. ; ; Rest_Name_Len contains the length of the pathspec of the previously ; processed fragment if any including the drive but not the trailing ; nul. ; ; Filespec_Ptr contains the offset of the filename of the previously ; processed fragment relative to Rest_Path if any. ; ; Returns if successful: ; ; Rest_Name contains the complete pathspec of the new fragment ; proposed to be restored with any slashes used as path separators ; converted to backslashes, i.e. MS DOS 3.2 uses slashes instead of ; backslashes for path separators in the BACKUP preambles. ; ; Rest_Name_Len contains the length of the new pathspec including the ; drive but not the trailing nul. ; ; Filespec_Ptr contains the offset of the filename in the new pathspec ; relative to Rest_path. ; ; Carry Flag = clear. ; ; Returns if diskette dir. scrambled or diskette from another set: ; ; Rest_Name, Rest_Name_Len and Filespec_Ptr as on entry. ; ; Carry Flag = set. ; lea si, Buffer.P_Hard_Name+1 ; Skip leading backslash. lea di, Rest_Path xor ch, ch mov cl, Buffer.P_Hard_Len ; Get length of string to process in cx. dec cx ; Don't bother with leading backslash. jle CP_Err1 ; Nul pathspecs. should never occur. cld test Flags, WrongOk ; Diskette out of sequence? jnz CP_Copy ; Yes, copy pathspec test Flags, LastFrag ; Last file continued? jnz CP_Copy ; No, copy new pathspec CP_More: cmp byte ptr [si], '/' ; Convert slashes to backslashes before jnz CP_Compare ; comparison. mov byte ptr [si], '\' ; Convert '/' to '\'. CP_Compare: cmpsb jnz CP_Err2 ; New pathspec is different. loop CP_More clc ; Return NC = all is in order. ret CP_Copy: lodsb ; Copy pathspec cmp al, '/' ; Convert slashes to backslashes. jnz CP_Not_Slash mov al, '\' CP_Not_Slash: stosb loop CP_Copy mov cl, Buffer.P_Hard_Len ; Calculate length of pathspec inc cx ; Add 2 for d: and subtract 1 for nul. mov Rest_Name_Len, cx mov al, '\' ; Calculate offset of filespec dec di ; Point at trailing nul of Rest_Name. std ; Scan backward for first '\'. repne scasb jcxz CP_Err1 ; Should never get that far. dec cx mov Filespec_Ptr, cx cld clc ; NC = all is in order. ret CP_Err1: say_err_msg Unknown call Abort CP_Err2: stc ; Return CY meaning dir. is scrambled or ret ; diskette is from another set. Copy_Pathspec endp ;============================================================== Scramble_Check proc near ; DOS 3.2 ; Nested in Restore_file ; Check for file fragments which are out of sequence either because the current ; diskette is from another set or because the diskette directory has been ; scrambled. Return CY if there's a problem, NC otherwise. If all is well, ; this routine also resets the diskette out of sequence and new diskette flags, ; calculates the next fragment sequence no., and sets the last fragment flag ; according to the current file fragment. mov dx, Want_Frag_Seq ; Save Want_Frag_Seq. cmp dx, Buffer.P_Frag_Seq ; Is this what was expected? je SC_OK ; Yes, return to caller. test Flags, WrongOk ; Was this diskette accepted out of ; sequence? jnz SC_OK ; Yes, no problem. stc ; CY = dir. is scrambled or ret ; diskette is from another set. SC_OK: and Flags, 0ffh-(WrongOk+NewDisk+LastFrag) ; 0f2h ; Clear diskette out of sequence, new ; diskette, and last fragment flags. mov ax, Buffer.P_Frag_Seq ; Calculate next sequence no. inc ax cmp Buffer.P_Last, 0 ; Is this last fragment of current file? je SC_More ; No. mov ax, 1 ; Yes, reset fragment sequence no. or Flags, LastFrag ; and set last fragment flag. SC_More: mov Want_Frag_Seq, ax clc ; NC = all is in order. ret Scramble_Check endp ;============================================================== Restore_File endp ;============================================================== Initial_Checks proc near ; Make sure DOS version is 2.0 or later and that there is sufficient memory to ; run RESTORE. mov ah, 30H ; Function = Get DOS Version. int 21H or ax, ax ; Abort if DOS Version below 2.0. jnz RM_Ver_OK lea dx, Wrong_Ver mov ah, 9 ; Function = Write String to Console. int 21H ; Use DOS 1.0 method. ret RM_Ver_OK: cmp sp, 100H + offset Buffer + BUFF_SIZE + STACK_SIZE ja RM_Enough_Mem ; Abort if insufficient memory. say_err_msg Insufficient call Abort RM_Enough_Mem: ret initial_checks EndP ;============================================================== Old_Main proc near ; DOS 3.2 ; Perform the restore operation using the DOS 3.2 format. say_msg Old_Bkp_Format OM_Next_Disk: lea dx, All_Pattern ; Get first file to restore. Comment | Rumor has it that MS DOS 3.2 might not return all normal files unless subdirs. are requested as well. Therefore, subdirs are included in the search here and filtered out later by Bypass_Bkp_ID. | end of comment. mov cx, 16H ; Include system and hidden files. Bits ; for RO. and Arc. have no effect, these ; bits do not exclude a file from being ; "normal". mov ah, 4EH ; Function = Search For First. OM_Next: int 21H ; Get next file. jc OM_No_More ; CY = no more files on this diskette. call Restore_File jc OM_No_More ; This diskette was rejected. mov ah, 4FH ; Function = Search For Next. jmp short OM_Next ; Get next file to restore. OM_No_More: test Flags, LastDisk ; Is this the last diskette? jnz OM_Done ; Yes, exit. call Get_Next_Disk ; No, get next diskette of set. jmp OM_Next_Disk OM_Done: call Bye ; Return to DOS. Old_Main endp ;============================================================== ;<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ; M A I N L I N E R O U T I N E ;<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><> ;============================================================== Restore_Main proc near ; DOS 3.3 and 3.2 (most of 3.2 handled by Old_Main) call Initial_Checks ; Check DOS version and for sufficient ; memory. call Parse ; parse command line. test Cmd_Flags, Quietly ; /Q switch specified? jnz RM_Quiet ; Yes, skip banner. if INSTALLING say_msg InstBanner else say_msg RestBanner call Strike_Any_Key endif RM_Quiet: call Get_Next_Disk test Format_Flags, DOS32 ; Is this set in old backup format? jnz Old_Main ; Yes, do DOS 3.2 restore. ; No, proceed with DOS 3.3 restore. say_msg New_Bkp_Format RM_More: call Open_Ctl_File call Open_Bkp_File call Process_Hdr call Get_Subdir ; CY means that the continuation jc RM_Again ; of a file on this diskette had a ; different dirspec. than the previous ; fragment, probably indicating the ; diskette is from another set. call Close_Bkp_File call Close_Ctl_File test Flags, LastDisk ; Is this the last diskette? jnz RM_Done ; If so, exit to DOS. RM_Again: call Get_Next_Disk jmp RM_More ; Go back for more. RM_Done: call Bye ; Terminate with return code of 0. Restore_Main endp ;============================================================== even Buffer label byte ; Buffer for disk I/O. Will actually be ; Buffer BUFF_SIZE dup (?). ; we place buffer here ; it won't take space in ; load module. Code ends end Start