summaryrefslogtreecommitdiff
path: root/v4.0/src/CMD/COMMAND/PATH1.ASM
blob: 4567174642f686609c95052fe323b3875a13735c (plain) (blame)
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