Metropoli BBS
VIEWER: whereis.asm MODE: TEXT (ASCII)
	name	whereis
	title	'WHEREIS -- find file'
	page	55,132
;
; WHEREIS -- find file in directory structure
; by John Scotia, first published in Softalk/IBM
; modified by Ray Duncan
;
lf	equ	0ah			;ASCII line feed
cr	equ	0dh			;ASCII carriage return
ff	equ	0ch			;ASCII form feed
tab	equ	09h
eom	equ	'$'			;end of message flag
;
					;Program Segment Prefix
default_fcb equ	05ch
command	equ	80h	 
default_dta equ	080h
;
;

; This is the format for the DOS Data Transfer Area used when DOS 2.0	
; searches for a file match in directories.				

DTA	STRUC
	RESERVED	DB	21 DUP (?)
	ATTRIBUTE	DB	0
	TIME		DW	0
	DATE		DW	0
	SIZE		DD	0
	NAME_FOUND	DB	13 DUP (?)
DTA	ENDS

cseg	segment	para public 'CODE'

	assume 	cs:cseg,ds:data,es:data,ss:stack

	page

; This is the main program that sets up the initial conditions for	
; SDIR which, in turn, does a recursive search.		
;									
; Reads:	PATH_NAME						
; Writes:	FILE_NAME						
; Calls:	SDIR					

whereis	proc	far

	push	ds			;save final return
	xor	ax,ax
	push	ax
	mov	bp,sp			;save pointer to final return
					; in case we want to exit
					; inside recursive routine

	mov	ax,data			;make data segment addressable
	mov	es,ax			;via ES

	call	infile			;get name of search target

	mov	ax,data			;make data segment addressable
	mov	ds,ax			;via DS
	jnc	whereis1		;jump if filename was ok
					;filename was missing,
	mov	dx,offset msg1		;print error message and exit
	mov	ah,9
	int	21h
	ret
whereis1:
	mov	ah,30h			;check DOS version
	int	21h
	cmp	al,2
	jae	whereis2		;proceed, DOS 2.0 or greater
	mov	dx,offset msg3		;DOS 1.1 --- print message
	mov	ah,9			;and exit
	int	21h
	ret
whereis2:
	MOV	DI,OFFSET PATH_NAME
	XOR	AL,AL			;Search for the zero at the end
	CLD				; of PATH_NAME
	MOV	CX,64			;Max. length of scan for 0
	REPNZ	SCASB
	MOV	BX,DI
	DEC	BX			;DS:BX points to end of PATH_NAME
	MOV	DX,0			;Tell SDIR this is first
	CALL	SDIR			;Now do the recursive search
	mov	ax,match_count		;were any matches found?
	or	ax,ax
	jz	whereis8		;no,jump
	ret				;yes,just exit
whereis8:
	mov	dx,offset msg2		;no,print "no match"
	mov	ah,9
	int	21h
	ret
					;this is a special crash
					;exit, which may be taken
					;from inside recursed proc.
whereis9:
	mov	sp,bp
	ret

whereis	endp

	page

;  This procedure searches all the files in the current directory	
; looking for a match.  It also prints the full name for each match.	
;									
;	DS:BX		Pointer to end of current path name		
;	DS:DX		Old disk transfer area (DTA)			
;									
; Reads:	Disk Transfer Area (DTA)				
; Writes:	Disk Transfer Area (DTA)				
; Calls:	BUILD_NAME, FMATCH, PNAME		
;		NMATCH, BUILD_STAR_NAME, SSUB	

SDIR	PROC	NEAR
	PUSH	SI			;Need to restore on exit
	PUSH	DX
	CALL	BUILD_NAME		;Build the absolute search name
	CALL	FMATCH			;See if there is a match here
	JC	sdir2 			;No match, check subdirectories
	CALL	PNAME			;Write name of match.
sdir1:
	CALL	NMATCH			;Find the next match
	JC	sdir2			;No match, search sub-directories
	CALL	PNAME			;Match, write absolute name
	JMP	sdir1			;Look for the next matching name
sdir2:					;No match, so try sub-directories.
	POP	DX			;Restore DTA
	PUSH	DX
	CALL	BUILD_STAR_NAME		;Search for all directories
	CALL	FMATCH			;Get first entry
	JC	SDIR5			;There are no entries
	MOV	SI,DX			;Put address of DTA into SI
	TEST	[SI].ATTRIBUTE,10H	;Is it a directory entry?
	JNZ	SDIR4			;Yes, then search sub-directory
SDIR3:
	CALL	NMATCH			;No, then find the next match
	JC	SDIR5			;There are no more entries
	TEST	[SI].ATTRIBUTE,10H	;Is this a directory?
	JZ	SDIR3			;No, then try again
SDIR4:
	CMP	[SI].NAME_FOUND,'.'	;Is this a . or .. directory?
	JE	SDIR3			;Yes, skip to next directory
	CALL	SSUB			;Search the sub-directory
	PUSH	AX			;Now reset the DTA
	MOV	AH,1AH
	INT	21H
	POP	AX
	JMP	SDIR3

SDIR5:
	POP	DX
	POP	SI
	RET
SDIR	ENDP

	page

; This procedure searches the subdirectory whos name is in the DTA	
;									
;	DS:BX		End of the current path name			
;	DS:[DX].NAME_FOUND	Name of subdirectory for search		
; Reads:	PATH_NAME						
; Writes:	PATH_NAME						
; Calls:	SDIR					

SSUB	PROC	NEAR
	PUSH	DI
	PUSH	SI
	PUSH	AX
	PUSH	BX
	CLD				;Set for increment
	MOV	SI,DX			;Put address of DTA into SI
	ADD	SI,OFFSET NAME_FOUND	;Set to start of sub-directory name
	MOV	DI,BX			;DS:DI -- 0 at end of path name
ssub1:					;Copy sub-directory to path name
	LODSB				;Copy one character
	STOSB
	OR	AL,AL			;Was it a 0?
	JNZ	ssub1			;No, keep copying
	MOV	BX,DI			;Set BX to end of new path name
	STD				;Set flag for decrement
	STOSB				;Store a 0 at end of string
	MOV	AL,'\'
	STOSB				;Place '\' at end of path name
	CALL	SDIR			;Search this new path
	POP	BX			;Restore the old end-of-path
	MOV	BYTE PTR [BX],0		;And store a zero here
	POP	AX
	POP	SI
	POP	DI
	RET
SSUB	ENDP

	page

; This procedure prints the matched name after the path name		
;
;	DS:DX		Pointer to current disk transfer area		
;									
; Reads:	PATH_NAME, NAME_FOUND (in DTA)				
; Calls:	pasciiz, CRLF					

PNAME	PROC	NEAR
	PUSH	AX
	PUSH	DX
	MOV	DX,OFFSET PATH_NAME
	MOV	AL,[BX]			;Save character at end of path
	MOV	BYTE PTR [BX],0		;Set for end of string	
	CALL	pasciiz
	MOV	[BX],AL			;Restore character
	POP	DX			;Recover old pointer
	PUSH	DX
	ADD	DX,OFFSET NAME_FOUND
	CALL	pasciiz
	cmp	byte ptr switch,'g'	;global switch set?
	je	pname2			;yes,keep searching
					;no, check if user wants 
					;to keep looking.
	push	dx			;save current DTA
	mov	dx,offset msg4		;print 'More? '
	mov	ah,9
	int	21h
	mov	ah,7			;read console without echo
	int	21h	
	or	al,20h			;fold to lower case
	cmp	al,'y'			;must be y, anything else exits 
	je	pname1
	jmp	whereis9		;take crash exit
pname1:	mov	dl,al			;if Y,echo it
	mov	ah,2
	int	21h
	pop	dx			;restore pointer to DTA
pname2:	CALL	CRLF
	inc	match_count		;count names displayed
	POP	DX
	POP	AX
	RET
PNAME	ENDP

	page

;  This procedure builds an absolute search name from PATH_NAME		
; followed by FILE_NAME.						
;									
; Reads:	FILE_NAME						
; Calls:	BUILD		to build the name			

BUILD_NAME	PROC	NEAR
	PUSH	SI
	MOV	SI,OFFSET FILE_NAME
	CALL	BUILD
	POP	SI
	RET
BUILD_NAME	ENDP

BUILD_STAR_NAME		PROC	NEAR
	PUSH	SI
	MOV	SI,OFFSET STAR_NAME
	CALL	BUILD
	POP	SI
	RET
BUILD_STAR_NAME		ENDP

; This procedure appends the string at DS:SI to PATH_NAME in		
; PATH_NAME.  It knows where the path name ends from knowing how	
; long PATH_NAME is.							
;									
;	DS:SI		Name of file					
;	DS:BX		End of PATH_NAME				
;									
; Reads:	DS:SI							
; Writes:	PATH_NAME						

BUILD	PROC	NEAR
	PUSH	AX
	PUSH	DI
	MOV	DI,BX
	CLD				;Set direction for increment
build1:
	LODSB				;Copy one character of name
	STOSB
	OR	AL,AL			;End of string yet?
	JNZ	build1			;No, keep copying
	POP	DI
	POP	AX
	RET
BUILD	ENDP

	page

;  This procedure finds the first match between the name given by	
; DS:DX and the directory entries found in the directory PATH_NAME.	
;									
;	DS:DX		Pointer to current disk transfer area		
; Returns:								
;	CF	0	A match was found				
;		1	No match found					
;	AX		Error code returned:				
;		2	File not found					
;		18	No more files					
;	DS:DX		Pointer to new disk transfer area		
;									
; Reads:	PATH_NAME						
; Writes:	dbuff

FMATCH	PROC	NEAR
	PUSH	CX
	CMP	DX,0			;First one?
	JA	ALLOCATE		;No, then allocate space
	MOV	DX,OFFSET dbuff-TYPE DTA
allocate:
	add	dx,type dta		;no, then allocate room for new DTA
	MOV	CX,10H			;Search attribute for files and dirs.
	MOV	AH,1AH			;Set disk transfer address
	INT	21H
	PUSH	DX			;Need DX for address of search name
	MOV	DX,OFFSET PATH_NAME
	MOV	AH,4EH			;Call for "find first match"
	INT	21H
	POP	DX
	POP	CX
	RET				;Return with carry flag info
FMATCH	ENDP


	page

; Get next match for filename
; (very similar to Get first match routine)

; Returns:								
;	CF	0	A match was found				
;		1	No match found					
;	AX		Error code returned:				
;		2	File not found					
;		18	No more files					
;									
; Reads:	PATH_NAME						
; Writes:	dbuff					

NMATCH	PROC	NEAR
	PUSH	CX
	PUSH	DX
	MOV	DX,OFFSET PATH_NAME
	MOV	CX,10H			;Attribute for files and directories
	MOV	AH,4FH			;Call for "Find next match"
	INT	21H
	POP	DX
	POP	CX
	RET				;Return with carry flag intact
NMATCH	ENDP


	page

;  Send CRLF sequence to the screen.	

CRLF	PROC	NEAR
	PUSH	AX
	PUSH	DX
	MOV	AH,02
	MOV	DL,0AH
	INT	21H
	MOV	DL,0DH
	INT	21H
	POP	DX
	POP	AX
	RET
CRLF	ENDP

	page

;  Display ASCIIZ string 
;  Call with DS:DX = string addr

pasciiz	PROC	NEAR
	PUSH	AX
	PUSH	DX
	PUSH	SI
	CLD				;Set direction for increment
	MOV	SI,DX			;Set up pointer to string
	MOV	AH,2
pasciiz1:
	LODSB				;Get character
	or	al,al			;if zero,all done
	jz	pasciiz2
	MOV	DL,AL
	INT	21H			;Write one character
	jmp	pasciiz1		;loop till string exhausted
pasciiz2:
	POP	SI
	POP	DX
	POP	AX
	RET
pasciiz	ENDP

	page


infile	proc	near		;process name of input file
				;DS:SI <- addr command line	
	mov	si,offset command
				;ES:DI <- addr filespec buffer
	mov	di,offset file_name
	cld
	lodsb			;any command line present?
	or	al,al		;return error status if not.
	jz	infile4
infile1:         		;scan over leading blanks
	lodsb			;to file name
	cmp	al,cr		;if we hit carriage return
	jz	infile4 	;filename is missing.
	cmp	al,20h		;is this a blank?
	jz	infile1		;if so keep scanning.
	xor	ah,ah		;reset "." found flag
				;found first char of name
infile2: 			
	cmp	al,'\'		;if slash,reset "." flag
	jne	infile22
	xor	ah,ah
infile22:			;check if extension specified
	cmp	al,'.'
	jne	infile24
	inc	ah
infile24:			;
	stosb			;move last char. to output
				;file name buffer. 
	lodsb			;check next character, found
	cmp	al,cr		;carriage return yet?  	
	je	infile3		;yes,exit with success code
	cmp	al,'/'		;or if we hit a switch delimiter
	jne	infile26
infile25:
	lodsb			;get the switch char and save it
	or	al,20h		;force to lower case
	mov	byte ptr es:switch,al
	jmp	infile3		;then jump to finish up
infile26:
	cmp	al,20h		;is this a blank?
	jne 	infile2		;if not keep moving chars.
infile27:
	lodsb			;search up to end, in case of switch
	cmp	al,'/'
	je	infile25	;found switch,go save it
	cmp	al,cr
	jne	infile27	;otherwise, fscan until CR found
infile3: 			;found end of input name
	or	ah,ah		;was "." found?
	jnz	infile35	;yes,jump
	mov	al,'.'		;no,force ext to wildcard
	stosb
	mov	al,'*'
	stosb
infile35:
	xor	al,al		;store trailing null byte
	stosb			;exit with CY=0 as success flag
	clc			
	ret

infile4: 			;exit with carry =1
	stc			;for error flag
	ret
infile  endp 

cseg	ends

	page

data 		segment para public 'DATA'

STAR_NAME	DB	'*.*',0

PATH_NAME	DB	'\',0
		DB	80 DUP (0)	;Space for 64 character path name
					; and 13 character file name

FILE_NAME	DB	13 DUP (0)	;Save room for full DOS file name

msg1		db	cr,lf
		db	'Missing file name.'
		db	cr,lf,eom

msg2		db	cr,lf
		db	'No match found.'
		db	cr,lf,eom

msg3		db	cr,lf
		db	'Requires DOS version 2.0 or greater.'
		db	cr,lf,eom

msg4		db	tab,'  More (y/n) ? ',eom

match_count	dw	0		;number of filenames matching
					;input filespec

switch		db	0		;char following / saved here

dbuff		equ	$		;this starts the scratch area
					;which will be used as search
					;buffers.  It is right under
					;the stack, which is made very
					;large to give it room.

data		ends 


stack	segment	para stack 'STACK'
	db	32768 dup (?)
stack	ends


	end	whereis

[ RETURN TO DIRECTORY ]