2 ; SCCSID = @(#)path2.asm 1.1 85/05/14
3 ; SCCSID = @(#)path2.asm 1.1 85/05/14
15 DATARES
SEGMENT PUBLIC BYTE
21 ;----------------------------------------------------------------------------
22 ; PATH.ASM contains the routines to perform pathname incovation. Path and
23 ; Parse share a temporary buffer and argv[] definitions. <Path_Search>,
24 ; given a pathname, attempts to find a corresponding executable or batch
25 ; file on disk. Directories specified in the user's search path will be
26 ; searched for a matching file, if a match is not found in the current
27 ; directory and if the pathname is actually only an MSDOS filename.
28 ; <Path_Search> assumes that the parsed command name can be found in
29 ; argv[0] -- in other words, <Parseline> should be executed prior to
30 ; <Path_Search>. Alternatively, the command name and appropriate
31 ; information could be placed in argv[0], or <Path_Search> could be
32 ; (easily) modified to make no assumptions about where its input is found.
33 ; Please find enclosed yet another important routine, <Save_Args>, which
34 ; places the entire arg/argv[]/argbuf structure on a piece of newly
35 ; allocated memory. This is handy for for-loop processing, and anything
36 ; else that wants to save the whole shebang and then process other command
39 ; Alan L, OS/MSDOS August 15, 1983
42 ; <Path_Search>: argv[0].
43 ; <Save_Args>: bytes to allocate in addition to arg structure
45 ; <Path_Search>: success flag, best pathname match in EXECPATH.
46 ; <Save_Args>: success flag, segment address of new memory
48 ; * <Argv_calc> handily turns an array index into an absolute pointer.
49 ; The computation depends on the size of an argv[] element (arg_ele).
50 ; * <Parseline> calls <cparse> for chunks of the command line. <Cparse>
51 ; does not function as specified; see <Parseline> for more details.
52 ; * <Parseline> now knows about the flags the internals of COMMAND.COM
53 ; need to know about. This extra information is stored in a switch_flag
54 ; word with each command-line argument; the switches themselves will not
55 ; appear in the resulting arg structure.
56 ; * With the exception of CARRY, flags are generally preserved across calls.
60 DEBUGx equ FALSE
; prints out debug info
65 TRANSPACE
SEGMENT PUBLIC BYTE ;AC000;
67 EXTRN BADPMES_ptr
:word
70 EXTRN ext_entered
:byte ;AN005;
74 EXTRN string_ptr_2
:word
78 TRANCODE
SEGMENT PUBLIC BYTE ;AC000;
80 assume
cs:trangroup
, ds:trangroup
, es:trangroup
, ss:nothing
84 ;----------------------------------------------------------------------------
85 ; SEARCH, when given a pathname, attempts to find a file with
86 ; one of the following extensions: .com, .exe, .bat (highest to
87 ; lowest priority). Where conflicts arise, the extension with
88 ; the highest priority is favored.
90 ; DX -- pointer to null-terminated pathname
91 ; fbuf -- dma buffer for findfirst/next
93 ; AX -- 8) file found with .com extension
94 ; 4) file found with .exe extension
95 ; 2) file found with .bat extension
96 ; 0) no such file to be found
97 ; (if AX is non-zero:)
98 ; [search_best] identical to AX
99 ; [search_best_buf] null-terminated filename
101 ; 1) Requires caller to have allocated a dma buffer and executed a setdma.
105 search_file_not_found equ
0
117 TRANSPACE
SEGMENT PUBLIC BYTE ;AC000;
118 EXTRN search_best
:byte
119 EXTRN search_best_buf
:byte
120 EXTRN search_curdir_buf
:byte
121 EXTRN search_error
:word
125 Procedure Search
,NEAR
133 push DX ; check drivespec (save pname ptr)
134 mov DI, DX ; working copy of pathname
135 mov SI, OFFSET TRANGROUP
:search_curdir_buf
136 xor DX, DX ; zero means current drive
137 cmp BYTE PTR [DI+1],':' ; is there a drive spec?
139 mov DL, [DI] ; get the drive byte
140 and DL, NOT 20H
; uppercase the sucker
141 sub DL, '@' ; and convert to drive number
144 trap Current_Dir
; can we get the drive's current
145 pop DX ; directory? If we can't we'll
146 jc search_invalid_drive
; assume it's a bad drive...
148 mov CX, search_attr
; filetypes to search for
149 trap Find_First
; request first match, if any
151 mov search_best
, search_file_not_found
152 mov [search_best_buf
], ANULL
; nothing's been found, yet
155 call search_ftype
; determine if .com, &c...
156 cmp AL, search_best
; better than what we've found so far?
157 jle search_next
; no, look for another
158 mov search_best
, AL ; found something... save its code
159 mov SI, OFFSET TRANGROUP
:fbuf
.find_buf_pname
160 mov DI, OFFSET TRANGROUP
:search_best_buf
161 mov CX, fname_max_len
163 rep movsb ; save complete pathname representation
164 cmp AL, search_com
; have we found the best of all?
167 search_next: ; keep on looking
169 trap Find_Next
; next match
172 search_done: ; it's all over with...
173 mov AL, search_best
; pick best to return with
174 cmp ext_entered
,1 ;AN005; Did user request a specific ext?
175 jz search_exit
;AN005; no - exit
176 mov al,ext_entered
;AN005; yes - get the real file type back
177 mov search_best
,al ;AN005; save the real file type
178 jmp short search_exit
180 search_invalid_drive: ; Tell the user path/drive
181 mov DX, [search_error
] ; appropriate error message
182 invoke std_printf
; and pretend no file found
184 search_no_file: ; couldn't find a match
185 mov AX, search_file_not_found
196 ;----------------------------------------------------------------------------
200 ;----------------------------------------------------------------------------
201 ; SEARCH_FTYPE determines the type of a file by examining its extension.
203 ; fbuf -- dma buffer containing filename
205 ; AX -- file code, as given in search header
207 ; * Implicit assumption that NULL == search_file_not_found
211 TRANDATA
SEGMENT PUBLIC BYTE ;AC000;
212 extrn comext
:byte,exeext
:byte,batext
:byte
215 Procedure Search_Ftype
,NEAR
219 mov AX, ANULL
; find the end of the filename
220 mov DI, OFFSET TRANGROUP
:fbuf
.find_buf_pname
221 mov CX, fname_max_len
223 repnz scasb ; search for the terminating null
224 jnz ftype_exit
; weird... no null byte at end
225 sub di,5 ; . + E + X + T + NULL
229 mov si,offset trangroup
:comext
235 mov AX, search_com
; success!
241 ftype_exe: ; still looking... now for '.exe'
243 mov si,offset trangroup
:exeext
248 mov AX, search_exe
; success!
254 ftype_bat: ; still looking... now for '.bat'
256 mov si,offset trangroup
:batext
261 mov AX, search_bat
; success!
264 ftype_fail: ; file doesn't match what we need
268 cmp ext_entered
,1 ;AN005; was an extension entered?
269 jz ftype_done
;AN005; no - exit
270 cmp ax,ANULL
;AN005; was any match found
271 jz ftype_done
;AN005; no - exit
272 mov ext_entered
,al ;AN005; save the match type found
273 mov AX, search_com
;AN005; send back best was found to stop search
282 ;----------------------------------------------------------------------------
286 ;----------------------------------------------------------------------------
287 ; STRIP copies the source string (argv[0]) into the destination buffer,
288 ; replacing any extension with wildcards.
290 ; BX -- maximum length of destination buffer
291 ; DS:SI -- address of destination buffer
292 ; argv[0] -- command name to be stripped
294 ; CF -- set if failure, clear if successful
307 mov ext_entered
,1 ;AN005; assume no extension on file name
308 mov DX, DS:arg
.argv
[0].argpointer
; save pointer to beginning of argstring
309 mov DI, DS:arg
.argv
[0].argstartel
; beginning of last pathname element
310 cmp BYTE PTR [DI], 0 ; *STARTEL == NULL means no command
312 mov CX, DX ; compute where end of argstring lies
313 add CX, DS:arg
.argv
[0].arglen
314 sub CX, DI ; and then find length of last element
315 inc CX ; include null as well
316 mov AL, dot
; let's find the filetype extension
318 repnz scasb ; wind up pointing to either null or dot
319 jcxz process_ext
;AN005; if no extension found, just continue
320 mov ext_entered
,0 ;AN005; we found an extension
321 mov al,ANULL
;AN005; continue scanning until the
322 repnz scasb ;AN005; end of line is reached.
325 mov CX, DI ; pointer to end of argstring yields
326 sub CX, DX ; number of bytes to be copied
327 sub BX, 4 ; can argstring fit into dest. buffer?
329 jg strip_error
; if not, we must have a bad pathname
330 mov DI, SI ; destination buffer
331 mov SI, DX ; source is beginning of pathname
333 rep movsb ; SI=arg,DI=buffer,CX=argend-argbeg
334 cmp ext_entered
,1 ;AN005; if an extension was entered
335 jnz skip_wilds
;AN005; don't set up wildcard ext.
337 dec DI ; overwrite null or dot
339 mov AL, wildchar
; now add wildcards
343 mov AL, ANULL
; and a terminating null
365 ;----------------------------------------------------------------------------
369 ;----------------------------------------------------------------------------
370 ; SAVE_ARGS attempts to preserve the existing argv[]/argvcnt/argbuffer
371 ; structure in newly allocated memory. The argv[] structure is found at the
372 ; beginning of this area. The caller indicates how much extra space is
373 ; needed in the resulting structure; Save_Args returns a segment number and
374 ; an offset into that area, indicating where the caller may preserve its own
375 ; data. Note that <argvcnt> can be found at <offset-2>.
377 ; BX -- size (in bytes) of extra area to allocate
379 ; AX -- segment of new area.
380 ; CF -- set if unable to save a copy.
382 ; 1) The allocated area will be AT LEAST the size requested -- since
383 ; the underlying MSDOS call, <alloc> returns an integral number of
385 ; 2) It is an error if MSDOS can't allocate AT LEAST as much memory
386 ; as the caller of Save_Args requests.
387 ; 3) AX is undefined if CF indicates an error.
389 Procedure Save_Args
,NEAR
398 add BX, SIZE arg_unit
+ 0FH ; space for arg structure, round up
399 mov CL, 4 ; to paragraph size and convert
400 shr BX, CL ; size in bytes to size in paragraphs
403 mov BP, AX ; save segment id
404 push ES ; save TRANGROUP address
405 mov ES, AX ; switch to new memory segment
407 mov CX, SIZE arg_unit
; get back structure size
408 xor DI, DI ; destination is new memory area
409 mov SI, OFFSET TRANGROUP
:arg
; source is arg structure
410 rep movsb ; move that sucker!
411 mov CX, arg
.argvcnt
; adjust argv pointers
412 xor AX, AX ; base address for argv_calc
413 mov SI, OFFSET TRANGROUP
:arg
.argbuf
- OFFSET arg_unit
.argbuf
416 dec CX ; exhausted all args?
418 mov BX, CX ; get arg index and
419 invoke argv_calc
; convert to a pointer
420 mov DX, DS:arg
.argv
[BX].argpointer
421 sub DX, SI ; adjust argpointer
422 mov ES:argv
[BX].argpointer
, DX
423 mov DX, DS:arg
.argv
[BX].argstartel
424 sub DX, SI ; and adjust argstartel
425 mov ES:argv
[BX].argstartel
, DX
426 mov DX, DS:arg
.argv
[BX].arg_ocomptr
427 sub DX, SI ; and adjust arg_ocomptr
428 mov ES:argv
[BX].arg_ocomptr
, DX
432 pop ES ; back we go to TRANGROUP
434 mov AX, BP ; restore segment id
455 ;----------------------------------------------------------------------------