不知你編過全盤搜尋程式沒有,本程式用在消毒程式以及其他需要主動搜尋磁盤上所有檔案的地方,由于以前我自己的注釋都是英文的,是以這次簡單加上了一些中文。
本程式要用到的 DOS 中斷很少,僅為 4EH 和 4FH,功能是查找第一個比對檔案名和繼續查找,程式設計難點是要用到類似于堆棧的資料結構,把找到的子目錄名暫時存起來,等目前目錄找完後在取出來,然後繼續查找下一個目錄,我用的方法是開一個緩沖區,定義一個指針,用來指向緩沖區結束的地方。用的是先進先出方式,為了節省空間,每個目錄項長度不等長,由每項的前 2 個位元組來指向前面一條目錄項。具體見源程式。
本程式要用到的 INT 21H 的 4EH 和 4FH 功能如下:
功能号 | 入口參數 | 出口參數 |
AH = 4EH 查找第一個比對檔案項 | CX = 檔案屬性 | 标志 CF 複位 = 成功 找到檔案名在 DTA 内 預設 DTA 在PSP:0080H 處 |
DS:DX = 要查找的檔案名 ASC 字元串 | 标志 CF 置位 = 出錯 | |
AH = 4FH 查找下一個比對檔案項 | 标志 CF 複位 = 成功 找到檔案名在 DTA 内 預設 DTA 在 PSP:0080H 處 | |
标志 CF 置位 = 出錯 |
DTA 的内容如下:
偏移量 | 長度 | 含義 |
15H | 位元組 | 找到檔案的屬性 |
16H | 字 | 檔案時間 位11-15:小時 位5-10:分 位0-4:秒/2 |
18H | 字 | 檔案日期 位9-15:年-1980 位8-5:月 位0-4:日 |
1AH | 雙字 | 檔案大小 |
1EH | 13位元組 | ASC II 檔案名+擴充名 |
源程式:
;Copyright by LuoYunBin
;http://asm.yeah.net
.286p
CODE SEGMENT
ASSUME CS:CODE,DS:CODE
ORG 100H
START:
jmp install
install:
call get_com_line ;處理指令行
call scan_disk ;全盤查找子程式
mov ah,4ch
int 21h
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;
; search the hole diskette files
; programmed by LYB
; include following procedures
; scan_disk: scan disk and get all filename
; -----------------------------------------
; enter ds:si = scan drive or path name
; return FIND_FILE = found file name
; SD_ATTR = file attribute
; use DO_FILE process to file operation
; the data in sd_buffer is like this
; addr1: word -1, + path string
; addr2: word addr1, + path string
; addr3: word addr2, + path string
; ...
; PATH_SP point to last address
SCAN_DISK PROC NEAR
jmp scan_start
; data for do_file process
SD_ATTR DW ? ;要找的檔案屬性
FIND_FILE DB 128 DUP (0) ;找到的檔案名
; data use scan_disk itself
path_ep dw ? ;目錄名緩沖區指針
path_sp dw path_buffer ;path buffer stack point
finding_file db 8 dup (0)
finding_ext db 3 dup (0)
temp_file db 8 dup (0)
temp_ext db 3 dup (0)
scan_start:
push cs
pop ds
push cs
pop es ;set ds,es = cs
mov si,80h
call phase_name ;phase file name
mov si,path_sp
sd_lop:
inc si
inc si
mov di,offset find_file
cld
call move_byte
dec di
cmp byte ptr ds:[di-1],'\' ;path as N:\
jz sd1
mov al,'\'
stosb
sd1:
mov path_ep,di ;path end point
mov ax,'.*'
stosw ;add *.*
mov al,'*'
stosb
xor al,al
stosb
mov ah,4eh
mov dx,offset find_file ;找第一個檔案
mov cx,37h ;找所有檔案屬性包括子目錄
int 21h
jb sd_lop2_end
sd_lop2:
call pros_dir ;處理目錄項
mov ah,4fh ;找下一個檔案
int 21h
jnb sd_lop2
sd_lop2_end:
mov si,path_sp ;檢查目錄緩沖區
cmp si,offset path_buffer ;如果還有目錄要找,則繼續
jnz sd_new_dir
ret
sd_new_dir:
mov di,[si] ;從緩沖區中取出新目錄
mov path_sp,di
jmp sd_lop
PROS_DIR PROC
mov si,80h ;80H 為 DTA 位址
xor ah,ah
mov al,ds:[si+0015h]
mov sd_attr,ax ;找到的檔案屬性
add si,1eh
push si
mov di,path_ep ;把檔案名加到路徑上
call move_byte
pop si
test sd_attr,10h ;是否為子目錄
jnz pd_is_dir
call check_find
jnz pd_ret
call do_file ;對找到的檔案進行處理
;本程式的處理程式僅為列印檔案名,實際應用中,如果是用于消毒程式,可以
;在 DO_FILE 子程式中對檔案進行檢查
pd_ret:
ret
pd_is_dir:
cmp byte ptr ds:[si],'.' ;是 . 或 .. 則忽略
jz pd_ret
mov di,path_sp ;如果找到新目錄
push di ;則把它加入目錄名緩沖區
inc di
inc di
mov cx,-1
xor al,al
repnz scasb ;find string end
pop [di] ;fill new address
mov path_sp,di ;point to previous address
inc di
inc di ;add path string
mov si,offset find_file
call move_byte
ret
PROS_DIR ENDP
MOVE_BYTE PROC
lodsb
or al,al
jz mb_ret
stosb
jmp short move_byte
mb_ret:
stosb
ret
MOVE_BYTE ENDP
;=================================================
; check file name in DTA if match
; the searching file name
;本子程式的作用是,如果指令行指定查找特定檔案
;則比較是否找到,如指令行輸入找 *.EXE 那麼
;就是在這兒比較
;=================================================
CHECK_FIND PROC
mov di,offset temp_file
mov cx,11
xor al,al
cld
rep stosb
mov si,80h+1eh
mov di,offset temp_file
cf_lop1:
lodsb
or al,al
jz cf_lop2_end
cmp al,'.'
jz cf_lop1_end
stosb
jmp short cf_lop1
cf_lop1_end:
mov di,offset temp_ext
cf_lop2:
lodsb
or al,al
jz cf_lop2_end
stosb
jmp short cf_lop2
cf_lop2_end:
mov si,offset temp_file
mov di,offset finding_file
mov cx,11
cf_lop3:
lodsb
mov ah,[di]
inc di
cmp ah,'?'
jz cf_match ;char match
cmp ah,al
jz cf_match ;char match
cf_end:
ret
cf_match:
loop cf_lop3
ret
CHECK_FIND ENDP
; get .ext and name and path name from com_line
; input data in DS:SI
; output scan file name in ES:DI
; 對要找的檔案字元串進行預處理,分離路徑和檔案名
PHASE_NAME PROC
mov di,offset find_file
mov bp,di
xor ax,ax
mov cx,128
cld
rep stosb
mov di,bp
dec bp ;bp point to find_file-1
pn_lop:
lodsb
stosb
or al,al
jz pn_lop_end
cmp al,'?'
jz pn_wild
cmp al,'*'
jz pn_wild
cmp al,'\' ;path end point
jz pn_para
cmp al,':'
jnz pn_lop
cmp byte ptr [si],'\'
jz pn_lop
mov al,'\'
stosb
pn_para:
mov bp,di ;save path end
dec bp
jmp short pn_lop
pn_wild:
mov ah,1 ;set wild char flag
jmp short pn_lop
pn_lop_end:
or ah,ah ;=1, then has wild char
jnz pn_spar
mov ah,4eh
mov cx,37h
mov dx,offset find_file ;check if is dir
int 21h
jb pn_spar ;sparater path and file name
test byte ptr ds:[80h+15h],10h
jz pn_spar ;if not dir
mov bp,di ;is dir
dec bp
mov ax,'.*'
stosw
mov al,'*'
stosb
xor al,al
stosb
pn_spar:
cmp bp,offset find_file-1
jz pn_spar1
mov byte ptr ds:[bp],0
mov si,offset find_file
mov di,offset path_buffer+2
call move_byte
pn_spar1:
inc bp
mov si,bp
mov di,offset finding_file
mov cx,8
pn_lop1:
lodsb ;get filename
cmp al,'.'
jz pn_lop2_1
or al,al
jz pn_lop3_end
cmp al,'*'
jz pn_lop1_store
stosb
loop pn_lop1
jmp short pn_lop2
pn_lop1_store:
mov al,'?'
rep stosb
pn_lop2:
lodsb
pn_lop2_1:
or al,al
jz pn_lop3_end
cmp al,'.'
jnz pn_lop2
mov di,offset finding_ext
mov cx,3
pn_lop3:
lodsb ;get ext name
or al,al
jz pn_lop3_end
cmp al,'*'
jz pn_lop3_store
stosb
loop pn_lop3
jmp short pn_lop3_end
pn_lop3_store:
mov al,'?'
rep stosb
pn_lop3_end:
mov al,'?'
cmp finding_file,0
jnz pn1
mov di,offset finding_file
mov cx,8
rep stosb
pn1:
cmp finding_ext,0
jnz pn2
mov di,offset finding_ext
mov cx,3
rep stosb
pn2:
ret
PHASE_NAME ENDP
SCAN_DISK ENDP
do_file proc
mov si,offset d_searching
call printf
ret
do_file endp
d_searching db 'Searching file: %c%79t',0dh,0
dw find_file
GET_COM_LINE PROC
; get command line file name
; to CL_FILE and return CY when no command line
; return NC when has command line
; the sub change lower letter to upper letter
; 指令行處理子程式
mov si,81h
mov di,80h
cld
cmd_lop:
lodsb
cmp al,0dh
jz cmd_lop_end
cmp al,' '
jbe cmd_lop ;如果是小寫字母
cmp al,'a' ;則轉換到大寫
jb stos_it ;因為大小寫字母 ASC 碼值剛好差 20H
cmp al,'z' ;是以把 ASC 碼減 20H 就換到大寫了
ja stos_it
sub al,20h ;convert to upper case
stos_it:
stosb
jmp short cmd_lop
cmd_lop_end:
xor al,al
stosb
ret
GET_COM_LINE ENDP
include printf.asm ;一個公用的顯示程式
FILE_END EQU THIS BYTE
path_buffer db 1000h dup (0) ;path buffer
CODE ENDS
END START