]> wirehaze git hosting - MS-DOS.git/blob - v4.0/src/CMD/COMMAND/PATH1.ASM

wirehaze git hosting

MZ is back!
[MS-DOS.git] / v4.0 / src / CMD / COMMAND / PATH1.ASM
1 page 80,132
2 ; SCCSID = @(#)path1.asm 1.1 85/05/14
3 ; SCCSID = @(#)path1.asm 1.1 85/05/14
4 .sall
5 .xlist
6 .xcref
7 INCLUDE DOSSYM.INC
8 include comsw.asm
9 include comseg.asm
10 include comequ.asm
11 .list
12 .cref
13
14 break <Path.Asm>
15 ;----------------------------------------------------------------------------
16 ; PATH.ASM contains the routines to perform pathname incovation. Path and
17 ; Parse share a temporary buffer and argv[] definitions. <Path_Search>,
18 ; given a pathname, attempts to find a corresponding executable or batch
19 ; file on disk. Directories specified in the user's search path will be
20 ; searched for a matching file, if a match is not found in the current
21 ; directory and if the pathname is actually only an MSDOS filename.
22 ; <Path_Search> assumes that the parsed command name can be found in
23 ; argv[0] -- in other words, <Parseline> should be executed prior to
24 ; <Path_Search>. Alternatively, the command name and appropriate
25 ; information could be placed in argv[0], or <Path_Search> could be
26 ; (easily) modified to make no assumptions about where its input is found.
27 ; Please find enclosed yet another important routine, <Save_Args>, which
28 ; places the entire arg/argv[]/argbuf structure on a piece of newly
29 ; allocated memory. This is handy for for-loop processing, and anything
30 ; else that wants to save the whole shebang and then process other command
31 ; lines.
32 ;
33 ; Alan L, OS/MSDOS August 15, 1983
34 ;
35 ; ENTRY:
36 ; <Path_Search>: argv[0].
37 ; <Save_Args>: bytes to allocate in addition to arg structure
38 ; EXIT:
39 ; <Path_Search>: success flag, best pathname match in EXECPATH.
40 ; <Save_Args>: success flag, segment address of new memory
41 ; NOTE(S):
42 ; * <Argv_calc> handily turns an array index into an absolute pointer.
43 ; The computation depends on the size of an argv[] element (arg_ele).
44 ; * <Parseline> calls <cparse> for chunks of the command line. <Cparse>
45 ; does not function as specified; see <Parseline> for more details.
46 ; * <Parseline> now knows about the flags the internals of COMMAND.COM
47 ; need to know about. This extra information is stored in a switch_flag
48 ; word with each command-line argument; the switches themselves will not
49 ; appear in the resulting arg structure.
50 ; * With the exception of CARRY, flags are generally preserved across calls.
51 ;---------------
52 ; CONSTANTS:
53 ;---------------
54 DEBUGx equ FALSE ; prints out debug info
55 ;---------------
56 ; DATA:
57 ;---------------
58
59 TRANDATA SEGMENT PUBLIC BYTE ;AC000;
60 EXTRN baddrv_ptr:word
61 TRANDATA ENDS
62
63 TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
64 EXTRN arg:byte
65 EXTRN BADPMES_ptr:word
66 EXTRN curdrv:byte
67 EXTRN EXECPATH:byte
68 EXTRN search_best_buf:byte
69 EXTRN search_error:word
70 EXTRN string_ptr_2:word
71 EXTRN tpbuf:byte
72 TRANSPACE ENDS
73
74 TRANCODE SEGMENT PUBLIC BYTE ;AC000;
75
76 assume cs:trangroup, ds:trangroup, es:trangroup, ss:nothing
77
78 break <Path_Search>
79 ;------------------------------------------------------------------------------
80 ; PATH_SEARCH tries to find the file it's given, somewhere. An initial value
81 ; of *argv[0].argstartel == 0 implies that there is no command (empty line
82 ; or 'd:' or 'd:/'). This check is done in strip; otherwise, strip formats
83 ; the filename/pathname into tpbuf. Search(tpbuf) is executed to see if we
84 ; have a match, either in the current working directory if we were handed
85 ; a filename, or in the specified directory, given a pathname. If this call
86 ; fails, and we were given a pathname, then Path_Search fails. Otherwise,
87 ; Path_Crunch is repeatedly invoked on tpbuf[STARTEL] (if there's a drive
88 ; prefix, we want to skip it) for each pathstring in userpath. Success on
89 ; either the first invocation of search or on one of the succeeding calls
90 ; sets up the appropriate information for copying the successful pathname
91 ; prefix (if any) into the result buffer, followed by the successful filename
92 ; match (from [search_best_buf]). The result is returned in in EXECPATH.
93 ; ENTRY:
94 ; argv[0] -- command name and associated information
95 ; EXIT:
96 ; AX -- non-zero indicates type of file found
97 ; EXECPATH -- successful pathname (AX non-zero)
98 ; NOTE(S):
99 ; 1) Uses the temporary buffer, tpbuf, from the parse routines.
100 ; 2) Some files are more equal than others. See search: for rankings.
101 ; 3) Path_Search terminates as soon as a call to search succeeds, even
102 ; if search returns an .exe or .bat.
103 ; 5) Clobbers dma address.
104
105 pbuflen equ 128 ; length of EXECPATH
106 path_sep_char equ ';'
107
108 TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
109 EXTRN fbuf:byte
110 EXTRN pathinfo:word
111 EXTRN psep_char:byte
112 TRANSPACE ENDS
113
114 Procedure Path_Search,NEAR
115
116 push BX
117 push CX
118 push DX ; could use a "stack 'em" instruction
119 push SI
120 push DI
121 push BP
122 pushf
123 test DS:arg.argv[0].argflags, (MASK wildcard) + (MASK sw_flag)
124 jz path_search_ok
125
126 path_failure_jmp:
127 jmp path_failure ; ambiguous commands not allowed
128
129 path_search_ok:
130 call store_pchar ; figure out the pathname separator
131 mov DX, OFFSET TRANGROUP:fbuf ; clobber old dma value with
132 trap set_dma ; a pointer to our dma buffer
133 push ES
134 invoke find_path ; get a handle (ES:DI) on user path
135 mov DS:pathinfo[0], ES ; and squirrel it away
136 mov DS:pathinfo[2], DI ; "old" pathstring pointer
137 mov DS:pathinfo[4], DI ; "new" pathstring pointer
138 pop ES
139
140 mov BX, pbuflen ; copy/format argv[0] into temp buffer
141 mov SI, OFFSET TRANGROUP:EXECPATH
142 invoke strip
143 jc path_failure_jmp ; if possible, of course
144
145 mov DX, SI ; search(EXECPATH, error_message)
146 mov [search_error], OFFSET TRANGROUP:BADDRV_ptr
147 invoke search ; must do at least one search
148 or AX, AX ; find anything?
149 jz path_noinit ; failure ... search farther
150
151 mov BP, AX ; success... save filetype code
152 mov DI, OFFSET TRANGROUP:EXECPATH
153 mov SI, DS:arg.argv[0].argpointer
154 mov CX, DS:arg.argv[0].argstartel
155 sub CX, SI ; compute prefix bytes to copy
156 ;
157 ; We have the number of bytes in the prefix (up to the final component).
158 ; We need to form the complete pathname including leading drive and current
159 ; directory.
160 ;
161 ; Is there a drive letter present?
162 ;
163
164 mov ah,':'
165 cmp cx,2 ; room for drive letter?
166 jb AddDrive ; no, stick it in
167 cmp [si+1],ah ; colon present?
168 jz MoveDrive ; yes, just move it
169
170 AddDrive:
171 mov al,curdrv ; get current drive
172 add al,"A" ; convert to uppercase letter
173 stosw ; store d:
174 jmp short CheckPath
175
176 MoveDrive:
177 lodsw ; move d:
178 stosw
179 sub cx,2 ; 2 bytes less to move
180
181 CheckPath:
182 or al,20h
183 mov dl,al
184 sub dl,"a"-1 ; convert to 1-based for current dir
185 ;
186 ; Stick in beginning path char
187 ;
188 mov al,psep_char
189 stosb
190 ;
191 ; Is there a leading /? If so, then no current dir copy is necessary.
192 ; Otherwise, get current dir for DL.
193 ;
194 cmp cx,1 ; is there room for path char?
195 jb AddPath ; no, go add path
196 lodsb
197 dec cx
198 cmp al,psep_char ; is there a path separator?
199 jz MovePath ; yes, go move remainder of path
200 inc cx
201 dec si ; undo the lodsb
202
203 AddPath:
204 SaveReg <SI>
205 mov si,di ; remainder of buffer
206 trap Current_dir
207 ;
208 ; The previous current dir will succeed a previous find_first already worked.
209 ;
210 ; Find end of string.
211 ;
212 mov di,si
213 RestoreReg <SI>
214 mov al,psep_char
215 cmp byte ptr [di],0 ; root (empty dir string)?
216 jz MovePath ; yes, no need for path char
217
218 ScanEnd:
219 cmp byte ptr [dI],0 ; end of string?
220 jz FoundEnd
221 inc di
222 jmp ScanEnd
223 ;
224 ; Stick in a trailing path char
225 ;
226 FoundEnd:
227 stosb
228 ;
229 ; Move remaining part of path. Skip leading path char if present.
230 ;
231 MovePath:
232 cmp [si],al ; first char a path char?
233 jnz CopyPath
234 inc si ; move past leading char
235 dec cx ; drop from count
236
237 CopyPath:
238 jcxz CopyDone ; no chars to move!
239 rep movsb
240
241 CopyDone:
242 jmp path_success ; run off and form complete pathname
243
244 path_noinit:
245 test DS:arg.argv[0].argflags, MASK path_sep
246 jnz path_failure ; complete pathname specified ==> fail
247
248 mov BH, path_sep_char ; semicolon terminates pathstring
249 mov DX, DS:arg.argv[0].argstartel ; this is where the last element starts
250 sub DX, DS:arg.argv[0].argpointer ; form pointer into EXECPATH,
251 add DX, OFFSET TRANGROUP:EXECPATH ; skipping over drive spec, if any
252
253 path_loop:
254 call path_crunch ; pcrunch(EXECPATH, pathinfo)
255 mov BP, AX ; save filetype code
256
257 lahf ; save flags, just in case
258 or BP, BP ; did path_crunch find anything?
259 jne path_found
260 sahf ; see? needed those flags, after all!
261 jnc path_loop ; is there anything left to the path?
262
263 path_failure:
264 xor AX, AX
265 ;; jmp short path_exit ; 3/3/KK
266 jmp path_exit ;AC000; 3/3/KK
267
268 path_found: ; pathinfo[] points to winner
269 mov DI, OFFSET TRANGROUP:EXECPATH
270 mov CX, pathinfo[4] ; "new" pointer -- end of string
271 mov SI, pathinfo[2] ; "old" pointer -- beginning of string
272
273 ;
274 ; BAS Nov 20/84
275 ; Look at the pathname and expand . and .. if they are the first element
276 ; in the pathname (after the drive letter)
277 ;
278 push ES
279 push pathinfo[0]
280 pop ES
281 cmp Byte Ptr ES:[SI+2],'.' ; Look for Current dir at start of path
282 jnz path_cpy
283
284 push CX ; Save pointer to end of string
285 mov AL, ES:[SI]
286 mov [DI],AL ; Copy drive letter, :, and root char
287 mov AL, ES:[SI+1] ; to EXECPATH
288 mov [DI+1],AL
289 mov AL,psep_char
290 mov [DI+2],AL
291 push SI ; Save pointer to begining of string
292 mov DL,ES:[SI] ; Convert device letter for cur dir
293 or DL,20h
294 sub DL,"a"-1
295 mov SI,DI ; pointer to EXECPATH
296 add SI, 3 ; Don't wipe out drive and root info
297 trap Current_dir
298 invoke DStrlen ; Determine length of present info
299 add SI,CX ; Don't copy over drive and root info
300 dec SI
301 mov DI,SI ; Point to end of target string
302 pop SI ; Restore pointer to begining of string
303 add SI, 3 ; Point past drive letter, :, .
304 pop CX ; Restore pointer to end of string
305
306 path_cpy:
307 pop ES
308 sub CX, SI ; yields character count
309 push DS ; time to switch segments
310 push pathinfo[0] ; string lives in this segment
311 pop DS
312 cld
313 ;; rep movsb 3/3/KK ; copy the prefix path into EXECPATH
314
315 Kloop: ;AN000; 3/3/KK
316 lodsb ;AN000; 3/3/KK
317 stosb ;AN000; 3/3/KK
318 invoke testkanj ;AN000; 3/3/KK
319 jz NotKanj1 ;AN000; 3/3/KK
320 dec cx ;AN000; 3/3/KK
321 JCXZ PopDone ;AN000; Ignore boundary error 3/3/KK
322 movsb ;AN000; 3/3/KK
323 dec cx ;AN000; 3/3/KK
324 cmp cx,1 ;AN000; One char (the terminator) left ? 3/3/KK
325 ja Kloop ;AN000; no. 3/3/KK
326
327 PopDone: ;AN000; 3/3/KK
328 POP DS ;AN000; Yes ES:DI->terminator, last char is 3/3/KK
329 mov AL, psep_char ;AN000; KANJI 3/3/KK
330 jmp Short path_store ;AN000; 3/3/KK
331
332 NotKanj1:
333 loop Kloop
334 pop DS ; return to our segment
335 dec DI ; overwrite terminator
336 mov AL, psep_char ; with a pathname separator
337 cmp al,byte ptr [di-1]
338 jz path_success
339
340 path_store: ;AN000; 3/3/KK
341 stosb
342
343 path_success:
344 mov SI, OFFSET TRANGROUP:search_best_buf
345 xor CX, CX
346
347 path_succ_loop:
348 lodsb ; append winning filename to path
349 stosb ; (including terminating null)
350 or al,al
351 jnz path_succ_loop
352 mov AX, BP ; retrieve filetype code
353
354 path_exit:
355 popf
356 pop BP
357 pop DI
358 pop SI ; chill out...
359 pop DX
360 pop CX
361 pop BX
362 ret
363 EndProc Path_Search
364
365 break <Store_Pchar>
366 ;----------------------------------------------------------------------------
367 ; STORE_PCHAR determines the pathname-element separator and squirrels
368 ; it away. In other words, must we say '/bin/ls' or '\bin\ls'?
369 ; ENTRY:
370 ; EXIT:
371 ; NOTE(S):
372 ; * Uses <psep_char>, defined in <path_search>.
373 ;---------------
374 ;---------------
375 Procedure Store_PChar,NEAR
376 ;---------------
377
378 push AX
379 mov AL, '/' ; is the pathname-element separator
380 invoke pathchrcmp ; a regular slash?
381 jz store_slash ; if yes, remember slash
382 mov al,'\'
383 mov [psep_char], al ; otherwise, remember back-slash
384 pop ax
385 ret
386
387 store_slash:
388 mov [psep_char], al
389 pop ax
390 return
391 ;---------------
392 EndProc Store_Pchar
393 ;----------------------------------------------------------------------------
394
395 break <Path_Crunch>
396 ;----------------------------------------------------------------------------
397 ; PATH_CRUNCH takes a prefix from a prefix string, and a suffix from
398 ; EXECPATH, and smooshes them into tpbuf. The caller may supply an
399 ; additional separator to use for breaking up the path-string. Null is the
400 ; default. Once the user-string has been formed, search is invoked to see
401 ; what's out there.
402 ; ENTRY:
403 ; BH -- additional terminator character
404 ; SI -- pointer into pathstring to be dissected
405 ; DX -- pointer to stripped filename
406 ; EXIT:
407 ; AX -- non-zero (file type), zero (nothing found)
408 ; SI -- moves along pathstring from call to call
409 ; [search_best_buf] -- name of best file (AX non-zero)
410 ; [tpbuf] -- clobbered
411 ; NOTE(S):
412 ; * Implicit in this code is the ability to specify when to search
413 ; the current directory (if at all) through the PATH defined by
414 ; the user, a la UNIX (e.g., PATH=;c:\bin;c:\etc searches the
415 ; current directory before the bin and etc directories of drive c).
416 ;---------------
417 Procedure Path_Crunch,NEAR
418 ;---------------
419 push BX
420 push CX
421 push DX
422 push DI
423 push SI
424 pushf
425 call store_pchar ; figure out pathname separator
426 mov DI, OFFSET TRANGROUP:tpbuf ; destination of concatenated string
427 mov SI, pathinfo[4] ; "new" pointer to start with
428 mov pathinfo[2], SI ; becomes "old" pointer
429 push DS ; save old segment pointer
430 push pathinfo[0] ; replace with pointer to userpath's
431 pop DS ; segment
432 xor cl,cl ;AN000; clear flag for later use 3/3/KK
433
434 path_cr_copy:
435 lodsb ; get a pathname byte
436 or al,al ; check for terminator(s)
437 jz path_seg ; null terminates segment & pathstring
438 cmp AL, BH
439 jz path_seg ; BH terminates a pathstring segment
440 invoke testkanj ;AN000; 3/3/KK
441 jz NotKanj2 ;AN000; 3/3/KK
442 stosb ;AN000; 3/3/KK
443 movsb ;AN000; 3/3/KK
444 MOV CL,1 ;AN000; CL=1 means latest stored char is DBCS 3/3/KK
445 jmp path_cr_copy ;AN000; 3/3/KK
446
447 NotKanj2: ;AN000; 3/3/KK
448 xor cl,cl ;AN000; CL=0 means latest stored char is SBCS 3/3/KK
449 stosb ; save byte in concat buffer
450 jmp path_cr_copy ; loop until we see a terminator
451
452 path_seg:
453 pop DS ; restore old data segment
454 mov pathinfo[4], SI ; save "new" pointer for next time
455 mov BL, AL ; remember if we saw null or not...
456 ;;; REMOVE NEXT 3 LINES FOR CURDIR SPEC
457 xor AX, AX ; in case nothing in pathstr...
458 cmp DI, OFFSET TRANGROUP:tpbuf ; was there really anything in pathstr?
459 je path_cr_leave ; if nothing was copied, pathstr empty
460
461 path_cr_look: ; form complete pathname
462 mov al, psep_char ; add pathname separator for suffix
463 or cl,cl ;AN000; 3/3/KK
464 jnz path_cr_store ;AN000; this is a trailing byte of ECS code 3/3/KK
465 cmp al,byte ptr [di-1]
466 jz path_cr_l1
467
468 path_cr_store: ;AN000; 3/3/KK
469 stosb
470
471 path_cr_l1:
472 mov SI, DX
473
474 path_cr_l2:
475 lodsb ; tack the stripped filename onto
476 stosb ; the end of the path, up to and
477 or AL, AL ; including the terminating null
478 jnz path_cr_l2
479 mov DX, OFFSET TRANGROUP:tpbuf ; and look for an appropriate file...
480 mov [search_error], OFFSET TRANGROUP:BADPMES_ptr
481 invoke search ; results are in AX & search_best_buf
482
483 path_cr_leave:
484 or BL, BL ; did we finish off the pathstring?
485 jz path_cr_empty ; null in BL means all gone...
486 popf ; otherwise, plenty left
487 clc
488 jmp short path_cr_exit
489
490 path_cr_empty:
491 popf
492 stc
493
494 path_cr_exit:
495 pop SI
496 pop DI
497 pop DX
498 pop CX
499 pop BX
500 ret
501 ;---------------
502 EndProc Path_Crunch
503 ;----------------------------------------------------------------------------
504
505
506 trancode ends
507 END