	page ,132
;/*
; *                      Microsoft Confidential
; *                      Copyright (C) Microsoft Corporation 1991
; *                      All Rights Reserved.
; */
;	SCCSID = @(#)path2.asm	1.1 85/05/14
;	SCCSID = @(#)path2.asm	1.1 85/05/14
.sall
.xlist
.xcref
	include dossym.inc
	include syscall.inc
	include find.inc
	include comsw.asm
	include comseg.asm
	include comequ.asm
.list
.cref


DATARES SEGMENT PUBLIC BYTE
	EXTRN	FORFLAG:BYTE
DATARES ENDS


break <Path.Asm>
;----------------------------------------------------------------------------
;    PATH.ASM contains the routines to perform pathname incovation.  Path and
;    Parse share a temporary buffer and argv[] definitions.  <Path_Search>,
;    given a pathname, attempts to find a corresponding executable or batch
;    file on disk.  Directories specified in the user's search path will be
;    searched for a matching file, if a match is not found in the current
;    directory and if the pathname is actually only an MSDOS filename.
;    <Path_Search> assumes that the parsed command name can be found in
;    argv[0] -- in other words, <Parseline> should be executed prior to
;    <Path_Search>.  Alternatively, the command name and appropriate
;    information could be placed in argv[0], or <Path_Search> could be
;    (easily) modified to make no assumptions about where its input is found.
;    Please find enclosed yet another important routine, <Save_Args>, which
;    places the entire arg/argv[]/argbuf structure on a piece of newly
;    allocated memory.	This is handy for for-loop processing, and anything
;    else that wants to save the whole shebang and then process other command
;    lines.
;
; Alan L, OS/MSDOS				    August 15, 1983
;
; ENTRY:
;   <Path_Search>:	    argv[0].
;   <Save_Args>:	    bytes to allocate in addition to arg structure
; EXIT:
;   <Path_Search>:	    success flag, best pathname match in EXECPATH.
;   <Save_Args>:	    success flag, segment address of new memory
; NOTE(S):
;   *	<Argv_calc> handily turns an array index into an absolute pointer.
;	The computation depends on the size of an argv[] element (arg_ele).
;   *	<Parseline> calls <cparse> for chunks of the command line.  <Cparse>
;	does not function as specified; see <Parseline> for more details.
;   *	<Parseline> now knows about the flags the internals of COMMAND.COM
;	need to know about.  This extra information is stored in a switch_flag
;	word with each command-line argument; the switches themselves will not
;	appear in the resulting arg structure.
;   *	With the exception of CARRY, flags are generally preserved across calls.
;---------------
; CONSTANTS:
;---------------
    DEBUGx	equ	    FALSE	; prints out debug info
;---------------
; DATA:
;---------------

TRANSPACE	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	arg:byte
	EXTRN	BADPMES_ptr:word
	EXTRN	curdrv:byte
	EXTRN	EXECPATH:byte
	EXTRN	ext_entered:byte	;AN005;
	EXTRN	fbuf:byte
	EXTRN	pathinfo:word
	EXTRN	psep_char:byte
	EXTRN	string_ptr_2:word
	EXTRN	tpbuf:byte
TRANSPACE	ENDS

TRANCODE	SEGMENT PUBLIC BYTE	;AC000;

assume cs:trangroup, ds:trangroup, es:trangroup, ss:nothing

break <Search>
;----------------------------------------------------------------------------
;   SEARCH, when given a pathname, attempts to find a file with
; one of the following extensions:  .com, .exe, .bat (highest to
; lowest priority).  Where conflicts arise, the extension with
; the highest priority is favored.
; ENTRY:
;   DX		--	pointer to null-terminated pathname
;   fbuf	--	dma buffer for findfirst/next
; EXIT:
;   AX		--	8)  file found with .com extension
;			4)  file found with .exe extension
;			2)  file found with .bat extension
;			0)  no such file to be found
;   (if AX is non-zero:)
;   [search_best]	identical to AX
;   [search_best_buf]	null-terminated filename
; NOTES:
;   1)	Requires caller to have allocated a dma buffer and executed a setdma.
;---------------
; CONSTANTS:
;---------------
search_file_not_found	    equ 	0
search_com		    equ 	8
search_exe		    equ 	4
search_bat		    equ 	2
fname_len		    equ 	8
fname_max_len		    equ 	13
dot			    equ 	'.'
wildchar		    equ 	'?'

;---------------
; DATA:
;---------------
TRANSPACE	SEGMENT PUBLIC BYTE	;AC000;
	EXTRN	search_best:byte
	EXTRN	search_best_buf:byte
	EXTRN	search_curdir_buf:byte
	EXTRN	search_error:word
TRANSPACE	ENDS

;---------------
Procedure   Search,NEAR
;---------------
	assume	ds:trangroup, es:trangroup
	push	CX
	push	DX
	push	DI
	push	SI
	pushf

	push	DX				; check drivespec (save pname ptr)
	mov	DI, DX				; working copy of pathname
	mov	SI, OFFSET TRANGROUP:search_curdir_buf
	xor	DX, DX				; zero means current drive
	cmp	BYTE PTR [DI+1],':'             ; is there a drive spec?
	jne	search_dir_check
	mov	DL, [DI]			; get the drive byte
	and	DL, NOT 20H			; uppercase the sucker
	sub	DL, '@'                         ; and convert to drive number

search_dir_check:
	trap	Current_Dir			; can we get the drive's current
	pop	DX				; directory?  If we can't we'll
	jc	search_invalid_drive		; assume it's a bad drive...

	mov	CX, search_attr 		; filetypes to search for
	trap	Find_First			; request first match, if any
	jc	search_no_file
	mov	search_best, search_file_not_found
	mov	[search_best_buf], ANULL	; nothing's been found, yet

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
	mov	SI, OFFSET TRANGROUP:fbuf.find_buf_pname
	mov	DI, OFFSET TRANGROUP:search_best_buf
	mov	CX, fname_max_len
	cld
	rep	movsb				; save complete pathname representation
	cmp	AL, search_com			; have we found the best of all?
	je	search_done

search_next:					; keep on looking
	mov	CX, search_attr
	trap	Find_Next			; next match
	jnc	search_loop

search_done:					; it's all over with...
	mov	AL, search_best 		; pick best to return with
	cmp	ext_entered,1			;AN005; Did user request a specific ext?
	jz	search_exit			;AN005; no - exit
	mov	al,ext_entered			;AN005; yes - get the real file type back
	mov	search_best,al			;AN005; save the real file type
	jmp	short search_exit

search_invalid_drive:				; Tell the user path/drive
	mov	DX, [search_error]		; appropriate error message
	invoke	std_printf			; and pretend no file found

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

search_exit:
	popf
	pop	SI
	pop	DI
	pop	DX
	pop	CX
	ret
;---------------
EndProc Search
;----------------------------------------------------------------------------


break <Search_Ftype>
;----------------------------------------------------------------------------
;   SEARCH_FTYPE determines the type of a file by examining its extension.
; ENTRY:
;   fbuf    --	    dma buffer containing filename
; EXIT:
;   AX	    --	    file code, as given in search header
; NOTE(S):
;   *	Implicit assumption that NULL == search_file_not_found
;---------------
; DATA:
;---------------
TRANDATA	SEGMENT PUBLIC BYTE		;AC000;
	extrn	comext:byte,exeext:byte,batext:byte
trandata     ends
;---------------
Procedure   Search_Ftype,NEAR
;---------------
	assume	ds:trangroup, es:trangroup
	push	DI
	push	si
	mov	AX, ANULL			; find the end of the filename
	mov	DI, OFFSET TRANGROUP:fbuf.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
	sub	di,5				; . + E + X + T + NULL
;
; Compare .COM
;
	mov	si,offset trangroup:comext
	mov	ax,di
	cmpsw
	jnz	ftype_exe
	cmpsw
	jnz	ftype_exe
	mov	AX, search_com			; success!
	jmp	short ftype_exit
;
; Compare .EXE
;

ftype_exe:					; still looking... now for '.exe'
	mov	di,ax
	mov	si,offset trangroup:exeext
	cmpsw
	jnz	ftype_bat
	cmpsw
	jnz	ftype_bat
	mov	AX, search_exe			; success!
	jmp	short ftype_exit
;
; Compare .BAT
;

ftype_bat:					; still looking... now for '.bat'
	mov	di,ax
	mov	si,offset trangroup:batext
	cmpsw
	jnz	ftype_fail
	cmpsw
	jnz	ftype_fail
	mov	AX, search_bat			; success!
	jmp	short ftype_exit

ftype_fail:					; file doesn't match what we need
	mov	ax,ANULL

ftype_exit:
	cmp	ext_entered,1			;AN005; was an extension entered?
	jz	ftype_done			;AN005; no - exit
	cmp	ax,ANULL			;AN005; was any match found
	jz	ftype_done			;AN005; no - exit
	mov	ext_entered,al			;AN005; save the match type found
	mov	AX, search_com			;AN005; send back best was found to stop search

ftype_done:					;AN005;
	pop	SI
	pop	DI
	ret

;---------------
EndProc Search_Ftype
;----------------------------------------------------------------------------


break <Strip>
;----------------------------------------------------------------------------
;    STRIP copies the source string (argv[0]) into the destination buffer,
; replacing any extension with wildcards.
; ENTRY:
;	BX		--		maximum length of destination buffer
;	DS:SI		--		address of destination buffer
;	argv[0] 	--		command name to be stripped
; EXIT:
;	CF		--		set if failure, clear if successful
; NOTE(S):
;---------------
Procedure   Strip,NEAR
;---------------
	assume	ds:trangroup, es:trangroup
	push	AX
	push	BX
	push	CX
	push	DX
	push	DI
	push	SI
	pushf

	mov	ext_entered,1			;AN005; assume no extension on file name
	mov	DX, DS:arg.argv[0].argpointer	; save pointer to beginning of argstring
	mov	DI, DS:arg.argv[0].argstartel	; beginning of last pathname element
	cmp	BYTE PTR [DI], 0		; *STARTEL == NULL means no command
	jz	strip_error
	mov	CX, DX				; compute where end of argstring lies
	add	CX, DS:arg.argv[0].arglen
	sub	CX, DI				; and then find length of last element
	inc	CX				; include null as well
	mov	AL, dot 			; let's find the filetype extension
	cld
	repnz	scasb				; wind up pointing to either null or dot
	jcxz	process_ext			;AN005; if no extension found, just continue
	mov	ext_entered,0			;AN005; we found an extension
	mov	al,ANULL			;AN005; continue scanning until the
	repnz	scasb				;AN005;    end of line is reached.

process_ext:					;AN005;
	mov	CX, DI				; pointer to end of argstring yields
	sub	CX, DX				; number of bytes to be copied
	sub	BX, 4				; can argstring fit into dest. buffer?
	cmp	CX, BX
	jg	strip_error			; if not, we must have a bad pathname
	mov	DI, SI				; destination buffer
	mov	SI, DX				; source is beginning of pathname
	cld
	rep	movsb				; SI=arg,DI=buffer,CX=argend-argbeg
	cmp	ext_entered,1			;AN005; if an extension was entered
	jnz	skip_wilds			;AN005;    don't set up wildcard ext.

	dec	DI				; overwrite null or dot
	stosb					; with a dot
	mov	AL, wildchar			; now add wildcards
	stosb
	stosb
	stosb
	mov	AL, ANULL			; and a terminating null
	stosb

skip_wilds:					;AN005;
	popf
	clc					; chill out...
	jmp	short strip_exit

strip_error:
	popf
	stc

strip_exit:
	pop	SI
	pop	DI
	pop	DX
	pop	CX
	pop	BX
	pop	AX
	ret
;---------------
EndProc Strip
;----------------------------------------------------------------------------


break <Save_Args>
;----------------------------------------------------------------------------
;   SAVE_ARGS attempts to preserve the existing argv[]/argvcnt/argbuffer
; structure in newly allocated memory.	The argv[] structure is found at the
; beginning of this area.  The caller indicates how much extra space is
; needed in the resulting structure; Save_Args returns a segment number and
; an offset into that area, indicating where the caller may preserve its own
; data.  Note that <argvcnt> can be found at <offset-2>.
; ENTRY:
;   BX	    --	    size (in bytes) of extra area to allocate
; EXIT:
;   AX	    --	    segment of new area.
;   CF	    --	    set if unable to save a copy.
; NOTE(S):
;   1)	The allocated area will be AT LEAST the size requested -- since
;	the underlying MSDOS call, <alloc> returns an integral number of
;	paragraphs.
;   2)	It is an error if MSDOS can't allocate AT LEAST as much memory
;	as the caller of Save_Args requests.
;   3)	AX is undefined if CF indicates an error.
;---------------
Procedure   Save_Args,NEAR
;---------------
	assume	ds:trangroup, es:trangroup
	push	BX
	push	CX
	push	DX
	push	DI
	push	SI
	push	BP
	pushf
	add	BX, SIZE arg_unit + 0FH 	; space for arg structure, round up
	mov	CL, 4				; to paragraph size and convert
	shr	BX, CL				; size in bytes to size in paragraphs
	trap	Alloc
	jc	save_error
	mov	BP, AX				; save segment id
	push	ES				; save TRANGROUP address
	mov	ES, AX				; switch to new memory segment
assume	ES:nothing
	mov	CX, SIZE arg_unit		; get back structure size
	xor	DI, DI				; destination is new memory area
	mov	SI, OFFSET TRANGROUP:arg	; source is arg structure
	rep	movsb				; move that sucker!
	mov	CX, arg.argvcnt 		; adjust argv pointers
	xor	AX, AX				; base address for argv_calc
;	Bugbug:	What did they mean by this?
;	Note that the replacement line produces exactly the same code.
;;	mov	SI, OFFSET TRANGROUP:arg.argbuf - OFFSET arg_unit.argbuf
	mov	SI, OFFSET TRANGROUP:arg

save_ptr_loop:
	dec	CX				; exhausted all args?
	jl	save_done
	mov	BX, CX				; get arg index and
	invoke	argv_calc			; convert to a pointer
	mov	DX, DS:arg.argv[BX].argpointer
	sub	DX, SI				; adjust argpointer
	mov	ES:argv[BX].argpointer, DX
	mov	DX, DS:arg.argv[BX].argstartel
	sub	DX, SI				; and adjust argstartel
	mov	ES:argv[BX].argstartel, DX
	mov	DX, DS:arg.argv[BX].arg_ocomptr
	sub	DX, SI				; and adjust arg_ocomptr
	mov	ES:argv[BX].arg_ocomptr, DX
	jmp	save_ptr_loop

save_done:
	pop	ES				; back we go to TRANGROUP
assume	ES:trangroup
	mov	AX, BP				; restore segment id
	jmp	short save_ok

save_error:
	popf
	stc
	jmp	short save_exit

save_ok:
	popf
	clc
save_exit:
	pop	BP
	pop	SI
	pop	DI
	pop	DX
	pop	CX
	pop	BX
	ret
;---------------
EndProc Save_Args
;----------------------------------------------------------------------------

trancode    ends
END
