        title   Path Searching Routines
;/*
; *                      Microsoft Confidential
; *			 Copyright (C) Microsoft Corporation 1993
; *                      All Rights Reserved.
; */
        Page    ,132

; PATH.ASM - Code to search the environment for a particular data string,
; and to search the path for a particular file.  Adapted from the original
; COMMAND.COM version.
;
; Routines supported:
;       Find_in_Environment - locate the start of a given string
;                               in the environment
;       Path_Crunch - concantenates a file name with a directory path from
;                       the PATH environment variable
;       Search - Finds executable or other files, given a base name
;
        include dossym.inc
        include curdir.inc
        include find.inc
        include pdb.inc
        include syscall.inc

	DATA segment para public 'DATA'
Path_str        db      "PATH="
Path_str_size   equ     $ - offset Path_Str

Comspec_str     db      "COMSPEC="
Comspec_str_size equ    $ - offset Comspec_str

comext  db      ".COM",0
exeext	db	".EXE",0

        DATA ends

	CODE segment para public 'CODE'
        assume	cs:CODE,ds:DATA

IFDEF DBCS
        extrn   IsDBCSLeadByte:near
ENDIF

;----------------------------------------------------------------------------
; Path_Crunch - takes a pointer into a environment PATH string and a file
; name, and sticks them together, for subsequent searching.
;
; ENTRY:
;   BH			--	additional terminator character (i.e., ';')
;   DS:SI		--	pointer into pathstring to be dissected
;   ES:DI               --      buffer to store target name
;   DX			--	pointer to filename
; EXIT:
;   SI			--	moves along pathstring from call to call
;   ES:DI               --      filled in with concatenated name
;   Carry set if end of path string has been reached.
;
;---------------
Path_Crunch PROC NEAR
        public  Path_Crunch
;---------------
        assume  ds:nothing
        assume  es:DATA

IFDEF DBCS
	xor	cl,cl				; clear flag for later use 3/3/KK
ENDIF

path_cr_copy:
	lodsb					; get a pathname byte
	or	al,al				; check for terminator(s)
	jz	path_seg			; null terminates segment & pathstring
	cmp	AL, BH
	jz	path_seg			; BH terminates a pathstring segment

IFDEF DBCS
	invoke	IsDBCSLeadByte			;
	jz	NotKanj2			;
	stosb					;
	movsb					;
	MOV	CL,1				; CL=1 means latest stored char is DBCS
	jmp	path_cr_copy			;

NotKanj2:					;
	xor	cl,cl				; CL=0 means latest stored char is SBCS
ENDIF

	stosb					; save byte in concat buffer
	jmp	path_cr_copy			; loop until we see a terminator

path_seg:
        push    si                              ; save resting place in env. seg.
	mov	BL, AL				; remember if we saw null or not...

path_cr_look:					; form complete pathname
	mov	al, '\'      			; add pathname separator for suffix

IFDEF DBCS
	or	cl,cl				;
	jnz	path_cr_store			; this is a trailing byte of ECS code 3/3/KK
ENDIF
	cmp	al,es:byte ptr [di-1]
	jz	path_cr_l1

path_cr_store:					
	stosb

path_cr_l1:
	mov	SI, DX

path_cr_l2:
	lods	byte ptr es:[si]       		; tack the stripped filename onto
	stosb					; the end of the path, up to and
	or	AL, AL				; including the terminating null
	jnz	path_cr_l2

path_cr_leave:
	or	BL, BL				; did we finish off the pathstring?
	clc
        jnz	path_cr_exit			; null in BL means all gone...
	cmc

path_cr_exit:
        pop     si                              ; retrieve
	ret

        assume  es:nothing

;---------------
Path_Crunch endp
;----------------------------------------------------------------------------

;----------------------------------------------------------------------------
;   SEARCH, when given a pathname, attempts to find a file with
; one of the following extensions:  .com, .exe (highest to
; lowest priority).  Where conflicts arise, the extension with
; the highest priority is favored.
; ENTRY:
;   DX		--	pointer to null-terminated pathname
;   BX  	--	dma buffer for findfirst/next
;   AL          --      0 if we should look for .COM and .EXE extensions
;                       1 if extensions is pre-specified
; EXIT:
;   AX		--	8)  file found with .com extension, or file with
;                           pre-specified extension found
;			4)  file found with .exe extension
;			0)  no such file to be found
;   DX          --      points to resolved path name 
;   DS          --      DATA
; NOTES:
;   1)	Requires caller to have allocated executed a setdma.
;       
;---------------
; CONSTANTS:
;---------------
search_attr                 equ         attr_read_only+attr_hidden
search_file_not_found	    equ 	0
search_com		    equ 	8
search_exe		    equ 	4
fname_len		    equ 	8
fname_max_len		    equ 	23
dot			    equ 	'.'
wildchar		    equ 	'?'

search_best                 db          (?)
;---------------
Search PROC NEAR
        public Search
;---------------
        push    si                              ; 
        push    ax                              ; save extension flag
	mov	DI, DX				; working copy of pathname

	mov	CX, search_attr 		; filetypes to search for
        mov     ah, Find_First			; request first match, if any
        int     21h
        pop     ax                              
	jc	search_no_file
        
        or      al,al                           ; looking for specific ext?
        jz      search_no_ext                   ; no, jump
        mov     search_best,search_com          ; report we found best match
        jmp     short search_file_found         ; yes, found it

search_no_ext:
        mov     search_best, search_file_not_found

search_loop:
	call	search_ftype			; determine if .com, &c...
	cmp	AL, search_best 		; better than what we've found so far?
	jle	search_next			; no, look for another
	mov	search_best, AL 		; found something... save its code
	cmp	AL, search_com			; have we found the best of all?
	je	search_done

search_next:					; keep on looking
	mov	CX, search_attr
        mov     ah, Find_Next                   ; next match
        int     21h
	jnc	search_loop

search_done:					; it's all over with...
        cmp     search_best, search_file_not_found
        je      search_no_file
        cmp     search_best, search_com
        mov     si, offset comext
        je      search_move_ext
        mov     si, offset exeext

search_move_ext:
        mov     di, dx
        mov     al, '.'
        mov     cx, DIRSTRLEN
        rep     scasb
        dec     di
        movsw   
        movsw   

search_file_found:
        mov     al, search_best
	jmp	short search_exit

search_no_file: 				; couldn't find a match
	mov	AX, search_file_not_found

search_exit:
        pop     si
	ret
Search endp
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
;   SEARCH_FTYPE determines the type of a file by examining its extension.
; ENTRY:
;   BX    --	    dma buffer containing filename
; EXIT:
;   AL	    --	    file code, as given in search header
;---------------

Search_Ftype PROC NEAR
        public Search_Ftype

	push	DI
	mov	AL, search_file_not_found	; find the end of the filename
	mov	DI, BX
	add	di,Find_Buf_Pname
	mov	CX, fname_max_len
	cld
	repnz	scasb				; search for the terminating null
	jnz	ftype_exit			; weird... no null byte at end
;
; Scan backwards to find the start of the extension
;
        dec     di                              ; point back to null
        mov     cx, 5                           ; . + E + X + T + null
        std                                     ; scan back
        mov     al, '.'
        repnz   scasb
        jnz     ftype_exit                      ; must not be any extension
        inc     di                              ; point to start of extension
        cld
;
; Compare .COM
;
	mov	si,offset comext
	mov	ax,di
	cmpsw
	jnz	ftype_exe
	cmpsw
	jnz	ftype_exe
	mov	AL, search_com			; success!
	jmp	short ftype_exit
;
; Compare .EXE
;
ftype_exe:					; still looking... now for '.exe'
	mov	di,ax
	mov	si,offset exeext
	cmpsw
	jnz	ftype_fail
	cmpsw
	jnz	ftype_fail
	mov	AL, search_exe			; success!
	jmp	short ftype_exit

ftype_fail:					; file doesn't match what we need
	mov	al,search_file_not_found

ftype_exit:
	pop	DI
	ret

Search_Ftype endp

;----------------------------------------------------------------------------
;
; Find_Comspec_In_Environment - find the beginning of the COMSPEC string
;       Entry : DS = DATA
;               ES = PSP
;       Exit  : ES:DI => start of Comspec path
;       

FIND_COMSPEC_IN_environment PROC NEAR
        public Find_Comspec_In_Environment
        lea     si,Comspec_str
	mov     cx,Comspec_str_size		; cx = length of name
        jmp     short Find_in_Environment
Find_Comspec_in_Environment        endp

;----------------------------------------------------------------------------
;
; Find_Path_In_Environment - find the beginning of the PATH string
;       Entry : DS = DATA
;               ES = PSP
;       Exit  : ES:DI => start of Path directory list
;       

FIND_PATH_IN_environment PROC NEAR
        public Find_Path_In_Environment
        lea     si,Path_str
	mov     cx,Path_str_size		; cx = length of name
; fall through to following
Find_Path_in_Environment        endp


; Find_In_Environment - locate a given string in the environment
;        
; Input :       SI = name to find in environment
;               CX = length of name
;               DS = DATA
;               ES = PSP segment
;
; Output: ES:DI points to the arguments in the environment
;	  carry is set if name not found
;

Find_in_Environment PROC NEAR
        public Find_In_Environment

	cld
        xor     di,di
        mov     ax,es:[di].PDB_Environ
        or      ax,ax                          ; is there an environment?
        jz      find_nf_exit                   ; no, quit now
        mov     es,ax
        assume  es:nothing        

find1:
        push    si
        push    cx                              ; save starting values
find11:

ifdef dbcs
	lodsb
	call	IsDBCSLeadByte
	jz	notkanj3
	dec	si
	lodsw
	inc	di
	inc	di
	cmp	ax,es:[di-2]
	jnz	find12
	dec	cx
	loop	find11
	jmp	short find12

notkanj3:
	inc	di
	cmp	al,es:[di-1]
	jnz	find12
	loop	find11

else    ;dbcs

        repe cmpsb   

endif   ;dbcs

find12:
        pop     dx
        pop     si                              ; clear stack
	jz      find_exit
        dec     di
	xor	al,al				; scan for a nul
	mov	cx,100h                         ; arbitrary size 
	repnz	scasb
	cmp	byte ptr es:[di],0              ; check for trailing null
        mov     cx,dx                           ; original count back in CX
	jnz	find1
	
find_nf_exit:
        stc					; indicate not found

find_exit:
	ret

Find_in_environment endp


CODE    ends
        end
