PAGE, 132 ; TITLE XCOPY WITH FULL MEMORY USE - Ver. 4.00 ;****************** START OF SPECIFICATIONS ***************************** ; MODULE NAME: XCOPY ; ; DESCRIPTIVE NAME: selectively copy groups of files, which can include ; lower level subdirectories. ; ; FUNCTION: The modules of XCOPY will be placed in the following order - ; SSEG, DSEG(MAIN DATA, MAIN MSG), CSEG (MAIN + INIT), ; DSEG_INIT(INIT DATA, INIT MSG) ; ; HEADER - informations needed about the file, subdirectory ... ; Continue_Info -> 0 - a whole single file in this header ; segment, or dir. ; 1 - Continuation of a small file. ; 2 - Continuation of a Big file ; 3 - Eof of continuation ; Next_Ptr -> points to the next header segment ; Before_Ptr -> points to the old header segment ; ; By optionally using the Archive bit in the directory of each ; file, XCOPY can be used as an alternative method of creating ; backup files which can be accessed directly by DOS and its ; applications without the need to "restore" the backup files. ; ; XCOPY is especially useful when several files are being copied ; and there is a generous amount of RAM available, because XCOPY ; will fill the memory with all the source files it can read in ; before starting to create output files. If the memory is not ; enough to hold all the source, this cycle will be repeated until ; the process is completed. For single drive systems, this maximum ; usage of the memory greatly reduces the amount of diskette ; swapping that would be required by the usual COPY command. ; ; ENTRY POINT: MAIN ; ; INPUT: (DOS COMMAND LINE PARAMETERS) ; ; SOURCE OPERAND: TARGET OPERAND: ; ; [d:] [path] filename[.ext] [d:] [path] [filename[.ext]] ; or ; [d:] path [filename[.ext]] ; or ; d: [path] [filename[.ext]] ; ; ; SWITCHES: ; ; /A /D /E /M /P /S /V /W ; ;The /A switch will copy only those files whose archive bit of the attribute is ;set to one. The attribute of the source file is not changed. This option is ;useful when making multiple backups when doing the non-final backup. ;The archive bit is one when a file has be created or Revised since the last ;time the bit was turned off. XCOPY /M or BACKUP /M will turn this bit off. ;The ATTRIB command can also be used to change the setting of the archive bit. ; ;The /D switch will copy only those files whose date is the same or later than ;the date specified. Depending on the country code you selected using the ;COUNTRY command, the date is specified in the format corresponding to the ;indicated country. ; ;The /E switch will create subdirectories on the target even if they end up ;being empty after all copying is over. If /E is not specified, empty ;subdirectories are not created. ; ;The /M switch will copy only those files whose archive bit is set in its ;attribute. Unlike the /A switch, /M will cause the archive bit in the source ;file to be turned off. This allows XCOPY to be used in making a final backup. ;The archive bit is one when a file has be created or Revised since the last ;time the bit was turned off. XCOPY /M or BACKUP /M will turn this bit off. ;The ATTRIB command can also be used to change the setting of the archive bit. ; ;The /P switch will prompt the operator before copying each file. In this ;situation, each file is copied onto the target before reading in the next ;file. The multi-file copy into a large memory buffer is not done. The prompt ;displays the complete filespec it proposes to copy and asks for (Y/N) ;response, which is then read in from the standard input device. ; ;The /S switch will not only copy the files in the current source directory but ;also those in all the subdirectories below the current one, with XCOPY ;following the Tree of the subdirectories to access these files. /S does not ;create an empty subdirectory on the target (unless /E is also specified). ;If the /S switch is not specified, XCOPY works only within the specified (or ;current) subdirectory of the source. ; ;The /V switch will cause DOS to verify that the sectors written on the target ;are recorded properly. This option has been provided so you can verify that ;critical data has been correctly recorded. This option will cause XCOPY to ;run more slowly, due to the additional overhead of verification. ; ;The /W switch will instruct XCOPY to pause before actually starting the ;movement of data, thus permit the copying of diskettes that do not actually ;have XCOPY available on them. The diskette containing XCOPY can be mounted ;first, the XCOPY command given with the /W option, then when the prompt ;requesting permission to continue is given, that diskette can then be removed ;and the source diskette mounted in its place, then the operator can press any ;key to continue after the pause. This feature is especially useful in a ;non-hardfile system. ; ; EXIT-NORMAL: ERRORLEVEL_0 - This is the normal completion code. ; ERRORLEVEL_2 - This is due to termination via Control-Break. ; ERRORLEVEL_4 - This is used to indicate an error condition. ; ; There are many types of problems that are detected and result in this ; return code, such as: ; ; write failure due to hard disk error ; disk full ; conflict between name of new subdirectory and existing filename ; access denied ; too many open files ; sharing violation ; lock violation ; general failure ; file not found ; path not found ; directory full ; invalid parms ; reserved file name as source ; insufficient memory ; incorrect DOS version ; ; ; INTERNAL REFERENCES: ; ; ROUTINES: ; ; ; DATA AREAS: ; ; ; EXTERNAL REFERENCES: ; ; ROUTINES: ; ; ; DATA AREAS: ; ; ; NOTES: This module should be processed with the SALUT pre-processor ; with the re-alignment not requested, as: ; ; SALUT XCOPY,NUL,; ; ; To assemble these modules, the sequential ; ordering of segments may be used. ; ; For LINK instructions: ; link profile ..\lib ; ; REVISION HISTORY: A000 Version 4.00: add PARSER, System Message Handler, ; Remove the BELL char.,turn off APPEND during TREE ; search,Extended Attribute processing, Uppercasing ; and "Out Of Space" during write to standard out. ; A001 PTM0011 XCOPY not handling path >63 characters. ; CHK_MAX_LENGTH proc(XCPYINIT) is Revised to err if ; >63 chrs. ; A002 PTM0012 XCOPY unnecessarily accessing current drive. ; ORG_S_T_DEF is Revised to ignore CHDIR if drive ; is not TARGET or SOURCE. ; A003 PTM0088 XCOPY (\) missing in 'FILE SHARING ERROR'. ; This problem is fixed with incorporation of the ; new message services. ; A004 PTM0700 9/02/87 Avoid duplicate switches and ; display parm in error. ; A005 DCR0201 9/11/87 Incorperate new format for EXTENDED ; ATTRIBUTES. ; A006 PTM1490 10/04/87 XCOPY /D CAUSES "INVALID PARAMETER" ; MSG AND SHOULD BE "INVALID NUMBER OF PARAMETERS" ALSO ; DATE IS NOT VALIDATED. ; A007 PTM1657 10/14/87 XCOPY INVALIDLY FAILS TO READ A READ ; ONLY FILE, AND OUTPUTS THE WRONG MSG. ; A008 PTM1688 10/15/87 XCOPY NOT CREATING EMPTY SUBDIRS IF ; THE SOURCE DIR. IS EMPTY. ; A009 PTM2199 11/02/87 XCOPY NOT HANDELING FILENAMES GREATER ; THAN 12 CHARACTERS. ; A010 PTM2203 11/03/87 XCOPY NOT HANDELING DBCS PATH NAMES ; PROPERLY. (INCORP. CHK. IF 1st BYTE IS DBCS) ; A011 PTM2271 11/04/87 XCOPY NOT HANDELING FILENAMES GREATER ; THAN 12 CHARACTERS.(S_FILE BUFFER OVERFLOWES). ; A012 PTM2347 11/09/87 XCOPY SETTING THE CODE PAGE OF A DEV. ; AND A DEV. IS NOT ALLOWED FOR A TARGET. ; A013 PTM2565 11/17/87 XCOPY HANGS AUTOTEST. SET EXTENDED ; ATTRIBUTE CALL TO DOS POINTS TO INVALID BUFFER. ; A014 PTM2597 11/20/87 XCOPY REPORTS FILE CREATION ERROR ; IF TARGET FILE IS GREATER THAN 12 CHARACTERS. ; A015 PTM2782 12/04/87 XCOPY FILENAME (EXTENSION) ; TRUNCATION ERROR. INCREASE HEADER BUFFER TO 3 PARA. ; A016 PTM2783 12/09/87 XCOPY ALLOWS 'ASSIGN' DRIVES TO ; BE COPIED ONTO THEMSELVS. ADD NEW CODE TO INIT. ; A017 PTM3139 01/15/88 XCOPY HANGS WHEN TRYING TO OUTPUT ; "INSUFFICIENT DISK SPACE" FOR MAKE DIRECTORY. ; A018 PTM3283 02/01/88 XCOPY NEEDS TO CHANGE 'FILE NOT ; FOUND' MSG TO EXTENDED ERROR MSG FORMAT. ALSO ; DELETED DEF 28 IN XCOPY.SKL & XMAINMSG.EQU ; A019 PTM3395 02/08/88 XCOPY FAILING TO SUSPEND THE ; 'APPEND /X' FUNCTION. FIX IN XCOPY.SAL, XCOPY.EQU, ; AND DOS.EQU. ; A020 PTM3344 02/09/88 XCOPY READING PAST TOP_OF_MEMORY, ; OVER-WRITING VIDIO BUFFER SET BY MODE 13H ON PS2s. ; A021 PTM3513 02/19/88 XCOPY READING PAST TOP_OF_MEMORY, ; OVER-WRITING VIDIO BUFFER SET BY MODE 13H ON PS2s. ; A022 PTM3933 03/18/88 XCOPY NOT RESTORING DIRECTORY OF ; DEFAULT DRIVE. FIX IN XCOPY.SAL. ; A023 PTM3904 03/18/88 XCOPY NOT USING PARSE 03 MSG. FOR ; 'INVALID SWITCH'. FIX IN XCOPY.SKL & XCPYINIT.SAL. ; A024 PTM3958 03/22/88 XCOPY MSGS DO NOT CONFORM TO SPEC. ; NEED NULL DELIMITER IN XCPYINIT.SAL. ; A025 PTM3965 03/23/88 XCOPY LEAVING CURRENT TARGET DIR. ; CHANGED. FIX IN XCPYINIT.SAL. ; A026 PTM4920 05/19/88 XCOPY NOT OVERLAYING FILES ON FULL ; TARGET DISK. FIX IN XCOPY.SAL. ; A027 PTM5022 06/03/88 'PATH TOO LONG' MSG. WITH TWO CHAR. ; SOURCE SUBDIR. FILESPEC. FIX IN XCPYINIT.SAL. ; ; Label: "The DOS XCOPY Utility" ; "Version 4.00 (C) Copyright 1988 Microsoft" ; "Licensed Material - Program Property of Microsoft" ; ;****************** END OF SPECIFICATIONS ***************************** ;-------------------------------- ; Include Files ;-------------------------------- INCLUDE XMAINMSG.EQU ;AN000;message file INCLUDE DOS.EQU ;AN000; INCLUDE XCOPY.EQU ;AN000; INCLUDE SYSMSG.INC ;AN000; MSG_UTILNAME ;AN000; ;------------------------------- ; Structures ;------------------------------- ;HEADER - informations needed about the file, subdirectory ... ;Continue_Info -> 0 - a whole single file in this header segment, or dir. ; 1 - Continuation of a small file. ; 2 - Continuation of a Big file ; 3 - EOF of continuation ;Next_Ptr -> points to the next header segment ;Before_Ptr -> points to the old header segment HEADER STRUC CONTINUE_INFO DB 0 ;set for filesize bigger then 0FFD0h NEXT_PTR DW ? ;next buffer ptr in para BEFORE_PTR DW ? ;before ptr in para DIR_DEPTH DB ? ;same as S_DEPTH CX_BYTES DW 0 ;actual # of bytes in this buffer seg. ATTR_FOUND DB ? ;attribute found FILE_TIME_FOUND DW ? FILE_DATE_FOUND DW ? LOW_SIZE_FOUND DW ? HIGH_SIZE_FOUND DW ? TARGET_DRV_LET DB " :" ;used for writing FILENAME_FOUND DB 13 DUP (0) ;AC015; FILENAME TERMINATE_STRING DB 16 DUP (0) ;AC015;TERM FILENAME STRING FOR DOS ATTRIB_LIST DW ? ;AC005;EXTENDED ATTRIBUTE BUFFER ;------------------------------------------------------------------- ; extended attribute list used by extended open & get extended ;------------------------------------------------------------------- ; ATTRIB_LIST LABEL BYTE extended attribute buffer ; ;EA STRUC ; EXTENDED ATTRIBUTE ;EA_TYPE DB ? ; TYPE ;EAISUNDEF EQU 0 ; UNDEFINED TYPE (ATTRIB SKIPS) ; ; (OR TYPE NOT APPLICABLE) ; ; LENGTH: 0 TO 64K-1 BYTES ;EAISLOGICAL EQU 1 ; LOGICAL (0 OR 1) (ATTRIB DISPLAYS) ; LENGTH: 1 BYTE ;EAISBINARY EQU 2 ; BINARY INTEGER (ATTRIB DISPLAYS) ; ; LENGTH: 1, 2, 4 BYTES ;EAISASCII EQU 3 ; ASCII TYPE (ATTRIB DISPLAYS) ; ; LENGTH: 0 TO 128 BYTES ;EAISDATE EQU 4 ; DOS FILE DATE FORMAT (ATTRIB DISPLAYS) ; ; LENGTH: 2 BYTES ;EAISTIME EQU 5 ; DOS FILE TIME FORMAT (ATTRIB DISPLAYS) ; ; LENGTH: 2 BYTES ; ; OTHER VALUES RESERVED ;EA_FLAGS DW ? ; FLAGS ;EASYSTEM EQU 8000H ; EA IS SYSTEM DEFINED ; ; (BUILTIN, NOT APPLICATION DEFINED) ;EAREADONLY EQU 4000H ; EA IS READ ONLY (CANT BE CHANGED) ;EAHIDDEN EQU 2000H ; EA IS HIDDEN FROM ATTRIB ;EACREATEONLY EQU 1000H ; EA IS SETABLE ONLY AT CREATE TIME ; ; OTHER BITS RESERVED ;EA_RC DB ? ; FAILURE REASON CODE (SET BY DOS) ;EARCNOTFOUND EQU 1 ; NAME NOT FOUND ;EARCNOSPACE EQU 2 ; NO SPACE TO HOLD NAME OR VALUE ;EARCNOTNOW EQU 3 ; NAME CAN'T BE SET ON THIS FUNCTION ;EARCNOTEVER EQU 4 ; NAME CAN'T BE SET ;EARCUNDEF EQU 5 ; NAME KNOWN TO THIS FS BUT NOT SUPPORTED ;EARCDEFBAD EQU 6 ; EA DEFINTION BAD (TYPE, LENGTH, ETC) ;EARCACCESS EQU 7 ; EA ACCESS DENIED ;EARCUNKNOWN EQU -1 ; UNDETERMINED CAUSE ;EA_NAMELEN DB ? ; LENGTH OF NAME ;EA_VALLEN DW ? ; LENGTH OF VALUE ;EA_NAME DB ? ; FIRST BYTE OF NAME ; ;EA_VALUE DB ? ; FIRST BYTE OF VALUE ; HEADER ENDS SUB_LIST STRUC DB 11 ;AN000; DB 0 ;AN000; DATA_OFF DW 0 ;AN000; offset of data to be inserted DATA_SEG DW 0 ;AN000; offset of data to be inserted MSG_ID DB 0 ;AN000; n of %n FLAGS DB 0 ;AN000; Flags MAX_WIDTH DB 0 ;AN000; Maximum field width MIN_WIDTH DB 0 ;AN000; Minimum field width PAD_CHAR DB 0 ;AN000; character for pad field SUB_LIST ENDS ;****************************************************************************** SSEG SEGMENT PARA STACK DB 64 DUP ('STACK ') ;256 words SSEG ENDS ;****************************************************************************** DGROUP GROUP DSEG,DSEG_INIT ;FOR CONVENIENT ADDRESSIBLITY OF ;DSEG_INIT in INIT routine ;****************************************************************************** DSEG SEGMENT PARA PUBLIC ; DATA Segment ;--- EXTERNAL VARIABLES --- EXTRN PARM_FLAG: BYTE EXTRN COMMAND_LINE: BYTE ;AN000;THE COMMAND LINE FOR THE PARSER ;--- PUBLIC VARIABLES --- PUBLIC ERRORLEVEL PUBLIC DISP_S_PATH PUBLIC DISP_T_PATH PUBLIC S_DRV PUBLIC S_DRV_1 PUBLIC T_DRV PUBLIC T_DRV_1 PUBLIC T_DRV_2 PUBLIC S_DRV_PATH PUBLIC S_PATH PUBLIC T_DRV_PATH PUBLIC T_PATH PUBLIC S_FILE PUBLIC T_FILENAME PUBLIC T_TEMPLATE PUBLIC T_MKDIR_LVL PUBLIC S_ARC_DRV PUBLIC S_ARC_PATH ; PUBLIC PSP_SEG PUBLIC SAV_DEFAULT_DRV PUBLIC SAV_DEFAULT_DIR PUBLIC SAV_S_DRV PUBLIC SAV_S_CURDIR PUBLIC SAV_T_DRV PUBLIC SAV_T_CURDIR PUBLIC S_DRV_NUMBER PUBLIC T_DRV_NUMBER PUBLIC TOP_OF_MEMORY PUBLIC BUFFER_PTR PUBLIC BUFFER_BASE PUBLIC BUFFER_LEFT PUBLIC MAX_BUFFER_SIZE PUBLIC MAX_CX ; PUBLIC MY_FLAG PUBLIC SYS_FLAG PUBLIC COPY_STATUS PUBLIC OPTION_FLAG PUBLIC INPUT_DATE PUBLIC INPUT_TIME PUBLIC SUBST_COUNT ;AN000; PUBLIC MSG_CLASS ;AN000; PUBLIC INPUT_FLAG ;AN000; PUBLIC MSG_NUM ;AN000; PUBLIC SUBLIST1 ;AN000;MSG SUBLIST USED BY INIT ;--- VARIABLES DEFINED --- MSG_SERVICES ;AN000; ERRORLEVEL DB 0 ;errorlevel INPUT_DATE DW 0 INPUT_TIME DW 0 PSP_SEG DW ? SAV_DEFAULT_DRV DB ? ;1 = A, 2 = B etc. saved default SAV_DEF_DIR_ROOT DB '\' SAV_DEFAULT_DIR DB 80 DUP (0) SAV_S_DRV DB 'A:\' SAV_S_CURDIR DB 80 DUP (0) SAV_T_DRV DB 'B:\' SAV_T_CURDIR DB 80 DUP (0) ; DISP_S_PATH DB 67 DUP (0) ;mirror image of source path. used for display message when copying DISP_S_FILE DB 13 DUP (0) DISP_T_PATH DB 67 DUP (0) ;mirror image of target path DISP_T_FILE DB 13 DUP (0) ; B_SLASH DB '\',0 ;AN000; FILE_COUNT LABEL WORD ;AN000; FILE_CNT_LOW DW 0 ;copied file count FILE_CNT_HIGH DW 0 ; ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - APPENDFLAG DW 0 ;AN000;append /X status save area FOUND_FILE_FLAG DB 0 ;used for showing the message "File not found" ; S_DRV_NUMBER DB 0 ;source, target drv # T_DRV_NUMBER DB 0 ; S_DRV_PATH LABEL BYTE ;source drv, path used for single_drv_copy S_DRV DB 'A:\' S_PATH DB 80 DUP (0) ;AN000;Initialized by calling GET CUR DIR S_DEPTH DB 0 S_DRV_1 DB 'A:' S_FILE DB '????????.???',0 ;default filename to find file S_FILE_OVERFLO DB 20 DUP (0) ;AN011;BUFFER IF MORE THAN 12 CHARS. S_DIR DB '????????.???',0 ;to find any subdirectory name S_DIR_OVERFLO DB 20 DUP (0) ;AN011;BUFFER IF MORE THAN 12 CHARS. S_PARENT DB '..',0 ;source parent used for non single_drv_copy S_HANDLE DW 0 ;file handle opened S_ARC_DRV_PATH LABEL BYTE ;informations used to change source file's S_ARC_DRV DB 'A:\' ;archieve bits. S_ARC_PATH DB 64 DUP (0) S_ARC_DEPTH DB 0 T_DRV_PATH LABEL BYTE ;target drv, path used all the time T_DRV DB 'B:\' T_PATH DB 80 DUP (0) ;AC016;init by call GET CUR DIR in INIT T_DEPTH DB 0 T_FILE LABEL BYTE ;target filename for file creation T_DRV_1 DB 'B:' ;target drv letter T_FILENAME DB 15 DUP (0) ;target filename T_TEMPLATE DB 15 DUP (0) ;if global chr entered, this will be used instead of filename. T_PARENT LABEL BYTE T_DRV_2 DB 'B:' T_PARENT_1 DB '..',0 T_HANDLE DW 0 ;target handle created T_MKDIR_LVL DB 0 ;# of target starting directories created. ; ;------------------------------------------ ; PRINT_STDOUT input parameter save area ;------------------------------------------ SUBST_COUNT DW 0 ;AN000; message substitution count MSG_CLASS DB 0 ;AN000; message class INPUT_FLAG DB 0 ;AN000; Type of INT 21 used for KBD input MSG_NUM DW 0 ;AN000; message number INPUT_BUFF db 20 dup(0) ;AN000; keyboard input buffer used ;for user response (Y/N) ;-------------------------------------------------------------- ; Following three sublists are used by the Message Retriever ;-------------------------------------------------------------- SUBLIST1 LABEL DWORD ;AN000;SUBSTITUTE LIST 1 DB 11 ;AN000;sublist size DB 0 ;AN000;reserved DD 0 ;AN000;substition data Offset DB 1 ;AN000;n of %n DB 0 ;AN000;data type DB 0 ;AN000;maximum field width DB 0 ;AN000;minimum field width DB 0 ;AN000;characters for Pad field SUBLIST2 LABEL DWORD ;AN000;SUBSTITUTE LIST 2 DB 11 ;AN000;sublist size DB 0 ;AN000;reserved DD 0 ;AN000;substition data Offset DB 2 ;AN000;n of %n DB 0 ;AN000;data type DB 0 ;AN000;maximum field width DB 0 ;AN000;minimum field width DB 0 ;AN000;characters for Pad field SUBLIST3 LABEL DWORD ;AN000;SUBSTITUTE LIST 3 DB 11 ;AN000;sublist size DB 0 ;AN000;reserved DD 0 ;AN000;substition data Offset DB 3 ;AN000;n of %n DB 0 ;AN000;data type DB 0 ;AN000;maximum field width DB 0 ;AN000;minimum field width DB 0 ;AN000;characters for Pad field FILE_SEARCH_ATTR DW NORM_ATTR DIR_SEARCH_ATTR DW INCL_H_S_DIR_ATTR ; OPEN_MODE DB Read_Only_Deny_Write ;READ_ONLY_DENY_WRITE ;access, sharing mode ; ;Equates are defined in XCOPY.EQU MY_FLAG DB 0 ;informations for a tree walk ; find_first_flag equ 01h ;set MY_FLAG by "OR" ; findfile_flag equ 02h ; no_more_file equ 04h ; single_copy_flag equ 08h ;single copy instead of multi copy ; visit_parent_flag equ 10h ;visit parent node ; found_flag equ 20h ;found flag - for find subdir ; missing_link_flag equ 40h ;insuffiecient info. for not creating empty dir ; is_source_flag equ 80h ;if set, dealing with source ; reset_find_first equ 0FEh ;reset by AND ; reset_findfile equ 0FDh ; reset_no_more equ 0FBh ; reset_visit_parent equ 0EFh ; reset_found equ 0DFh ; reset_missing_link equ 0BFh ; reset_is_source equ 07Fh FILE_FLAG DB 0 ; cont_flag equ 01h ; eof_flag equ 02h ; big_file_flag equ 04h ; file_bigger_flag equ 08h ; created_flag equ 10h ; reset_cont equ 0FEh ; reset_eof equ 0FDh ; reset_big_file equ 0FBh ; reset_file_bigger equ 0F7h ; reset_created equ 0EFh ; reset_readfile equ 0F0h ;reset FILE_FLAG for read a file ; COPY_STATUS DB 0 ; open_error_flag equ 01h ; read_error_flag equ 02h ; create_error_flag equ 04h ; write_error_flag equ 08h ; mkdir_error_flag equ 10h ; chdir_error_flag equ 20h ; maybe_itself_flag equ 40h ; disk_full_flag equ 80h ; reset_open_error equ 0FEh ; reset_read_error equ 0FDh ; reset_create_error equ 0FBh ; reset_write_error equ 0F7h ; reset_close_error equ 0EFh ; reset_chdir_error equ 0DFh ; ACTION_FLAG DB 0 ; reading_flag equ 01h ;display "Reading source files..." ; reset_reading equ 0FEh ;do not display. ; SYS_FLAG DB 0 ;system information ; one_disk_copy_flag equ 01h ;xcopy with only one logical drive. ; default_drv_set_flag equ 02h ;default drive has been changed by this program ; default_s_dir_flag equ 04h ;source current directory saved. ; default_t_dir_flag equ 08h ;target current directory saved. ; removalble_drv_flag equ 10h ; sharing_source_flag equ 20h ;source shared ; sharing_target_flag equ 40h ; turn_verify_off_flag equ 80h ;turn the verify off when exit to dos ; reset_default_s_dir equ 0FBh ;reset default_s_dir_flag ; OPTION_FLAG DB 0 ; slash_a equ 01h ;soft archieve ? ; slash_d equ 02h ;date? ; slash_e equ 04h ;create empty dir? ; slash_m equ 08h ;hard archieve ? (turn off source archieve bit) ; slash_p equ 10h ;prompt? ; slash_s equ 20h ;walk the tree? ; slash_v equ 40h ;verify on? ; slash_w equ 80h ;show "Press any key to begin copying" msg) ; reset_slash_a equ 0FEh ;turn off soft archieve ; reset_slash_m equ 0F7h ;turn off hard archieve MAX_CX DW 0 ;less than 0FFD0h ACT_BYTES DW 0 ;actual bytes read. HIGH_FILE_SIZE DW 0 LOW_FILE_SIZE DW 0 ; TOP_OF_MEMORY DW 0 ;para BUFFER_BASE DW 0 ;para MAX_BUFFER_SIZE DW 0 ;para. BUFFER_LEFT at INIT time. BUFFER_LEFT DW 0 ;para BUFFER_PTR DW 0 ;para. If buffer_left=0 then invalid value DATA_PTR DW 0 ;buffer_ptr + HEADER OLD_BUFFER_PTR DW 0 ;last buffer_ptr SIZ_OF_BUFF DW ? ;AN005;para. EXTENDED ATTRIB BUFF SIZE BYTS_OF_HDR DW ? ;AN005;bytes TOTAL HEADER SIZE PARA_OF_HDR DW 3 ;AC008;para. TOTAL HDR SIZE INIT TO 3 OPEN_FILE_COUNT DW ? ;AN005;TRACKING OF OPEN FLS FOR BUFFER ; ;SIZE CALCULATION. DBCSEV_OFF DW 0 ;AN010; remember where dbcs vector is DBCSEV_SEG DW 0 ;AN010;next time I don't have to look ; ;structured data storage allocation FILE_DTA Find_DTA <> ;DTA for find file DTAS Find_DTA 32 dup (<>) ;DTA STACK for find dir ;** Througout the program BP will be used for referencing fieldsname in DTAS. ;For example, DS:[BP].dta_filename. DSEG ENDS ;****************************************************************************** CSEG SEGMENT PUBLIC ASSUME CS:CSEG, DS:DGROUP, SS:SSEG MSG_SERVICES ;AN000; MSG_SERVICES ;AN000; ;--- EXTERNAL PROCEDURES --- EXTRN INIT: NEAR ;INIT PROC ; ;--- PUBLIC PROCEDURES --- ;USED BY INIT PUBLIC SET_BUFFER_PTR PUBLIC STRING_LENGTH PUBLIC CONCAT_ASCIIZ PUBLIC LAST_DIR_OUT PUBLIC CHK_DRV_LETTER PUBLIC COMPRESS_FILENAME PUBLIC PRINT_STDOUT PUBLIC PRINT_STDERR PUBLIC SET_DEFAULT_DRV PUBLIC MAIN_EXIT PUBLIC MAIN_EXIT_A PUBLIC CTRL_BREAK_EXIT PUBLIC SWITCH_DS_ES PUBLIC MY_INT24 ;--- INT 24 ADDR ---------- PUBLIC SAV_INT24_OFF PUBLIC SAV_INT24_SEG PUBLIC SYSLOADMSG ;AN000; PUBLIC SYSDISPMSG PUBLIC SYSGETMSG SAV_INT24 LABEL DWORD SAV_INT24_OFF DW 0 ;original int 24 addr holder SAV_INT24_SEG DW 0 ;--- START OF A PROGRAM --- ASSUME DS:NOTHING ;AN000; ASSUME ES:NOTHING ;AN000; MAIN PROC FAR PUSH AX ;AN000;PRESERVE FOR INIT DRV VALIDITY MOV BX,DGROUP MOV ES,BX ;AN000;SET UP ADDRESS OF DSEG IN ES ASSUME ES:DGROUP ;AN000; MOV SI,81H ;AN000;POINT TO THE INPUT STRING LEA DI,COMMAND_LINE ;AN000;POINT TO THE SAVE AREA IN PARSER MOV CX,127 ;AN000;GET ALL THE DATA(LOOP COUNT) REP MOVSB ;AN000;MOVE IT MOV PSP_SEG,DS ;AN000;REMEMBER WHERE THE PSP IS MOV DS,BX ;AN000;SET UP ADDRESS OF DSEG IN DS ASSUME DS:DGROUP ;AN000; CALL SYSLOADMSG ;AN000; preload all messages jnc XCOPY_INIT ;AN000; no error, do xcopy init CALL SYSDISPMSG ;AN000; else display error message POP AX ;AN000;WAS PRESERVED FOR DRV VALIDATION JMP JUST_EXIT ;AN000; exit XCOPY_INIT: POP AX ;AN000;WAS PRESERVED FOR DRV VALIDATION CALL INIT ;initialization JC MAIN_EXIT ;error. (Already message has been displayed) MOV BP, OFFSET DTAS ;initialize BP OR ACTION_FLAG, READING_FLAG ;set reading flag for copy message ;Before walking the tree, find out the /X status of APPEND and save it. ;Then terminate the /X feature. After the tree search, restore the ;original /X status. This is done at EXIT time. MOV AX,CHK_APPEND ;AN000;CHECK IF APPEND INSTALLED INT 2FH ;AN000; OR AL,AL ;AN000;INSTALLED? ; $IF NZ ;AN000;YES JZ $$IF1 MOV AX,VER_APPEND ;AN019;ASK IF DOS VERSION OF APPEND INT 2FH ;AN019;CALL THE FUNCTION CMP AX,D_V_APPEND ;AN000;DOS VERSION? ; $IF E ;AN000;YES JNE $$IF2 MOV AX,GET_APPEND ;AN000;GET THE APPEND STATE INT 2FH ;AN000; MOV APPENDFLAG,BX ;AN000;SAVE THE STATE TO RESTORE TEST APPENDFLAG,F_APPEND ;AN019;IS THE /X BIT ON? ; $IF NZ ;AN000;YES JZ $$IF3 MOV AX,SET_APPEND ;AN000;SET THE APPEND STATE MOV BX,APPENDFLAG ;AN000;GET THE SAVED STATE XOR BX,F_APPEND ;AN000;TURN OFF THE /X BIT INT 2FH ;AN000;DO IT ; $ENDIF ;AN000; $$IF3: ; $ENDIF ;AN000; $$IF2: ; $ENDIF ;AN000; $$IF1: CALL TREE_COPY CALL ORG_S_DEF ;restore the original source default dir CALL WRITE_FROM_BUFFER ;write from buffer if we missed it. MAIN_EXIT: MOV BX, DGROUP MOV DS, BX ;re initialize ds, es MOV ES, BX ;exit here if the status of source, target or default drv has been changed. CALL CHK_FILE_NOT_FOUND ;if no files has been found, show the message. ; ; Set message substitution list LEA SI,SUBLIST1 ;AN000; get addressability to sublist LEA DX,FILE_COUNT ;AN000; offset to file count MOV [SI].DATA_OFF,DX ;AN000; save data offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,1 ;AN000; message ID MOV [SI].FLAGS,RIGHT_ALIGN+UNSGN_BIN_DWORD ;AN018; MOV [SI].MAX_WIDTH,9 ;AN018; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,9 ;AN018; MINIMUM FIELD WITH MOV [SI].PAD_CHAR,SPACE ;AN018; MINIMUM FIELD WITH ; Set message parameters MOV AX,MSG_FILES_COPIED ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,PARM_SUBST_ONE ;AN000; one message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no user input CALL PRINT_STDOUT ;AN000; display file count MAIN_EXIT_A: MOV BX, DGROUP MOV DS, BX ;re initialize ds, es MOV ES, BX ;exit here if the status of source, target or default drv has been changed. CALL CHK_MKDIR_LVL ;starting target directory has been created? CALL ORG_S_T_DEF ;restore original target, source, default drv, and verify status JUST_EXIT: ;unconditional immediate exit ; Restore the original status of APPEND if active. MOV BX,APPENDFLAG ;AN000;GET THE STATUS WORD OR BX,BX ;AN019;IF FLAGS SAVED, THIS IS DOS VER. ; $IF NZ ;AN019;IF ACTIVE, JZ $$IF7 MOV AX,SET_APPEND ;AN000;SET TO THE ORIGINAL STATE INT 2FH ;AN000; turn on the /X feature ; $ENDIF ;AN000; $$IF7: ; MOV AH, 4Ch ;return to dos MOV AL, ERRORLEVEL ;set return code whatever INT 21H CTRL_BREAK_EXIT: MOV ERRORLEVEL, 2 ;set errorlevel to 2 for control break JMP MAIN_EXIT_A MAIN ENDP ; ;----------------- SUBROUTINES --------------------------------------------- TREE_COPY PROC NEAR ;Walk the source tree to read files and subdirectories OR MY_FLAG, FINDFILE_FLAG ;deals with files OR MY_FLAG, FIND_FIRST_FLAG ;find first CALL SET_MY_DTA ;set DTA to FILE_DTA ; $DO $$DO9: CALL FIND_FILE ;find first (next) TEST MY_FLAG, NO_MORE_FILE ;no more file? ; $LEAVE NZ ;then exit loop JNZ $$EN9 CALL READ_INTO_BUFFER ;else read the file into the buffer ; $ENDDO JMP SHORT $$DO9 $$EN9: TEST OPTION_FLAG, SLASH_S ;walk the tree? ; $IF NZ,LONG JNZ $$XL1 JMP $$IF12 $$XL1: AND MY_FLAG, RESET_FINDFILE ;now, deals with directory OR MY_FLAG, FIND_FIRST_FLAG ;find first ; $DO $$DO13: CALL SET_MY_DTA ;set DTA to DTAS according to BP CALL FIND_DIR ;find first (next) TEST MY_FLAG, NO_MORE_FILE ;no more subdirectory? ; $LEAVE NZ ;then leave this loop to return to caller JNZ $$EN13 LEA DI, S_DRV_PATH LEA SI, [BP].DTA_FILENAME CMP S_PATH, 0 ;root directory? ; $IF E JNE $$IF15 MOV AL, 0FFh ;then '\' is already provided. Just concat. ; $ELSE JMP SHORT $$EN15 $$IF15: MOV AL, PATH_DELIM ;put delimiter ; $ENDIF $$EN15: CALL CONCAT_ASCIIZ ;make new path test option_flag, slash_p ;prompt mode? ; $IF NZ JZ $$IF18 call p_concat_display_path ; $ENDIF $$IF18: INC S_DEPTH ;increase depth CALL MAKE_HEADER ;make header in the buffer OR MY_FLAG, IS_SOURCE_FLAG ;dealing with source AND MY_FLAG, RESET_VISIT_PARENT ;going to visit child node CALL CHANGE_S_DIR ;change source dir ADD BP, type FIND_DTA ;increase DTAS stack pointer CALL TREE_COPY ;tree copy the sub directory ; $ENDDO JMP SHORT $$DO13 $$EN13: CMP S_DEPTH, 0 ;starting directory? then exit ; $IF NE ;else JE $$IF21 DEC S_DEPTH ;dec depth TEST OPTION_FLAG, SLASH_E ;copy subdirectories even if empty? ; $IF Z JNZ $$IF22 CALL DEL_EMPTY ;then check the old_buffer_ptr and ;if it is a directory, then restore ;buffer_ptr to old. ; $ENDIF $$IF22: LEA DI, S_DRV_PATH CALL LAST_DIR_OUT ;change environments test option_flag, slash_p ;prompt mode? ; $IF NZ JZ $$IF24 call p_cut_display_path ; $ENDIF $$IF24: LEA DX, S_DRV_PATH ;before returning to the caller OR MY_FLAG, IS_SOURCE_FLAG OR MY_FLAG, VISIT_PARENT_FLAG CALL CHANGE_S_DIR SUB BP, type FIND_DTA ; $ENDIF $$IF21: ; $ENDIF ;walk the tree $$IF12: RET TREE_COPY ENDP ; DEL_EMPTY PROC NEAR ;If buffer is not full, and the tree walk is going to return to the parents, ;this routine should be called. ;If old_buffer_ptr points to a directory, then set buffer_ptr to this, and ;increase buffer_left by HEADER (para) and set old_buffer_ptr to that of ;BEFORE_PTR. i.e. delete the empty directory entry from the buffer. PUSH ES PUSH CS POP AX CMP OLD_BUFFER_PTR, AX ;buffer is empty? JE DE_EXIT ;yes, exit MOV ES, OLD_BUFFER_PTR TEST ES:ATTR_FOUND, 10h ;directory? JZ DE_EXIT ;if not, exit MOV AX, OLD_BUFFER_PTR MOV BUFFER_PTR, AX ;set new BUFFER_PTR MOV AX, ES:BEFORE_PTR MOV OLD_BUFFER_PTR, AX ;set new OLD_BUFFER_PTR MOV AX, PARA_OF_HDR ;AN005;GET THE HEADER SIZE (para.) ADD BUFFER_LEFT, AX ;AC005;dir entry must be only hdr SIZE. DE_EXIT: POP ES RET DEL_EMPTY ENDP ; P_concat_display_path proc near ;concatenate subdirectory name found from DTAS to the ;DISP_S_PATH which will be used for prompts ;DS - data seg MOV DI, OFFSET DISP_S_PATH LEA SI, [BP].DTA_FILENAME CMP S_DEPTH, 0 ;this will be the first subdir? ; $IF E JNE $$IF28 MOV AL, 0FFh ;then do not put '\' ; $ELSE JMP SHORT $$EN28 $$IF28: MOV AL, Path_delim ; $ENDIF $$EN28: CALL CONCAT_ASCIIZ RET P_concat_display_path endp ; P_cut_display_path proc near ;take the last dir out from the DISP_S_PATH for prompt. ;DS,ES - data seg MOV SI, OFFSET DISP_S_PATH MOV DI, OFFSET DISP_S_PATH CALL LAST_DIR_OUT ; $IF C JNC $$IF31 CALL CHK_DRV_LETTER ; $IF NC JC $$IF32 MOV BYTE PTR DS:[SI], 0 ; $ELSE JMP SHORT $$EN32 $$IF32: MOV BYTE PTR [DI], 0 ; $ENDIF $$EN32: ; $ELSE JMP SHORT $$EN31 $$IF31: CMP S_DEPTH, 0 ; $IF E JNE $$IF36 MOV DI, AX DEC DI MOV BYTE PTR [DI], '\' MOV BYTE PTR [DI+1], 0 ; $ENDIF $$IF36: ; $ENDIF $$EN31: RET P_cut_display_path endp ; READ_INTO_BUFFER PROC NEAR ;Read *** a *** file into buffer TEST MY_FLAG, SINGLE_COPY_FLAG ;single copy? ; $IF Z,AND ;no, multi copy JNZ $$IF39 TEST ACTION_FLAG, READING_FLAG ;show message? ; $IF NZ ;yes. JZ $$IF39 MOV AX,MSG_READING_SOURCE ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDOUT ;AN000;show message "Reading source files" AND ACTION_FLAG, RESET_READING ;reset it ; $ENDIF $$IF39: AND FILE_FLAG, RESET_READFILE ;reset file_flag to read a file MOV AX,FILE_DTA.DTA_FILE_SIZE_HIGH MOV HIGH_FILE_SIZE, AX MOV AX,FILE_DTA.DTA_FILE_SIZE_LOW MOV LOW_FILE_SIZE, AX CALL CMP_FILESIZE_TO_BUFFER_LEFT ;compare sizes MOV AX, PARA_OF_HDR ;AN005;GET THE HEADER SIZE (para.) CMP MAX_BUFFER_SIZE, AX ;AN005;IS EA BUFFER TOO LARGE? ; $IF B JNB $$IF41 CLC ;AN005;CLEAR CARRY MOV AX, MSG_INSUF_MEMORY ;AC005;GET THE MESSAGE ID MOV MSG_NUM,AX ;AN005;NEED MESSAGE ID FOR PRINT MOV SUBST_COUNT,NO_SUBST ;AN005;NO SUBSTITUTION TEXT MOV INPUT_FLAG,NO_INPUT ;AN005;NO INPUT = 0 MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN005;MESSAGE CLASS = -1 CALL PRINT_STDERR ;AN005;print error. AX points to msg ID ; $ENDIF ;AN005;WE HAVE ENOUGH MEMORY $$IF41: MOV AX, PARA_OF_HDR ;AN005;GET THE HEADER SIZE (para.) CMP MAX_BUFFER_SIZE,AX ;AN005;IS EA BUFFER TOO LARGE? JB RIB_ERROR ;AN005;CLOSE THE FILE AND GET THE NEXT TEST FILE_FLAG, FILE_BIGGER_FLAG ;filesize > buffer_left - HEADER ? JZ RIB_SMALL ;if not, then small file MOV BX, S_HANDLE ;AN005; CALL CLOSE_A_FILE ;AN005;ONLY OPENED TO GET BUFFER SIZE CALL WRITE_FROM_BUFFER CALL CMP_FILESIZE_TO_BUFFER_LEFT ;compare again TEST FILE_FLAG, FILE_BIGGER_FLAG ;still bigger? JNZ RIB_BIG ;yes. Big file RIB_SMALL: CALL SMALL_FILE JC RIB_ERROR JMP RIB_EXIT RIB_BIG: MOV BX, S_HANDLE ;AN005; CALL CLOSE_A_FILE ;AN005;ONLY OPENED TO GET BUFFER SIZE CALL BIG_FILE JNC RIB_EXIT RIB_ERROR: TEST COPY_STATUS, OPEN_ERROR_FLAG ;open error? JNZ RIB_EXIT ;just exit. find next file MOV BX, S_HANDLE ;else write error CALL CLOSE_A_FILE ;close the troubled file ;and find next file RIB_EXIT: TEST MY_FLAG, SINGLE_COPY_FLAG ;single copy? ; $IF NZ JZ $$IF43 CALL WRITE_FROM_BUFFER ;then write a file ; $ENDIF $$IF43: RET READ_INTO_BUFFER ENDP ; SMALL_FILE PROC NEAR ;handles a file smaller than max_buffer_size or buffer_left, i.e. fit in memory. ;This routine will call MAKE_HEADER, SET_BUFFER_PTR< READ_A_FILE, ;CALC_FILE_SIZE, CMP_FILE_FFD0h, CLOSE_A_FILE. SMF_CONT: CALL CMP_FILE_FFD0h ;filesize > 0FFD0h ? TEST FILE_FLAG, FILE_BIGGER_FLAG JZ SMF_EOF ;filesize <= 0FFD0h OR FILE_FLAG, CONT_FLAG ;filesize > 0FFD0h. set cont_flag MOV CX, 0FFD0h ;# of bytes to read CALL READ_A_FILE JC SMF_ERROR ;unsuccessful read? CALL MAKE_HEADER ;else make header and ready for next CALL CALC_FILE_SIZE ;filesize = filesize - bytes read JMP SMF_CONT ;loop. compare again with the rest SMF_EOF: MOV CX, LOW_FILE_SIZE ;rest of the bytes to read OR FILE_FLAG, EOF_FLAG ;AN000;set EOF CALL READ_A_FILE JC SMF_ERROR CALL MAKE_HEADER MOV BX, S_HANDLE CALL CLOSE_A_FILE JMP SMF_EXIT SMF_ERROR: ; SMF_EXIT: RET SMALL_FILE ENDP ; BIG_FILE PROC NEAR ;handles a file which is bigger than max_buffer_size ;Needs 2 file handles open concurrently for read and write OR FILE_FLAG, BIG_FILE_FLAG OR FILE_FLAG, CONT_FLAG CALL OPEN_A_FILE JC BIF_ERROR ;error in open? CMP MAX_BUFFER_SIZE, 0FFFh ;max buffer size > 0FFFh in para ? JA BIF_BIG ;yes. large buffer system ;else small buffer MOV CX, MAX_CX ;CX = max_buffer_size * 16 - HEADER BIF_SM: CALL READ_A_FILE JC BIF_ERROR ;read error? CALL MAKE_HEADER CALL WRITE_FROM_BUFFER JC BIF_ERROR ;write error? TEST FILE_FLAG, EOF_FLAG ;end of file set by READ_A_FILE? JZ BIF_SM ;if not, read again MOV BX, S_HANDLE CALL CLOSE_A_FILE JMP BIF_EXIT ;finished. BIF_BIG: MOV CX, 0FFD0h ;max # of data bytes this program supports BIF_BIG1: CALL READ_A_FILE JC BIF_ERROR CALL MAKE_HEADER CALL CALC_FILE_SIZE ;modify file size BIF_BIG2: CALL CMP_FILESIZE_TO_BUFFER_LEFT ;filesize > buffer_left? TEST FILE_FLAG, FILE_BIGGER_FLAG ;yes. JZ BIF_END ;if it is not, call small_file MOV AX, PARA_OF_HDR ;AN021;GET THE ATTR. HDR SIZE ADD AX, 0FFFh ;AN021; CMP BUFFER_LEFT, AX ;AC021;BUFFER_LEFT >= 0FFF0h+HDR SIZE? JAE BIF_BIG ;then loop again. MOV AX, PARA_OF_HDR ;AN021;GET THE ATTR. HDR SIZE ADD AX, 140H ;AN021; CMP BUFFER_LEFT, AX ;AC021;BUFFER_LEFT >= 5Kbytes+HDR SIZE? ;minimum buffer size this pgm supports. JL BIF_BIG3 ;then flush buffer and try again. **IF system buffer left < 5 K then infinit loop can happen. MOV AX,BUFFER_LEFT SUB AX,PARA_OF_HDR ;AC005;FOR HEADER SIZE para. MOV CX,16 MUL CX ;AN020;MAKE IT NUMBER OF BYTES MOV CX,AX ;AN020;FOR READ JMP BIF_BIG1 ;read again BIF_BIG3: CALL WRITE_FROM_BUFFER JC BIF_ERROR JMP BIF_BIG2 ;flush buffer and compare again. BIF_END: CALL SMALL_FILE ;when filesize <= buffer_left then SMALL_FILE will finish it. JC BIF_ERROR ;something wrong? CALL WRITE_FROM_BUFFER ;else finish copying this file JNC BIF_EXIT BIF_ERROR: ;what happened? BIF_EXIT: RET BIG_FILE ENDP ; MAKE_HEADER PROC NEAR ;When called by READ_A_FILE after the data had been read into the buffer, this ;routine will put the header which is just below the data area where the ;current BUFFER_PTR points. The header E.A.BUFFER SIZE + (3 para) long. And ;this routine will also call SET_BUFFER_PTR to set the BUFFER_PTR, BUFFER_LEFT ;for the next process. ;If called by TREE_COPY for a SUBDIRECTORY handle, this routine should ;check the BUFFER_LEFT (when called by READ_A_FILE, the caller is assumed ;to check the size of buffer_left before calling.) In this case, this ;routine will set the next BUFFER_PTR, BUFFER_LEFT, OLD_BUFFER_PTR ;instead of SET_BUFFER_PTR routine. ;Informations are obtained from the DTA area (for file - FILE_DTA.xxx ;dir - DS:[BP].xxx ) and stored into the header by referencing ES:field;s name. ;DS - Program Data area ;ES - will be used for a header segment in the buffer. ; PUSH ES ;save ES PUSH AX MH_AGAIN: MOV AX,BUFFER_PTR ;buffer_ptr is a segment MOV ES, AX ;now, ES is a header seg. ; MOV AX, PARA_OF_HDR ;AN005;GET THE HEADER SIZE (para.) CMP BUFFER_LEFT,AX ;AC005;buffer_left=less than NEEDED? JAE MH_START CALL WRITE_FROM_BUFFER ;if so, flush buffer JC MH_ERROR_BRIDGE ;write error? JMP SHORT MH_AGAIN ;reinitialize ES to new buffer ptr MH_START: TEST MY_FLAG, FINDFILE_FLAG ;identify caller. JNZ MH_FILE ;if a file, jmp to MH_FILE ;else deals with directory. MOV ES:CONTINUE_INFO, 0 ;not a continuation. MOV AX,OLD_BUFFER_PTR MOV ES:BEFORE_PTR, AX ;set before_ptr in header MOV AX,BUFFER_PTR MOV OLD_BUFFER_PTR, AX ;set variable OLD_BUFFER_PTR ADD AX,PARA_OF_HDR ;AC005;AX = BUFFER_PTR+HEADER(para) MOV BUFFER_PTR, AX ;set new BUFFER_PTR MOV ES:NEXT_PTR, AX ;set NEXT_PTR in the header MOV AX, PARA_OF_HDR ;AN005;GET THE HEADER SIZE (para.) SUB BUFFER_LEFT,AX ;AC005;adjust BUFFER_LEFT CMP BUFFER_LEFT,AX ;AC005;less than HEADER SIZE (para) ? ; $IF B JNB $$IF45 MOV BUFFER_LEFT, 0 ;indicate buffer_full ; $ENDIF $$IF45: MOV AL, S_DEPTH MOV ES:DIR_DEPTH, AL ;now save other info's MOV AL, DS:[BP].DTA_ATTRIBUTE MOV ES:ATTR_FOUND, AL ;in this case, DIR MOV AL, BYTE PTR T_DRV MOV ES:TARGET_DRV_LET, AL ;mov target drive letter MOV ES:TARGET_DRV_LET+1, DRV_delim ; ':' MOV CX, 13 LEA SI, [BP].DTA_FILENAME ;DS:SI MOV DI, OFFSET ES:FILENAME_FOUND ;ES:DI REP MOVSB ;mov sting until cx = 0 JMP MH_EXIT MH_ERROR_BRIDGE: JMP MH_ERROR MH_FILE: ;handles a file header hereafter. TEST FILE_FLAG, CONT_FLAG ;continuation? JZ MH_WHOLE_FILE ;no, just a whole file TEST FILE_FLAG, EOF_FLAG ;Eof flag set? JNZ MH_CONT_END ;yes, must be end of continuation TEST FILE_FLAG, BIG_FILE_FLAG ;Is this a big file? JNZ MH_BIG ;yes MOV ES:CONTINUE_INFO, 1 ;else small file continuation. JMP MH_A_FILE MH_WHOLE_FILE: MOV ES:CONTINUE_INFO, 0 JMP MH_A_FILE MH_CONT_END: MOV ES:CONTINUE_INFO, 3 JMP MH_A_FILE MH_BIG: MOV ES:CONTINUE_INFO, 2 MH_A_FILE: MOV AX,FILE_DTA.DTA_FILE_TIME MOV ES:FILE_TIME_FOUND, AX MOV AX, FILE_DTA.DTA_FILE_DATE MOV ES:FILE_DATE_FOUND, AX MOV AX, FILE_DTA.DTA_FILE_SIZE_LOW MOV ES:LOW_SIZE_FOUND, AX MOV AX, FILE_DTA.DTA_FILE_SIZE_HIGH MOV ES:HIGH_SIZE_FOUND, AX MOV AL, BYTE PTR T_DRV MOV ES:TARGET_DRV_LET, AL MOV ES:TARGET_DRV_LET+1, DRV_DELIM MOV CX, 13 MOV SI, OFFSET FILE_DTA.DTA_FILENAME MOV DI, OFFSET ES:FILENAME_FOUND REP MOVSB ; Get Extended Attribute list of the opened file and save in attribute buff. MOV BX,S_HANDLE ;AN005; BX = handle MOV SI,ALL_ATTR ;AN005; SELECT ALL ATTRIBUTES SIZE MOV CL, PARAGRAPH ;AN005; PARAGRAPH = 4 FOR DIV BY 16 MOV AX,SIZ_OF_BUFF ;AN005; GET THE SIZE EXPRESSED IN para. SHL AX, CL ;AN005; GET # OF BYTES FROM para. MOV CX, AX ;AN005; NEEDS TO BE IN CX MOV DI, OFFSET ES:ATTRIB_LIST ;AN005; ES:DI = E A LIST IN BUFFER MOV AX, GET_ATTRIB ;AN005; extended attribute code 5702H INT 21H ;AN005; get extended attribute list JC MH_ERROR ;AN000; jump if error MOV AX, OLD_BUFFER_PTR MOV ES:BEFORE_PTR, AX MOV AX, ACT_BYTES MOV ES:CX_BYTES, AX CALL SET_BUFFER_PTR ;set buffer_ptr for next. AX is already set. MOV AX, BUFFER_PTR MOV ES:NEXT_PTR, AX ;next buffer_ptr is next_ptr MOV AL, S_DEPTH MOV ES:DIR_DEPTH, AL ;same as source depth MOV AL, FILE_DTA.DTA_ATTRIBUTE MOV ES:ATTR_FOUND, AL ;attribute found JMP MH_EXIT ;AN000; MH_ERROR: OR COPY_STATUS, OPEN_ERROR_FLAG ;AN000; CALL EXTENDED_ERROR_HANDLER ;AN000; MH_EXIT: POP AX POP ES RET MAKE_HEADER ENDP ; OPEN_A_FILE PROC NEAR ;------------------------------------------------------------------------- ; Use extended open DOS call to open source file, ; if successfully open, then save filehand to S_HANDLE. ; And update the open file count. ;------------------------------------------------------------------------- LEA SI,FILE_DTA.DTA_FILENAME ;AN005; DS:SI-->NAME TO OPEN MOV DX,OPN_FLAG ;AN000; flag = 0101H MOV CX,OPN_ATTR ;AN000; attribute = 0 MOV BX,OPN_MODE ;AN007; open mode = 0000H (READ) MOV DI, NUL_LIST ;AN005; ES:DI = -1 MOV AX, Ext_Open ;AN000; = 6Ch INT 21H ;AN000; OPEN SOURCE FILE JC OF_ERROR MOV S_HANDLE, AX ;save filehandle INC OPEN_FILE_COUNT ;AN005;UPDATE THE OPEN FILE COUNTER JMP OF_EXIT ;AN000; exit OF_ERROR: OR COPY_STATUS, OPEN_ERROR_FLAG CALL EXTENDED_ERROR_HANDLER OF_EXIT: RET OPEN_A_FILE ENDP ; CMP_FILE_FFD0h PROC NEAR ;check whether the filesize in HIGH_FILE_SIZE, LOW_FILE_SIZE is bigger than ;0FFD0h. If it is, then set FILE_BIGGER_FLAG, else reset it. CMP HIGH_FILE_SIZE, 0 ; $IF E,AND JNE $$IF47 CMP LOW_FILE_SIZE, 0FFD0h ; $IF BE JNBE $$IF47 AND FILE_FLAG, RESET_FILE_BIGGER ;filesize <= 0FFD0h ; $ELSE JMP SHORT $$EN47 $$IF47: OR FILE_FLAG, FILE_BIGGER_FLAG ; $ENDIF $$EN47: RET CMP_FILE_FFD0h ENDP ; CALC_FILE_SIZE PROC NEAR ;subtract the bytes read (ACT_BYTES) from the filesize in HIGH_FILE_SIZE, ;LOW_FILE_SIZE. MOV AX, ACT_BYTES SUB LOW_FILE_SIZE, AX SBB HIGH_FILE_SIZE, 0 RET CALC_FILE_SIZE ENDP ; READ_A_FILE PROC NEAR ;read a file. ;if after reading, AX < CX or AX = 0 the set EOF_FLAG. ;INPUT:CX - # of bytes to read ; BUFFER_PTR ; S_HANDLE ;OUTPUT: ACT_BYTES PUSH DS ;save program data seg MOV AH, Read MOV BX, S_HANDLE MOV DX, BUFFER_PTR ;current buffer header seg ADD DX, PARA_OF_HDR ;AC005;skip the header part MOV DS, DX ;now DS = buffer_ptr + HDR, data area XOR DX, DX ;offset DX = 0 INT 21H POP DS ;restore program data area JC RF_ERROR ;read error? CMP AX, CX JE RF_OK OR FILE_FLAG, EOF_FLAG ;EOF reached. AX = 0 or AX < CX RF_OK: CLC ;clear carry caused from CMP MOV ACT_BYTES, AX ;save actual bytes read JMP RF_EXIT RF_ERROR: OR COPY_STATUS, READ_ERROR_FLAG CALL EXTENDED_ERROR_HANDLER RF_EXIT: RET READ_A_FILE ENDP ; FIND_IT PROC NEAR ;set first or next depending on FIND_FIRST_FLAG. ;once called, reset FIND_FIRST_FLAG. TEST MY_FLAG, FIND_FIRST_FLAG ; $IF NZ ;yes JZ $$IF50 MOV AH, Find_First ; $ELSE JMP SHORT $$EN50 $$IF50: MOV AH, Find_Next ; $ENDIF $$EN50: AND MY_FLAG, RESET_FIND_FIRST ;reset FIND_FIRST_FLAG INT 21H RET FIND_IT ENDP ; FIND_FILE PROC NEAR ;find a file ;set NO_MORE_FILE if carry. ; $SEARCH $$DO53: TEST MY_FLAG, FIND_FIRST_FLAG ;find first ? ; $IF NZ JZ $$IF54 MOV DX, OFFSET S_FILE MOV CX, File_Search_Attr ;normal = 0 ; $ELSE JMP SHORT $$EN54 $$IF54: MOV DX, OFFSET FILE_DTA ; $ENDIF $$EN54: CALL FIND_IT ; $EXITIF C JNC $$IF53 OR MY_FLAG, NO_MORE_FILE ;no more file in this directory ; $ORELSE JMP SHORT $$SR53 $$IF53: MOV FOUND_FILE_FLAG, 1 ;set the flag for "File not found" msg. CALL FILTER_FILES ;found. filter it with options TEST MY_FLAG, FOUND_FLAG ; $ENDLOOP NZ ;if found, leave this loop else start again JZ $$DO53 AND MY_FLAG, RESET_NO_MORE ; $ENDSRCH $$SR53: RET FIND_FILE ENDP ; FIND_DIR PROC NEAR ;find directory entry ;set NO_MORE_FLAG if carry. ; $SEARCH $$DO61: TEST MY_FLAG, FIND_FIRST_FLAG ; $IF NZ JZ $$IF62 MOV DX, OFFSET S_DIR MOV CX, DIR_SEARCH_ATTR ; $ELSE JMP SHORT $$EN62 $$IF62: MOV DX, BP ; $ENDIF $$EN62: CALL FIND_IT ; $EXITIF C ;no more file JNC $$IF61 OR MY_FLAG, NO_MORE_FILE ;set MY_FLAG and exit this loop ; $ORELSE ;otherwise found a file JMP SHORT $$SR61 $$IF61: CMP DS:[BP].DTA_ATTRIBUTE, Is_subdirectory ; directory? ; $IF E,AND JNE $$IF67 CMP DS:[BP].DTA_FILENAME, A_dot ;starts with . ? ; $IF NE ;if not, then desired subdir JE $$IF67 OR MY_FLAG, FOUND_FLAG ;found ; $ELSE JMP SHORT $$EN67 $$IF67: AND MY_FLAG, RESET_FOUND ; $ENDIF $$EN67: TEST MY_FLAG, FOUND_FLAG ; $ENDLOOP NZ ;if found, leave this loop else start again JZ $$DO61 AND MY_FLAG, RESET_NO_MORE ;found. set my_flag and exit ; $ENDSRCH $$SR61: RET FIND_DIR ENDP ; FILTER_FILES PROC NEAR ;FILE_DTA.XXX HAS INFORMATIONS ;this routine also show the prompt of source path, filename, if SLASH_P is on. TEST OPTION_FLAG, SLASH_A ;soft archieve? JNZ SLASH_AM_RTN ;yes TEST OPTION_FLAG, SLASH_M ;then hard archieve? JNZ SLASH_AM_RTN ;yes FF_D: TEST OPTION_FLAG, SLASH_D ;date? JNZ SLASH_D_RTN FF_P: TEST OPTION_FLAG, SLASH_P ;prompt mode? ** this should be placed last. JNZ SLASH_P_RTN JMP SHORT FF_FOUND ;no more selective options. copy this file. SLASH_AM_RTN: ;soft or hard archieve. CALL CHK_ARCHIEVE_BIT JC FF_NOT_FOUND JMP SHORT FF_D ;check other options SLASH_D_RTN: CALL CHK_DATE_FILE ;check file's date JC FF_NOT_FOUND JMP SHORT FF_P slash_p_rtn: call prompt_path_file ;show message and get input from the user jc ff_not_found ;user does not want this file FF_FOUND: OR MY_FLAG, FOUND_FLAG ;set found_flag JMP SHORT FF_EXIT FF_NOT_FOUND: AND MY_FLAG, RESET_FOUND ;this file is not what we want to copy FF_EXIT: RET FILTER_FILES ENDP ; CHK_ARCHIEVE_BIT PROC NEAR ;check the current FILE.DTA area and if archieve bit is on, found. TEST FILE_DTA.DTA_ATTRIBUTE, 20h ;archieve on? ; $IF NZ ;yes JZ $$IF72 CLC ;clear carry ; $ELSE JMP SHORT $$EN72 $$IF72: STC ;archieve bit is off. Don't ; $ENDIF ;have to copy this file $$EN72: RET CHK_ARCHIEVE_BIT ENDP ; CHK_DATE_FILE PROC NEAR ; MOV CX, FILE_DTA.DTA_FILE_DATE CMP CX, INPUT_DATE ;FILE_DATE < INPUT_DATE ; $IF B JNB $$IF75 STC ;not found ; $ELSE JMP SHORT $$EN75 $$IF75: CLC ;found desired file ; $ENDIF $$EN75: RET CHK_DATE_FILE ENDP ; PROMPT_PATH_FILE PROC NEAR ;show the current source path, filename found, and get the user input. ;if it is yes, then reset carry, no, set carry otherwise show ;the whole message again. ;DS, ES - data seg MOV CX, 13 ;13 max LEA SI, FILE_DTA.DTA_FILENAME MOV DI, OFFSET DISP_S_FILE REP MOVSB ;filename => disp_s_file PPF_AGAIN: LEA SI,SUBLIST1 ;AN000; get addressability to sublist LEA DX,DISP_S_PATH ;AN000; offset to PATH NAME MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,1 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH LEA SI,SUBLIST2 ;AN000; get addressability to sublist LEA DX,DISP_S_FILE ;AN000; offset to FILE NAME MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,2 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH LEA SI,SUBLIST1 ;AN000; CMP S_DEPTH,0 ;now dealing with starting dir? JE PATH_FILE_QUERY ;ask (Y/N) JMP PPF_1 PATH_FILE_QUERY: MOV AX,P_S_PATH_FILE0 ;no back slash, since it is already there JMP PPF_PRT ;AN000; PPF_1: MOV AX,P_S_PATH_FILE1 ; Path and file name with ; back slash delemeter PPF_PRT: MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,PARM_SUBST_TWO ;AN000; substitution count MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,DOS_KEYB_INP ;AN000; Y or N INPUT CALL PRINT_STDOUT ;AN000; Display message PUSH AX ;AN000; SAVE IT MOV AX,MSG_CR_LF_STR ;AN000; JUST CR,LF MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; substitution count = 0 MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; NO INPUT CALL PRINT_STDOUT ;AN000; Display message POP AX ;AN000; GET IT BACK ; On return from prompt msg, AX contains Y or N response character MOV DL,AL ;AN000; MOV AH,65H ;AN000; MOV AL,023H ;AN000; Y/N check function INT 21H ;AN000; Issue Extended country to ; capitalize the Y/N response JC PPF_RETRY ;AN000; NOT Y OR N, ASK AGAIN CMP AX,1 ;AN000; look for Y JG PPF_RETRY ;AN000; NOT Y OR N, ASK AGAIN CMP AX,0 ;AN000; look for N JE PPF_NO ;AN000; PPF_YES: CLC ;AN000;CLEAR CARRY JMP SHORT PPF_EXIT PPF_RETRY: JMP PPF_AGAIN ;AN000;ASK AGAIN PPF_NO: STC ;AN000;set carry PPF_EXIT: RET PROMPT_PATH_FILE ENDP ; SET_MY_DTA PROC NEAR ;set DS:DX for find_first(next). If MY_FLAG is set to FINDFILE_FLAG then ;set it to the offset FILE_DTA, otherwise to BP. ;DS should be set to the area whre FILE_DTA, DTAS are. PUSH DX ;save current DX TEST MY_FLAG, FINDFILE_FLAG ;handling file? ; $IF NZ JZ $$IF78 MOV DX, OFFSET FILE_DTA ; $ELSE JMP SHORT $$EN78 $$IF78: MOV DX, BP ; $ENDIF $$EN78: MOV AH, Set_DTA INT 21H POP DX RET SET_MY_DTA ENDP ; CHANGE_S_DIR PROC NEAR ;change source directory ;DS points to program data seg. CMP S_DRV[2], 0 ;LAST_DIR_OUT have took '\' out? ; $IF E JNE $$IF81 MOV S_DRV[2], '\' ;then restore '\' for root dir MOV S_DRV[3], 0 ; $ENDIF $$IF81: TEST SYS_FLAG, ONE_DISK_COPY_FLAG ;one drive letter copy? ; $IF NZ,OR ;yes JNZ $$LL83 TEST OPTION_FLAG, SLASH_M ;hard archive option? (should use full path ; $IF NZ ; since hard archieve operation will corrupt the current directory) JZ $$IF83 $$LL83: MOV DX, OFFSET S_DRV_PATH ;always use full path ; $ELSE JMP SHORT $$EN83 $$IF83: TEST MY_FLAG, VISIT_PARENT_FLAG ;now going toward the root? ; $IF NZ ;yes JZ $$IF85 MOV DX, OFFSET S_PARENT ;just '..',0 ; $ELSE JMP SHORT $$EN85 $$IF85: LEA DX, [BP].DTA_FILENAME ;use the subdir name just found ; $ENDIF $$EN85: ; $ENDIF $$EN83: MOV AH, Chdir ; = 3Bh INT 21H ; $IF C JNC $$IF89 OR COPY_STATUS, CHDIR_ERROR_FLAG ;chdir error in source. critical CALL EXTENDED_ERROR_HANDLER ; $ENDIF $$IF89: RET CHANGE_S_DIR ENDP ; CHANGE_T_DIR PROC NEAR ;change target dir according to t_drv_path. ;Since this routine is called by WRITE_FROM_BUFFER and DS now points ;to buffer area while ES points to the program data area, we set DS ;to data seg again here for the function call Chdir. PUSH DS ;save current buffer seg PUSH ES ;currentpy es is a data seg POP DS ;restore DS value as program data seg CMP T_DRV[2], 0 ;LAST_DIR_OUT took '\' out? ; $IF E JNE $$IF91 MOV T_DRV[2], '\' ;then put it back for root dir MOV T_DRV[3], 0 ; $ENDIF $$IF91: MOV DX, OFFSET T_DRV_PATH MOV AH, CHDIR INT 21H POP DS ;restore caller's DS value RET CHANGE_T_DIR ENDP ; CMP_FILESIZE_TO_BUFFER_LEFT PROC NEAR ;Compare buffer_left (paragraph) with filesize (high_file_size, low_file_size.) ;if filesize is bigger than buffer_left, then set FILE_BIGGER_FLAG ;indicating filesize > buffer_left. ; PUSH DX PUSH AX CMP OPEN_FILE_COUNT,NUL ;AN005;ARE THERE ANY OPEN FILES ; $IF Z ;AN005;NO, THEN GO AHEAD AND OPEN JNZ $$IF93 CALL OPEN_A_FILE ;AN005;OPEN A FILE USING FILE_DTA ; Get extended Attribute list size. MOV BX,S_HANDLE ;AN005; BX = handle MOV AX, GET_ATTRIB ;AN005; extended attribute code 5702H MOV SI,ALL_ATTR ;AN005; SELECT ALL ATTRIBUTES SIZE XOR CX,CX ;AN005; JUST QUERY SIZE NEEDED MOV DI,NUL_LIST ;AN005; DI = LIST FOR NO DATA RETURNED INT 21H ;AN005; get extended attribute SIZE ADD CX,PARA_BOUND ;AN005; TO FIGURE THE NEXT PARAGRAPH MOV AX,CX ;AN005; MOV CL,PARAGRAPH ;AN005; GET PARAGRAPHS (DIV BY 16) SHR AX,CL ;AN005; MOV SIZ_OF_BUFF,AX ;AN005;SAVE BUFF SIZE FOR THE HEADER ADD AX,FIXD_HD_SIZ ;AN005;GET THE TOTAL HEADER SIZE MOV PARA_OF_HDR,AX ;AN005;SAVE FOR LATER SHL AX, CL ;AN005;CONVERT BACK TO TOTAL BYTES MOV BYTS_OF_HDR,AX ;AN005;SAVE FOR LATER ; $ENDIF ;AN005; $$IF93: AND FILE_FLAG, RESET_FILE_BIGGER MOV AX,PARA_OF_HDR ;AN005;GET THE HEADER SIZE (para.) CMP BUFFER_LEFT,AX ;AC005;buffer_left >= HEADER SIZE ; $IF AE JNAE $$IF95 MOV AX, BUFFER_LEFT ;buffer_left in para SUB AX,PARA_OF_HDR ;AC005;consider header size in advance MOV CX, 16 MUL CX ;* 16. result in DX;AX CMP HIGH_FILE_SIZE, DX ; $IF A ;if high_filesize > dx JNA $$IF96 OR FILE_FLAG, FILE_BIGGER_FLAG ; $ELSE JMP SHORT $$EN96 $$IF96: ; $IF E JNE $$IF98 CMP LOW_FILE_SIZE, AX ; $IF A JNA $$IF99 OR FILE_FLAG, FILE_BIGGER_FLAG ; $ENDIF $$IF99: ; $ENDIF $$IF98: ; $ENDIF $$EN96: ; $ELSE JMP SHORT $$EN95 $$IF95: OR FILE_FLAG, FILE_BIGGER_FLAG ;buffer_left < HEADER SIZE ; $ENDIF $$EN95: POP AX POP DX RET CMP_FILESIZE_TO_BUFFER_LEFT ENDP ; SET_BUFFER_PTR PROC NEAR ;set BUFFER_PTR, BUFFER_LEFT, OLD_BUFFER_PTR in paragraph boundary ;to be used when reading a file into buffer. ;this routine uses current BUFFER_PTR to figure out the next BUFFER_PTR. ;So, at initialization time set BUFFER_PTR to CS, and set AX to the offset ;of INIT, then the resultant BUFFER_PTR indicates the BUFFER_BASE and ;OLD_BUFFER_PTR indicates CS.(This means if old_buffer_ptr = cs, then ;it is the start of buffer) ;To get the next BUFFER_PTR during multi-copy, just set the AX to the ;number of bytes read. This routine will add E.A.BUFFER SIZE + 3 para. ;for header size and will set the next BUFFER_PTR. ;input: AX - offset of buffer ; Top_of_memory in segment ; current BUFFER_PTR ; current OLD_BUFFER_PTR ; current BUFFER_LEFT ;output: BUFFER_PTR for next reading ; OLD_BUFFER_PTR ; BUFFER_LEFT (Top_of_memory - Buffer_Ptr. If it is 0, then indicates ; the BUFFER is FULL. In this case, the BUFFER_PTR is ; invalid, but OLD_BUFFER_PTR keep the former buffer_ptr ; value which says that it is the last header in the buffer) ;** Currently this program support maxium top of memory in seg 0FFFF - resident ; area. This routine will check the overflow case to gaurd the next buffer_ptr ; not to exceed FFFF. PUSH CX MOV CX, BUFFER_PTR MOV OLD_BUFFER_PTR, CX ;set old_buffer_ptr MOV CL, 4 SHR AX, CL ;get paragraphs INC AX ;get next paragraph ADD AX,PARA_OF_HDR ;AC005;consider header size ADD BUFFER_PTR, AX ;add this to the current buffer_ptr ; $IF NC,AND ;not exceed 16 bit. JC $$IF105 MOV AX, Top_of_memory SUB AX, BUFFER_PTR ;AX = Top_of_memory - Buffer_ptr ; $IF A ;if buffer_left > 0 JNA $$IF105 MOV BUFFER_LEFT, AX ; $ELSE JMP SHORT $$EN105 $$IF105: MOV BUFFER_LEFT, 0 ;indication of buffer full ; $ENDIF $$EN105: POP CX RET SET_BUFFER_PTR ENDP ; WRITE_FROM_BUFFER PROC NEAR ;Write from the first header starting at buffer_base until finishes ;the last header which, actually, happens to be the old_buffer_ptr ;at the time of the call. After the writing, reset the buffer_ptr ;to buffer_base again for the next read_into_buffer. ;If continue_info is 1 or 2 (Continue of small, bigfile) then after ;the creation of a target file, it will set the CREATED_FLAG. ;This flag will be reset when it found the continue_info to be 3 ;(End of contine). ;For convenience of use of function call, ES will be used for ;the program data seg while DS will be used for the BUFFER seg. ; PUSH DS PUSH ES ;save ds, es PUSH DS POP ES ;set ES to program data seg OR ACTION_FLAG, READING_FLAG ;show reading message next time ; AND ES:MY_FLAG, RESET_IS_SOURCE ;now, deals with target ;set this for change_dir MOV AX, ES:BUFFER_BASE MOV DS, AX PUSH CS POP AX CMP ES:OLD_BUFFER_PTR, AX ;if old_buffer_ptr = CS then ;buffer is empty. Just exit JE WFB_EXIT_BRIDGE WFB_CD: CALL CHANGE_T_DIR JC WFB_ERROR_BRIDGE ;error? WFB_CHATT: CMP DS:ATTR_FOUND, Is_subdirectory ;a subdirectory? = 10H JNE WFB_FILE ;no. a file WFB_CMP_DEPTH: MOV AH, ES:T_DEPTH ;yes. a subdir. CMP DS:DIR_DEPTH, AH ;DIR_DEPTH > T_DEPTH ? JBE WFB_DEC_DEPTH ;if not, go to parent node LEA DI, ES:T_DRV_PATH ;else goto child node LEA SI, DS:FILENAME_FOUND CMP ES:T_PATH, 0 ;root directory? ; $IF E JNE $$IF108 MOV AL, 0FFh ;then don't need to put delim since it is already there ; $ELSE JMP SHORT $$EN108 $$IF108: MOV AL, Path_delim ;path_delim '\' ; $ENDIF $$EN108: CALL CONCAT_ASCIIZ call concat_display_path ;modify the path for display INC ES:T_DEPTH CALL MAKE_DIR ;try to make a new sub directory JC WFB_EXIT_A_BRIDGE ;there exists a file with same name. MOV AX, DS ;current buffer seg = old_buffer_ptr? CMP ES:OLD_BUFFER_PTR, AX JNE WFB_NEXT ;not finished yet. jmp to next OR ES:MY_FLAG, MISSING_LINK_FLAG ;Finished. Missing link condition occurred regarding empty sub dir JMP WFB_EXIT_A ;check archieve options. WFB_NEXT: MOV DS, DS:NEXT_PTR ;let's handles next header. JMP WFB_CD ;change directory first. WFB_EXIT_BRIDGE: JMP WFB_EXIT WFB_ERROR_BRIDGE: JMP WFB_ERROR WFB_EXIT_A_BRIDGE: JMP WFB_EXIT_A WFB_DEC_DEPTH: LEA DI, ES:T_DRV_PATH CALL RM_EMPTY_DIR ;check flags and remove empty dir CALL LAST_DIR_OUT ;take off the last dir from path call cut_display_path ;modify path for display purpose DEC ES:T_DEPTH ;and decrease depth JMP WFB_CD ;CHANGE DIR AND compare the depth again. WFB_FILE: ;Handling a file AND ES:MY_FLAG, RESET_MISSING_LINK ;if found a file, then current dir is not empty. TEST ES:FILE_FLAG, CREATED_FLAG ; A file handle is created ? JNZ WFB_WRITE ;yes, skip create again. CALL CREATE_A_FILE ;create a file in the cur dir JC WFB_ERROR ;file creation error? WFB_WRITE: CALL WRITE_A_FILE JC WFB_EXIT_A ;target file has been already deleted. CMP DS:CONTINUE_INFO, 0 ; $IF E,OR ;if continue_info = 0 or 3 JE $$LL111 CMP DS:CONTINUE_INFO, 3 ; $IF E JNE $$IF111 $$LL111: MOV BX, ES:T_HANDLE CALL SET_FILE_DATE_TIME ;then set file's date, time PUSH DS ;AN005;SAVE THE BUFFER PTR PUSH ES ;AN005;WE NEED THE DATA PTR POP DS ;AN005;DS = THE DATA PTR CALL CLOSE_A_FILE ;and close the handle POP DS ;AN005;DS = THE BUFFER PTR AGAIN CALL RESTORE_FILENAME_FOUND ;if filename_found has been changed, restore it for reset_s_archieve. AND ES:FILE_FLAG, RESET_CREATED ;and reset created_flag CALL INC_FILE_COUNT ;increase file count ; $ENDIF $$IF111: MOV AX, DS CMP ES:OLD_BUFFER_PTR, AX ;current header is the last one? JE WFB_EXIT_A ;then exit MOV DS, DS:NEXT_PTR ;else set ds to the next ptr JMP WFB_CHATT ;handle the next header WFB_ERROR: jmp main_exit ;meaningful when MKDIR failed because ;of there already exist same named file, ;or disk_full case. WFB_EXIT_A: test ES:option_flag, slash_m ;hard archieve ? - turn off source archieve bit. jz wfb_exit_B ;no, chk error flag and exit call reset_s_archieve ;reset source file(s) archieve bit using header info(s). WFB_EXIT_B: test ES:copy_status, mkdir_error_flag ;mkdir error happened? JNZ WFB_ERROR ;yes, exit test ES:copy_status, disk_full_flag ;disk full happened? JNZ WFB_ERROR ;yes, exit WFB_EXIT: MOV ES:OLD_BUFFER_PTR, CS ;set old_buffer_ptr to CS MOV AX, ES:BUFFER_BASE MOV ES:BUFFER_PTR, AX ;set buffer_ptr to base MOV AX, ES:MAX_BUFFER_SIZE MOV ES:BUFFER_LEFT, AX ;set buffer_left POP ES POP DS TEST SYS_FLAG, ONE_DISK_COPY_FLAG ;one drive letter copy? ; $IF NZ ;yes JZ $$IF113 CALL CHANGE_S_DIR ;then change current dir to s dir ; $ENDIF $$IF113: RET WRITE_FROM_BUFFER ENDP ; INC_FILE_COUNT PROC NEAR ;increase the file count by one. ;increase file_cnt_low, file_cnt_high. ;input: DS - buffer ; ES - data seg INC ES:FILE_CNT_LOW JNZ IFC_EXIT INC ES:FILE_CNT_HIGH ;if carry over, then inc file_cnt_high IFC_EXIT: RET INC_FILE_COUNT ENDP ; RM_EMPTY_DIR PROC NEAR ;check the slash_E option, missing_link_flag. Remove the empty directory ;from the target disk. ;INPUT: DS - buffer ; ES - data seg ; DI - points to the current target drv, path TEST ES:OPTION_FLAG, SLASH_E ;user want to copy empty subdir? JNZ RED_EXIT ;then exit TEST ES:MY_FLAG, MISSING_LINK_FLAG ;missing informations for not to copying empty dir ;at the tree travesal phase? JZ RED_EXIT ;no. CALL SWITCH_DS_ES ;ds - data, es - buffer MOV DX, OFFSET T_PARENT ;chdir to parent dir MOV AH, 3Bh ;Chdir INT 21h PUSH DI POP DX ;DS:DX points to drv, path MOV AH, 3Ah ;REMOVE SUBDIR INT 21h CALL SWITCH_DS_ES ;restore ds, es RED_EXIT: RET RM_EMPTY_DIR ENDP ; RESTORE_FILENAME_FOUND PROC NEAR ;when the filename_found has been Revised according to the user's specified ;input parm, then restore the original source filename in filename_found. ;This will be used when reset_s_archieve routine reset the source file's ;archieve bit. ;input: DS - buffer ; ES - data seg CMP ES:T_FILENAME, 0 ;if t_filename ot t_template is not blank, ; $IF NE,OR ;then filename_found has been Revised. JNE $$LL115 CMP ES:T_TEMPLATE, 0 ; $IF NE JE $$IF115 $$LL115: CALL SWITCH_DS_ES ;DS - data seg, ES - buffer MOV CX, 13 LEA SI, DS:DISP_S_FILE ;we know filename_found has been save into DISP_S_FILE when create the file. LEA DI, ES:FILENAME_FOUND ;use this to restore source filename this time. REP MOVSB ;disp_s_file => filename_found CALL SWITCH_DS_ES ;restore ds, es ; $ENDIF $$IF115: RET RESTORE_FILENAME_FOUND ENDP ; RESET_S_ARCHIEVE PROC NEAR ;INPUT: DS - buffer ; ES - data seg TEST ES:COPY_STATUS, DISK_FULL_FLAG ;called when disk full? JZ RSA_START ;no, just goto start ;else disk_full. MOV AX, DS ;current DS when called CMP ES:BUFFER_BASE, AX ;current DS(BUFFER) is the first one? JE RSA_EXIT_BRIDGE ;yes, just exit MOV AX, DS:BEFORE_PTR ;set old_buffer_ptr to the header MOV ES:OLD_BUFFER_PTR, AX ;that is just before the troubled one. RSA_START: MOV AX, ES:BUFFER_BASE MOV DS, AX ;set DS to buffer base again to start traveling RSA_CD: CALL CHANGE_ARC_S_DIR ;change souce dir RSA_CHATT: CMP DS:ATTR_FOUND, Is_subdirectory ; = 10h JNE RSA_FILE ;no a file RSA_CMP_DEPTH: MOV AH, ES:S_ARC_DEPTH ;yes, a subdir CMP DS:DIR_DEPTH, AH ;dir_depth > s_arc_depth? JBE RSA_DEC_DEPTH ;if not, goto parent node LEA DI, ES:S_ARC_DRV_PATH LEA SI, DS:FILENAME_FOUND CMP ES:S_ARC_PATH, 0 ;root dir? ; $IF E JNE $$IF117 MOV AL, 0FFh ; $ELSE JMP SHORT $$EN117 $$IF117: MOV AL, Path_delim ;path_delim '\' ; $ENDIF $$EN117: CALL CONCAT_ASCIIZ INC ES:S_ARC_DEPTH MOV AX, DS CMP ES:OLD_BUFFER_PTR, AX JE RSA_EXIT_A ;finished. Set the source current dir and return to caller MOV DS, DS:NEXT_PTR ;else let's handles next header JMP RSA_CD ;chdir first. RSA_EXIT_A: CALL CHANGE_ARC_S_DIR ;to restore the same current source dir ;as that of the READ_INTO_BUFFER proc. RSA_EXIT_BRIDGE:JMP RSA_EXIT RSA_DEC_DEPTH: LEA DI, ES:S_ARC_DRV_PATH CALL LAST_DIR_OUT DEC ES:S_ARC_DEPTH JMP RSA_CD RSA_FILE: CMP DS:CONTINUE_INFO, 0 ; $IF E,OR JE $$LL120 CMP DS:CONTINUE_INFO, 3 ; $IF E JNE $$IF120 $$LL120: CALL CHANGE_S_FILEMODE ;change source file mode ; $ENDIF $$IF120: MOV AX, DS CMP ES:OLD_BUFFER_PTR, AX ;current header is the last one? JE RSA_EXIT MOV DS, DS:NEXT_PTR JMP RSA_CHATT RSA_EXIT: OR ES:SYS_FLAG, DEFAULT_S_DIR_fLAG ;this is for restoring default source dir before exit to DOS. RET ;return to caller RESET_S_ARCHIEVE ENDP ; CHANGE_S_FILEMODE PROC NEAR ;input: DS - buffer ; ES - data seg LEA DX, DS:FILENAME_FOUND MOV AH, 43h ;chmod MOV AL, 0 ;get attribute in CX INT 21h MOV AH, 43h MOV AL, 1 AND CX, 0FFDFh ;turn off the archieve bit INT 21h RET CHANGE_S_FILEMODE ENDP ; CHANGE_ARC_S_DIR PROC NEAR ;change the source directory according to S_ARC_DRV_PATH ;INPUT: DS - buffer ; ES - data seg call switch_ds_es CMP S_ARC_DRV[2], 0 ; $IF E JNE $$IF122 MOV S_ARC_DRV[2], '\' ;LAST_DIR_OUT have took '\' out? MOV S_ARC_DRV[3],0 ;then restore it ; $ENDIF $$IF122: MOV DX, OFFSET S_ARC_DRV_PATH ;use full drv, path MOV AH, CHDIR ; = 3Bh INT 21h ; $IF C JNC $$IF124 OR COPY_STATUS, CHDIR_ERROR_FLAG CALL EXTENDED_ERROR_HANDLER ; $ENDIF $$IF124: call switch_ds_es RET CHANGE_ARC_S_DIR ENDP ; CONCAT_DISPLAY_PATH PROC NEAR ;concatenate subdirectory name found from the header to DISP_S_PATH which ;will be used for display copying file messages. ;if slash_p option has been set, then just return. ;DS: buffer header ;ES: data seg ; TEST ES:OPTION_FLAG, SLASH_P ;prompt option? ; $IF Z ;no JNZ $$IF126 LEA DI, ES:DISP_S_PATH LEA SI, DS:FILENAME_FOUND CMP ES:T_DEPTH, 0 ;this will be the first child directory? ; $IF E ;yes JNE $$IF127 MOV AL, 0FFh ;then do not put '\' between them ; $ELSE JMP SHORT $$EN127 $$IF127: MOV AL, Path_delim ;else put '\' ; $ENDIF $$EN127: CALL CONCAT_ASCIIZ ; $ENDIF ;else just return $$IF126: RET CONCAT_DISPLAY_PATH ENDP ; CUT_DISPLAY_PATH PROC NEAR ;take the last dir out from the DISP_S_PATH for display copy messages. ;if prompt option has been set, just return. ;INPUT: DS - buffer header ; ES - data seg ; TEST ES:OPTION_FLAG, SLASH_P ;prompt? ; $IF Z ;no. JNZ $$IF131 PUSH DS ;save DS PUSH ES POP DS ;ds = es = data seg MOV SI, OFFSET DISP_S_PATH ;for CHK_DRV_LETTER MOV DI, OFFSET DISP_S_PATH ;for LASR_DIR_OUT CALL LAST_DIR_OUT ; $IF C ;failure? no '\' found JNC $$IF132 CALL CHK_DRV_LETTER ;drive letter? ; $IF NC ;yes. "D:filename",0 case JC $$IF133 MOV BYTE PTR DS:[SI], 0 ;make it "D:",0 since SI now points to the next chr ; $ELSE ;no. "filename",0 case JMP SHORT $$EN133 $$IF133: MOV BYTE PTR [DI], 0 ;set DISP_S_PATH to 0 ; $ENDIF $$EN133: ; $ELSE ;found '\' and last '\' became 0 JMP SHORT $$EN132 $$IF132: CMP T_DEPTH, 1 ;now going to the starting path? ; $IF E ;yes. restore it for concat_display_path routine. JNE $$IF137 MOV DI, AX ;we want to restore '\' and put 0 just after that. DEC DI ;for ex, "D:\DIR1"=>"D:"=>"D:\" -- original starting path MOV BYTE PTR [DI], '\' ; "D:dir1\dir2"=>"D:dir1"(starting path) => "D:dir1\" MOV BYTE PTR [DI+1], 0 ; $ENDIF $$IF137: ; $ENDIF $$EN132: POP DS ;restore ds to buffer header ; $ENDIF $$IF131: RET CUT_DISPLAY_PATH ENDP ; ;*************************************************************************** CHK_DRV_LETTER PROC NEAR ; ** CHECK CURRENT CHR IS ALPHA CHR FOLLOWED BY COLON. * ; INPUT: DS:SI POINTS TO THE CURRENT CHR TO BE CHECKED. * ; OUTPUT: FOUND - SI POINTS TO THE NEXT CHR. * ; IF THIS HAD BEEN A LAST WORD, ZERO FLAG WILL BE SET. * ; NOT FOUND - CARRY IS SET. DI, CX UNCHANGED. * ;*************************************************************************** PUSH AX PUSH SI ;AN010;IN CASE DRIVE LETTER NOT FOUND ; $DO ;AN010; $$DO141: CLC ;AN010;INITIALIZE TO NOT DBCS MOV AL,DS:BYTE PTR [SI] ;AN010;GET THE 1st CHAR TO TEST CALL CHK_DBCS ;AN010;SEE IF WE ARE IN DBCS ; $LEAVE NC ;AN010;THIS IS NOT DBCS JNC $$EN141 INC SI ;AN010;GO TO THE NEXT CHAR TO CHECK INC SI ;AN010;DITO ; $ENDDO ;AN010; JMP SHORT $$DO141 $$EN141: CMP AL, 'A' JB CK_DR_1 ;LESS THAN 'A', THEN NOT FOUND. CMP AL, 'Z' JA CK_DR_1 ;ABOVE 'Z', THEN NOT FOUND. MOV AL, DS:BYTE PTR [SI+1] ;LOOK AHEAD THE FOLLOWING CHR. CMP AL, ':' ;SHOULD BE A COLON. JNZ CK_DR_1 ;NOT FOUND. POP AX ;AN010;THROW AWAY SAVED SI INC SI ;FOUND. SI TO THE NEXT CHR. INC SI JMP CK_DR_2 CK_DR_1: STC ;SET CARRY POP SI ;AN010;RESTORE SI TO ENTRY VALUE CK_DR_2: POP AX RET CHK_DRV_LETTER ENDP ; CREATE_A_FILE PROC NEAR ;create a file in the header and return the file handle in T_HANDLE. ;Set CREATED_FLAG. This will be reset by WRITE_FROM_BUFFER when it ;close the handle. ;this routine will check the T_FILENAME and T_TEMPLATE if any target ;filename has been entered. If T_FILENAME is there, then DX will ;points to this (This is the case when the user has specified non_global ;chr filename and any source filename be changed to this name.) ;If T_TEMPLATE is present, then modify the filename found in the ;header part. ;Also, this routine show copy messages just before a file creation using ;FILENAME_FOUND. ;ES - data seg ;DS - buffer seg PUSH DS PUSH ES ;save the original filename from the header MOV CX, 13 ;max 13 chr LEA SI, DS:FILENAME_FOUND ;original source file name LEA DI, ES:DISP_S_FILE ;filename to be displayed REP MOVSB ;filename_found => disp_s_file test es:option_flag, slash_p ; $IF Z JNZ $$IF144 CALL SHOW_COPY_MESSAGE ;show the source path, file ; $ENDIF $$IF144: CMP ES:T_FILENAME, 0 ; $IF NE ;non_global target filename entered. JE $$IF146 TEST ES:COPY_STATUS, MAYBE_ITSELF_FLAG ; $IF NZ JZ $$IF147 LEA SI, DS:FILENAME_FOUND LEA DI, ES:T_FILENAME CALL COMP_FILENAME ;compare it. if same then show ;file cannot be copied onto itself and ;abort ; $ENDIF $$IF147: CALL SWITCH_DS_ES ;now ds - data, es - buffer MOV CX, 13 LEA SI, DS:T_FILENAME LEA DI, ES:FILENAME_FOUND REP MOVSB ; t_filename => filename_found MOV AL, NUL ;AN014;DOS NEEDS A NUL TO TERM. MOV ES:TERMINATE_STRING,AL ;AN014;PUT IT IN THE HEADER CALL SWITCH_DS_ES ;now ds - buffer, es - data seg ; $ELSE JMP SHORT $$EN146 $$IF146: CMP ES:T_TEMPLATE, 0 ;global chr target filename entered? ; $IF NE ;yes, entered. modify the filename found JE $$IF150 CALL MODIFY_FILENAME TEST ES:COPY_STATUS, MAYBE_ITSELF_FLAG ; $IF NZ JZ $$IF151 LEA SI, DS:FILENAME_FOUND ;compare the Revised filename LEA DI, ES:DISP_S_FILE ;with original name CALL COMP_FILENAME ;if same, then issue error message and exit ; $ENDIF $$IF151: ; $ELSE JMP SHORT $$EN150 $$IF150: TEST ES:COPY_STATUS, MAYBE_ITSELF_FLAG ;*.* CASE ; $IF NZ JZ $$IF154 PUSH ES POP DS ;ds - data seg ; Set message parameters MOV AX,MSG_COPY_ITSELF ;AN000; MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message subst. MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no user input CALL PRINT_STDERR ;AN000; display error JMP MAIN_EXIT ; $ENDIF $$IF154: ; $ENDIF $$EN150: ; $ENDIF $$EN146: ;------------------------------------------------------------------------- ; Use extended open DOS call to create the target file, use attribute list ; obtained from the previous Get Extended attribute DOS call ;------------------------------------------------------------------------- MOV AX, Ext_Open ;AN000; = 6Ch MOV BX,CREATE_MODE ;AN000;CREATE MODE = 0002H MOV CX,CREATE_ATTR ;AN000; attribute = 0 MOV DX,CREATE_FLAG ;AN000; flag = 0112H MOV SI,OFFSET TARGET_DRV_LET ;AN005; DS:SI-->NAME TO CREATE MOV DI,NUL_LIST ;AN012; ES:DI = -1 INT 21H ;AN000; create file JC CAF_ERROR ;AN000; MOV ES:T_HANDLE, AX ;AN000;save handle CALL CHK_T_RES_DEVICE ;check target handle is a reserved dev MOV AX,SET_ATTRIB ;AN012;5704H CALL SWITCH_DS_ES ;AN013;now ds - data, es - buffer MOV BX,T_HANDLE ;AC013;THE FILE HANDLE LEA DI,ES:ATTRIB_LIST ;AN013;PARAMETER LIST (ES:DI) INT 21H ;AN012;SET EXTENDED ATTRIBUTES CALL SWITCH_DS_ES ;AN013;now es - data, ds - buffer JC CAF_ERROR ;AN012; OR ES:FILE_FLAG, CREATED_FLAG ;set created_flag JMP CAF_EXIT CAF_ERROR: PUSH DS PUSH ES POP DS OR COPY_STATUS, CREATE_ERROR_FLAG CALL EXTENDED_ERROR_HANDLER POP DS CAF_EXIT: POP ES POP DS RET CREATE_A_FILE ENDP ; chk_t_res_device proc near ;check the target handle if it is for reserved device ;input: ES - data seg ; DS - buffer ; AX - filehandle created cmp es:t_filename,0 ;if no user specified filename jne ctrd_ioctl ;then should not be a reserved device name cmp es:t_template,0 je ctrd_exit ctrd_ioctl: mov bx, ax ;file handle mov ax, 4400h ;IOCTL get device info. int 21h test dx, 80h ;is device? (not a block device?) jz ctrd_exit PUSH ES ;AN000; POP DS ;AN000;ds - data seg ; Set message parameters MOV AX,MSG_RES_T_NAME ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDOUT ;AN000; display message jmp main_exit ctrd_exit: ret chk_t_res_device endp ; MODIFY_FILENAME PROC NEAR ;modify the filename in the header using T_TEMPLATE. ;INPUT: ;DS: BUFFER ;ES: DATA SEG PUSH DS ;save ds, es = data seg PUSH ES PUSH DS PUSH ES MOV ES, ES:PSP_SEG ;ES points to PSP MOV DI, PSPFCB2_DRV ;DI points to FCB2, 6c MOV SI, OFFSET DS:TARGET_DRV_LET ;filename found, DS = buffer header MOV AH, 29H ;parse a filename MOV AL, 0 ;control bits INT 21h ;unfold the filename found into PSP FCB2 area POP DS ;now DS=data seg, ES=PSP seg MOV SI, OFFSET T_TEMPLATE ;SI points to template MOV DI, PSPFCB2_DRV INC DI ;DI points to the formatted filename MOV CX, 11 CLD ; $DO $$DO158: CMP CX, 0 ;done? ; $LEAVE E ;yes. exit JE $$EN158 LODSB ;[SI] => AL, SI = SI + 1 CMP AL, '?' ;global chr? ; $IF E ;yes JNE $$IF160 INC DI ;just skip the corresponding target chr ; $ELSE ;no JMP SHORT $$EN160 $$IF160: STOSB ;change the target chr to this. DI = DI + 1 ; $ENDIF $$EN160: DEC CX ; $ENDDO JMP SHORT $$DO158 $$EN158: POP ES ;now ES = Buffer MOV DI, OFFSET ES:FILENAME_FOUND ; di points to filename in the header MOV DS, PSP_SEG ;DS = PSP seg MOV SI, PSPFCB2_DRV INC SI ;di points to Revised filename CALL COMPRESS_FILENAME ;fold it POP ES POP DS RET MODIFY_FILENAME ENDP ; COMP_FILENAME PROC NEAR ;this routine is called when MAYBE_COPY_ITSELF flag in on. ;SI, DI asciiz string will be compared and if they are identical ;the show "Cannot copy onto itself" msg and jmp to main_exit. ;INPUT: DS - buffer ; ES - data seg CLD MOV AL, 0 PUSH DI ;save DI CALL STRING_LENGTH ;CX get the length of string MOV BX, CX ;now, BX got the length of the target filename entered. PUSH BX ;save BX PUSH ES ;save ES PUSH DS POP ES ;now ES set to DS PUSH SI POP DI ;now DI points to the source filename found. MOV AL, 0 CALL STRING_LENGTH ;CX got the length of the string POP ES ;restore ES POP BX ;restore BX POP DI ;restore DI CMP BX, CX ;COMPARE LENGTH JNE CF_EXIT ;IF THEY ARE DIFFERENT, EXIT REPE CMPSB ;compare SI, DI until not equal, CMP CX, 0 ;finish at cx = 0? JE CF_SAME JMP SHORT CF_EXIT CF_SAME: PUSH ES POP DS ;ds = data seg ; Set message parameters MOV AX,MSG_COPY_ITSELF ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;AN000; display error message JMP MAIN_EXIT CF_EXIT: RET COMP_FILENAME ENDP ; SHOW_COPY_MESSAGE PROC NEAR ;show the source path, filename that is ready for creation in the target disk. ;INPUT: ES - data seg ; DS - buffer header seg PUSH DS ;save DS PUSH ES POP DS ;DS = data seg LEA SI,SUBLIST1 ;AN000; get addressability to list LEA DX,DISP_S_PATH ;AN000; offset to path name MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,1 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH LEA SI,SUBLIST2 ;AN000; get addressability to list LEA DX,DISP_S_FILE ;AN000; offset to file name MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,2 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH LEA SI,SUBLIST1 ;AN000; CMP ES:T_DEPTH, 0 ;starting directory? ; $IF E ;yes JNE $$IF164 MOV AX,S_PATH_FILE0 ;AN000;NO BACK SLASH BETWEEN PATH,FNAME ; $ELSE JMP SHORT $$EN164 $$IF164: MOV AX,S_PATH_FILE1 ;AN000;BACK SLASH IS BETWEEN PATH,FNAME ; $ENDIF $$EN164: MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,PARM_SUBST_TWO ;AN000; substitution count = 2 MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDOUT ;show message "Reading source POP DS ;restore DS RET SHOW_COPY_MESSAGE ENDP ; WRITE_A_FILE PROC NEAR ;write a file from the data area in the buffer. ;Remember the caller is WRITE_FROM_BUFFER which use ES for ;the program data area and DS for the header in the buffer. MOV AH, Write ; = 40h MOV BX, ES:T_HANDLE ;handle saved in the program data area MOV DX, ES:BYTS_OF_HDR ;AC005;skip header MOV CX, DS:CX_BYTES ;get the # from the header INT 21h JC WAF_ERROR ;write error CMP AX, DS:CX_BYTES JNE WAF_DISKFULL JMP WAF_EXIT WAF_ERROR: CALL CLOSE_DELETE_FILE ;close delete troubled file OR COPY_STATUS, WRITE_ERROR_FLAG CALL SWITCH_DS_ES ;AN000;DS = DATA SEG, ES = BUFFER CALL EXTENDED_ERROR_HANDLER CALL SWITCH_DS_ES ;AN000;ES = DATA SEG, DS = BUFFER WAF_DISKFULL: MOV ERRORLEVEL, 4 ;set errorlevel ; Set message parameters ; Target disk full, critical error PUSH DS ;AN000;DS = BUFFER PUSH ES ;AN000;ES = DATA SEG POP DS ;AN000;ES => DS = DATA SEG MOV AX,MSG_DISK_FULL ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;AN000; display error message OR COPY_STATUS, DISK_FULL_FLAG ;set disk_full_flag POP DS ;AN000;RESTORE DS = BUFFER CALL CLOSE_DELETE_FILE STC ;set carry and return to caller WAF_EXIT: RET WRITE_A_FILE ENDP ; SET_FILE_DATE_TIME PROC NEAR ;input: BX - target file handle ; MOV AH, File_date_time ; = 57h MOV AL, Set_file_time ; = 1 MOV CX, DS:FILE_TIME_FOUND MOV DX, DS:FILE_DATE_FOUND INT 21h RET SET_FILE_DATE_TIME ENDP ; CLOSE_A_FILE PROC NEAR ; ;CLOSE A FILE AND UPDATE COUNT OF OPEN FILES ; ;INPUT: BX - file handle to be closed ; CMP OPEN_FILE_COUNT,NUL ;AN005;ARE THERE ANY OPEN FILES? ; $IF A ;AN005; JNA $$IF167 DEC OPEN_FILE_COUNT ;AN005;IF SO, REDUCE THE COUNT BY 1. ; $ENDIF ;AN005; $$IF167: MOV AH, Close ; = 3Eh INT 21H RET CLOSE_A_FILE ENDP ; DELETE_A_FILE PROC NEAR ;input: DS:DX - points to ASCIIZ string MOV AH, 41h ; = 41h INT 21H RET DELETE_A_FILE ENDP ; MAKE_DIR PROC NEAR ;make a subdirectory in the current target directory. ;The directory name is in the header part Target_drv_Let ;with the drive letter. ;input:DS - buffer ; ES - data seg MOV AH, Mkdir ; = 39h MOV DX, OFFSET DS:TARGET_DRV_LET ;target drv and filename INT 21h JC MD_ERROR JMP MD_EXIT MD_ERROR: ;cannot distinguish between cases of: 1. already there exists a directory. ; 2. there has been a file exist with the same name in the target. ; 3. no disk space to make dir. ; Case 1, should ignore and just exit this routine ; Case 2, critical error. ; Case 3, critical error. call chk_disk_full ;check disk full condition first jc MD_EXIST ;AC026;yes, disk full, check if exist push es ;else check a file with the same name. push ds push dx mov ah, 2fH ;get current DTA addr in ES:BX int 21h mov ds, es:psp_seg mov dx, 80h mov ah, 1ah int 21h ;set dta to psp default dta area pop dx ;restore DX - target drv and filename pop ds ;restore DS - buffer mov cx, 6 ;HIDDEN + SYSTEM inclusive search mov ah, 4Eh ;FIND FIRST MATCHING FILE int 21h jc md_ok ;not found. There exists subdir. ignore stc ;else found a file with same name. jmp short MD_RESTORE MD_OK: clc ;else there exists dir., ignore error. MD_RESTORE: pushf push ds ;save ds again - buffer push es ;es - save dta seg pop ds ;ds = saved DTA seg mov dx, bx ; saved DTA off mov ah, 1ah int 21h ;restore DTA pop ds ;restore ds popf pop es ;restore ES jnc md_exit ;if no error, then exit jmp md_err ;AN026; ;else check a file with the same name. MD_EXIST: push es ;AN026; push ds ;AN026; push dx ;AN026; mov ah, 2fH ;AN026;get current DTA addr in ES:BX int 21h ;AN026; mov ds, es:psp_seg ;AN026; mov dx, 80h ;AN026; mov ah, 1ah ;AN026; int 21h ;AN026;set dta to psp default dta area pop dx ;AN026;restore DX - tar drv and filenm pop ds ;AN026;restore DS - buffer mov cx, 10h ;AN026;sub-directory search mov ah, 4Eh ;AN026;FIND FIRST MATCHING dir. int 21h ;AN026; pushf ;AN026;save carry state push ds ;AN026;save ds again - buffer push es ;AN016;es - save dta seg pop ds ;AN026;ds = saved DTA seg mov dx, bx ;AN026; saved DTA off mov ah, 1ah ;AN026; int 21h ;AN026;restore DTA pop ds ;AN026;restore ds popf ;AN026;get carry state from find pop es ;AN026;restore ES jnc md_exit ;AN026;if no error, then dir. exits MD_ERR: call switch_ds_es ;switch ds, es mov errorlevel, 4 ;set the errorlevel to 4 test copy_status, disk_full_flag ;disk full? jnz MD_FULL ;yes, full. mov ax,msg_unable_create ; else make dir fails because of ; the same file name or copy_status, mkdir_error_flag ;set make dir error flag jmp short MD_PRT MD_FULL: mov ax,msg_disk_full MD_PRT: ; Set message parameters PUSH ES ;AN017;ES = BUFFER PUSH DS ;AN017;DS = DATA SEG POP ES ;AN017;DS => ES = DATA SEG MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;AN000; display message POP ES ;AN017;RESTORE ES = BUFFER call switch_ds_es ;restore ds, es stc ;error - set carry MD_EXIT: RET MAKE_DIR ENDP ; CHK_DISK_FULL PROC NEAR ;check target disk space, and if no more clusters then set carry, disk_full_flag. ;this routine is called by MAKE_DIR routine. ;INPUT: DS - buffer ; ES - data seg PUSH AX PUSH BX PUSH CX PUSH DX MOV AH, 36h ;GET DISK FREE SPACE MOV DL, ES:T_DRV_NUMBER ;OF TARGET INT 21h CMP BX, 0 ;NO MORE CLUSTER? JE CDF_FULL CLC JMP SHORT CDF_EXIT CDF_FULL: OR ES:COPY_STATUS, DISK_FULL_FLAG ;SET DISK FULL FLAG STC ;SET CARRY CDF_EXIT: POP DX POP CX POP BX POP AX RET CHK_DISK_FULL ENDP ; CHK_FILE_NOT_FOUND PROC NEAR ;if FILE_CNT_LOW=FILE_CNT_HIGH=FOUND_FILE_FLAG=0 AND NO INIT ERROR, ; then show "File not found" msg ;INPUT: ES, DS = data seg TEST PARM_FLAG, INIT_ERROR_FLAG ; $IF Z,AND ;no init error JNZ $$IF169 CMP FILE_CNT_LOW, 0 ; $IF E,AND JNE $$IF169 CMP FILE_CNT_HIGH, 0 ; $IF E,AND JNE $$IF169 CMP FOUND_FILE_FLAG, 0 ; $IF E JNE $$IF169 MOV ES, PSP_SEG ;use PSP area for parsing MOV DI, PSPFCB1_DRV MOV SI, OFFSET S_FILE MOV AH, 29h MOV AL, 0 ;control byte INT 21h CALL SWITCH_DS_ES ;now, ds - psp seg, es - data seg MOV DI,OFFSET DISP_S_FILE MOV SI,PSPFCB1_DRV INC SI ;now SI points to the formatted filename area CALL COMPRESS_FILENAME ;[pspfcb1_drv+1] => disp_s_file PUSH ES POP DS ;now DS=ES=data seg LEA SI,SUBLIST1 ;AN000; MOV DI,OFFSET DISP_S_FILE ;AN000; MOV [SI].DATA_OFF,DI ;AN000; SI-->File name MOV [SI].DATA_SEG,DS ;AN000; DS-->Segment MOV [SI].MSG_ID,0 ;AN018; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH MOV AX,MSG_FILE_NOT_ERR ;AN018; MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,PARM_SUBST_ONE ;AN000; substitution count MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDOUT ;AN000; display message ; $ENDIF $$IF169: RET CHK_FILE_NOT_FOUND ENDP ; subttl string_length page ;****************************************************************************** ;PURPOSE: Get the length of a string pointed by ES:DI until it encounters ; the same character given by the user in AL. ; The length will be an output in CX. The number includes the ; charater found. ; For example, if you want to determine the length of an ASCIIZ string, ; set ES:DI to that string and set AL to 0. The output CX is the ; total length of the ASCIIZ string including 0. ; So, if the first character pointed by DI is the same as that of AL, ; then the length will be 1. ; !!! It is the user's responsibility to make it sure that the string ; contains the character given in AL. If not, unpredictable ; results will occur.!!! ; ; DATA INPUT ; REGISTERS: AL - ASCII CHARACTER ; ES:DI - POINTER TO THE STRING. ; DATA OUTPUT ; REGISTERS: AX,DX,SI etc - PRESERVED. ; BX - DISTROYED ; CX - STRING LENGTH UNTIL FOUND THE GIVEN CHARACTER. ; DI - POINTS TO THE NEXT CHARACTER AFTER THE STRING. ; DIRECTION FLAG - CLEARED ; ZERO FLAG - RESET ;****************************************************************************** ; STRING_LENGTH PROC NEAR PUBLIC STRING_LENGTH CLD ;CLEAR DIRECTION MOV BX,DI ;SAVE ORIGINAL DI VALUE MOV CX,80H ;TRY MAX 128 BYTES REPNE SCASB ;SCAN THE STRING UNTIL FOUND PUSH DI ;SAVE CURRENT DI VALUE WHICH POINTS TO NEXT CHR AFTER STRING SUB DI,BX ;GET THE LENGTH MOV CX,DI ;MOV THE LENGTH TO CX POP DI RET STRING_LENGTH ENDP ; subttl concat_asciiz page ;****************************************************************************** ;PURPOSE: Concatenate two ASCIIZ string into one ASCIIZ string. ; The ASCIIZ string pointed by DS:SI will be concatenated to ; the one pointed by ES:DI. The result string will be pointed by ; ES:DI. ; AL is used to put the delimeter character in between the strings. ; If you *DON'T* like to put the delimeter ***make AL to 0FFh***. ; For example, assume sting1 "ABCDE",0 pointed by DI and string2 ; "FGHI",0 pointed by SI. ; If you want a delimeter "\" between two string, set AL to "\" ; before calling. The result will "ABCDE\FGHI",0 pointed by DI. ; If you set AL to "0FFh", then it becomes "ABCDEFGHI",0. ; This feature is useful for handling PATH if you set AL to "\" ; and, for any general string processes if you set AL to "0FFh". ; This routine will call subroutine STRING_LENGTH. ;DATA INPUT ; REGISTERS: AL - DELIMETER OR 0FFh ; ES:DI - POINTER TO THE DESTINATION STRING. ; DS:SI - POINTER TO THE SOURCE TO BE CONCATENATED. ;DATA OUTPUT ; REGISTERS: AL, DX - preserved ; DI - preserved. POINTER TO THE RESULT STRING ; SI - DISTROYED ; CX - RESULT ASCIIZ STRING LENGTH INCLUDE 0 ; DIRECTION FLAG - CLEARED ;****************************************************************************** CONCAT_ASCIIZ PROC NEAR PUBLIC CONCAT_ASCIIZ PUSH DI ;SAVE POINTER VALUE WHICH WILL BE RETRUNED TO CALLER. PUSH AX ;SAVE VALUE IN AL. MOV AL, 0 ;DEALING WITH ASCIIZ STRING CALL STRING_LENGTH ;LET DI POINTS TO THE NEXT CHR AFTER THIS STRING ;DIRECTION WILL BE CLEARED. DEC DI ;MAKE DI POINT TO THE LAST CHARACTER 0 POP AX ;RESTORE AL. CMP AL, 0FFh ; $IF NE ;IF THE USER WANTS TO PUT DIMIMETER, JE $$IF171 STOSB ; REPLACE 0 WITH IT. ; $ELSE JMP SHORT $$EN171 $$IF171: DEC CX ;ELSE DECREASE LENGTH BY 1 ; $ENDIF $$EN171: ; $DO $$DO174: LODSB ;MOV [SI] TO AL STOSB ;STORE AL TO [DI] INC CX ;INCREASE LENGTH CMP AL, 0 ;WAS IT A LAST CHARACTER? ; $ENDDO E ;THEN EXIT THIS LOOP JNE $$DO174 POP DI RET CONCAT_ASCIIZ ENDP ; subttl last_dir_out page ;****************************************************************************** ;PURPOSE: Take off the last directory name from the path pointed by DI. ; This routine assumes the pattern of a path to be an ASCIIZ string ; in the form of "[d:][\]dir1\dir2". Notice that this path does not ; have entailing "\". This routine will simply travel the string ; until it found last "\" which will, then, be replaced with 0. ; If no "\" found, then carry will be set. ; *** This should be not be used for the path in the form of ; *** "d:\", 0 for the root directory, since in this case the returned ; *** string will be "d:",0 and AX value returned be meaningless (Just ; *** points to 0.) ;DATA INPUT ; REGISTERS: DI - points to an ASCIIZ path string. ; ES - assumed default segment for DI ;DATA OUTPUT ; REGISTERS: DI - points to the resultant path string. ; AX - offset value of the last subdirectory name taken out, in case ; of the user's need. ; Other register will be unchanged. ; CARRY FLAG WILL SET IF NOT FOUND. ;****************************************************************************** LAST_DIR_OUT PROC NEAR PUBLIC LAST_DIR_OUT PUSH DI PUSH SI ;save current DI, SI CLD ;clear direction MOV SI, 0FFFFh ;used as a not_found flag if unchanged. ; $DO $$DO176: ; $DO ;AN010; $$DO177: CLC ;AN010;INITIALIZE TO NOT DBCS MOV AL,BYTE PTR [DI] ;AN010;GET THE 1st CHAR TO TEST CALL CHK_DBCS ;AN010;SEE IF WE ARE IN DBCS ; $LEAVE NC ;AN010;THIS IS NOT DBCS JNC $$EN177 INC DI ;AN010;GO TO THE NEXT CHAR TO CHECK INC DI ;AN010;DITO ; $ENDDO ;AN010; JMP SHORT $$DO177 $$EN177: MOV AL, 0 SCASB ; $LEAVE Z ;if [DI] = 0, then end of string. Ends this loop. JZ $$EN176 DEC DI ;if [DI] <> 0, then go back and scan char again MOV AL, "\" ;to see it was a back slash. SCASB ; $IF Z ;if it was, then save the addr to SI. JNZ $$IF181 PUSH DI POP SI DEC SI ; $ENDIF ;else do loop again. $$IF181: ; $ENDDO JMP SHORT $$DO176 $$EN176: CLC ;clear carry flag. CMP SI, 0FFFFh ;Had SI been changed? ; $IF E JNE $$IF184 STC ;No, set the carry. Not found. ; $ELSE JMP SHORT $$EN184 $$IF184: MOV BYTE PTR ES:[SI], 0 ;Yes, replace "\" with 0. Seg override to get default DI seg. MOV AX, SI INC AX ;let AX have the last dir offset value. CLC ;clear carry ; $ENDIF $$EN184: POP SI ;restore original value POP DI ;original string offset RET LAST_DIR_OUT ENDP ; ; HEADER ;***************************************************************************** ; Check DBCS environment ;***************************************************************************** ; Function: Check if a specified byte is in ranges of the DBCS lead bytes ; Input: AL = Code to be examined ; Output: If CF is on then a lead byte of DBCS ; Register: FL is used for the output, others are unchanged. PUBLIC CHK_DBCS Chk_DBCS PROC ;AN010; PUSH DS ;AN010; save these regs, about to be clobbered PUSH SI ;AN010; CMP DBCSEV_SEG,0 ;AN010; ALREADY SET ? ; $IF E ;AN010; if the vector not yet found JNE $$IF187 PUSH AX ;AN010; MOV AX,6300H ;AN010; GET DBCS EV CALL INT 21H ;AN010; ds:si points to the dbcs vector ASSUME DS:NOTHING ;AN010; that function clobbered old DS MOV DBCSEV_OFF,SI ;AN010; remember where the dbcs vector is MOV DBCSEV_SEG,DS ;AN010; so next time I don't have to look for it POP AX ;AN010; ; $ENDIF ;AN010; $$IF187: LDS SI,DWORD PTR DBCSEV_OFF ;AN010;SET DS:SI TO POINT TO THE DBCS VECTOR ; $SEARCH ;AN010; $$DO189: CMP WORD PTR [SI],0 ;AN010; vector ends with a nul terminator entry ; $LEAVE E ;AN010; if that was the terminator entry, quit JE $$EN189 CMP AL,[SI] ;AN010; look at LOW value of vector ; $EXITIF NB,AND ;AN010; if this byte is in range with respect to LOW JB $$IF189 CMP AL,[SI+1] ;AN010; look at HIGH value of vector ; $EXITIF NA ;AN010; if this byte is still in range JA $$IF189 STC ;AN010; set flag to say, found a DBCS char. ; $ORELSE ;AN010; since char not in this vector JMP SHORT $$SR189 $$IF189: ADD SI,2 ;AN010; go look at next vector in dbcs table ; $ENDLOOP ;AN010; go back and check out new vector entry JMP SHORT $$DO189 $$EN189: CLC ;AN010; set flag to say, this is not a DBCS character ; $ENDSRCH ;AN010; $$SR189: POP SI ;AN010; restore the regs POP DS ;AN010; ; ASSUME DS:DSEG ;AN010; tell masm, DS back to normal RET ;AN010; chk_DBCS ENDP ;AN010; ; ; subttl Compress_Filename page ; ;****************************************************************************** ; ; PURPOSE: ; -------- ; Compress the FCB style filename into an ASCIIZ packed name. ; For example, 'ABC?????EXE' = > 'ABC?????.EXE',0 ; or 'ABC EXE' = > 'ABC.EXE',0 ; Note that the length of the source is *** 11 *** byte long. ; The max length of result is *** 13 *** bytes long. ; In the usual practice, the source filename with extention can be obtained ; by using function call 29h (Parse a Filename). So this routine is ; an inverse function of fun. 29h except DI should be the *** starting point ; of destination string *** instead of that of an unopened FCB (When you use ; fun 29h together with this routine, keep this thing in mind. Also if ES, DS ; values are different in your program, be careful to use them correctly.) ;------------------------------------------------------------------------------ ; REGISTERS INPUT ; ---------------- ; AX: ; BX: ; CX: ; DX: ; SI: offset of source unpacked filename with extention ; DI: offset where the resultant asciiz filename(.ext) will be placed. ; SP: ; BP: ; DS: source seg ; ES: result seg ; SS: ; ; DATA INPUT ; ----------- ; Memory_Label - ; ;----------------------------------------------------------------------------- ; REGISTERS OUTPUT ; ---------------- ; AX: ; BX: ; CX: ; DX: ; SI: ; DI: ; SP: ; BP: ; DS: ; ES: ; SS: ; ; DATA OUTPUT ; ----------- ; ; FLAG OUTPUT ; ----------- ;****************************************************************************** COMPRESS_FILENAME PROC NEAR PUBLIC COMPRESS_FILENAME PUSH DI XOR CX, CX ;CX = 0 ; $DO $$DO195: LODSB ;[SI] => AL, SI = SI + 1 CMP CX, 10 ;CX > 10 then exit ; $LEAVE A JA $$EN195 CMP CX, 8 ;filename extention position ; $IF B ;CX < 8. handling filename JNB $$IF197 CMP AL, ' ' ;AL = blank ? ; $IF E JNE $$IF198 MOV AX, 7 SUB AX, CX ADD SI, AX ;SI = SI + (7 - CX) MOV CX, 8 ;then skip to handles extention ; $ELSE JMP SHORT $$EN198 $$IF198: STOSB ;AL => [DI], DI = DI + 1 INC CX ;CX = CX + 1 ; $ENDIF $$EN198: ; $ELSE ;extention part JMP SHORT $$EN197 $$IF197: CMP AL, ' ' ; $IF E JNE $$IF202 MOV CX, 11 ;exit this loop ; $ELSE JMP SHORT $$EN202 $$IF202: CMP CX, 8 ;the first chr of extention? ; $IF E ;yes JNE $$IF204 PUSH AX ;save cur chr MOV AL, '.' ;and put a dot STOSB ; . => [DI], DI = DI + 1 POP AX ;restore AX ; $ENDIF $$IF204: STOSB ;AL => [DI], DI = DI + 1 INC CX ;CX = CX + 1 ; $ENDIF $$EN202: ; $ENDIF $$EN197: ; $ENDDO JMP SHORT $$DO195 $$EN195: MOV AL, 0 STOSB ;put 0 at the current [DI] POP DI ;restore DI RET COMPRESS_FILENAME ENDP ; SET_DEFAULT_DRV PROC NEAR ;change source drv as a default drv for conveniece of find, read operation ;of source. (handling target should be more specific as for as drive letter ;goes.) ;input: DL - drive # (0 = A, 1 = B ...) MOV AH, Select_Disk ; = 0Eh INT 21H OR SYS_FLAG, DEFAULT_DRV_SET_FLAG ;indicates default drv has been changed ;Used for exit the program to restore default drv RET SET_DEFAULT_DRV ENDP ; ORG_S_DEF PROC NEAR ;restore the original source directory. PUSH ES PUSH DS PUSH DS POP ES ;DS=ES=data seg TEST SYS_FLAG, DEFAULT_S_DIR_FLAG ;source default direcotry saved? ; $IF NZ JZ $$IF209 MOV DX, OFFSET SAV_S_DRV ;saved source drive letter & directory MOV AH, 3Bh INT 21h ;restore source AND SYS_FLAG, RESET_DEFAULT_S_DIR ;reset the flag ; $ENDIF $$IF209: POP DS POP ES RET ORG_S_DEF ENDP ; ORG_S_T_DEF PROC NEAR ;retore original target, source and default drv and directory ;check default_s(t)_dir_flag, default_drv_set_flag to restore source, ;or target directory and default drive. TEST SYS_FLAG, TURN_VERIFY_OFF_FLAG ;turn off verify? ; $IF NZ ;yes JZ $$IF211 MOV AX, 2E00h ;turn it off INT 21H ; $ENDIF $$IF211: TEST SYS_FLAG, DEFAULT_DRV_SET_FLAG ;default drive has been changed? ; $IF NZ ;yes JZ $$IF213 MOV DL, SAV_DEFAULT_DRV DEC DL CALL SET_DEFAULT_DRV ;restore default drv. ; Following is a fix for PTR 0000012 . The fix is to skip changing default ; drive directory if source drive is not the default drive. MOV AL, S_DRV_NUMBER ;AN002; get source drive number CMP AL, SAV_DEFAULT_DRV ;AN002; src drive is the default drv ? ; $IF NE ;AC022;NO, SO SEE IF DEF. DRV. IS CHGD. JE $$IF214 TEST SYS_FLAG, DEFAULT_DRV_SET_FLAG ;AN022;DEF DRV CHGD? ; $IF NZ ;AN022;YES, RESET IT JZ $$IF215 MOV DX, OFFSET SAV_DEF_DIR_ROOT ;AN022;GET THE SETTING MOV AH, Chdir ;AN022;MAKE THE CALL INT 21H ;AN022; ; $ENDIF ;AN022; $$IF215: ; $ELSE ;AN022;SRC IS DEF DRIVE! JMP SHORT $$EN214 $$IF214: MOV DX, OFFSET SAV_DEF_DIR_ROOT MOV AH, Chdir INT 21H ;restore current dir of default dir ; $ENDIF ;AN022; $$EN214: ; $ENDIF $$IF213: TEST SYS_FLAG, DEFAULT_S_DIR_FLAG ;source default direcotry saved? ; $IF NZ JZ $$IF220 MOV DX, OFFSET SAV_S_DRV ;saved source drive letter & directory MOV AH, 3Bh INT 21h ;restore source. This is for the case of ERROR exit. ; $ENDIF $$IF220: TEST SYS_FLAG, DEFAULT_T_DIR_FLAG ;target default directory saved? ; $IF NZ ;then assume both source, target default saved JZ $$IF222 MOV DX, OFFSET SAV_T_DRV ;saved target drive letter & directory MOV AH, 3Bh INT 21h ;restore target ; $ENDIF $$IF222: RET ORG_S_T_DEF ENDP ; CHK_MKDIR_LVL PROC NEAR ;if starting target directories has been created, and no files has been found to copy, ;and /E option is not specified, then remove the directories created. ; CMP T_MKDIR_LVL, 0 ;target starting directory created? ; $IF A,AND ;yes. JNA $$IF224 TEST OPTION_FLAG, SLASH_E ;/E option taken? ; $IF Z,AND ;no. JNZ $$IF224 CMP FOUND_FILE_FLAG, 0 ;found any file? ; $IF E JNE $$IF224 CALL T_RM_STARTING_DIR ;then, remove created directories. ; $ENDIF $$IF224: RET CHK_MKDIR_LVL ENDP ; T_RM_STARTING_DIR PROC NEAR ;based on the current target directory, remove directories T_MKDIR_LVL times ;INPUT: T_MKDIR_LVL ; T_DRV_NUMBER ; T_DRV_PATH ; T_PATH MOV DL, T_DRV_NUMBER LEA SI, T_PATH MOV AH, Get_Current_Directory INT 21h TRSD_AGAIN: MOV DX, OFFSET T_PARENT ;chdir to the parent directory MOV AH, 3Bh ;Chdir INT 21h MOV DX, OFFSET T_DRV_PATH MOV AH, 3Ah ;Rmdir INT 21h MOV DI, OFFSET T_DRV_PATH CALL LAST_DIR_OUT ;take out the last removed dir name DEC T_MKDIR_LVL ;decrease the number CMP T_MKDIR_LVL, 0 ;no more? JA TRSD_AGAIN RET T_RM_STARTING_DIR ENDP ; ;************************************************************ ;* ;* SUBROUTINE NAME: PRINT_STDOUT ;* ;* SUBROUTINE FUNCTION: ;* Display the requested message to the specified handle ;* ;* INPUT: ;* Paramters in parater storage area ;* DS:SI-->Substitution List ;* ES:DI-->PTR to input buffer if buffered keyboard ;* input is specified (DL = 0A) ;* OUTPUT: ;* AX = Single character entered if DL=01 ;* OR ;* ES:DI-->input buffer where string is returned if DL=0A ;* ;* The message corresponding to the requested msg number will ;* be written to Standard Out. Message substitution will ;* be performed if specified ;* ;* NORMAL EXIT: ;* Message will be successfully written to requested handle. ;* ;* ERROR EXIT: ;* None. Note that theoretically an error can be returned from ;* SYSDISPMSG, but there is nothing that the application can do. ;* ;* INTERNAL REFERENCES: SysDispMsg ;* ;* EXTERNAL REFERENCES: ;* None ;* ;************************************************************ PRINT_STDOUT PROC NEAR ;AN000: PUSH BX ;AN000; PUSH CX ;AN000; PUSH DX MOV AX,MSG_NUM ;AN000; Message ID MOV BX,STDOUT ;AN000; standard input message handle MOV CX,SUBST_COUNT ;AN000; message substitution count MOV DH,MSG_CLASS ;AN000; message class MOV DL,INPUT_FLAG ;AN000; Type of INT 10 for KBD input CALL SYSDISPMSG ;AN000: AX=Extended key value if wait ;for key JNC DISP_DONE ;AN000: If CARRY SET then registers ;will contain extended error info ; AX - Extended error Number ; BH - Error Class ; BL - Suggested action DISP_DONE: ;AN000: CH - Locus POP DX POP CX ;AN000; POP BX ;AN000; RET ;AN000: PRINT_STDOUT ENDP ;AN000: ;************************************************************ ;* ;* SUBROUTINE NAME: PRINT_STDERR ;* ;* FUNCTION: Display the requested message to Standard Out ;* ;* INPUT: ;* Parameters in parameter storage area ;* DS:SI-->Substitution List ;* ES:DI-->PTR to input buffer if buffered keyboard ;* input is specified (DL = 0A) ;* ;* OUTPUT: ;* AX = Single character entered if DL=01 ;* OR ;* ES:DI-->input buffer where string is returned if DL=0A ;* The message corresponding to the requested msg number will ;* be written to the Standard Error. Message substitution will ;* be performed if specified ;* ;* NORMAL EXIT: ;* Message will be successfully written to requested handle. ;* ;* ERROR EXIT: ;* None. Note that theoretically an error can be returned from ;* SYSDISPMSG, but there is nothing that the application can do. ;* ;* INTERNAL REFERENCES: SysDispMsg ;* ;* EXTERNAL REFERENCES: None ;* ;************************************************************ PRINT_STDERR PROC NEAR ;AN000: PUSH AX ;AN000; PUSH BX ;AN000; PUSH CX ;AN000; PUSH DX MOV AX,MSG_NUM ;AN000; Message ID MOV BX,STDERR ;AN000; Handle MOV CX,SUBST_COUNT ;AN000; message substitution count MOV DH,MSG_CLASS ;AN000; message class MOV DL,INPUT_FLAG ;AN000; INT 10 KBD input type CALL SYSDISPMSG ;AN000: AX=Extended key value if wait ;for key JNC DISP_EXIT ;AN000: If CARRY SET then registers ;will contain extended error info ; AX - Extended error Number ; BH - Error Class ; BL - Suggested action DISP_EXIT: ;AN000: CH - Locus POP DX POP CX ;AN000; POP BX ;AN000; POP AX ;AN000; RET ;AN000: PRINT_STDERR ENDP ;AN000: ; EXTENDED_ERROR_HANDLER PROC NEAR ;This routine calls fun 59(Get extended error) and ;check the actions returned. If it is Immediate exit, then jmp to JUST_EXIT ;If it is abort, then jmp to MAIN_EXIT. ;Or else, it check the COPY_STATUS flag. If is not open, read, create or ;write, then it is considered as a critical error and jmp to MAIN_EXIT. ;If access denied ; too many open files ; sharing violation ; lock violation ; general failure ;then show the message and jmp to the MAIN_EXIT. ; *** Currently, this routine directly jump to the main_exit instead of ; *** returing to the caller. The reason is we regard the above error conditions ; *** as being not suitable to continue copying and, hence, to simplify ; *** the error process. ;INPUT: ; DS - DATA SEG ;OUTPUT: ; ALL THE REG PRESERVED PUSH ES ;save ES PUSH AX PUSH BX PUSH CX PUSH DS ;AN000; POP ES ;AN000;DS = ES = DATA SEG PUSHF ;save flags PUSH ES MOV AH, 59h ;get extended error MOV BX, 0 ;version 3.0 INT 21h POP ES MOV ERRORLEVEL, 4 ;error in operation TEST COPY_STATUS, OPEN_ERROR_FLAG ;open error? JNZ OPEN_ERROR_RTN ;yes TEST COPY_STATUS, READ_ERROR_FLAG ;read error? JNZ READ_ERROR_RTN TEST COPY_STATUS, CREATE_ERROR_FLAG ;create error? JNZ CREATE_ERROR_RTN TEST COPY_STATUS, WRITE_ERROR_FLAG ;write error? JNZ WRITE_ERROR_RTN TEST COPY_STATUS, CHDIR_ERROR_FLAG ;chdir error? JNZ CHDIR_ERROR_RTN JMP SHORT GOTO_MAIN_EXIT OPEN_ERROR_RTN: ;open error. show error message and exit CALL SHOW_S_PATH_FILE_ERR ;show the troubled path filename CALL SHOW_ERROR_MESSAGE JMP SHORT GOTO_MAIN_EXIT ;abort READ_ERROR_RTN: CALL SHOW_S_PATH_FILE_ERR CALL SHOW_ERROR_MESSAGE ;show message and abort JMP SHORT GOTO_MAIN_EXIT CREATE_ERROR_RTN: CMP AX, 2 ;"file not found" to create? JNE CER_1 ; Set message parameters MOV AX,MSG_FILE_CREATE_ERR ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; no message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;show "File creation error" message ;instead of "File not found" JMP SHORT GOTO_MAIN_EXIT CER_1: CALL SHOW_ERROR_MESSAGE ;show error_message JMP SHORT GOTO_MAIN_EXIT WRITE_ERROR_RTN: CALL SHOW_ERROR_MESSAGE ;show message JMP SHORT GOTO_MAIN_EXIT CHDIR_ERROR_RTN: PUSH AX ;AN000; ; Set substitution list LEA SI,SUBLIST1 ;AN000; get addressability to sublist LEA DX,S_DRV_PATH ;AN000; offset to PATH NAME MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,1 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH ; Set message parameters MOV AX,DISPLAY_S_PATH ;AN000; message number MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,PARM_SUBST_ONE ;AN000; one message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;show source drv,path POP AX ;AN000; CALL SHOW_ERROR_MESSAGE ;display error message GOTO_MAIN_EXIT: JMP MAIN_EXIT ;restore conditions ;and exit QUICK_EXIT: JMP JUST_EXIT ;immediate exit EEH_EXIT: MOV ERRORLEVEL, 0 ;reset errorlevel POPF POP CX POP BX POP AX POP ES RET EXTENDED_ERROR_HANDLER ENDP ; SHOW_ERROR_MESSAGE PROC NEAR ;called immediately after Get_extended error ;This will show simple error message according to error_code in AX ;If the message is not what it wanted, just exit without message- Set carry. ;input: DS - data seg ;output: Carry flag is distroyed. clc ;clear carry CMP AX, 5 ;access denied? JE ACCESS_DENIED_MESSAGE CMP AX, 4 ;too many open files? JE TOO_MANY_OPEN_MESSAGE CMP AX, 31 ;general failure? JE GENERAL_FAIL_MESSAGE CMP AX, 32 ;sharing violation? JE SHARING_VIOL_MESSAGE CMP AX, 33 ;lock violation? JE LOCK_VIOL_MESSAGE CMP AX, 3 ;path not found? JE PATH_NOT_MESSAGE CMP AX, 2 ;file not found error? JE FILE_NOT_ERR_MESSAGE CMP AX, 65 ;access denied on the network? JE ACCESS_DENIED_MESSAGE CMP AX, 82 ;no more directory entry to create a file? JE FILE_CREATE_ERR_MESSAGE STC ;else set carry JMP GOTO_MAIN_EXIT ;and exit ACCESS_DENIED_MESSAGE: MOV AX, MSG_ACCESS_DENIED ;AN000; JMP SHORT SHOW_MESSAGE TOO_MANY_OPEN_MESSAGE: MOV AX, MSG_TOO_MANY_OPEN ;AN000; JMP SHORT SHOW_MESSAGE GENERAL_FAIL_MESSAGE: MOV AX, MSG_GENERAL_FAIL ;AN000; JMP SHORT SHOW_MESSAGE SHARING_VIOL_MESSAGE: MOV AX, MSG_SHARING_VIOL ;AN000; JMP SHORT SHOW_MESSAGE LOCK_VIOL_MESSAGE: MOV AX, MSG_LOCK_VIOL ;AN000; JMP SHORT SHOW_MESSAGE PATH_NOT_MESSAGE: MOV AX, MSG_PATH_NOT ;AN000; JMP SHORT SHOW_MESSAGE FILE_NOT_ERR_MESSAGE: MOV AX, MSG_FILE_NOT_ERR ;AN000; JMP SHORT SHOW_MESSAGE FILE_CREATE_ERR_MESSAGE: MOV AX, MSG_FILE_CREATE_ERR ;AN000; SHOW_MESSAGE: ; Display error message ; Set message parameters MOV MSG_NUM,AX ;AN000; set message number MOV SUBST_COUNT,NO_SUBST ;AN000; NO message substitution MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;AN000; print it RET SHOW_ERROR_MESSAGE ENDP ; SHOW_S_PATH_FILE_ERR PROC NEAR ;show current source path(drv, full path), and filename to the ;standard error display device. ;input: ds: data seg PUSH ES ;save ES PUSH AX ;save ERROR_CODE push ds pop es ;es = ds MOV DI,OFFSET S_DRV_PATH CALL STRING_LENGTH ;cx got the length MOV SI,OFFSET S_DRV_PATH ;full path of source MOV DI,OFFSET DISP_S_PATH REP MOVSB ;S_DRV_PATH => DISP_S_PATH MOV CX, 13 ;max 13 chr MOV SI,OFFSET FILE_DTA.DTA_FILENAME MOV DI,OFFSET DISP_S_FILE REP MOVSB ;dta_filename => disp_s_file LEA SI,SUBLIST1 ;AN000; get addressability to list LEA DX,DISP_S_PATH ;AN000; offset to path name MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,1 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH LEA SI,SUBLIST2 ;AN000; get addressability to list LEA DX,DISP_S_FILE ;AN000; offset to file name MOV [SI].DATA_OFF,DX ;AN000; save offset MOV [SI].DATA_SEG,DS ;AN000; save data segment MOV [SI].MSG_ID,2 ;AN000; message ID MOV [SI].FLAGS,010H ;AN000; ASCIIZ string, left align MOV [SI].MAX_WIDTH,0 ;AN000; MAXIMUM FIELD WITH MOV [SI].MIN_WIDTH,0 ;AN000; MINIMUM FIELD WITH CMP S_DEPTH,0 ;AN000;it happened, when dealing with the starting dir? ; $IF E JNE $$IF226 LEA SI,SUBLIST2 ;AN007;PIONT TO THE FIRST LIST MOV [SI].MSG_ID,1 ;AN007; message ID MOV AX,DISPLAY_S_PATH ;AC007;ITS ONLY A FILE NAME MOV SUBST_COUNT,PARM_SUBST_ONE ;AN007; ONE message sub ; $ELSE JMP SHORT $$EN226 $$IF226: LEA SI,SUBLIST1 ;AN007;PIONT TO THE FIRST LIST MOV AX,S_PATH_FILE1 ;AN000;put '\' MOV SUBST_COUNT,PARM_SUBST_TWO ;AN007;TWO message subs ; $ENDIF $$EN226: ; Set message parameters MOV MSG_NUM,AX ;AN000; set message number MOV MSG_CLASS,UTILITY_MSG_CLASS ;AN000; message class MOV INPUT_FLAG,NO_INPUT ;AN000; no input CALL PRINT_STDERR ;display error message POP AX ;restore ERROR_CODE POP ES RET SHOW_S_PATH_FILE_ERR ENDP ; CLOSE_DELETE_FILE PROC NEAR ;when writing error occurs, then this routine is called to ;clean up the troubled target file. ;INPUT: DS - buffer seg ; ES - data seg MOV BX, ES:T_HANDLE ;close target file PUSH DS ;AN005;SAVE THE BUFFER PTR PUSH ES ;AN005;WE NEED THE DATA PTR POP DS ;AN005;DS = THE DATA PTR CALL CLOSE_A_FILE ;and close the handle POP DS ;AN005;DS = THE BUFFER PTR AGAIN LEA DX, DS:target_drv_let ;target drv, filename CALL DELETE_A_FILE ;delete it RET CLOSE_DELETE_FILE ENDP ; ; SWITCH_DS_ES PROC NEAR ; switch DS, ES PUSH DS PUSH ES POP DS POP ES RET SWITCH_DS_ES ENDP MY_INT24: CMP CS:INT24_ABORT_CNT, 0 ;if aborted more than once, then just exit. JNE MI_JUST_EXIT PUSHF ;we are calling interrupt handler CALL DWORD PTR CS:SAV_INT24 ;call original int 24 handler CMP AL, 1 ;retry? JE MI_RETRY CMP AL, 0 ;ignore? Cannot ignore. Try again JE MI_RETRY POP CX ;remove IP, CS, FLAGS POP CX ;since we are not going back POP CX ;to the place int 24 was called. CMP AL, 2 ;abort? JE MI_ABORT CMP AL, 3 ;AN000;fail? JE MI_ABORT JMP MAIN_EXIT ;show files copied message ;restore default value and exit MI_ABORT: INC CS:INT24_ABORT_CNT ;increase the count of int24_abort JMP MAIN_EXIT_A ;restore default value and exit MI_JUST_EXIT: POP CX POP CX POP CX JMP JUST_EXIT MI_RETRY: IRET ;return where it happened ;and retry that operation. ; INT24_ABORT_CNT DB 0 ; include msgdcl.inc CSEG ENDS DSEG_INIT SEGMENT PARA PUBLIC ;AN000; DSEG_INIT ENDS ;AN000; END MAIN