MS-DOS/v2.0/source/FIND.ASM

932 lines
38 KiB
NASM
Raw Normal View History

1983-08-13 01:53:34 +01:00
title MSDOS V2.0 FIND
;--------------------------------------------------------------------;
; Revision History: ;
; ;
; V1.1 8/23/82 M.A.Ulloa ;
; ;
; V1.2 9/22/82 M.A.Ulloa ;
; Added the -c and -n options ;
; ;
; 9/23/82 M.A.Ulloa ;
; Added DOS version number control ;
; ;
; 10/07/82 Rev.2 M.A.Ulloa ;
; Changed quote for double quotes, and added ;
; file name printing ;
; ;
; 10/20/82 Rev.3 M.A.Ulloa ;
; Modified IBM name to FIND, and changed the text ;
; of some messages. ;
; ;
; 10/25/82 Rev.4 M.A.Ulloa ;
; Changed name to FIND and all messages to the ;
; IBM form. ;
; ;
; 10/27/82 Rev.5 M.A.Ulloa ;
; Made the correct exit on version check in case ;
; of a 1.x DOS. ;
; ;
; 11/4/82 Rev. 5 A.R. Reynolds ;
; Messages moved to external module ;
; ;
; 11/10/82 Rev. 6 M.A. Ulloa ;
; Corrected problem with line numbers, and a problem ;
; with seeking for 0 chars. ;
; ;
; 03/30/83 Rev. 7 M.A. Ulloa ;
; Added patch area for bug fixing. ;
; ;
; 04/14/83 Rev. 8 M.A. Ulloa ;
; Made changes for Kanji characters. (uhg!) ;
; ;
;--------------------------------------------------------------------;
FALSE equ 0
TRUE equ NOT FALSE
KANJI equ FALSE ;set to true is kanji vers.
;--------------------------------------------------------------------;
; FIND program following the standart UNIX operation. ;
; ;
; FORMAT: ;
; find {option} string {filename {filename} {...}} ;
; ;
; NOTES: ;
; 1) String arguments HAVE to be enclosed ;
; in double quotes. (Two double quotes if a ;
; doble quote is to be included). Only ONE ;
; string argument is presently allowed. ;
; ;
; 2) Options are available: ;
; v All lines but those matching are considered ;
; c Only print a count of matching lines ;
; n Each line is preceded by its relative ;
; line number in the file. ;
; ;
; - Options can be Upper or lower case. ;
; - Format: The switch character followed by an options ;
; character. I.e.: In the IBM PC: /v ;
; ;
; 3) The program returns: ;
; 0 - OK, and some matches ;
; 2 - Some Error ;
; ;
; 4) The maximum line size is determined by ;
; buffer size. Bigger lines will bomb the program. ;
; ;
; 5) If no file name is given then it will asssume ;
; the input is comming from the Standart Input. NO ;
; errors are reported when reading from Standart Input. ;
;--------------------------------------------------------------------;
code segment public
assume cs:code,ss:code,ds:nothing,es:nothing
CR equ 0dh ;A Carriage Return
LF equ 0ah ;A Line Feed
quote_char equ 22h ;A double quote character
buffer_size equ 4096 ;file buffer size
st_buf_size equ 128 ;string arg. buffer size
fname_buf_size equ 64 ;file name buffer size
;----- DOS EQUATES --------------------------------------------------;
std_in equ 0 ;STD input handle
std_out equ 1 ;STD output handle
std_err equ 2 ;STD error handle
dos_ent equ 21h ;DOS entry point
std_con_string_output equ 9
get_version equ 48
char_oper equ 55 ;get configuration parameters
open equ 61 ;DOS std open code
close equ 62 ;DOS std close code
read equ 63 ;DOS std read code
write equ 64 ;DOS std write code
lseek equ 66 ;DOS file seek
exit equ 76 ;DOS process exit code
;----- Misc Data -----------------------------------------------;
make db "***MAUlloa/Microsoft/V12***"
rev db "8"
colon db ": "
n1_buf db "["
n2_buf db 8 dup(0) ;buffer for number conversion
;----- OPTION FLAGS -------------------------------------------------;
; If a flag is set (0ffh) then the option has been selected, if
;reset (0) then it has been not. All options are reset initially.
; NOTE: the order of this table has to remain consistent with the
;options dispatch code. If any changes are made they have to
;correspond with the code.
opt_tbl:
v_flg db 0
c_flg db 0
n_flg db 0
x_flg db 0 ;not used
l_flg db 0 ;not used
;----- LINE COUNTERS ------------------------------------------------;
mtch_cntr dw 0 ;matched lines counter
line_cntr dw 0 ;line counter
;----- MAIN ROUTINE -------------------------------------------------;
start:
;----- CHECK VERSION NUMBER -----------------------------------------;
mov ah,get_version
int 21h
cmp al,2
jge vers_ok
push cs
pop ds
mov dx,offset bad_vers
mov ah,std_con_string_output
int 21h
push es ;bad vers, exit a la 1.x
xor ax,ax
push ax
badfart proc far ;(what a hack!!)
ret
badfart endp
vers_ok:
push cs ;load ES to the right area,
pop es ; for use with DI register
assume es:code
;--------------------------------------------------------------------;
mov si,81h ;Start addrss. of commad line buf.
call kill_bl ;Get rid of blanks
or bx,bx ;A CR found?
jz find_opt ;no, first find the options
args_missing:
mov dx,offset errmsg1 ;empty command line, no args: error.
mov cl,cs:errlen1
call prt_err
mov al,2 ;error code for exit
jmp done
;----- FIND THE OPTION IF ANY ---------------------------------------;
find_opt:
mov ah,char_oper ;get the dos switch char.
mov al,0
int dos_ent ;switch char in DL
push dx
another_opt:
lodsb ;get the first char of command line
cmp al,' ' ;a blank?
je cont_scan
cmp al,CR ;a Carriage Return
je args_missing
pop dx ;get switch character
cmp al,dl ;is it the switch char?
jne find_str ;no, no options: get the string
push dx ;save for another round
lodsb ;get the option character
cmp al,' ' ;a blank?
je cont_scan ;yes, ignore and continue
cmp al,CR ;a CR?
je args_missing ;yes, error...
call make_caps ;Capitalize the character
mov bx,offset opt_tbl ;pointer to option flag table
cmp al,'V' ;the v option?
je opt_v
cmp al,'C' ;the c option?
je opt_c
cmp al,'N' ;the n option?
je opt_n
mov cs:errmsg5_opt,al ;save the option
mov dx,offset errmsg5 ;unknown option: error
mov cl,cs:errlen5
call prt_err
mov dx,offset crlf ;print a CRLF
mov cx,2
call prt_err
jmp another_opt ;process next option
opt_v:
mov di,0
jmp short opt_dispatch
opt_c:
mov di,1
jmp short opt_dispatch
opt_n:
mov di,2
opt_dispatch:
mov es:byte ptr[bx+di],0ffh ;set the corresponding flag
jmp another_opt ;process the rest of the options
cont_scan:
dec si ;adjust SI
call kill_bl ;get rid of blanks
or bx,bx ;A CR found?
jz another_opt ;no, test for other options
jmp args_missing ;yes, error...
;----- FIND STRING ARGUMENT -----------------------------------------;
find_str:
cmp al,quote_char ;string should start with a
jnz bad_str_err ; quote character, if not: error.
mov di,offset st_buffer ;String argument buffer addrss.
xor cx,cx ;Clear to keep string length.
move_str:
lodsb
cmp al,CR ;if a CR is found in the string
jnz str_ok ; then it's a bad string
bad_str_err:
mov dx,offset errmsg2 ;bad string error message
mov cl,cs:errlen2
call prt_err ;print the error.
mov al,2
jmp done
str_ok:
cmp al,quote_char ;look for a quote character
jnz move_char ;not an apost., move to buffer
lodsb ;an apost., check next char.
cmp al,quote_char ;another quote character?
je move_char ;yes, move it to the buffer
dec si ;no, adjust the pointer
mov es:st_length,cx ;store the string length
or cx,cx ;Is the string empty?
jnz other_args ;no: get the rest of the args.
mov al,1 ;empty: no matches(!?)
jmp done
move_char:
stosb ;put in buffer
inc cx ;increment string length
jmp move_str
;----- FIND THE FILE ARGUMENTS --------------------------------------;
other_args: ;Process the rest of the command
; line arguments.
call kill_bl ;get rid of leading blanks
or bx,bx ;At least one argument necessary,
jz further_args ; if a CR not found: ok.
;----- USE STD IN FOR INPUT -----------------------------------------;
push cs
pop ds
mov ax,std_in ;handle
jmp fill
further_args:
call clr_cntrs ;set all counters to zero
mov di,offset file_name_buf ;Set pointer to the name buffer
xor cx,cx ;zero file name length
move_fname:
lodsb
cmp al,' ' ;A blank: end of file name,
je done_move
cmp al,CR ;A CR: idem.
je done_move
stosb ;store in name buffer
inc cx ;increment file name length
jmp move_fname
done_move:
dec si ;Adjust pointer for next round.
mov es:byte ptr[di],00h ;File names are null terminated
push si ;Save SI to continue com. line scan.
push ds ;Save DS register contents for
; later because it points to the
; rest of the arguments.
mov es:file_name_len,cx ;save the name length
;----- OPEN FILE FOR READING ----------------------------------------;
push cs ;Load new DS with CS
pop ds
mov dx,offset file_name_buf ;addrss. of the file name
mov ah,open
mov al,0 ;file open for reading
int dos_ent ;call the DOS
jnc say_name ;if no carry then no errors
jmp open_error
;----- PRINT FILE NAME ----------------------------------------------;
say_name:
push ax ;save file handle
mov dx,offset heading
mov cl,cs:heading_len
xor ch,ch
call prout
mov dx,offset file_name_buf
mov cx,ds:file_name_len
call prout
cmp ds:c_flg,0ffh ;count only flag set?
je xx1
mov dx,offset crlf
mov cx,2
call prout
xx1:
pop ax
;----- Fill Buffer for Matching -------------------------------------;
fill:
mov bx,ax ;retrieve handle
refill:
mov dx,offset buffer ;data buffer addrss.
mov cx,buffer_size
mov ah,read
int dos_ent
jnc no_read_error ;if carry then read error
jmp read_error
no_read_error:
or ax,ax ;if ax=0 then all done
jnz go_match
cmp ds:c_flg,0ffh ;count only flag set?
jne sj2
call print_count
sj2:
cmp bx,std_in ;Using STD IN?
jnz regular
jmp foo ;if so: all done, exit
regular:
mov ah,close ;otherwise close the file
int dos_ent
jmp scan_rest ;get another file
;----- MATCH ROUTINE ------------------------------------------------;
;Note: If input is being taken from a file the stack contains
; (from top to bottom):
; - Pointer to the next command in the command line
; - Pointer to the program segment prefix (to be loaded into
; DS to access the command line.
; if the imput is from the standart input then NONE of it will be
; in the stack.
go_match:
push bx ;save the file handle
mov bp,offset buffer ;ptr to first line of file
mov di,ax ;dispalcement from beg of buffer
cmp ax,buffer_size-1 ;last line of the file?
jg no_last_line ;if yes, add a CRLF just in case
mov bx,bp
cmp byte ptr[bx+di-1],LF ;finished with a LF?
je no_last_line ;yes, it's an OK line.
mov byte ptr[bx+di],CR ;put a CR at the end of the data
inc di
mov byte ptr[bx+di],LF ;put a LF ...
inc di
no_last_line:
push di ;save the # of chars. in the buffer
push bp
mov dx,ds:st_length ;length of the string arg.
dec dx ;adjust for later use
jmp short try_again
more_stuff_o:
jmp more_stuff
;----- SCAN LINES IN THE BUFFER FOR A MATCH -------------------------;
;Note: at this point the stack contains (from top to bottom):
; - Stuff mentioned before
; - File Handle
; - Number of chars. left in the buffer from the next line.
; - Addrs. of the next line in the buffer.
;
; plus, DX has the adjusted length of the string argument.
try_again:
inc ds:line_cntr ;increment line counter
pop bp ;addrs. of next line in the buffer
mov di,bp ;points to beg. of a line
pop cx ;get # of chars left in the buffer
mov bx,cx ;save in case a non-complete line
mov al,LF ;search for a Line Feed
jcxz more_stuff_o ;no chars left in buffer
repnz scasb
jnz more_stuff_o ;no full line left in buffer
push cx ;save chars left in buffer
push di ;points to beg. of next line
mov cx,di
sub cx,bp ;length of the current line
mov bx,cx ;save in case it has a match
dec cx
dec cx ;CRLF characters discounted
jcxz try_again_opt ;if line empty go to next line
mov di,bp ;pointer to the beg. of current line
another_char:
;
; On entry:
; BX line length
; CX adjusted line length
; DX adjusted string argument length
; DI points to beg. of line
;
IF KANJI
push dx ;save for next line
lop:
pop dx
push dx
inc dx ;different algorithm!
mov si,offset st_buffer ;pointer to beg. of string argument
comp_next_char:
push di
mov di,si
call is_prefix ;check for a prefix char
pop di
jnc nopre
lodsw
cmp cx,1 ; Can not compare a two byte char
jz try_again_opt1 ; if there is only one available
cmp ax,word ptr [di]
jz kmatch1
call next_kchar ;no match, advance di to next kanji
jc try_again_opt1 ;not enough chars left in line
jmp short lop ;try another char in line
nopre:
lodsb
cmp al,byte ptr [di]
jz kmatch
call next_kchar ;no match, advance di to next kanji
jc try_again_opt1 ;not enough chars left in line
jmp short lop ;try another char in line
try_again_opt1:
pop dx
jmp try_again_opt
kmatch1:
dec dx ;last char had prefix so it was
; long.
kmatch:
dec dx
jz a_matchk ; no chars left: a match!
call next_kchar
jc try_again_opt1
jmp comp_next_char ; loop if chars left in arg.
a_matchk:
pop dx
ELSE
mov si,offset st_buffer ;pointer to beg. of string argument
lodsb ;get first character of the str. arg.
repnz scasb ;search for a match in current line
jnz try_again_opt ;no match, try the next line
cmp cx,dx ;compare lengths, a full match is not
jb try_again_opt ; possible if CX < DX.
push di ;save addrs. of next char. in the line
push cx ;save the # of chars. left in the line
mov cx,dx ;get the adjusted string arg. length
jcxz a_match ;if a single char string, then match!
repz cmpsb ;compare string with line
jz a_match ;a match found, hurrah!
pop cx ;no match, get # of chars remaining
; in the line.
pop di ;position of the next char. in the line
jmp another_char
;----- A MATCH: CHECK FOR THE v OPTION ------------------------------;
a_match:
pop ax ;adjust stack
pop ax
ENDIF
cmp ds:v_flg,0ffh ;is flag set?
jne prt_line ;no, print the line
jmp try_again
;----- NO MATCH: CHECK FOR THE v OPTION -----------------------------;
try_again_opt:
cmp ds:v_flg,0ffh ;is flag set?
jne try_again ;no goto next line
;----- PRINT THE LINE WITH THE MATCH --------------------------------;
;Note: at this point the stack contains (top to bottom)
; - Stuff mentioned before
;
; plus, BP points to begginig of the current line, BX has the length
;of the current line including the CRLF, and DX the adjusted length of
;the string argument.
prt_line:
cmp ds:c_flg,0ffh ;is count only flag set?
jne no_c_flg
inc ds:mtch_cntr ;yes, increment counter
jmp try_again
no_c_flg:
push dx ;save the adjusted string arg. length
cmp ds:n_flg,0ffh ;is line number flag set?
jne no_n_flg
call prt_lcntr
no_n_flg:
mov dx,bp
mov cx,bx
call prout
pop dx ;restore
jmp try_again
;----- READ MORE TEXT LINES INTO THE BUFFER -------------------------;
; The scanning routines have detected that the buffer does not
;contain a full line any more. More lines have to be read into the
;buffer. But first perform a seek on the file in order to re-read
;the non-complete line into the begining of the buffer.
; Uppon entry BP contains points to the begining of the non-complete
;line, and BX has the number of characters left in the buffer.
; The Stack contains (top to bottom):
; - Pointer to the next command in the command line
; - Pointer to the program segment prefix (to be loaded into
; DS to access the command line).
; - File handle.
more_stuff:
mov dx,bx ;get chars left in buffer
pop bx ;get the handle
or dx,dx ;are there 0 left?
jz no_seek ;yes, do not seek
neg dx ;form two's complement
mov cx,-1
mov al,1 ;seek from the current position
mov ah,lseek ;seek on file
int dos_ent
jc read_error
no_seek:
jmp refill ;no errors: refill the buffer
read_error:
cmp bx,std_in ;Using STD IN?
je foo ;if so: all done, exit
mov ah,close ;close the file
int dos_ent
mov dx,offset errmsg4_pre ;read error
mov cl,cs:errlen4_pre
call prt_file_name ;print the file name in error
mov dx,offset errmsg4_post ;read error
mov cl,cs:errlen4_post
jmp r_error
;----- PRINT ERRORS -------------------------------------------------;
open_error:
mov dx,offset errmsg3_pre ;error in open operation
mov cl,cs:errlen3_pre
call prt_err_2 ;print error message
call prt_file_name ;print the file name in error
mov dx,offset errmsg3_post ;error in open operation
mov cl,cs:errlen3_post
r_error:
call prt_err_2 ;print error message
;----- SCAN THE REST OF THE COMMAND LINE ----------------------------;
scan_rest:
pop ds ;restore pointer to comm. line
pop si ;restore pointer to next comm.
call kill_bl ;look for further args.
or bx,bx ;test for a CR
jnz foo
jmp further_args
foo:
mov al,0 ;Proper code
done:
mov ah,exit ;All done, exit with proper code.
int dos_ent
;--------------------------------------------------------------------;
; Get rid of blanks in command line. ;
; Advances the SI reg till the next non-blank character, if the ;
; character is a CR (0dh) then returns with BX non-zero, otherwise ;
; BX is zero. ;
; ;
; entry: ;
; SI points to the first character on the line to scan. ;
; ;
; exit: ;
; SI points to the first non-blank character found. ;
; BX contains 0D hex if the first non-blank found is ;
; a Carriage Return, otherwise it is 0. ;
; ;
; modifies: ;
; BX, SI, and AX ;
; ;
;--------------------------------------------------------------------;
kill_bl:
cld ;increment
xor bx,bx ;zero bx to start: no CR found
no_bl:
lodsb ;get rid of blanks
cmp al,' '
je no_bl
cmp al,CR
jnz no_cr
mov bx,ax ;make bx non-zero (actually 0dh)
no_cr:
dec si ;adjust pointer
ret
;--------------------------------------------------------------------;
; Clear Counters ;
;--------------------------------------------------------------------;
clr_cntrs:
mov byte ptr es:mtch_cntr,0
mov byte ptr es:line_cntr,0
ret
;--------------------------------------------------------------------;
; Print Count of Matched lines ;
; ;
; Modifies: AX,CX,DX and DI ;
;--------------------------------------------------------------------;
print_count:
push bx ;save handle
cmp bx,std_in ;using std_in?
jz sj3 ;if so do not print file name
mov dx,offset colon
mov cx,2
call prout ;print colon
sj3:
mov ax,ds:mtch_cntr
mov di,offset n2_buf ;buffer for characters
call bin2asc ;convert to ascii
mov dx,offset n2_buf
call prout ;print the number
mov dx,offset crlf
mov cx,2
call prout ;print an end of line
pop bx
ret
;--------------------------------------------------------------------;
; Print relative line number ;
; ;
; Modifies: AX,CX and DI ;
;--------------------------------------------------------------------;
prt_lcntr:
push bx
push dx
mov ax,ds:line_cntr
mov di,offset n2_buf
call bin2asc
mov byte ptr[di],"]"
inc cx
inc cx
mov dx,offset n1_buf
call prout
pop dx
pop bx
ret
;--------------------------------------------------------------------;
; Print string to STD_OUT ;
;--------------------------------------------------------------------;
prout:
mov bx,std_out
mov ah,write
int dos_ent
ret
;--------------------------------------------------------------------;
; Binary to Ascii conversion routine ;
; ;
; Entry: ;
; AX Binary number ;
; DI Points to one past the last char in the ;
; result buffer. ;
; ;
; Exit: ;
; Result in the buffer MSD first ;
; CX Digit count ;
; ;
; Modifies: ;
; AX,BX,CX,DX and DI ;
; ;
;--------------------------------------------------------------------;
bin2asc:
mov bx,0ah
xor cx,cx
go_div:
inc cx
cmp ax,bx
jb div_done
xor dx,dx
div bx
add dl,'0' ;convert to ASCII
push dx
jmp short go_div
div_done:
add al,'0'
push ax
mov bx,cx
deposit:
pop ax
stosb
loop deposit
mov cx,bx
ret
;--------------------------------------------------------------------;
; Print the current file name ;
; ;
; modifies: ;
; DX, CX, BX and AX ;
;--------------------------------------------------------------------;
prt_file_name:
mov dx,offset file_name_buf ;print the file name
mov cx,ds:file_name_len ;retrive file name length
jmp short prt_err_2
;--------------------------------------------------------------------;
; Print an error message to the Standart error ;
; ;
; entry: ;
; DX has the pointer to the message ;
; CX has the length of the message ;
; ;
; modifies: ;
; BX and AX ;
;--------------------------------------------------------------------;
prt_err:
push ds ;Save the current DS
push cs ;Make DS point to the right
pop ds ; place, for DOS use.
call prt_err_2
pop ds
ret
prt_err_2:
xor ch,ch
mov bx,std_err
mov ah,write
int dos_ent ;write error message
ret
;--------------------------------------------------------------------;
; CAPIALIZES THE CHARACTER IN AL ;
; ;
; entry: ;
; AL has the character to Capitalize ;
; ;
; exit: ;
; AL has the capitalized character ;
; ;
; modifies: ;
; AL ;
;--------------------------------------------------------------------;
make_caps:
cmp al,'a'
jb no_cap
cmp al,'z'
jg no_cap
and al,0dfh
no_cap:
ret
IF KANJI
;--------------------------------------------------------------------;
; ADVANCE POINTER TO NEXT KANJI CHARACTER ;
; ;
; entry: DI points to a Kanji string ;
; CX length in bytes of the string ;
; ;
; exit: DI points to next Kanji char ;
; CX has number of bytes left ;
; ;
; modifies: AX ;
; ;
;--------------------------------------------------------------------;
next_kchar:
jcxz no_kleft
call is_prefix
jnc no_p
inc di
dec cx
jcxz no_kleft ; for insurance
no_p:
inc di
dec cx
clc
ret
no_kleft:
stc
ret
;--------------------------------------------------------------------;
; FIND OUT IS THE BYTE IS A KANJI PREFIX ;
; ;
; entry: DI points to a kanji string ;
; ;
; exit: Carry set if it is a kanji prefix ;
; ;
; modifies: AX ;
; ;
;--------------------------------------------------------------------;
is_prefix:
mov al,byte ptr [di]
cmp al,81h
jb nok
cmp al,0a0h
jb isk
cmp al,0e0h
jb nok
cmp al,0fdh
jb isk
nok:
clc
ret
isk:
stc
ret
ENDIF
;----- PATCH AREA ---------------------------------------------------;
patch_area dw 100h dup(?)
;----- BUFFER AREA --------------------------------------------------;
st_length dw 0 ;String argumnet length
st_buffer db st_buf_size dup(?) ;String argument buffer
file_name_len dw 0 ;File name length
file_name_buf db fname_buf_size+1 dup(?) ;File name buffer,(allow for
; null at the end).
buffer db buffer_size+1 dup(?) ;file buffer, the last byte is
;a guard in case of forced insertion
;of a CRLF pair.
;----- ERROR MESSAGES -----------------------------------------------;
EXTRN bad_vers:byte,crlf:byte,errmsg1:byte,errlen1:byte,errmsg2:byte
EXTRN errmsg3_pre:byte,errlen3_pre:byte
EXTRN errmsg3_post:byte,errlen3_post:byte
EXTRN errmsg4_pre:byte,errlen4_pre:byte
EXTRN errmsg4_post:byte,errlen4_post:byte
EXTRN heading:byte,heading_len:byte,errlen2:byte
EXTRN errmsg5:byte,errmsg5_opt:byte,errlen5:byte
code ends
;----- STACK AREA ---------------------------------------------------;
stack segment stack
dw 64 dup(?,?)
stack_top equ $
stack ends
end start