PAGE 60,132 TITLE Xall - High speed eXeute in ALL subdirs utility SUBTTL General Program description ;-----------------------------------------------------------------------------; ; Xall - eXecute any command in current directory plus all subdirectories ; ;-----------------------------------------------------------------------------; ; XALL 1.0 þ PCDATA TOOLKIT Copyright (C) 1990 Ziff Communications Co. ; ; PC Magazine þ Wolfgang Stiller ; ; ; ; The Author (Wolfgang Stiller) reserves all rights to prepare ; ; derivative works based on this and the other related programs. ; ;-----------------------------------------------------------------------------; ;Purpose: ; ; XALL will eXecute any command in the current directory plus all ; ; subdirectories below that one. This program is written for high ; ; speed execution (lean and mean!). ; ;-----------------------------------------------------------------------------; ;Syntax: ; ; ; ;XALL anyDOScommand ; ; ; ; "anyDOScommand" may be any batch file, program, or DOS command. Any ; ; programs or batch files should be located in a ; ; directory specified in the DOS path (PATH=). ; ;-----------------------------------------------------------------------------; ;Remarks: ; ; "anyDOScommand" will be executed first in the current directory and ; ; then in each subdirectory subordinate to the current directory. This ; ; program has been optimized for speed and efficiency of execution. If ; ; execution is terminated, you will be in the directory currently being ; ; processed. This program does NOT check for execution of other ; ; programs that change the drive or directory. (Speed is the goal!) ; ; XALL has advanced error recovery and provides clear explanations to ; ; PCDATA Toolkit user of what has gone wrong, such as an ; ; invalid COMSPEC environment variable. ; ; ; ; XALL will return the following DOS errorlevels: ; ; ; ; 00 - normal completion ; ; 32 - any abnormal occurance - indicates fatal termination. ; ;-----------------------------------------------------------------------------; ;-----------------------------------------------------------------; ; Constants: ; ;-----------------------------------------------------------------; BOX EQU 254 ;Small box character code CR EQU 0Dh ; Carriage return LF EQU 0Ah ; Line feed CRLF EQU 0A0D ; CR+LF in one word First_time EQU 00h ; Constant to indicate first time Not_First_Time EQU 0FFh ; indicate NOT first time Subdirectory_attrib EQU 10h ; Bit pattern to find directories CSEG SEGMENT ASSUME CS:CSEG, DS:CSEG,ES:CSEG,SS:CSEG ORG 2Ch ;Segment of environment found here Environment LABEL BYTE ORG 7DH ;Just before paramter area in PSP XALL_PARM LABEL BYTE ORG 80h ;Normal DOS PSP parameter area PSP_PARM LABEL BYTE ;***************************************************************************** ;** Main program begins here: XALL - Subdirectory Command eXecuter ** ;** ---- ** ;***************************************************************************** ORG 100h ;This is a .COM type program XALL: MOV SP,OFFSET STACK_TOP ;Create new stack MOV DX,OFFSET XALL_CR_MSG ;Copyright message MOV AH,09h ;DOS print string function INT 21h MOV BX,offset Program_top ;End of program + 15 (segment roundng) MOV CX,4 ;Prepare to div by 4 (calc # of paras) SHR BX,CL ;Convert to segment address MOV AH,4Ah ;Release uneeded memory INT 21h CALL Get_COMSPEC ;Locate the compsec in the environment CALL Create_EXEC_parm_BLOCK ;Create parameter block for DOS EXEC CALL Create_Command_tail ;Create /C command for COMMAND.COM ;***********************; ; MAIN loop begins here ; ;***********************; EXEC_Next_Command: ;Come here to execute a command ;in each directory CALL DISPLAY_XALL_MSG ;Display msg that we are this directry ;---------------------------------------------------------------------------; ; Use DOS EXEC function to invoke COMMAND.COM with user's specified command ; ; tail from the parameter area at 80h. ; LDS DX,[Command_Adr] ;DS:DX is address of command.com str MOV BX,OFFSET Parm_block ;ES:BX is adr of parameter block MOV AX,4b00h ;DOS EXEC sub-task function INT 21h ;COMMAND.COM now executing user's cmd ; Return from execution of DOS shell - all registers may be corrupted: MOV AX,CS ;CS is only OK register MOV ES,AX MOV DS,AX MOV SS,AX MOV SP,OFFSET Stack_top ;Reset stack pointer JC Handle_Exec_Error ;If DOS EXEC function failed handle it ;-----------------------------------------------------------------------------; ; Now find NEXT (or first) subdirectory below the current one ; ;-----------------------------------------------------------------------------; Find_another_SubDirectory: MOV DX,[DTA_Ptr] ;Tell DOS to use different DTA MOV AH,1Ah ;DOS set DTA function INT 21h CMP FF_Flag,First_time ;Is this first time in this directory? JNE Find_next ; IF not, do a find next MOV DX,OFFSET Global_str ; Else do an initial find on *.* MOV CX,Subdirectory_attrib ; find only subdirectories MOV AH,4Eh ; DOS FIND 1st function INT 21h JMP SHORT Validate_directory ;Skip the FIND NEXT function call Find_Next: ;Find the next directory MOV AH,4Fh ;DOS find next function INT 21h Validate_directory: ;Check that we found valid directory JC Return_to_Mother ;If find failed, Return to mother dir MOV DI,[DTA_Ptr] ;Get address of current DTA ; Check the file attribute bits in the DTA (is it a directory or a file?) TEST BYTE PTR [DI+21],Subdirectory_attrib ;Is this a directory? JE Find_Next ;If its not a directory keep looking ADD DI,30 ;Point to directory name in DTA CMP BYTE PTR [DI],'.' ;IS it a '.' or '..' dir entry? JE Find_Next ; IF so, ignore it and find next dir ADD [DTA_Ptr],43 ;Go down one level + use a new DTA MOV FF_Flag,First_time ;Set flag to do intial *.* dir find ; Change to new daughter directory: MOV DX,DI ;point to directory to change to MOV AH,3Bh ;DOS change directory function INT 21h JMP SHORT EXEC_Next_Command ;Go execute command in this new Dir ;-----------------------------------------------------------------------------; ; HANDLE EXEC ERROR - Produce specific error messages for the possible DOS ; ; return codes from the EXEC function which may reasonably; ; happen as a result of command or configuration error. ; ;-----------------------------------------------------------------------------; Handle_Exec_Error: ;Come here if, DOS EXEC func failed CMP AX,02 ;= file not found/path invalid ? JNE Handle_Exec_A ; If not, check other codes MOV DX, OFFSET EXEC_02_Msg ; Else put out appropriate error msg JMP Error_Exit ; Print error message and terminate Handle_Exec_A: ;Keep checking other possible codes: CMP AX,05 ;= Access denied return code? JNE Handle_Exec_B ; If not, check other codes MOV DX, OFFSET EXEC_05_Msg ; Else put out appropriate error msg JMP Error_Exit ; Print error message and terminate Handle_Exec_B: ;Keep checking other possible codes: CMP AX,08 ;= Isufficient memory return code? JNE Unexpected_error ; If not, its an unexpected error.. MOV DX, OFFSET EXEC_08_Msg ; Else put out appropriate error msg JMP Error_Exit ; Print error message and terminate Unexpected_error: ;Put out a general catchall error msg MOV DX,OFFSET XALL_fail_msg ;message for unexpected errors JMP Error_Exit ; Print error message and terminate ;-----------------------------------------------------------------------------; ; R E T U R N T O M O T H E R - No more files in prior directory so we ; ; return to the previous level and find next; ;-----------------------------------------------------------------------------; Return_to_Mother: ;Go up one level to mother directory CMP [DTA_Ptr],OFFSET DTA_Area ;Are we back in original directory? JNZ Return_to_Prior_Dir ; If not, set directory back 1 level JMP Normal_termination ; If so, we are all done Return_to_Prior_Dir: MOV DX,OFFSET DotDot_str ;Point to ".." string (prev dir) MOV AH,3Bh ;DOS change dir function INT 21h ;Change to prior (mother) directory SUB [DTA_Ptr],43 ;Go back and re-use prior DTA MOV FF_Flag,Not_first_time ;Set flag so we do a find next rather ; than a find first. (re-use DTA!) JMP Find_Another_SubDirectory ;Keep checking for DIRs PAGE SUBTTL General use subroutines ;***************************************************************************; ;** G e n e r a l U s e S u b r o u t i n e s - start here **; ;***************************************************************************; ;-----------------------------------------------------------------------------; ; Get COMSPEC-search the environment for COMSPEC= string (loc of COMMAND.COM) ; ;-----------------------------------------------------------------------------; Get_COMSPEC: MOV ES,WORD PTR [Environment] ;Segment address of Environment XOR DI,DI ;Start at beginning of segment MOV SI,OFFSET COMPSEC_str ;Point to "compspec="string CLD ;Forward direction XOR AX,AX ;Constant of zero for comparison Check_Next_ENV_string: CMP BYTE PTR ES:[DI],0 ;If points to zero, no compspec JZ Missing_COMSPEC ; FATAL error.... MOV DX,DI ;Save string pointers MOV BP,SI MOV CX,8 ;COMSPEC= is 8 characters REPZ CMPSB ;Check if we are pointing at comspec= MOV DI,DX ;Restore the pointers MOV SI,BP JZ Found_COMSPEC ;We've got a match MOV CX,200h ;Search for a max of 512 bytes REPNZ SCASB ;Search for zero byte (AL=0) JNZ Missing_COMSPEC ; FATAL error.... JMP SHORT Check_Next_ENV_string ;Keep looking for environment strin Missing_COMSPEC: MOV DX,OFFSET Missing_CS_MSG ;Message to say comspec= missing JMP Error_Exit Found_COMSPEC: ADD DI,8 ;Point to string after COMSPEC= MOV WORD PTR [Command_Adr],DI ;Save address: offset MOV WORD PTR [Command_Adr+2],ES ; and SEGMENT too RET ;---------------------------------------------------------------; ; Create EXEC Parm block- setup the parameter block for DOS EXEC; ;---------------------------------------------------------------; Create_EXEC_parm_BLOCK: MOV [Parm_Block],ES ;Environment segment MOV [Parm_Block+4],CS ;Segment of parameter MOV [Parm_Block+8],CS ;Segment of FCB #1 MOV [Parm_Block+12],CS ;Segment of FCB #2 MOV AX,CS ;Point ES back to data segment MOV ES,AX RET ;----------------------------------------------------------------------; ; Create Command tail - setup /C followed by user's program to execute ; ; This program is later executed by invokation of COMMAND.COM ; ;----------------------------------------------------------------------; Create_Command_Tail: ; check program to invoke (parameter at 80h) AKA: PSP parameter MOV AL,PSP_PARM ;Check # of characters in parameter OR AL,AL ;Check if no characters (AL=0) JNZ Parameter_detected ; If a parameter passed.. MOV DX,OFFSET XALL_Help_MSG ;If no input,put out syntax msg JMP Error_Exit Parameter_detected: ; Insert a " /C" in front of the PSP parameter ADD AL,3 ;Increase length by 3 for " /C" MOV [XALL_Parm],AL ;Store new character count MOV BYTE PTR[XALL_Parm+1],' ' ;Store blank separator MOV WORD PTR[XALL_Parm+2],'C/';Store /C (after this is actual parm) RET ;----------------------------------------------------------------------; ; Display XALL message - Display name of current directory we are ; ; executing the user's command in. ; ;----------------------------------------------------------------------; Display_XALL_MSG: ;Display name of current directory MOV DX, offset In_Dir_MSG ;Begining of "Xing in:" message MOV CX,12 ;message has 12 chars MOV AH,40h ;DOS Write func MOV BX,1 ;Handle for std output device INT 21h ;Write Start message ; Get the current directory string MOV SI,OFFSET Current_Dir ;Location to store directory string XOR DX,DX ;Default drive MOV AH,47h ;DOS Get directory function INT 21h ; Scan the string in Current_Dir to determine its length: MOV DI,SI ;Dir string start MOV DX,SI ;Save a copy for use later XOR AX,AX ;Scan for zero termination of string MOV CX,64 ;Scan up to 64 characters REPNE SCASB ;Find 1st zero byte SUB DI,DX ;Length of directory string MOV CX,DI ;Characters to display in dir string ;DX = start of dir strng, CX = # of chars in string MOV AH,40h ;DOS Write func INT 21h ;Display the actual directory string MOV DX,OFFSET CRLF_str ;Prepare to put out carriage ret + LF MOV CX,2 ;Print 2 characters MOV AH,40h ;DOS Write func INT 21h RET ;---------------------------------------------------------------; ; Error Exit - called with DX pointing to error message to send ; ;---------------------------------------------------------------; Error_Exit: MOV AH,09h ;DOS display string function INT 21h MOV DX,OFFSET Hit_Key_Msg ;Ask user to hit any key MOV AH,09h ;DOS display string function INT 21H ; Wait for user to hit any key XOR AX,AX INT 16h ;Wait for user to hit a key MOV AX,4C20h ;Terminate with 32 errorlevel INT 21h ;Terminate Normal_Termination: ; ;Terminate with zero (DOS) errorLEVEL MOV AX,4C00h INT 21h SUBTTL Definition of Data Structures PAGE ;-----------------------------------------------------------------; ; Data storage definition: ; ;-----------------------------------------------------------------; Current_Dir LABEL BYTE ;Overlay copyright msg with this XALL_CR_msg DB 'XALL 1.0 - high speed eXecute ALL subdirectories utility.' DB CR,LF DB CR,LF,"XALL 1.0 ",BOX," PCDATA TOOLKIT (C) 1990" DB " Ziff Communications Co.",CR,LF DB "PC Magazine ",BOX," Wolfgang Stiller",CR,LF,"$" XALL_Help_MSG DB CR,LF,'Syntax is: "XALL anyDOScommand"',CR,LF DB ' "anyDOScommand" will then be eXecuted in the current and ' DB 'all ',CR,LF DB ' subordinate subdirectories. The program or batch file to' DB ' be executed',CR,Lf DB ' must be included in the DOS PATH.$' Missing_CS_Msg LABEL BYTE ;A message starting with comspec strg COMPSEC_str DB 'COMSPEC=' ;String in environment to search for DB ' missing.',CR,LF,'$' XALL_Fail_msg DB 'XALL failure.',CR,LF DB 'Probably due to bad COMSPEC - no COMMAND.COM.',CR,LF,'$' EXEC_02_Msg DB 'Unable to find program.','$' EXEC_05_Msg DB 'Program access denied.','$' EXEC_08_Msg DB 'Too little free mem.','$' Hit_Key_Msg DB CR,LF,LF,'Please hit a key.' CRLF_Str DB CR,LF,'$' In_Dir_MSG DB CR,LF,'Xing in: \' COMMAND_Adr DD 00 ;Full address COMMAND.COM strng in ENV Global_str DB '*.*',0 ;String for find all files DotDot_str DB '..',0 ;String to return to mother directory Parm_Block DW 00 ;Parameter block for DOS EXEC command DW XALL_PARM,0 DW 5Ch,0 DW 6Ch,0 FF_flag DB 0 ;First Find flag (=0 means find 1st) DTA_ptr DW DTA_AREA ;Pointer to a 43 char DTA block DTA_area LABEL BYTE ;area of 32 43 byte DTAs STACK_Bottom EQU DTA_area+ 32*43 ;Lower limit of the stack STACK_TOP EQU STACK_Bottom + 80h ;Allocate 128 byte stack Program_top EQU Stack_top+15 ;End of prog + 15 for paragr rounding CSEG EndS END XALL