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 contains the routines to perform pathname incovation. Path and ; Parse share a temporary buffer and argv[] definitions. , ; 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. ; assumes that the parsed command name can be found in ; argv[0] -- in other words, should be executed prior to ; . Alternatively, the command name and appropriate ; information could be placed in argv[0], or could be ; (easily) modified to make no assumptions about where its input is found. ; Please find enclosed yet another important routine, , 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: ; : argv[0]. ; : bytes to allocate in addition to arg structure ; EXIT: ; : success flag, best pathname match in EXECPATH. ; : success flag, segment address of new memory ; NOTE(S): ; * handily turns an array index into an absolute pointer. ; The computation depends on the size of an argv[] element (arg_ele). ; * calls for chunks of the command line. ; does not function as specified; see for more details. ; * 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 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 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 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 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 , defined in . ;--------------- ;--------------- 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 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