2025-04-27 07:49:33 -04:00

527 lines
16 KiB
NASM
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

page ,132
;/*
; * Microsoft Confidential
; * Copyright (C) Microsoft Corporation 1991
; * All Rights Reserved.
; */
; SCCSID = @(#)path1.asm 1.1 85/05/14
; SCCSID = @(#)path1.asm 1.1 85/05/14
.sall
.xlist
.xcref
include dossym.inc
include syscall.inc
include comsw.asm
include comseg.asm
include comequ.asm
.list
.cref
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:
;---------------
TRANDATA SEGMENT PUBLIC BYTE ;AC000;
EXTRN baddrv_ptr:word
TRANDATA ENDS
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
EXTRN arg:byte
EXTRN BADPMES_ptr:word
EXTRN curdrv:byte
EXTRN EXECPATH:byte
EXTRN search_best_buf:byte
EXTRN search_error:word
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 <Path_Search>
;------------------------------------------------------------------------------
; PATH_SEARCH tries to find the file it's given, somewhere. An initial value
; of *argv[0].argstartel == 0 implies that there is no command (empty line
; or 'd:' or 'd:/'). This check is done in strip; otherwise, strip formats
; the filename/pathname into tpbuf. Search(tpbuf) is executed to see if we
; have a match, either in the current working directory if we were handed
; a filename, or in the specified directory, given a pathname. If this call
; fails, and we were given a pathname, then Path_Search fails. Otherwise,
; Path_Crunch is repeatedly invoked on tpbuf[STARTEL] (if there's a drive
; prefix, we want to skip it) for each pathstring in userpath. Success on
; either the first invocation of search or on one of the succeeding calls
; sets up the appropriate information for copying the successful pathname
; prefix (if any) into the result buffer, followed by the successful filename
; match (from [search_best_buf]). The result is returned in in EXECPATH.
; ENTRY:
; argv[0] -- command name and associated information
; EXIT:
; AX -- non-zero indicates type of file found
; EXECPATH -- successful pathname (AX non-zero)
; NOTE(S):
; 1) Uses the temporary buffer, tpbuf, from the parse routines.
; 2) Some files are more equal than others. See search: for rankings.
; 3) Path_Search terminates as soon as a call to search succeeds, even
; if search returns an .exe or .bat.
; 5) Clobbers dma address.
pbuflen equ EXECPATHLEN ; len EXECPATH - ntvdm extended
path_sep_char equ ';'
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
EXTRN fbuf:byte
EXTRN pathinfo:word
EXTRN psep_char:byte
TRANSPACE ENDS
Procedure Path_Search,NEAR
assume ds:trangroup, es:trangroup
push BX
push CX
push DX ; could use a "stack 'em" instruction
push SI
push DI
push BP
pushf
test DS:arg.argv[0].argflags, (MASK wildcard) + (MASK sw_flag)
jz path_search_ok
path_failure_jmp:
jmp path_failure ; ambiguous commands not allowed
path_search_ok:
call store_pchar ; figure out the pathname separator
mov DX, OFFSET TRANGROUP:fbuf ; clobber old dma value with
trap set_dma ; a pointer to our dma buffer
push ES
invoke find_path ; get a handle (ES:DI) on user path
mov DS:pathinfo[0], ES ; and squirrel it away
mov DS:pathinfo[2], DI ; "old" pathstring pointer
mov DS:pathinfo[4], DI ; "new" pathstring pointer
pop ES
mov BX, pbuflen ; copy/format argv[0] into temp buffer
mov SI, OFFSET TRANGROUP:EXECPATH
invoke strip
jc path_failure_jmp ; if possible, of course
mov DX, SI ; search(EXECPATH, error_message)
mov [search_error], OFFSET TRANGROUP:BADDRV_ptr
invoke search ; must do at least one search
or AX, AX ; find anything?
jz path_noinit ; failure ... search farther
mov BP, AX ; success... save filetype code
mov DI, OFFSET TRANGROUP:EXECPATH
mov SI, DS:arg.argv[0].argpointer
mov CX, DS:arg.argv[0].argstartel
sub CX, SI ; compute prefix bytes to copy
;
; We have the number of bytes in the prefix (up to the final component).
; We need to form the complete pathname including leading drive and current
; directory.
;
; Is there a drive letter present?
;
cmp word ptr [si], 05c5ch ; If a UNC name, copy straight in
je CopyPath
mov ah,':'
cmp cx,2 ; room for drive letter?
jb AddDrive ; no, stick it in
cmp [si+1],ah ; colon present?
jz MoveDrive ; yes, just move it
AddDrive:
mov al,curdrv ; get current drive
add al,"A" ; convert to uppercase letter
stosw ; store d:
jmp short CheckPath
MoveDrive:
lodsw ; move d:
stosw
sub cx,2 ; 2 bytes less to move
CheckPath:
or al,20h
mov dl,al
sub dl,"a"-1 ; convert to 1-based for current dir
;
; Stick in beginning path char
;
mov al,psep_char
stosb
;
; Is there a leading /? If so, then no current dir copy is necessary.
; Otherwise, get current dir for DL.
;
cmp cx,1 ; is there room for path char?
jb AddPath ; no, go add path
lodsb
dec cx
cmp al,psep_char ; is there a path separator?
jz MovePath ; yes, go move remainder of path
inc cx
dec si ; undo the lodsb
AddPath:
SaveReg <SI>
mov si,di ; remainder of buffer
trap Current_dir
;
; The previous current dir will succeed a previous find_first already worked.
;
; Find end of string.
;
mov di,si
RestoreReg <SI>
mov al,psep_char
cmp byte ptr [di],0 ; root (empty dir string)?
jz MovePath ; yes, no need for path char
ScanEnd:
cmp byte ptr [dI],0 ; end of string?
jz FoundEnd
inc di
jmp ScanEnd
;
; Stick in a trailing path char
;
FoundEnd:
stosb
;
; Move remaining part of path. Skip leading path char if present.
;
MovePath:
cmp [si],al ; first char a path char?
jnz CopyPath
inc si ; move past leading char
dec cx ; drop from count
CopyPath:
jcxz CopyDone ; no chars to move!
rep movsb
CopyDone:
jmp path_success ; run off and form complete pathname
path_noinit:
test DS:arg.argv[0].argflags, MASK path_sep
jnz path_failure ; complete pathname specified ==> fail
mov BH, path_sep_char ; semicolon terminates pathstring
mov DX, DS:arg.argv[0].argstartel ; this is where the last element starts
sub DX, DS:arg.argv[0].argpointer ; form pointer into EXECPATH,
add DX, OFFSET TRANGROUP:EXECPATH ; skipping over drive spec, if any
path_loop:
call path_crunch ; pcrunch(EXECPATH, pathinfo)
mov BP, AX ; save filetype code
lahf ; save flags, just in case
or BP, BP ; did path_crunch find anything?
jne path_found
sahf ; see? needed those flags, after all!
jnc path_loop ; is there anything left to the path?
path_failure:
xor AX, AX
;; jmp short path_exit ; 3/3/KK
jmp path_exit ;AC000; 3/3/KK
path_found: ; pathinfo[] points to winner
mov DI, OFFSET TRANGROUP:EXECPATH
mov CX, pathinfo[4] ; "new" pointer -- end of string
mov SI, pathinfo[2] ; "old" pointer -- beginning of string
;
; BAS Nov 20/84
; Look at the pathname and expand . and .. if they are the first element
; in the pathname (after the drive letter)
;
push ES
push pathinfo[0]
pop ES
;SR;
; Oops! Gets fooled if path= \;..
; We should also check if a drive letter is really present
;
cmp Byte Ptr ES:[SI+2],'.' ; Look for Current dir at start of path
jnz path_cpy
cmp byte ptr es:[si+1],':' ;does path have drive letter?
jnz path_cpy ;no, copy the path string
push CX ; Save pointer to end of string
mov AL, ES:[SI]
mov [DI],AL ; Copy drive letter, :, and root char
mov AL, ES:[SI+1] ; to EXECPATH
mov [DI+1],AL
mov AL,psep_char
mov [DI+2],AL
push SI ; Save pointer to begining of string
mov DL,ES:[SI] ; Convert device letter for cur dir
or DL,20h
sub DL,"a"-1
mov SI,DI ; pointer to EXECPATH
add SI, 3 ; Don't wipe out drive and root info
trap Current_dir
invoke DStrlen ; Determine length of present info
add SI,CX ; Don't copy over drive and root info
dec SI
mov DI,SI ; Point to end of target string
pop SI ; Restore pointer to begining of string
add SI, 3 ; Point past drive letter, :, .
pop CX ; Restore pointer to end of string
path_cpy:
pop ES
sub CX, SI ; yields character count
push DS ; time to switch segments
push pathinfo[0] ; string lives in this segment
pop DS
cld
;; rep movsb 3/3/KK ; copy the prefix path into EXECPATH
Kloop: ;AN000; 3/3/KK
lodsb ;AN000; 3/3/KK
stosb ;AN000; 3/3/KK
invoke testkanj ;AN000; 3/3/KK
jz NotKanj1 ;AN000; 3/3/KK
dec cx ;AN000; 3/3/KK
JCXZ PopDone ;AN000; Ignore boundary error 3/3/KK
movsb ;AN000; 3/3/KK
dec cx ;AN000; 3/3/KK
cmp cx,1 ;AN000; One char (the terminator) left ? 3/3/KK
ja Kloop ;AN000; no. 3/3/KK
PopDone: ;AN000; 3/3/KK
POP DS ;AN000; Yes ES:DI->terminator, last char is 3/3/KK
mov AL, psep_char ;AN000; KANJI 3/3/KK
jmp Short path_store ;AN000; 3/3/KK
NotKanj1:
loop Kloop
pop DS ; return to our segment
dec DI ; overwrite terminator
mov AL, psep_char ; with a pathname separator
cmp al,byte ptr [di-1]
jz path_success
path_store: ;AN000; 3/3/KK
stosb
path_success:
mov SI, OFFSET TRANGROUP:search_best_buf
xor CX, CX
path_succ_loop:
lodsb ; append winning filename to path
stosb ; (including terminating null)
or al,al
jnz path_succ_loop
mov AX, BP ; retrieve filetype code
path_exit:
popf
pop BP
pop DI
pop SI ; chill out...
pop DX
pop CX
pop BX
ret
EndProc Path_Search
break <Store_Pchar>
;----------------------------------------------------------------------------
; STORE_PCHAR determines the pathname-element separator and squirrels
; it away. In other words, must we say '/bin/ls' or '\bin\ls'?
; ENTRY:
; EXIT:
; NOTE(S):
; * Uses <psep_char>, defined in <path_search>.
;---------------
;---------------
Procedure Store_PChar,NEAR
;---------------
assume ds:trangroup, es:trangroup
push AX
mov AL, '/' ; is the pathname-element separator
invoke pathchrcmp ; a regular slash?
jz store_slash ; if yes, remember slash
mov al,'\'
mov [psep_char], al ; otherwise, remember back-slash
pop ax
ret
store_slash:
mov [psep_char], al
pop ax
return
;---------------
EndProc Store_Pchar
;----------------------------------------------------------------------------
break <Path_Crunch>
;----------------------------------------------------------------------------
; PATH_CRUNCH takes a prefix from a prefix string, and a suffix from
; EXECPATH, and smooshes them into tpbuf. The caller may supply an
; additional separator to use for breaking up the path-string. Null is the
; default. Once the user-string has been formed, search is invoked to see
; what's out there.
; ENTRY:
; BH -- additional terminator character
; SI -- pointer into pathstring to be dissected
; DX -- pointer to stripped filename
; EXIT:
; AX -- non-zero (file type), zero (nothing found)
; SI -- moves along pathstring from call to call
; [search_best_buf] -- name of best file (AX non-zero)
; [tpbuf] -- clobbered
; NOTE(S):
; * Implicit in this code is the ability to specify when to search
; the current directory (if at all) through the PATH defined by
; the user, a la UNIX (e.g., PATH=;c:\bin;c:\etc searches the
; current directory before the bin and etc directories of drive c).
;---------------
Procedure Path_Crunch,NEAR
;---------------
assume ds:trangroup, es:trangroup
push BX
push CX
push DX
push DI
push SI
pushf
call store_pchar ; figure out pathname separator
mov DI, OFFSET TRANGROUP:tpbuf ; destination of concatenated string
mov SI, pathinfo[4] ; "new" pointer to start with
mov pathinfo[2], SI ; becomes "old" pointer
push DS ; save old segment pointer
push pathinfo[0] ; replace with pointer to userpath's
pop DS ; segment
xor cl,cl ;AN000; clear flag for later use 3/3/KK
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
invoke testkanj ;AN000; 3/3/KK
jz NotKanj2 ;AN000; 3/3/KK
stosb ;AN000; 3/3/KK
movsb ;AN000; 3/3/KK
MOV CL,1 ;AN000; CL=1 means latest stored char is DBCS 3/3/KK
jmp path_cr_copy ;AN000; 3/3/KK
NotKanj2: ;AN000; 3/3/KK
xor cl,cl ;AN000; CL=0 means latest stored char is SBCS 3/3/KK
stosb ; save byte in concat buffer
jmp path_cr_copy ; loop until we see a terminator
path_seg:
pop DS ; restore old data segment
mov pathinfo[4], SI ; save "new" pointer for next time
mov BL, AL ; remember if we saw null or not...
;;; REMOVE NEXT 3 LINES FOR CURDIR SPEC
xor AX, AX ; in case nothing in pathstr...
cmp DI, OFFSET TRANGROUP:tpbuf ; was there really anything in pathstr?
je path_cr_leave ; if nothing was copied, pathstr empty
path_cr_look: ; form complete pathname
mov al, psep_char ; add pathname separator for suffix
or cl,cl ;AN000; 3/3/KK
jnz path_cr_store ;AN000; this is a trailing byte of ECS code 3/3/KK
cmp al,byte ptr [di-1]
jz path_cr_l1
path_cr_store: ;AN000; 3/3/KK
stosb
path_cr_l1:
mov SI, DX
path_cr_l2:
lodsb ; 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
mov DX, OFFSET TRANGROUP:tpbuf ; and look for an appropriate file...
mov [search_error], OFFSET TRANGROUP:BADPMES_ptr
invoke search ; results are in AX & search_best_buf
path_cr_leave:
or BL, BL ; did we finish off the pathstring?
jz path_cr_empty ; null in BL means all gone...
popf ; otherwise, plenty left
clc
jmp short path_cr_exit
path_cr_empty:
popf
stc
path_cr_exit:
pop SI
pop DI
pop DX
pop CX
pop BX
ret
;---------------
EndProc Path_Crunch
;----------------------------------------------------------------------------
trancode ends
END