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
|
page 80,132
; SCCSID = @(#)path2.asm 1.1 85/05/14
; SCCSID = @(#)path2.asm 1.1 85/05/14
.sall
.xlist
.xcref
INCLUDE DOSSYM.INC
include comsw.asm
include comseg.asm
include comequ.asm
.list
.cref
DATARES SEGMENT PUBLIC BYTE
EXTRN FORFLAG:BYTE
DATARES ENDS
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:
;---------------
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
EXTRN arg:byte
EXTRN BADPMES_ptr:word
EXTRN curdrv:byte
EXTRN EXECPATH:byte
EXTRN ext_entered:byte ;AN005;
EXTRN fbuf:byte
EXTRN pathinfo:word
EXTRN psep_char:byte
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 <Search>
;----------------------------------------------------------------------------
; SEARCH, when given a pathname, attempts to find a file with
; one of the following extensions: .com, .exe, .bat (highest to
; lowest priority). Where conflicts arise, the extension with
; the highest priority is favored.
; ENTRY:
; DX -- pointer to null-terminated pathname
; fbuf -- dma buffer for findfirst/next
; EXIT:
; AX -- 8) file found with .com extension
; 4) file found with .exe extension
; 2) file found with .bat extension
; 0) no such file to be found
; (if AX is non-zero:)
; [search_best] identical to AX
; [search_best_buf] null-terminated filename
; NOTES:
; 1) Requires caller to have allocated a dma buffer and executed a setdma.
;---------------
; CONSTANTS:
;---------------
search_file_not_found equ 0
search_com equ 8
search_exe equ 4
search_bat equ 2
fname_len equ 8
fname_max_len equ 13
dot equ '.'
wildchar equ '?'
;---------------
; DATA:
;---------------
TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
EXTRN search_best:byte
EXTRN search_best_buf:byte
EXTRN search_curdir_buf:byte
EXTRN search_error:word
TRANSPACE ENDS
;---------------
Procedure Search,NEAR
;---------------
push CX
push DX
push DI
push SI
pushf
push DX ; check drivespec (save pname ptr)
mov DI, DX ; working copy of pathname
mov SI, OFFSET TRANGROUP:search_curdir_buf
xor DX, DX ; zero means current drive
cmp BYTE PTR [DI+1],':' ; is there a drive spec?
jne search_dir_check
mov DL, [DI] ; get the drive byte
and DL, NOT 20H ; uppercase the sucker
sub DL, '@' ; and convert to drive number
search_dir_check:
trap Current_Dir ; can we get the drive's current
pop DX ; directory? If we can't we'll
jc search_invalid_drive ; assume it's a bad drive...
mov CX, search_attr ; filetypes to search for
trap Find_First ; request first match, if any
jc search_no_file
mov search_best, search_file_not_found
mov [search_best_buf], ANULL ; nothing's been found, yet
search_loop:
call search_ftype ; determine if .com, &c...
cmp AL, search_best ; better than what we've found so far?
jle search_next ; no, look for another
mov search_best, AL ; found something... save its code
mov SI, OFFSET TRANGROUP:fbuf.find_buf_pname
mov DI, OFFSET TRANGROUP:search_best_buf
mov CX, fname_max_len
cld
rep movsb ; save complete pathname representation
cmp AL, search_com ; have we found the best of all?
je search_done
search_next: ; keep on looking
mov CX, search_attr
trap Find_Next ; next match
jnc search_loop
search_done: ; it's all over with...
mov AL, search_best ; pick best to return with
cmp ext_entered,1 ;AN005; Did user request a specific ext?
jz search_exit ;AN005; no - exit
mov al,ext_entered ;AN005; yes - get the real file type back
mov search_best,al ;AN005; save the real file type
jmp short search_exit
search_invalid_drive: ; Tell the user path/drive
mov DX, [search_error] ; appropriate error message
invoke std_printf ; and pretend no file found
search_no_file: ; couldn't find a match
mov AX, search_file_not_found
search_exit:
popf
pop SI
pop DI
pop DX
pop CX
ret
;---------------
EndProc Search
;----------------------------------------------------------------------------
break <Search_Ftype>
;----------------------------------------------------------------------------
; SEARCH_FTYPE determines the type of a file by examining its extension.
; ENTRY:
; fbuf -- dma buffer containing filename
; EXIT:
; AX -- file code, as given in search header
; NOTE(S):
; * Implicit assumption that NULL == search_file_not_found
;---------------
; DATA:
;---------------
TRANDATA SEGMENT PUBLIC BYTE ;AC000;
extrn comext:byte,exeext:byte,batext:byte
trandata ends
;---------------
Procedure Search_Ftype,NEAR
;---------------
push DI
push si
mov AX, ANULL ; find the end of the filename
mov DI, OFFSET TRANGROUP:fbuf.find_buf_pname
mov CX, fname_max_len
cld
repnz scasb ; search for the terminating null
jnz ftype_exit ; weird... no null byte at end
sub di,5 ; . + E + X + T + NULL
;
; Compare .COM
;
mov si,offset trangroup:comext
mov ax,di
cmpsw
jnz ftype_exe
cmpsw
jnz ftype_exe
mov AX, search_com ; success!
jmp short ftype_exit
;
; Compare .EXE
;
ftype_exe: ; still looking... now for '.exe'
mov di,ax
mov si,offset trangroup:exeext
cmpsw
jnz ftype_bat
cmpsw
jnz ftype_bat
mov AX, search_exe ; success!
jmp short ftype_exit
;
; Compare .BAT
;
ftype_bat: ; still looking... now for '.bat'
mov di,ax
mov si,offset trangroup:batext
cmpsw
jnz ftype_fail
cmpsw
jnz ftype_fail
mov AX, search_bat ; success!
jmp short ftype_exit
ftype_fail: ; file doesn't match what we need
mov ax,ANULL
ftype_exit:
cmp ext_entered,1 ;AN005; was an extension entered?
jz ftype_done ;AN005; no - exit
cmp ax,ANULL ;AN005; was any match found
jz ftype_done ;AN005; no - exit
mov ext_entered,al ;AN005; save the match type found
mov AX, search_com ;AN005; send back best was found to stop search
ftype_done: ;AN005;
pop SI
pop DI
ret
;---------------
EndProc Search_Ftype
;----------------------------------------------------------------------------
break <Strip>
;----------------------------------------------------------------------------
; STRIP copies the source string (argv[0]) into the destination buffer,
; replacing any extension with wildcards.
; ENTRY:
; BX -- maximum length of destination buffer
; DS:SI -- address of destination buffer
; argv[0] -- command name to be stripped
; EXIT:
; CF -- set if failure, clear if successful
; NOTE(S):
;---------------
Procedure Strip,NEAR
;---------------
push AX
push BX
push CX
push DX
push DI
push SI
pushf
mov ext_entered,1 ;AN005; assume no extension on file name
mov DX, DS:arg.argv[0].argpointer ; save pointer to beginning of argstring
mov DI, DS:arg.argv[0].argstartel ; beginning of last pathname element
cmp BYTE PTR [DI], 0 ; *STARTEL == NULL means no command
jz strip_error
mov CX, DX ; compute where end of argstring lies
add CX, DS:arg.argv[0].arglen
sub CX, DI ; and then find length of last element
inc CX ; include null as well
mov AL, dot ; let's find the filetype extension
cld
repnz scasb ; wind up pointing to either null or dot
jcxz process_ext ;AN005; if no extension found, just continue
mov ext_entered,0 ;AN005; we found an extension
mov al,ANULL ;AN005; continue scanning until the
repnz scasb ;AN005; end of line is reached.
process_ext: ;AN005;
mov CX, DI ; pointer to end of argstring yields
sub CX, DX ; number of bytes to be copied
sub BX, 4 ; can argstring fit into dest. buffer?
cmp CX, BX
jg strip_error ; if not, we must have a bad pathname
mov DI, SI ; destination buffer
mov SI, DX ; source is beginning of pathname
cld
rep movsb ; SI=arg,DI=buffer,CX=argend-argbeg
cmp ext_entered,1 ;AN005; if an extension was entered
jnz skip_wilds ;AN005; don't set up wildcard ext.
dec DI ; overwrite null or dot
stosb ; with a dot
mov AL, wildchar ; now add wildcards
stosb
stosb
stosb
mov AL, ANULL ; and a terminating null
stosb
skip_wilds: ;AN005;
popf
clc ; chill out...
jmp short strip_exit
strip_error:
popf
stc
strip_exit:
pop SI
pop DI
pop DX
pop CX
pop BX
pop AX
ret
;---------------
EndProc Strip
;----------------------------------------------------------------------------
break <Save_Args>
;----------------------------------------------------------------------------
; SAVE_ARGS attempts to preserve the existing argv[]/argvcnt/argbuffer
; structure in newly allocated memory. The argv[] structure is found at the
; beginning of this area. The caller indicates how much extra space is
; needed in the resulting structure; Save_Args returns a segment number and
; an offset into that area, indicating where the caller may preserve its own
; data. Note that <argvcnt> can be found at <offset-2>.
; ENTRY:
; BX -- size (in bytes) of extra area to allocate
; EXIT:
; AX -- segment of new area.
; CF -- set if unable to save a copy.
; NOTE(S):
; 1) The allocated area will be AT LEAST the size requested -- since
; the underlying MSDOS call, <alloc> returns an integral number of
; paragraphs.
; 2) It is an error if MSDOS can't allocate AT LEAST as much memory
; as the caller of Save_Args requests.
; 3) AX is undefined if CF indicates an error.
;---------------
Procedure Save_Args,NEAR
;---------------
push BX
push CX
push DX
push DI
push SI
push BP
pushf
add BX, SIZE arg_unit + 0FH ; space for arg structure, round up
mov CL, 4 ; to paragraph size and convert
shr BX, CL ; size in bytes to size in paragraphs
trap Alloc
jc save_error
mov BP, AX ; save segment id
push ES ; save TRANGROUP address
mov ES, AX ; switch to new memory segment
assume ES:nothing
mov CX, SIZE arg_unit ; get back structure size
xor DI, DI ; destination is new memory area
mov SI, OFFSET TRANGROUP:arg ; source is arg structure
rep movsb ; move that sucker!
mov CX, arg.argvcnt ; adjust argv pointers
xor AX, AX ; base address for argv_calc
mov SI, OFFSET TRANGROUP:arg.argbuf - OFFSET arg_unit.argbuf
save_ptr_loop:
dec CX ; exhausted all args?
jl save_done
mov BX, CX ; get arg index and
invoke argv_calc ; convert to a pointer
mov DX, DS:arg.argv[BX].argpointer
sub DX, SI ; adjust argpointer
mov ES:argv[BX].argpointer, DX
mov DX, DS:arg.argv[BX].argstartel
sub DX, SI ; and adjust argstartel
mov ES:argv[BX].argstartel, DX
mov DX, DS:arg.argv[BX].arg_ocomptr
sub DX, SI ; and adjust arg_ocomptr
mov ES:argv[BX].arg_ocomptr, DX
jmp save_ptr_loop
save_done:
pop ES ; back we go to TRANGROUP
assume ES:trangroup
mov AX, BP ; restore segment id
jmp short save_ok
save_error:
popf
stc
jmp short save_exit
save_ok:
popf
clc
save_exit:
pop BP
pop SI
pop DI
pop DX
pop CX
pop BX
ret
;---------------
EndProc Save_Args
;----------------------------------------------------------------------------
trancode ends
END
|