home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pcmagazi
/
1987
/
04
/
search.asm
next >
Wrap
Assembly Source File
|
1987-12-12
|
29KB
|
608 lines
; Search.asm
; FORMAT: SEARCH [d:][path][filename] [string][/P][/C][/B]
; At least one parameter must be entered.
; /P=printer /C=case sensitive /B=include executable files
; in string searches. Default is diskwide search. Limit search
; by adding parameters. String must be enclosed in quotes.
CODE SEGMENT ;*************************
ASSUME CS:CODE,DS:CODE ;* *
ORG 100H ;* REMEMBER TO EXE2BIN *
;* *
START: JMP BEGINNING ;*************************
; DATA AREA
; ---------
COPYRIGHT DB 'Copyright 1986 Ziff Davis Publishing Co.',1AH
PROGRAMMER DB 'Michael J. Mefford'
SEARCH_SPEC DW OFFSET GLOBAL
GLOBAL DB '*.*',0
ROOT DB '\',0
PARENT DB '..',0
CURRENT_DISK DB ?
PATH_END DW ?
PARA_START DW ?
GOOD_PARA DB 0
STRING_START DW ?
STRING_CT DW 0
LINE_NUM DW 1
STRING_FLAG DB 0
PATH_FLAG DB 0
PRINT_FLAG DB 0
CASE_FLAG DB 0
BINARY_FLAG DB 0
EOF_FLAG DB 0
MATCH_FLAG DB 0
DISPLAY_FLAG DB 0
EXE DB 'EXE'
COM DB 'COM'
BAD_PARA DB 'Invalid parameters',7,'$'
SEARCH DB 13,10,'Searching for ',0
DIRECTORY DB 13,10,13,10,'Directory ',0
LINE DB 9,'Line number ',0
SPACING DB 13,10,32,32,0
DIR_ATTRIBUTE EQU 10H
ALL_FILES EQU 6H
QUOTES EQU 22H
;--------------------------------------------------------------;
; The first task is to initialize 100 bytes at the end of code ;
; to one. This will be used to track the directory level. ;
; The current drive and directory and drive will be saved. ;
; Then the command line at 80H in the PSP will be parsed. ;
;--------------------------------------------------------------;
BEGINNING: CLD ;All string instructions will be
MOV AL,1 ; done in a forward direction.
MOV CX,100 ;Initialize directory subscripts
MOV DI,OFFSET SUBSCRIPTS ;to one.
REP STOSB
MOV AH,19H ;Retrieve and save the current
INT 21H ; drive.
MOV CURRENT_DISK,AL
MOV SI,OFFSET CURRENT_DIR+1 ;Leave room for "\".
MOV DL,0
MOV AH,47H ;Retrieve and save the
INT 21H ; the current directory.
MOV DX,OFFSET DTA ;Point the disk transfer address
CALL CHANGE_DTA ; to the end of code.
CMP BYTE PTR DS:[80H],0 ;If no parameters, exit with error.
JA FIRST_PARA
JMP EXIT
FIRST_PARA: MOV SI,82H ;Point to the first byte of the
NEXT_PARA: LODSB ; command line.
CMP AL,32 ;Scan off leading spaces.
JZ NEXT_PARA
CMP AL,QUOTES ;If no filespec, parse string.
JNZ CK_CR
JMP STRING
CK_CR: CMP AL,13 ;If no parameters, exit with error.
JNZ CK_DRIVE
JMP EXIT
CK_DRIVE: CMP BYTE PTR [SI],':'
JNZ NO_DRIVE ;If drive spec exists,
AND AL,5FH ; convert drive to DOS format by
SUB AL,'A' ; capitalizing and changing to
MOV DL,AL ; hex number.
MOV AH,0EH ;Change drive.
INT 21H
INC SI
CALL PARA_END ;If end of filespec, done parsing
JC DRIVE_ONLY ; here
LODSB ; otherwise get next parameter.
CMP AL,QUOTES ;If it's quotes exit with error.
JNZ NO_DRIVE ; (need a space delimiter
JMP EXIT ; before string.)
NO_DRIVE: MOV PARA_START,SI ;We now are pointing to the start
DEC PARA_START ; of the filespec parameter.
MOV PATH_END,SI ;Will assume the end of path to
DEC PATH_END ; be parameter start-1.
DEC PATH_END
CMP AL,'\' ;If search is to start at root,
JNZ CK_GLOBAL ; change the directory to the root
MOV DX,OFFSET ROOT ; and adjust the start pointer.
CALL CHANGE_DIR
MOV PATH_FLAG,1 ;Flag that a path exists.
INC PARA_START
CALL PARA_END
DRIVE_ONLY: JC NEXT_DELIMIT ;If no other filespec, done here.
CK_GLOBAL: CMP AL,'*' ;If *.*, search the entire disk.
JNZ CK_SWITCH
LODSB
CMP AL,'.'
JZ GOT_DOT
JMP EXIT
GOT_DOT: LODSB
CMP AL,'*'
JZ NEXT_DELIMIT
CK_SWITCH: CMP AL,'/' ; We will loop from here to >----+
JNZ NEXT_FILESPEC ; |
CALL SWITCH ; |
CMP AL,QUOTES ;If quotes without space, |
JNZ NEXT_FILESPEC ; |
JMP EXIT ; exit with error. |
NEXT_FILESPEC: LODSB ; |
CMP AL,32 ;If space character or below, |
JBE FILESPEC ; we have the filespec. |
CMP AL,'\' ;Check for path. |
JNZ CK_SWITCH ; |
MOV PATH_FLAG,1 ; |
MOV PATH_END,SI ;If path found, update variables. |
DEC PATH_END ; |
JMP SHORT CK_SWITCH ; here to find the filespec. <----+
;------------------------------------------------------------;
; Now that we have the filespec, will check if it is a path. ;
;------------------------------------------------------------;
FILESPEC: MOV BL,AL ;Save current delimiter.
MOV BYTE PTR [SI-1],0 ;Format the end for DOS with a 0.
MOV BYTE PTR DTA+21,0 ;Initiate file attribute to 0.
MOV DX,PARA_START
MOV CX,10H
CALL FIND_FIRST ;Get file's attribute via DOS.
FOUND_FILE: CMP BYTE PTR DTA+21,10H
JNZ NO_PATH ;If it is a path, update variables
MOV PATH_FLAG,1
MOV PATH_END,SI
DEC PATH_END
JMP SHORT CK_DELIMITER
NO_PATH: MOV DX,PATH_END ; otherwise, it is a filename;
INC DX ; update path end; change search
MOV SEARCH_SPEC,DX ; spec from global to filename.
CK_DELIMITER: CMP BL,13 ;Check if delimiter is carriage
JZ NO_STRING ; return. If yes, no string.
;------------------------------------------------------------------;
; Done with filespec. Now continuing by parsing string parameter. ;
;------------------------------------------------------------------;
NEXT_DELIMIT: LODSB
CMP AL,32 ;Scan off delimiting spaces.
JZ NEXT_DELIMIT
CMP AL,'/' ;Check if switch.
JNZ NO_SWITCH
CALL SWITCH
JMP SHORT NEXT_DELIMIT
NO_SWITCH: CMP AL,13 ;If carriage return, no string.
JZ NO_STRING
CMP AL,QUOTES ;If quotes, start of string.
JNZ NEXT_DELIMIT
STRING: MOV STRING_FLAG,1 ;Flag that string exists.
MOV STRING_START,SI ;Point to start of string.
XOR BH,BH
MOV BL,BYTE PTR DS:[80H]
ADD BX,81H ;Point to end of parameters and
NEXT_QUOTE: DEC BX ;work backwards until find
CMP BYTE PTR DS:[BX],QUOTES ;first quotes.
JZ END_STRING
CMP BX,SI ;If only one quote,
JZ EXIT ; exit with error.
JMP SHORT NEXT_QUOTE
END_STRING: MOV BYTE PTR DS:[BX],0 ;Got end of string; mark with 0.
PUSH BX ;Save position.
SUB BX,SI ;Subtract to get string length.
MOV STRING_CT,BX
POP SI ;Restore position.
LAST_PARA: LODSB ;We have the string. Now all
CMP AL,'/' ; have left to do is check for
JNZ LAST_SWITCH ; switches.
CALL SWITCH
LAST_SWITCH: CMP AL,13
JNZ LAST_PARA
;-----------------------------------------------------;
; If no case sensitive switch, capitalize the string. ;
;-----------------------------------------------------;
CMP CASE_FLAG,1
JZ NO_STRING
MOV CX,STRING_CT
MOV BX,STRING_START
CALL CAPITALIZE
;----------------------------------------------------------;
; If we got this far, the parameters were found valid. ;
; If a string was found, display it. If path was found, ;
; change directory and search, otherwise do global search. ;
;----------------------------------------------------------;
NO_STRING: MOV GOOD_PARA,1 ;Flag as good parameters.
CMP STRING_FLAG,1 ;If string exists, display it.
JNZ STRINGLESS
MOV SI,OFFSET SEARCH
CALL WRITE_STRING
MOV SI,STRING_START
CALL WRITE_STRING
STRINGLESS: CMP PATH_FLAG,1
JNZ GLOBAL_SEARCH ;If path does not exist, do global
MOV BX,PATH_END ; search, otherwise, change
MOV BYTE PTR [BX],0 ; directory and display it.
MOV DX,PARA_START
CALL CHANGE_DIR
CALL PRINT_DIR
MOV DX,SEARCH_SPEC ;Find first matching filename.
MOV CX,ALL_FILES
CALL FIND_FIRST
JC EXIT ;If no match, exit.
CALL READ_FILE ;If string, read the file.
NEXT_PATH: CALL FIND_NEXT ;Loop here finding subsequent
JC EXIT ; matching files until no more.
CALL READ_FILE
JMP SHORT NEXT_PATH
;-------------------------------------------------------;
; This is the exit. The drive and directory is restored ;
; to how it was found. If parameters are invalid, error ;
; message is displayed, otherwise, just return to DOS. ;
;-------------------------------------------------------;
EXIT: MOV DL,CURRENT_DISK ;Change drive.
MOV AH,0EH
INT 21H
MOV DX,OFFSET ROOT ;Assume we came from root.
CMP BYTE PTR CURRENT_DIR,0 ;If root, it will be a zero.
JZ CHANGE_IT
MOV BYTE PTR CURRENT_DIR,'\' ;Else, format with "\"
MOV DX,OFFSET CURRENT_DIR
CHANGE_IT: CALL CHANGE_DIR ;Change directory.
MOV DL,13 ;Clear printer buffer
CALL DISPLAY ; carriage return and linefeed.
MOV DL,10
CALL DISPLAY
CMP GOOD_PARA,1
JZ DONE
MOV DX,OFFSET BAD_PARA
MOV AH,9H
INT 21H ;Display error message if invalid.
DONE: INT 20H ;Terminate.
;---------------------------------------------------------------;
; This routine will systematically change directories up and ;
; down the directory tree, searching for the matching filename. ;
;---------------------------------------------------------------;
GLOBAL_SEARCH: MOV DX,OFFSET ROOT ;Start the search from root.
CALL CHANGE_DIR
MOV BP,OFFSET SUBSCRIPTS ;Point to first level directory.
FIRST_FILE: CALL PRINT_DIR ;Print the directory.
MOV DX,SEARCH_SPEC
MOV CX,ALL_FILES ;Find first matching filename
CALL FIND_FIRST ; in this directory.
JC FIRST_DIR
CALL READ_FILE
NEXT_FILE: CALL FIND_NEXT ;Find next matching filename.
JC FIRST_DIR
CALL READ_FILE ;If string, read the file.
JMP SHORT NEXT_FILE ;Loop here until no more matches.
PARENT_DIR: CMP BP,OFFSET SUBSCRIPTS ;When we try to return to parent
JZ EXIT ; directory and are in root, we
MOV DX,OFFSET PARENT ; are done. Otherwise, change
CALL CHANGE_DIR ; to parent directory.
MOV BYTE PTR DS:[BP],1 ;Put one back in previous level
DEC BP ; and point to parent level.
FIRST_DIR: XOR BL,BL ;Use BL as pointer to directory no.
MOV DX,OFFSET GLOBAL
MOV CX,DIR_ATTRIBUTE ;Ask for global match.
CALL FIND_FIRST ;Find first matching.
JC PARENT_DIR
CK_DIR: CMP BYTE PTR DTA+21,10H ;If not a directory get next.
JNZ NEXT_DIR
CMP BYTE PTR DTA+30,'.' ;If it is a dot directory get next.
JZ NEXT_DIR
INC BL ;Increment position in directory.
CMP BL,DS:[BP] ;Continue until new directory.
JNZ NEXT_DIR
INC BYTE PTR DS:[BP] ;Update variables.
MOV DX,OFFSET DTA+30
CALL CHANGE_DIR ;Change the directory.
INC BP
JMP SHORT FIRST_FILE ;Get all files in new directory.
NEXT_DIR: CALL FIND_NEXT ;Get all subdirectories in current
JC PARENT_DIR ; directory then go to parent.
JMP SHORT CK_DIR
;*************;
; SUBROUTINES ;
;*************;
CHANGE_DIR: MOV AH,3BH
INT 21H
RET
CHANGE_DTA: MOV AH,1AH
INT 21H
RET
FIND_FIRST: MOV AH,4EH
INT 21H
RET
FIND_NEXT: MOV AH,4FH
INT 21H
RET
;---------------------------------------------------;
; This subroutine checks for delimiting characters. ;
;---------------------------------------------------;
PARA_END: CMP BYTE PTR [SI],32
JBE SET_CARRY
CMP BYTE PTR [SI],'/'
JZ SET_CARRY
CLC
RET
SET_CARRY: STC
RET
;---------------------------------------------------;
; This subroutine checks for the switch characters. ;
;---------------------------------------------------;
SWITCH: MOV BYTE PTR [SI-1],0 ;Zero in place of /.
MOV BL,DS:[SI] ;Retrieve the character.
AND BL,5FH ;Capitalize.
CMP BL,'P'
JNZ CK_CASE
MOV PRINT_FLAG,1
CK_CASE: CMP BL,'C'
JNZ CK_BINARY
MOV CASE_FLAG,1
CK_BINARY: CMP BL,'B'
JNZ END_SWITCH
MOV BINARY_FLAG,1
END_SWITCH: RET
;---------------------------------------------------------;
; This subroutine prints the current drive and directory. ;
;---------------------------------------------------------;
PRINT_DIR: MOV SI,OFFSET DIRECTORY ;Print "Directory "
CALL WRITE_STRING
MOV AH,19H ;Get current drive and display.
INT 21H
ADD AL,'A'
MOV DL,AL
CALL DISPLAY
MOV DL,':' ;Add the colon.
CALL DISPLAY
MOV DL,'\' ;And root.
CALL DISPLAY
MOV SI,OFFSET WORKING_DIR
MOV DL,0
MOV AH,47H ;Get the current directory
INT 21H ; and display.
CMP BYTE PTR WORKING_DIR,0
JZ END_DIR
CALL WRITE_STRING
END_DIR: RET
;-----------------------------------------------------------;
; This is the major subroutine. If a string is to be found, ;
; the file is opened, read and compared with the string. ;
;-----------------------------------------------------------;
READ_FILE: PUSH BP ;Save the BP; caller is using it.
CMP STRING_FLAG,1 ;If no string, simply display file.
JZ BINARY_READ
JMP PRINT_FILE
BINARY_READ: CMP BINARY_FLAG,1 ;If /B was found open the file
JZ OPEN_FILE ; otherwise, skip EXE and COM files
CALL EXE_COM
JNC OPEN_FILE
JMP RETURN
OPEN_FILE: MOV EOF_FLAG,0 ;Reset EOF and MATCH flags.
MOV MATCH_FLAG,0
MOV DX,OFFSET DTA+30
MOV AX,3D00H ;Open the file for reading.
INT 21H
MOV BX,AX
GET_FILE: PUSH BX ;Save the file handle for closing.
MOV DX,OFFSET FILE
MOV CX,63488 ;Attempt to read 63488 bytes.
MOV AH,3FH
INT 21H
CMP AX,63488 ;If less than 63488, we got
JZ FILE_END ; all of the file; flag so.
MOV EOF_FLAG,1
FILE_END: CMP AX,0 ;If no bytes to read, close file.
JZ CLOSE_FILE
WORDSTAR: MOV CX,AX
MOV BX,OFFSET FILE
STRIP: AND BYTE PTR DS:[BX],7FH ;Strip the high bit for WORDSTAR.
INC BX
LOOP STRIP
CMP CASE_FLAG,1 ;If not case sensitive, capitalize.
JZ SENSITIVE
MOV CX,AX
MOV BX,OFFSET FILE
CALL CAPITALIZE
SENSITIVE: CMP AX,STRING_CT ;If string count is larger than
JB CLOSE_FILE ; file, we are done here.
MOV BP,AX ;BP will count bytes of file.
MOV BX,OFFSET FILE ;BX will track position in file.
MOV DX,STRING_START ;Store string start in DX
SUB BP,STRING_CT
INC BP
NEXT_BYTE: MOV SI,BX ;Recover file position.
MOV DI,DX ;Recover string start.
MOV CX,STRING_CT ;Get string length.
MOV AH,CL ;Track delimiters.
LOAD_BYTE: LODSB ;Get a byte.
CMP AL,32 ;If it is below space, skip.
JAE COMPARE
DEC AH ;Track skips so not endless loop.
JNZ LOAD_BYTE
JMP SHORT NEXT_LOAD
COMPARE: DEC AH ;Compare with string.
SCASB
JNZ NEXT_LOAD ;If no match, move up in file.
NEXT_COMPARE: LOOP LOAD_BYTE
MOV MATCH_FLAG,1 ;If all bytes match, flag.
MOV LINE_NUM,SI ;Save position in file.
JMP SHORT CLOSE_FILE ;And close file.
NEXT_LOAD: INC BX ;Otherwise, point to next byte
DEC BP ;in file and decrement byte counter
JNZ NEXT_BYTE ;and get next byte.
EOF: CMP EOF_FLAG,1 ;If we did not get all the file,
JZ CLOSE_FILE ;return for more, otherwise close.
MOV CX,0FFFFH ;Bump the file pointer back the
MOV DX,STRING_CT ;length of the file so we will
NEG DX ;not miss match at the break.
POP BX
MOV AX,4201H ;Move pointer via DOS.
INT 21H
JMP GET_FILE ;And get more bytes.
CLOSE_FILE: POP BX ;Recover file handle.
MOV AH,3EH ;And close.
INT 21H
CMP MATCH_FLAG,1 ;if no match, return.
JNZ RETURN
PRINT_FILE: MOV SI,OFFSET SPACING ;Indent then display the file.
CALL WRITE_STRING
MOV SI,OFFSET DTA+30
CALL WRITE_STRING
CMP STRING_FLAG,1
JNZ RETURN
CALL PRINT_LINE ;Print line number match was found.
RETURN: POP BP ;Restore BP
RET ;And return.
;------------------------------------------;
; These two subroutines do the displaying. ;
;------------------------------------------;
DISPLAY: CMP PRINT_FLAG,1 ;If /P, echo to the printer.
JNZ NOT_PRINTER
MOV AH,5
INT 21H
NOT_PRINTER: MOV AH,2
INT 21H
RET
WRITE_IT: MOV DL,AL
CALL DISPLAY
WRITE_STRING: LODSB
CMP AL,0 ;Zero marks end of string.
JNZ WRITE_IT
RET
;-------------------------------------------;
; This subroutine will capitalize the file. ;
;-------------------------------------------;
CAPITALIZE: CMP BYTE PTR DS:[BX],'a'
JB NEXT_CAP
AND BYTE PTR DS:[BX],5FH
NEXT_CAP: INC BX
LOOP CAPITALIZE
RET
;------------------------------------------------------------;
; This subroutine will check to see it file is .EXE or .COM. ;
;------------------------------------------------------------;
EXE_COM: MOV SI,OFFSET DTA+30 ;Point to filename.
EXTENSION: LODSB ; and search for dot.
CMP AL,0 ;If zero, end of filename.
JZ NOT_BINARY
CMP AL,'.'
JNZ EXTENSION
MOV AX,SI ;Save extension pointer.
MOV DI,OFFSET EXE ;Compare first with EXE
MOV CX,3
REP CMPSB
JZ BINARY
MOV SI,AX
MOV DI,OFFSET COM ;Then compare with COM
MOV CX,3
REP CMPSB
JZ BINARY
NOT_BINARY: CLC ;Return with no carry is no match.
RET
BINARY: STC ;Return with carry is match.
RET
;----------------------------------------------------------------------------;
; This subroutine will calculate and display the line number for each match. ;
;----------------------------------------------------------------------------;
PRINT_LINE: XOR BH,BH ;Get the cursor position to see
MOV AH,3 ; if need to add a tab character
INT 10H ; to format the display.
CMP DL,7 ;If column 8 or less, add tab.
JA NO_TAB
MOV DL,9
CALL DISPLAY
NO_TAB: MOV SI,OFFSET LINE ;Display "Line number "
CALL WRITE_STRING
MOV CX,LINE_NUM ;Retrieve offset into file to
SUB CX,OFFSET FILE ; calculate the number of bytes
MOV SI,OFFSET FILE ; to look for carriage returns.
MOV BX,1 ;Start at line one.
CK_LINE: LODSB
CMP AL,13 ;Count the carriage returns.
JNZ NEXT_LINE
INC BX
NEXT_LINE: LOOP CK_LINE
MOV DISPLAY_FLAG,0 ;Reset the display flag.
TENTHS: MOV CX,10000 ;Get ten thousands by dividing.
CALL DIVIDE
MOV CX,1000 ;Get thousands by dividing.
CALL DIVIDE
MOV CX,100 ;Get hundreds by dividing.
CALL DIVIDE
MOV CX,10 ;Get tens by dividing.
CALL DIVIDE
MOV CX,1 ;Get ones by dividing.
CALL DIVIDE
RET
DIVIDE: MOV AX,BX ;Number in AX
XOR DX,DX ; and zero in DX
DIV CX ; divide by CX
MOV BX,DX ; remainder into BX
MOV DL,AL ; and quotient into DL.
CMP AL,0 ;Is it zero?
JZ FLAG ;If yes, is a non zero displayed?
OR DISPLAY_FLAG,AL ;If non zero indicate by flag.
FLAG: CMP DISPLAY_FLAG,0 ;Has there been a display?
JZ END_LINE ;If no, return.
DISP_NUMBER: ADD DL,30H ;Convert hexadecimal to decimal
CALL DISPLAY ;And display.
END_LINE: RET
SUBSCRIPTS:
CURRENT_DIR EQU SUBSCRIPTS+100
WORKING_DIR EQU CURRENT_DIR+64
DTA EQU WORKING_DIR+64
FILE EQU DTA+65
CODE ENDS
END START