1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
|
page 80,132
; SCCSID = @(#)path1.asm 1.1 85/05/14
; SCCSID = @(#)path1.asm 1.1 85/05/14
.sall
.xlist
.xcref
INCLUDE DOSSYM.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 128 ; length of EXECPATH
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
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?
;
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
cmp Byte Ptr ES:[SI+2],'.' ; Look for Current dir at start of path
jnz path_cpy
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
;---------------
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
;---------------
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
|