summaryrefslogtreecommitdiff
path: root/v4.0/src/CMD/COMMAND/TFOR.ASM
diff options
context:
space:
mode:
authorGravatar Mark Zbikowski2024-04-25 21:24:10 +0100
committerGravatar Microsoft Open Source2024-04-25 22:32:27 +0000
commit2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch)
tree80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/CMD/COMMAND/TFOR.ASM
parentMerge pull request #430 from jpbaltazar/typoptbr (diff)
downloadms-dos-main.tar.gz
ms-dos-main.tar.xz
ms-dos-main.zip
MZ is back!HEADmain
Diffstat (limited to 'v4.0/src/CMD/COMMAND/TFOR.ASM')
-rw-r--r--v4.0/src/CMD/COMMAND/TFOR.ASM551
1 files changed, 551 insertions, 0 deletions
diff --git a/v4.0/src/CMD/COMMAND/TFOR.ASM b/v4.0/src/CMD/COMMAND/TFOR.ASM
new file mode 100644
index 0000000..8454d5c
--- /dev/null
+++ b/v4.0/src/CMD/COMMAND/TFOR.ASM
@@ -0,0 +1,551 @@
1 page 80,132
2; SCCSID = @(#)tfor.asm 4.1 85/09/17
3; SCCSID = @(#)tfor.asm 4.1 85/09/17
4TITLE Part3 COMMAND Transient Routines
5
6; For loop processing routines
7
8
9.xlist
10.xcref
11 include comsw.asm
12 INCLUDE DOSSYM.INC
13 INCLUDE DEVSYM.INC
14 include comseg.asm
15 include comequ.asm
16.list
17.cref
18
19
20DATARES SEGMENT PUBLIC BYTE ;AC000;
21 EXTRN BATCH:WORD
22 EXTRN ECHOFLAG:BYTE
23 EXTRN FORFLAG:BYTE
24 EXTRN FORPTR:WORD
25 EXTRN NEST:WORD
26 EXTRN NULLFLAG:BYTE
27 EXTRN PIPEFILES:BYTE
28 EXTRN SINGLECOM:WORD
29DATARES ENDS
30
31TRANDATA SEGMENT PUBLIC BYTE ;AC000;
32 EXTRN Extend_buf_ptr:word ;AN000;
33 extrn fornestmes_ptr:word
34 EXTRN msg_disp_class:byte ;AN000;
35 extrn string_buf_ptr:word
36TRANDATA ENDS
37
38TRANSPACE SEGMENT PUBLIC BYTE ;AC000;
39 extrn arg:byte ; the arg structure!
40 EXTRN COMBUF:BYTE
41 EXTRN RESSEG:WORD
42 EXTRN string_ptr_2:word
43TRANSPACE ENDS
44
45TRANCODE SEGMENT PUBLIC BYTE
46
47ASSUME CS:TRANGROUP,DS:NOTHING,ES:NOTHING,SS:NOTHING
48
49 EXTRN cerror:near
50 EXTRN docom:near
51 EXTRN docom1:near
52 EXTRN forerror:near
53 EXTRN tcommand:near
54
55 PUBLIC $for
56 PUBLIC forproc
57
58
59; All batch proccessing has DS set to segment of resident portion
60ASSUME DS:RESGROUP,ES:TRANGROUP
61
62
63FORTERM:
64 push cs ;AN037; Get local segment into
65 pop ds ;AN037; DS, ES
66 push cs ;AN037;
67 pop es ;AN037;
68 call ForOff
69 mov ds,ResSeg
70ASSUME DS:RESGROUP
71 CMP [SINGLECOM],0FF00H
72 JNZ BATCRLF
73 CMP NEST,0 ;G See if we have nested batch files
74 JNZ BATCRLF ;G Yes - don't exit just yet
75 MOV [SINGLECOM],-1 ; Cause a terminate
76 JMP SHORT NOFORP2
77
78BATCRLF:
79 test [ECHOFLAG],1 ;G Is echo on?
80 JZ NOFORP2 ;G no - exit
81 TEST [BATCH], -1 ;G print CRLF if in batch
82 JZ NOFORP2 ;G
83 invoke CRLF2
84
85NOFORP2:
86 JMP TCOMMAND
87
88
89;------
90; For-loop processing. For loops are of the form:
91; for %<loop-variable> in (<list>) do <command>
92; where <command> may contain references of the form %<variable>, which are
93; later substituted with the items in <list>. The for-loop structure is
94; set-up by the procedure '$for'; successive calls to 'forproc' execute
95; <command> once for each item in <list>. All of the information needed for
96; loop processing is stored on a piece of memory gotten from 'alloc'. This
97; structure is actually fairly large, on the order of 700 bytes, and includes
98; a complete copy of the original command-line structure as parsed by
99; 'parseline', loop control variables, and a dma buffer for the
100; 'FindFirst/FindNext' expansion of wildcard filenames in <list>. When loop
101; processing has completed, this chunk of memory is returned to the system.
102;
103; All of the previously defined variables, in 'datares', used for loop
104; processing may be erased. Only one, (DW) ForPtr, need be allocated.
105;
106; The error message, 'for_alloc_mes', should be moved into the file
107; containing all of the other error messages.
108;
109; Referencing the allocated for-loop structure is a little tricky.
110; At the moment, a byte is defined as part of a new segment, 'for_segment'.
111; When 'forproc' actually runs, ES and DS are set to point to the base of the
112; new chunk of memory. References to this byte, 'f', thus assemble correctly
113; as offsets of ES or DS. 'f' would not be necessary, except that the
114; assembler translates an instruction such as 'mov AX, [for_minarg]' as an
115; immediate move of the offset of 'for_minarg' into AX. In other words, in
116; terms of PDP-11 mnemonics, the assembler ACTUALLY assembles
117; mov AX, #for_minarg ; AX := 02CA (for example)
118; instead of
119; mov AX, for_minarg ; AX := [02CA] (contents of 02CA)
120; By using 'f', we pretend that we are actually referencing an allocated
121; structure, and the assembler coughs up the code we want. Notice that it
122; doesn't matter whether we put brackets around the location or not -- the
123; assembler is "smart" enough to know that we want an address instead of the
124; contents of that location.
125;
126; Finally, there now exists the potential to easily implement nested loops.
127; One method would be to have a link field in each for-structure pointing to
128; its parent. Variable references that couldn't be resolved in the local
129; frame would cause a search of prior frames. For-structures would still be
130; allocated and released in exactly the same fashion. The only limit on the
131; number of nested loops would be memory size (although at 700 bytes a pop,
132; memory wouldn't last THAT long). Alternately, a small structure could be
133; maintained in the resident data area. This structure would be an array of
134; control-variable names and pointers to for-structure blocks. This would
135; greatly speed up the resolution of non-local variable references. However,
136; since space in the resident is precious, we would have to compromise on a
137; "reasonable" level of nesting -- 10, 16, 32 levels, whatever. For-structure
138; allocation and de-allocation would have to be modified slightly to take this
139; new structure into account.
140;
141; Oops, just one more thing. Forbuf need not be a part of the for-structure.
142; It could just as well be one structure allocated in 'transpace'. Actually,
143; it may be easier to allocate it as part of 'for_segment'.
144;------
145
146 include fordata.asm
147
148$for_exit:
149 jmp forterm ; exceeding maxarg means all done
150
151forproc:
152assume DS:resgroup
153 mov AX, [ForPtr]
154 mov DS, AX
155 mov ES, AX ; operate in for-info area
156assume DS:for_segment, ES:for_segment
157
158 mov DX, OFFSET fordma
159 trap Set_Dma
160for_begin:
161 cmp f.for_expand, 0 ; non-zero for_expand equals FALSE
162 je for_begin1
163 inc f.for_minarg
164for_begin1:
165 mov BX, f.for_minarg ; current item in <list> to examine
166 cmp BX, f.for_maxarg
167 jg $for_exit ; exceeding maxarg means all done
168 mov AX, OFFSET for_args.argv
169 invoke argv_calc ; compute argv[x] address
170
171 mov CX, [BX].argstartel
172 mov DX, [BX].argpointer
173 test [bx].argflags,00000100b ; Is there a path separator in this arg?
174 jnz forsub ; Yes, argstartel should be correct
175 mov si, [BX].argpointer
176 mov al,lparen
177 cmp byte ptr [si-1],al ; If the current token is the first
178 jnz forsub ; one in the list and originally had
179 inc cx ; the opening paren as its first char,
180 ; the argstartel ptr needs to be
181 ; advanced passed it before the prefix
182 ; length is computed.
183 mov al,':'
184 cmp byte ptr [si+1],al ; If the token begins with "(d:",
185 jnz forsub ; argstartel has to be moved over the
186 add cx,2 ; rest of the prefix as well.
187
188forsub:
189 sub CX, DX ; compute length of pathname prefix
190 cmp f.for_expand, 0 ; are we still expanding a name?
191 je for_find_next ; if so, get next matching filename
192
193 test [BX].argflags, MASK wildcard
194 jnz for_find_first ; should we expand THIS (new) arg?
195 mov CX, [BX].arglen ; else, just copy all of it directly
196 jmp for_smoosh
197
198for_find_first:
199 PUSH CX
200 XOR CX,CX
201 trap Find_First ; and search for first filename match
202 POP CX
203 jmp for_result
204for_find_next:
205 trap Find_Next ; search for next filename match
206
207for_result:
208 mov AX, -1 ; assume worst case
209 jc forCheck
210 mov ax,0
211forCheck: ; Find* returns 0 for SUCCESS
212 mov f.FOR_EXPAND, AX ; record success of findfirst/next
213 or AX, AX ; anything out there?
214 jnz for_begin ; if not, try next arg
215
216for_smoosh:
217 mov SI, [BX].argpointer ; copy argv[arg][0,CX] into destbuf
218 mov DI, OFFSET forbuf ; some days this will be the entire
219 rep movsb ; arg, some days just the path prefix
220
221 cmp f.FOR_EXPAND, 0 ; if we're not expanding, we can
222 jnz for_make_com ; skip the following
223
224 mov SI, OFFSET fordma.find_buf_pname
225for_more: ; tack on matching filename
226 cmp BYTE PTR [SI], 0
227 je for_make_com
228 movsb
229 jnz for_more
230
231for_make_com:
232 xor AL, AL ; tack a null byte onto the end
233 stosb ; of the substitute string
234
235 xor CX, CX ; character count for command line
236 not CX ; negate it -- take advantage of loopnz
237 xor BX, BX ; argpointer
238 mov DI, OFFSET TRANGROUP:COMBUF+2
239 mov bl, f.FOR_COM_START ; argindex
240 mov DH, f.FOR_VAR ; %<for-var> is replaced by [forbuf]
241 ; time to form the <command> string
242 push CS
243 pop ES
244assume ES:trangroup
245
246 mov AX, OFFSET for_args ; translate offset to pointer
247 invoke argv_calc
248 mov si,[bx].arg_ocomptr
249 inc si ; mov ptr passed beginning space
250
251for_make_loop:
252 mov al,[si] ; the <command> arg, byte by byte
253 inc si
254 cmp AL,'%' ; looking for %<control-variable>
255 jne for_stosb ; no % ... add byte to string
256 cmp BYTE PTR [SI], DH ; got the right <variable>?
257 jne for_stosb ; got a %, but wrong <variable>
258 inc SI ; skip over <for-variable>
259
260 push SI
261 mov SI, OFFSET forbuf ; substitute the <item> for <variable>
262 ; to make a final <command> to execute
263sloop:
264 lodsb ; grab all those <item> bytes, and
265 stosb ; add 'em to the <command> string,
266 or AL, AL ; until we run into a null
267 loopnz sloop
268 dec DI ; adjust length and <command> pointer
269 inc CX ; so we can overwrite the null
270
271 pop SI
272 jmp for_make_loop ; got back for more <command> bytes
273for_stosb:
274 stosb ; take a byte from the <command> arg
275 dec CX ; and put it into the <command> to be
276 ; executed (and note length, too)
277 cmp al,0dh ; If not done, loop.
278 jne for_make_loop
279
280for_made_com: ; finished all the <command> args
281 not CL ; compute and record command length
282 mov [COMBUF+1], CL
283
284 mov DS, [RESSEG]
285assume DS:resgroup
286
287 test [ECHOFLAG],1 ; shall we echo this <command>, dearie?
288 jz noecho3
289 cmp nullflag,nullcommand ;G was there a command last time?
290 jz No_crlf_pr ;G no - don't print crlf
291 invoke CRLF2 ;G Print out prompt
292
293no_crlf_pr:
294 mov nullflag,0 ;G reset no command flag
295 push CS
296 pop DS
297 assume DS:trangroup
298 push di
299 invoke PRINT_PROMPT ;G Prompt the user
300 pop di
301 mov BYTE PTR ES:[DI-1],0 ; yeah, PRINT it out...
302 mov string_ptr_2,OFFSET TRANGROUP:COMBUF+2
303 mov dx,offset trangroup:string_buf_ptr
304 invoke std_printf
305 mov BYTE PTR ES:[DI-1], 0DH
306 jmp DoCom
307noecho3: ; run silent, run deep...
308 assume DS:resgroup
309 mov nullflag,0 ;G reset no command flag
310 push CS
311 pop DS
312 assume DS:trangroup
313 jmp docom1
314
315
316fornesterrj: ; no multi-loop processing... yet!
317assume ES:resgroup
318 call ForOff
319 jmp fornesterr
320
321forerrorj:
322 jmp forerror
323
324 break $For
325assume ds:trangroup,es:trangroup
326
327$for:
328 mov ES, [RESSEG]
329assume ES:resgroup
330
331 cmp ForFlag,0 ; is another one already running?
332 jnz fornesterrj ; if flag is set.... boom!
333
334;
335; Turn off any pipes in progress.
336;
337 cmp [PIPEFILES],0 ; Only turn off if present.
338 jz NoPipe
339 invoke PipeDel
340NoPipe:
341 xor DX, DX ; counter (0 <= DX < argvcnt)
342 call nextarg ; move to next argv[n]
343 jc forerrorj ; no more args -- bad forloop
344 cmp AL,'%' ; next arg MUST start with '%'...
345 jne forerrorj
346 mov BP, AX ; save forloop variable
347 lodsb
348 or AL, AL ; and MUST end immediately...
349 jne forerrorj
350
351 call nextarg ; let's make sure the next arg is 'in'
352 jc forerrorj
353 and AX, NOT 2020H ; uppercase the letters
354 cmp AX, in_word
355 jne forerrorj
356 lodsb
357 or AL, AL ; it, too, must end right away
358 je CheckLParen
359;
360; Not null. Perhaps there are no spaces between this and the (:
361; FOR %i in(foo bar...
362; Check for the Lparen here
363;
364 CMP AL,lparen
365 JNZ forerrorj
366;
367; The token was in(... We strip off the "in" part to simulate a separator
368; being there in the first place.
369;
370 ADD [BX].argpointer,2 ; advance source pointer
371 ADD [BX].arg_ocomptr,2 ; advance original string
372 SUB [BX].arglen,2 ; decrement the appropriate length
373;
374; SI now points past the in(. Simulate a nextarg call that results in the
375; current value.
376;
377 MOV ax,[si-1] ; get lparen and next char
378 jmp short lpcheck
379
380CheckLParen:
381 call nextarg ; lparen delimits beginning of <list>
382 jc forerrorj
383lpcheck:
384 cmp al, lparen
385 jne forerrorj
386 cmp ah,0
387 je for_paren_token
388
389 cmp ah, rparen ; special case: null list
390 jne for_list_not_empty
391 jmp forterm
392
393for_list_not_empty:
394 inc [bx].argpointer ; Advance ptr past "("
395 ; Adjust the rest of this argv entry
396 dec [bx].arglen ; to agree.
397 inc si ; Inc si so check for ")" works
398 jmp for_list
399
400for_paren_token:
401 call nextarg ; what have we in our <list>?
402 jc forerrorj
403 cmp ax, nullrparen ; special case: null list
404 jne for_list
405 jmp forterm
406
407forerrorjj:
408 jmp forerror
409
410for_list: ; skip over rest of <list>
411 mov CX, DX ; first arg of <list>
412skip_list:
413 add si,[bx].arglen
414 sub si,3 ; si = ptr to last char of token
415 mov al,rparen
416 cmp byte ptr [si],al ; Is this the last element in <list>
417 je for_end_list ; Yes, exit loop.
418 call nextarg ; No, get next arg <list>
419 jc forerrorjj ; If no more and no rparen, error.
420 jmp skip_list
421for_end_list:
422 mov DI, DX ; record position of last arg in <list>
423 mov byte ptr [si],0 ; Zap the rparen
424 cmp ax,nullrparen ; Was this token only a rparen
425 jz for_do ; Yes, continue
426 inc di ; No, inc position of last arg
427
428for_do:
429 call nextarg ; now we had BETTER find a 'do'...
430 jc forerrorjj
431 and AX, NOT 2020H ; uppercase the letters
432 cmp AX, do_word
433 jne forerrorjj
434 lodsb
435 or AL, AL ; and it had BETTER be ONLY a 'do'...
436 jne forerrorjj
437
438 call nextarg ; on to the beginning of <command>
439 jc forerrorjj ; null <command> not legal
440
441 push AX
442 push BX
443 push CX
444 push DX ; preserve registers against disaster
445 push DI
446 push SI
447 push BP
448 invoke FREE_TPA ; need to make free memory, first
449ASSUME ES:RESGROUP
450 call ForOff
451 mov BX, SIZE for_info - SIZE arg_unit
452 invoke Save_Args ; extra bytes needed for for-info
453 pushf
454 mov [ForPtr], AX
455 invoke ALLOC_TPA ; ALLOC_TPA clobbers registers...
456 popf
457 pop BP
458 pop SI
459 pop DI
460 pop DX
461 pop CX
462 pop BX
463 pop AX
464 jc for_alloc_err
465
466 push ES ; save resgroup seg...
467 push [ForPtr]
468 pop ES
469assume ES:for_segment ; make references to for-info segment
470
471 dec CX ; forproc wants min pointing before
472 dec DI ; first arg, max right at last one
473 mov f.for_minarg, CX
474 mov f.for_maxarg, DI
475 mov f.for_com_start, DL
476 mov f.for_expand, -1 ; non-zero means FALSE
477 mov AX, BP
478 mov f.for_var, AH
479 pop ES
480assume ES:resgroup
481
482 inc [FORFLAG]
483 cmp [SINGLECOM], -1
484 jnz for_ret
485 mov [SINGLECOM], 0FF00H
486for_ret:
487 ret
488
489for_alloc_err:
490 mov msg_disp_class,ext_msg_class ;AN000; set up extended error msg class
491 mov dx,offset TranGroup:Extend_Buf_ptr ;AC000; get extended message pointer
492 mov Extend_Buf_ptr,error_not_enough_memory ;AN000; get message number in control block
493 jmp cerror
494
495nextarg:
496 inc DX ; next argv[n]
497 cmp DX, arg.argvcnt ; make sure we don't run off end
498 jge nextarg_err ; of argv[]...
499 mov BX, DX
500 mov AX, OFFSET TRANGROUP:arg.argv
501 invoke argv_calc ; convert array index to pointer
502 mov SI, [BX].argpointer ; load pointer to argstring
503 lodsw ; and load first two chars
504 clc
505 ret
506nextarg_err:
507 stc
508 ret
509
510
511ASSUME DS:TRANGROUP,ES:TRANGROUP
512
513FORNESTERR:
514 PUSH DS
515 MOV DS,[RESSEG]
516ASSUME DS:RESGROUP
517 MOV DX,OFFSET TRANGROUP:FORNESTMES_ptr
518 CMP [SINGLECOM],0FF00H
519 JNZ NOFORP3
520 MOV [SINGLECOM],-1 ; Cause termination
521NOFORP3:
522 POP DS
523ASSUME DS:TRANGROUP
524 JMP CERROR
525;
526; General routine called to free the for segment. We also clear the forflag
527; too. Change no registers.
528;
529PUBLIC ForOff
530ForOff:
531 assume DS:NOTHING,ES:NOTHING
532 SaveReg <AX,ES>
533 mov es,ResSeg
534 assume es:ResGroup
535 mov AX,ForPtr
536 or ax,ax
537 jz FreeDone
538 push es
539 mov es,ax
540 mov ah,dealloc
541 int 21h
542 pop es
543FreeDone:
544 mov ForPtr,0
545 mov ForFlag,0
546 RestoreReg <ES,AX>
547 return
548
549trancode ends
550 end
551 \ No newline at end of file