472 lines
14 KiB
NASM
472 lines
14 KiB
NASM
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 it
|
||
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 it
|
||
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
|
||
|