Repository navigation
Expand file tree
/
Copy pathcli.mac
More file actions
2184 lines (2115 loc) · 58.2 KB
/
Copy pathcli.mac
File metadata and controls
2184 lines (2115 loc) · 58.2 KB
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
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
name('cli')
TITLE Nextor - COMMAND3.COM
SUBTTL The command loop
INCLUDE command.inc
;******************************************************************************
;
; The main command loop: prompting, reading and expanding the command
; line (aliases, %-variables and %-parameters), command separators, I/O
; redirection and piping, and dispatching to internal commands and to
; .COM, .BAT and .BTM files.
;
; Based on COMMAND.COM 2.31, copyright (1986) IS Systems Ltd, and on
; the COMMAND2.COM 2.40/2.41 additions by Fokke Post, whose source code was
; released as freeware in 2003.
;
;******************************************************************************
CLI:: LD HL,#80
LD C,(HL)
LD B,#00
ADD HL,BC
INC HL
LD (HL),B
SBC HL,BC
CALL GETCH##
LD (CMDCHR1##),A
LD (CMDCHR2##),A
EXX
LD HL,TEMPFN
LD DE,PIPEFN##
LD BC,#07
LDIR
LD A,R
AND #07
ADD A,"0"
LD (DE),A
INC DE
LD C,#04
LDIR
XOR A
LD (REDIRID##),A
LD (PIPEID##),A
LD (INHNDL##),A
LD (OUTHNDL##),A
LD (XTRACRLF##),A
LD L,A
LD H,A
LD (PIPEADDR##),HL
LD (NEXTADDR##),HL
DEC A
LD (BATHNDL##),A
LD (STRCODE##),A
LD HL,CMDTEND##
LD (LINEADDR##),HL
LD HL,GOSUBLIST##
LD (GOSUBADDR##),HL
LD HL,IFFLIST##
LD (IFFADDR##),HL
LD D,H
LD E,L
INC DE
LD BC,15
LD (HL),#00
LDIR
LD DE,CLI_ABORT
LD C,_DEFAB##
CALL BDOS
CLI10: CALL SETLANG## ; Refresh the message language.
CALL SETYEN## ; Redefine the yen sign as a backslash
; again if YENFLAG says so (the main
; registers are free here, HL' is not).
XOR A ; No hint message pending from the
LD (HINTMSG##),A ; previous command (see ERROR).
LD IY,REDIRST##
LD (IY+#0E),#00
LD (IY+#10),#00
LD (IY+#00),#00
LD (IY+#29),#00
LD (IY+#2A),#00
LD C,_CURDRV##
CALL BDOS
INC A
LD (CURDRIVE##),A
LD DE,(PIPEADDR##) ; Another command in the pipeline
LD A,D ; pending ?
OR E
JP NZ,REPIPE ; Go and execute it if yes.
LD DE,(NEXTADDR##) ; Another ^-separated command
LD A,D ; pending (see MULTICMD) ?
OR E
JP NZ,CMD10 ; Run it (aliasing is already done).
LD HL,REDIR$ ; REDIRST bit 7 set <= REDIR is OFF
CALL OFF?## ; (redirection/piping disabled).
CCF
SBC A,A
LD (REDIRST##),A
LD HL,ALIAS$ ; ALISTATE bit 7 set <= ALIAS is OFF
CALL OFF?## ; (alias expansion disabled).
CCF
SBC A,A
LD (ALISTATE##),A
LD HL,SEPAR$ ; SEPSTATE bit 7 set <= SEPAR is OFF
CALL OFF?## ; (^ separators disabled).
CCF
SBC A,A
LD (SEPSTATE##),A
EXX
LD A,(HL)
OR A
JP NZ,CMD
LD A,(BATHNDL##)
INC A
JR Z,NOBAT
LD HL,ERRCODE##
LD A,(HL)
LD (HL),#00
OR A
JR Z,NOTEND
PUSH AF
LD A,(REDIRID2##)
LD (REDIRID##),A
LD HL,CMDTEND##
LD (LINEADDR##),HL
LD A,#FF
LD (BATHNDL##),A
LD A,(BATPAGE##)
OR A
CALL NZ,FREEBTM##
POP AF
CP .OKCMD##
JR NZ,JERROR
XOR A
JERROR: JP ERROR
NOTEND: CALL ECHO?## ; Batch file ECHO ON ?
JR C,PRO20 ; Go & read command if not.
SCF
JR PROMPT ; Else print prompt & get command.
NOBAT: LD A,(CMDCHR2##) ; If EXITF is NZ, then exit COMMAND.COM
OR A ; with the error code from the last
JR Z,PROMPT ; command, which is in ERR (NC).
LD A,(ABORTERR##) ; If no error, then return .OKCMD,
OR A ; which does not produce an error
JR NZ,JEXIT ; but which is used by MSX-DOS.SYS to
LD A,.OKCMD## ; prevent a spurious re-load of
JEXIT: JP EXIT##
PROMPT: PUSH AF
LD HL,XTRACRLF##
XOR A
CP (HL)
LD (HL),A
CALL NZ,LPRINT##
LD HL,PROMPT$
CALL GENV2##
LD A,(DE)
OR A
JR Z,PRO6 ; Use the default prompt if not set.
LD B,#FF ; Else get the length of the value, to
PUSH DE ; compare it against the special
PRO2: INC B ; values "ON" and "OFF" (restored in
LD A,(DE) ; 2.44 for compatibility with the
INC DE ; original COMMAND.COM).
OR A
JR NZ,PRO2
POP DE
PUSH DE
DEC DE
LD HL,ONWORD$##
CALL COMPARE ; "ON" (however cased) selects the
POP DE ; default prompt, "%_CWD%>".
JR NC,PRO6
PUSH DE
DEC DE
LD HL,OFFWORD$##
CALL COMPARE ; "OFF" selects the drive letter only,
POP DE ; "%_DISK%>".
JR C,PRO10
LD HL,OPROMPT$
LD BC,9
JR PRO8
PRO6: LD HL,DPROMPT$
LD BC,8
PRO8: PUSH DE
LDIR
POP DE
PRO10: LD HL,BUFFER1##
LD IX,XBUF3##
LD (IX),#00
CALL REPLACE
EX DE,HL
INC DE
CALL PRTSTR##
POP AF
JR NC,GETCON
PRO20: CALL INBAT##
JR C,PRO20
JR Z,GOTBAT
CP .EOF##
JR Z,BATEOF
PUSH AF
CALL LPRINT##
POP AF
JP ERROR
BATEOF: LD A,.OKCMD## ; OKCMD => return no error.
GOTBAT: LD (ERRCODE##),A
SCF ; Skip console read.
GETCON: CALL NC,INLI## ; Read from console if not batch file.
INC HL
CMD: EX DE,HL
CALL ALIASING
JP NZ,ERROR
CMD10: CALL MULTICMD
LD HL,BUFFER1##
LD IX,XBUF3##
LD (IX),#00
CALL REPLACE
JR NZ,ERROR
EX DE,HL
INC DE
PUSH DE
LD A,(BATHNDL##)
INC A
SCF
CALL NZ,ECHO?##
CALL NC,DMSCR##
LD C,_FORK##
CALL BDOS
POP DE
JR NZ,ERROR
LD A,B
LD (REDIRID##),A
LD HL,BUFFER2##
CALL REDIRECT
JR NZ,ERROR
EX DE,HL
INC DE
XOR A
LD (INHNDL##),A
LD (OUTHNDL##),A
LD H,A
LD L,A
LD (PIPEADDR##),HL
REPIPE: PUSH DE
CALL PIPE ; Set up any piping.
POP HL
JR NZ,ERROR
PUSH HL
CALL UNQUOTE ; Remove quote characters.
POP HL
CALL DO_CMD ; Execute the command.
CP .BADCMD## ; 'Unrecognized command' ?
JR NZ,ERROR
LD HL,UNKCMD$ ; Then, if an alias UNKNOWN_CMD is
LD DE,ENVDATA## ; defined, its value replaces the
CALL GETALIAS## ; failed command line and is
JR NZ,ERROR ; dispatched again.
LD A,(DE)
OR A
JR NZ,CMD10
LD A,.BADCMD## ; No such alias: the usual error.
JR ERROR
UNKCMD$: DEFB "UNKNOWN_CMD",0 ; Alias run on 'Unrecognized command'.
CLI_ABORT:: CP .INERR## ; Error on standard input ?
JR Z,CLI20 ; Go if yes.
CP .OUTERR## ; Error on standard output ?
JR NZ,ERROR ; Give error if not.
LD A,#3D ; Error type = output error.
DEFB #21
CLI20: LD A,#3C
LD (STRCODE##),A
LD A,(REDIRID##) ; If not forked, then exit with error
OR A ; code since there's no way of recov-
LD A,B ; ering from this situation.
JP Z,EXIT## ; Else give error.
ERROR: LD SP,BOOTSTRAP## ; Set up SP again in case an abort.
LD (ABORTERR##),A ; Save error for terminating COMMAND3.
LD B,A ; B=error code.
PUSH BC ; Save it for later.
XOR A ; Turn /P paging off before printing
LD (ROWMAX##),A ; anything: after a transient
LD (COLCNT##),A ; program ROWMAX and COLCNT hold
LD A,B ; whatever it left there (they are
; past BUFFER1, so they are not
; restored at re-load), and CLI10
; only resets them once the message
; has been printed; garbage there
; made the message pause with 'Press
; any key to continue'.
LD DE,ENVNAME## ; DE -> buffer to get message into.
CP " " ; User error ?
JR C,ERR10 ; Go if yes (no message required).
CP .OKCMD## ; Means command executed OK from
JR Z,ERR10 ; COMMAND, so no msg for this either.
CP #80 ; Means 'Y' response from 'Terminate
JR Z,ERR10 ; batch file ?' so no message.
LD C,_EXPLAIN## ; Get message to (DE) from system.
CALL BDOS
DEFB #21
ERR10: XOR A
LD (DE),A
LD BC,#FF5F ; Flush buffers for all drives.
LD D,#00 ; Flush but don't invalidate.
CALL BDOS ; (error ignored)
POP BC ; B=error code.
LD C,B ; And C.
LD HL,CMDCHR1##
LD A,(HL) ; Suppress error message (<>0 => yes) ?
LD (HL),#00 ; (reset again for next time round)
OR A
JR NZ,TSTBAT ; Test for ending batch file if yes.
LD A,B ; Get error code.
CP .BADCMD## ; Turn off redirection if command abort
CALL NZ,ABORT? ; error or 'Unrecognized command'.
JR NZ,NABERR
PUSH BC
LD BC,_REDIR##
LD A,#01 ; No redirection of error messages if
CALL BDOS ; command aborted prematurely.
POP BC
LD A,B
CP .ABORT## ; Extra CR,LF if command aborted, but
JR Z,NABERR ; not if .ABORT or if .BADCMD.
CP .BADCMD##
CALL NZ,LPRINT##
NABERR: LD A,(ENVNAME##)
OR A ; Message given ?
JR Z,TSTBAT ; Go if not.
CALL LPRINT## ; CR, LF before error message.
LD A,#0D ; Three stars before message.
CALL DMESS##
LD A,(STRCODE##) ; Get error type.
ADD A,#01 ; Print type message if not FF.
DEC A
PUSH AF
CALL NC,DMESS##
LD DE,ENVNAME##
CALL PRTSTR## ; Print actual error message.
LD A,#0E ; Followed by message after error.
CALL DMESSCR##
LD HL,HINTMSG## ; Print the pending hint message,
LD A,(HL) ; if any (e.g. where the help
LD (HL),#00 ; index file is expected to be),
OR A ; then forget it.
CALL NZ,DMESSCR##
POP AF
CP #3B ; If error from reading batch file,
JR Z,TBF ; then issue 'Terminate' prompt.
CP #3A ; If piping error, then cancel all
JR Z,NOPIPE ; piping and issue prompt.
TSTBAT: LD A,C ; Get error code.
CP #80 ; If it's .BATEND, then immediately
JR Z,ENDBAT ; terminate any executing batch file.
OR A ; If it's a command abort error, then
JR Z,NABORT ; issue 'Terminate batch file' prompt
CALL ABORT? ; but only if the secondary error
JR NZ,NABORT ; code is non-zero.
NOPIPE: CALL DELPIPE
TBF: LD BC,_REDIR## ; Turn off redirection to
LD A,#01 ; do 'Terminate batch file'
CALL BDOS ; prompting.
LD A,(BATHNDL##)
INC A
LD A,#36 ; Prompt 'Terminate batch file (Y/N)?'
CALL NZ,YN## ; and get response if in batch file.
JR Z,NABORT ; Don't abort if response was 'N'.
ENDBAT: LD HL,ERRCODE## ; BATEND=NZ => end batch file, if any.
LD (HL),#80 ; Error for 'batch file terminated'.
NABORT: LD A,#3A ; Set error type to piping error.
LD (STRCODE##),A
CALL UNPIPE ; Tidy up if command was piped.
JP NZ,ERROR
DEC A ; Clear ERRTYPE.
LD (STRCODE##),A
LD DE,(PIPEADDR##)
LD A,D ; Another command in pipe to execute ?
OR E
JR NZ,DONE ; Execute it if yes.
LD HL,REDIRID## ; If forked, then do a join to cancel
LD B,(HL) ; redirection.
XOR A
LD (HL),A ; 0 => not forked.
OR B
LD C,_JOIN##
CALL NZ,BDOS ; Join, and give error if any (should
JP NZ,ERROR ; not normally occur).
LD HL,CMDTEND## ; No command to execute.
EXX
DONE: JP CLI10
ABORT?: CP .ABORT## ; Return Z if command abort error.
RET Z
CP .STOP##
RET Z
CP .CTRLC##
RET Z
LD A,(STRCODE##) ; Get error type.
CP #3C ; If an input or output error then
RET Z ; command was aborted.
CP #3D
RET
DSKERR:: LD A,#01 ; Disk error routine for printing
RET ; prompt just auto-aborts.
ABRT:: POP HL
LD A,#FF
RET
;******************************************************************************
;
; The alias expansion engine (new in 2.40). Aliases are named
; commands kept in the mapper segment (see io.mac); whenever the first
; word of a command matches an alias name, the whole command - name and
; parameters - is replaced by the alias value, which can refer to those
; parameters as %n / %n&.
;
; Each ^-separated command in the line is treated in turn: the
; command is copied to ENVNAME, its first word upper-cased and looked
; up with GETALIAS. If an alias exists, its value is expanded with
; REPLACE - with LINEADDR temporarily pointed at the ENVNAME copy, so
; that %n picks up the parameters typed after the alias name - and the
; result is spliced into the line in place of the original command.
; Scanning then resumes at the start of the spliced-in text, so the
; result is itself alias-expanded again (recursive aliases; a _CONST
; call in the loop gives CTRL-STOP a chance to break alias loops like
; ALIAS A=B / ALIAS B=A). A command prefixed with @ is not expanded
; (the @ is removed), and when the ALIAS environment item is OFF no
; expansion is done at all (the line is just copied to LINEBUF).
;
; On entry, DE -> ASCIIZ command line.
; On exit, Z => DE -> the expanded line, now copied to LINEBUF.
; NZ => A = .BUFUL (the line grew past 255 characters).
;
ALIASING:: PUSH DE ; Save start of the command line.
XOR A
BIT 7,(IY+#29) ; ALIAS OFF: no aliasing, just copy
JP NZ,ALI100 ; the line to LINEBUF.
ALI10: LD A,(DE) ; Skip spaces and ^ separators to the
INC DE ; start of the command word.
CP " "
JR Z,ALI10
CP "^"
JR Z,ALI10
DEC DE
LD HL,ENVNAME##
PUSH DE ; Save start of this command.
LD A,(DE)
CP "@" ; @ prefix: no aliasing for this
JR NZ,ALI30 ; command.
LD L,E
LD H,D
ALI20: INC HL ; Remove the @ by shifting the rest
LD A,(HL) ; of the line down one place,
LD (DE),A
INC DE
OR A
JR NZ,ALI20
POP DE
LD C,#00 ; and go skip to the next command.
JR ALI90
ALI30: LD (HL),A ; Copy the whole command (up to the
INC HL ; end of the line or a ^) into
INC DE ; ENVNAME.
LD A,(DE)
OR A
JR Z,ALI40
CP "^"
JR NZ,ALI30
ALI40: LD (HL),#00
EX DE,HL ; HL -> the terminator or ^ in the
CALL GETCH## ; line, ie. the rest of the line.
PUSH HL ; Save it.
LD HL,ENVNAME##
PUSH HL
DEC HL
ALI50: INC HL ; Upper-case the copied command name
LD A,(HL) ; in place, up to the first space
CALL MKUPPER ; or / option (the alias lookup is
LD (HL),A ; an exact match).
OR A
JR Z,ALI60
CP " "
JR Z,ALI60
CP "/"
JR NZ,ALI50
ALI60: LD A,(HL) ; Cut the name there, saving the
LD (HL),#00 ; delimiter character,
LD B,H
LD C,L
POP HL ; HL = ENVNAME = the name:
PUSH AF
PUSH BC
LD DE,XBUF1## ; look it up; the value goes to
CALL GETALIAS## ; XBUF1 (null if no such alias).
POP BC
POP AF
LD (BC),A ; Put the delimiter back.
LD HL,(LINEADDR##) ; Point LINEADDR (the %n source
LD (FIB2##+1),HL ; line) at the copied command, so
LD HL,ENVNAME## ; that %n in the alias value picks
LD (LINEADDR##),HL ; up the parameters typed after
LD HL,XBUF2## ; the alias name (FIB2+1 is just
CALL REPLACE ; scratch for the old LINEADDR),
LD HL,(FIB2##+1) ; expand the value from XBUF1 into
LD (LINEADDR##),HL ; XBUF2, and restore LINEADDR.
POP HL ; HL = rest of the line.
LD A,(XBUF1##)
POP DE ; DE = start of this command.
LD C,#00
OR A ; Null value: no such alias, leave
JR Z,ALI90 ; the command as it is.
PUSH DE
CALL NCOPY## ; Delete the command (name and all
POP HL ; parameters) by copying the rest
PUSH HL ; of the line down over it.
LD DE,XBUF2## ; Then insert the expanded alias
LD A,(DE) ; value (length byte first) in its
OR A ; place, byte by byte.
JR Z,ALI80
INC DE
ALI70: LD A,(DE)
OR A
JR Z,ALI80
LD C,A
CALL INSBYTE##
INC HL
INC DE
JR ALI70
ALI80: LD C,_CONST## ; Poll the console, so that CTRL-C /
CALL BDOS ; CTRL-STOP can break alias loops.
POP DE ; Re-scan from the start of the
JP ALI10 ; spliced-in text (recursion).
ALI90: CALL RDCHAR ; Skip to the next command: find the
JR Z,ALI100 ; next unquoted ^ (Z = end of the
JR C,ALI90 ; line).
CP "^"
JR NZ,ALI90
JP ALI10
ALI100: LD B,A ; A=0, so B=0: 256 byte limit.
LD DE,LINEBUF## ; Copy the expanded line to LINEBUF.
POP HL
ALI110: LD A,(HL)
LD (DE),A
OR A
JR Z,ALI120
INC HL
INC DE
DEC B
JR NZ,ALI110
LD A,.BUFUL## ; Line too long (over 255 chars).
RET
ALI120: LD DE,LINEBUF## ; Return DE -> the expanded line,
XOR A ; no error.
RET
; Split off the first of several ^-separated commands (new in
; 2.40): the line at (DE) is terminated at the first unquoted ^, and
; the address of what follows is stored in NEXTADDR - the main loop
; picks that up on the next time round, like a pipe continuation. No
; splitting is done when the SEPAR environment item is OFF.
;
; On entry, DE -> ASCIIZ command line.
; On exit, DE is preserved; NEXTADDR = rest of the line, or 0 if none.
;
MULTICMD:: PUSH DE
XOR A
BIT 7,(IY+#2A) ; SEPAR OFF: no ^ splitting.
JR NZ,MUL20
LD C,#00 ; Clear quotes flag.
MUL10: CALL RDCHAR ; Find the first unquoted ^
JR Z,MUL20 ; (Z = end of line: no separator).
JR C,MUL10
CP "^"
JR NZ,MUL10
EX DE,HL
DEC HL ; Terminate the first command where
LD (HL),#00 ; the ^ was,
INC HL
CALL GETCH## ; skip blanks after it,
JR NZ,MUL30 ; and save what follows - if
MUL20: LD H,A ; anything - in NEXTADDR (A=0
LD L,A ; here, so HL=0 = none).
MUL30: LD (NEXTADDR##),HL
POP DE
RET
; Remove the quote characters from the command line at (HL), in
; place. Quotes (" or ') protect text from the expansion, redirection
; and separator processing; by now they have served their purpose and
; are removed in pairs, keeping the text between them. An unmatched
; quote is removed too. Called just before DO_CMD.
;
UNQUOTE:: LD A,(HL) ; Find the next quote character
INC HL ; (end of line: done).
OR A
RET Z
CP #22
JR Z,UNQ10
CP "'"
JR NZ,UNQUOTE
UNQ10: LD B,A ; B = the quote character.
LD D,H
LD E,L
DEC DE ; DE = position of the quote itself.
UNQ20: LD A,(HL) ; Shift the quoted text down one
CP B ; place, over the opening quote,
JR Z,UNQ30 ; until the closing quote.
LD (DE),A
OR A ; Unmatched quote: the rest of the
RET Z ; line has been shifted; done.
INC HL
INC DE
JR UNQ20
UNQ30: PUSH HL
UNQ40: INC HL ; Closing quote: remove it as well
LD A,(HL) ; by shifting the rest of the line
LD (DE),A ; down over it,
INC DE
OR A
JR NZ,UNQ40
POP HL
DEC HL ; then continue scanning for more
JR UNQUOTE ; quoted sections.
;******************************************************************************
;
; The %-expansion engine (2.31's REPLACE, much extended in
; 2.40/41). Copies the ASCIIZ string at (DE) into the buffer at (HL)
; (which gets a length byte followed by the text and a null - the
; WRCHAR format), expanding on the way:
;
; %n, %n& batch parameters (n = 0 to 255, decimal), taken from the
; command line saved at (LINEADDR) by EXPAND below. %n&
; gives parameter n and all the following ones; %& means
; %1&.
; %% a single % character.
; %NAME% the value of environment item NAME (via _GENV), nothing
; if it is not set. If _GENV rejects the name the text is
; copied unchanged (less the opening %).
; %_NAME% an internal variable: the handler from IVTAB is called
; and returns DE -> the ASCIIZ value.
; %@FUN[arg]% a function: the argument is first run through REPLACE
; itself (into a fresh buffer carved out of the scratch
; area chained through IX), then the handler from FNTAB is
; called with HL -> the expanded argument, returning DE ->
; the ASCIIZ result.
;
; While scanning for the % that closes a name, [ and ] are
; counted and any % inside brackets is skipped, so constructs like
; %@LEN[%PATH%]% nest properly. Characters inside single or double
; quotes are copied without any expansion (RDCHAR keeps the quote state
; in C). Unknown variable/function names expand to nothing.
;
; Called on every command line, on the PROMPT string, and
; recursively on alias values and function arguments.
;
; On entry, DE -> ASCIIZ input string.
; HL -> output buffer (the text is limited to 127 characters
; by WRCHAR).
; IX -> scratch buffer for nested expansion; the top level
; caller initialises it empty ((IX)=0, usually XBUF3).
; On exit, HL is preserved and points to the output length byte.
; Z => OK, NZ => error code in A (eg. .BUFUL).
;
REPLACE: XOR A
LD (HL),A ; Initialise length byte.
LD C,A ; Not in quotes initially.
LD (FIB2##),A ; FIB2+0 is scratch: the %n& flag.
REP10: CALL RDCHAR ; Get character.
JP C,RP30 ; Quoted characters are not expanded.
CP "%" ; Start of a % construct ?
JP NZ,RP30
LD A,(DE) ; Look at what follows the %.
INC DE
CP "&" ; %& => all parameters (= %1&).
JR Z,REP20
CP "0"
JR C,RP20 ; Not a digit: try %name% etc.
CP "9"+1
JR NC,RP20
DEC DE
PUSH BC ; Save quotes flag,
PUSH HL ; output buffer pointer,
PUSH DE ; and command line pointer.
EX DE,HL
CALL GET8## ; Get the parameter number (0-255).
JR Z,REP30
POP DE ; Number no good: emit the digits as
POP HL ; ordinary text.
POP BC
LD A,(DE)
INC DE
JR RP20
REP20: PUSH BC ; (as above)
PUSH HL
PUSH DE
EX DE,HL
LD A,#FF ; Flag 'expand all the following
LD (FIB2##),A ; parameters as well',
LD E,#01 ; starting at parameter 1.
REP30: LD A,E ; Parameter number, +1 as EXPAND
INC A ; wants it (1 => %0).
POP DE ; Recover the pointers: DE = past
EX DE,HL ; the number (as left in HL by
POP HL ; GET8), HL = output buffer,
POP BC ; C = quotes flag.
PUSH BC
PUSH AF
LD A,(DE) ; %n& (number followed by '&') ?
CP "&"
JR NZ,REP40
LD A,#FF ; Yes: set the 'all following' flag
LD (FIB2##),A ; and swallow the &.
INC DE
REP40: POP AF
PUSH DE
CALL EXPAND ; Expand parameter(s) from (LINEADDR).
POP DE
POP BC
RET NZ ; Return if error.
JR REP10 ; Continue with the rest of the line.
RP20: CP "%" ; %% => a single %.
JP Z,RP30
DEC DE
PUSH BC ; Save quotes flag and output buffer
PUSH HL ; pointer.
XOR A
LD (ENVNAME##),A
LD L,E ; HL scans for the % that closes the
LD H,D ; name.
PUSH DE ; Save start of the name.
LD D,#00 ; D = [] nesting depth.
RPC10: LD A,(HL)
INC HL
OR A
JR Z,RPC50 ; End of line: no closing %.
CP "["
JR NZ,RPC20
INC D
RPC20: CP "]"
JR NZ,RPC30
DEC D
RPC30: INC D ; (tests D without changing A)
DEC D
JR NZ,RPC10 ; A % inside [] does not close the
CP "%" ; name - this is what lets
JR NZ,RPC10 ; %@FUN[..%VAR%..]% nest.
POP DE ; DE = start of the name.
PUSH HL ; Save pointer past the closing %.
PUSH DE
DEC HL
LD (HL),#00 ; Make the name ASCIIZ (the closing
EX DE,HL ; % becomes the terminator).
LD DE,ENVNAME##
LD A,(HL)
CP "_" ; %_name% => internal variable.
JP Z,RPC60
CP "@" ; %@FUN[..]% => function.
JP Z,RPC100
LD BC,#FF6B ; Else environment item: _GENV with
CALL BDOS ; B=255, value to ENVNAME.
JR NZ,RP28 ; Bad name: emit the text unchanged.
PUSH IX
RPC40: POP IX ; Common return path for env items,
POP HL ; internal variables and functions.
POP HL ; Recover the pointer past the
EX (SP),HL ; closing %, swapping it with the
RP22: LD A,(DE) ; saved output pointer: HL = the
INC DE ; output buffer again. Then copy
CALL WRCHAR ; the ASCIIZ value at (DE) into
JR NZ,RP23 ; the output, until the null (or
JR C,RP22 ; an error).
RP23: POP DE ; DE = past the closing %: continue
POP BC ; with the rest of the line.
RET NZ ; Return if error.
JP REP10
RPC50: POP DE ; No closing %: drop the % and
JR RP29 ; rescan from the character after.
RP28: POP DE ; _GENV did not like the name: put
POP HL ; the closing % back and re-emit
DEC HL ; the text as it stands (less the
LD (HL),"%" ; opening %).
RP29: POP HL
POP BC
LD A,(DE)
INC DE
RP30: CALL WRCHAR ; Put character in the output buffer.
RET NZ ; Return if error.
JP C,REP10 ; Repeat for next character if not end.
RET ; Else return with no error.
; %_NAME%: look up an internal variable (%_CWD%, %_TIME%, ...).
;
RPC60: EX DE,HL ; DE = the name (at the '_').
PUSH DE
LD B,#FF ; Measure the name: B = length not
RPC70: INC DE ; counting the leading _ (IVTAB
INC B ; names are stored without it, and
LD A,(DE) ; COMPARE skips the character at
OR A ; (DE) itself).
JR NZ,RPC70
POP DE
LD HL,IVTAB##-2 ; Look the name up in the internal
XOR A ; variables table.
RPC80: JR NZ,RPC90 ; (NZ from COMPARE => name shorter
INC HL ; than the rest of the table,
INC HL ; which is sorted by length)
LD A,(HL)
OR A ; End of table: no such variable.
JR Z,RPC90
CALL COMPARE
JR C,RPC80
LD A,(HL) ; Found: fetch the handler address
INC HL ; and 'call' it with a return
LD H,(HL) ; address of RPC40 - the handler
LD L,A ; returns DE -> the ASCIIZ value,
PUSH IX ; which RPC40 copies to the
LD DE,RPC40 ; output.
PUSH DE
PUSH HL
XOR A ; Handlers are entered with A=0.
RET
RPC90: LD DE,CMDTEND## ; Unknown name: the value is the
PUSH IX ; null string (CMDTEND is a zero
JP RPC40 ; byte).
; %@FUN[argument]%: expand the argument, then call the function.
;
RPC100: EX DE,HL ; DE = the name (at the '@').
PUSH DE
LD B,#FF ; Measure the name up to the '[':
RPC110: INC DE ; B = length not counting the @
INC B ; (FNTAB names are stored without
LD A,(DE) ; it).
OR A
JP Z,RPC160 ; No '[' at all: A=0 there makes
CP "[" ; RPC160 give the null value.
JR NZ,RPC110
EX DE,HL ; HL -> after the '[', DE = ENVNAME.
INC HL
PUSH BC ; Save the name length,
PUSH HL ; and the start of the argument.
LD D,#01 ; D = bracket nesting depth.
RPC120: LD A,(HL) ; Find the matching ']'.
INC HL
OR A
JR Z,RPC140 ; End of line: no closing ']'.
CP "["
JR NZ,RPC130
INC D
RPC130: CP "]"
JR NZ,RPC120
DEC D
JR NZ,RPC120
DEC HL ; Make the argument ASCIIZ (the ']'
LD (HL),#00 ; becomes the terminator).
PUSH IX ; Expand the argument itself with a
POP HL ; recursive REPLACE, into a fresh
POP DE ; buffer carved out just past the
PUSH IX ; text already in the scratch
LD C,(HL) ; buffer at IX:
LD B,#00
ADD HL,BC ; new buffer = IX + (IX) + 2.
INC HL
INC HL
PUSH HL
POP IX
CALL REPLACE
JR RPC150
RPC140: POP HL ; No matching ']': discard the
POP HL ; argument start, the name length
POP HL ; and the name pointer, and give
JP RP28 ; up on this % construct,
; re-emitting it as plain text.
; (2.41 fell into RPC150 here,
; loading the name length into IX
; - the output pointer - and
; leaving the stack skewed.)
RPC150: POP IX ; Scratch buffer pointer back.
JP NZ,RP29
POP BC ; Recover the name length, and
LD A,B ; juggle the registers into: B =
EX AF,AF' ; name length (for COMPARE), BC =
LD A,(HL) ; the expanded argument buffer
LD B,H ; (via A'), A = the expanded
LD C,L ; argument's length.
EX AF,AF'
LD B,A
LD A,H
EX AF,AF'
RPC160: POP DE ; DE = the function name (at '@').
OR A ; No (or empty) [argument] => the
JP Z,RPC90 ; null value.
LD HL,FNTAB##-2 ; Look the name up in the functions
XOR A ; table (same scheme as IVTAB
RPC170: JP NZ,RPC90 ; above; not found => the null
INC HL ; value).
INC HL
LD A,(HL)
OR A
JP Z,RPC90
CALL COMPARE
JR C,RPC170
EX AF,AF' ; Found: BC -> the expanded argument
LD B,A ; text (skipping its length byte),
INC BC
PUSH BC
LD C,(HL) ; fetch the handler address,
INC HL
LD B,(HL)
POP HL ; HL -> the argument, and 'call' the
LD DE,RPC40 ; handler with RPC40 as return
PUSH IX ; address - it returns DE -> the
PUSH DE ; ASCIIZ result, which RPC40
PUSH BC ; copies to the output.
RET
; This routine is called during the command line parsing to expand the
; user's batch file replacable parameters, accessed as %0 to %9.
; When the batch file was invoked, the command line was saved in a
; buffer and the pointer PERC0 pointed to the start of it. When not in a batch
; file, PERC0 points to a null (a dummy null command line) so that %0 to %9
; are replaced by a null string.
; On entry, B is the number of the parameter, 1 to 10 for parameters %0
; to %9. The length byte of the current command line is saved, and the first
; parameter expanded from the command at (PERC0). Then B is decremented and if
; not 0, then the old length byte is restored which causes the just expanded
; parameter to be ignored, and then the next parameter is expanded. When B
; decrements to 0 the length byte is not restored, thus making the newly
; expanded parameter part of the command line. This is not the most efficient
; way of doing it but it is adequate.
; On entry, B is the parameter number required (1 to 10).
; HL points to the length byte of the new expanded command line.
; On exit, HL is preserved and the parameter has been expanded.
; AF = error code.
EXPAND: LD B,A ; Save parameter number, 1 => %0.
LD C,#00 ; Not in quotes initially.
LD DE,(LINEADDR##) ; Point to parameter's command line.
LD A,(HL) ; Save current length byte.
EX AF,AF'
EXP10: CALL RDCHAR
JR C,EXP20
CP "/"
JR Z,GOTSEP
CALL SEP?##
JR NZ,EXP20
GOTSEP: EX DE,HL
DEC HL
PUSH BC
CALL GETCH##
POP BC
EX DE,HL
DJNZ NXTPAR
LD A,(FIB2##)
OR A
RET Z
DEC DE
LD A,(DE)
CALL SEP?##
JR Z,GOT10
INC DE
GOT10: LD A,(DE)
INC DE
CALL WRCHAR
RET NZ
JR C,GOT10
RET
NXTPAR: EX AF,AF'
LD (HL),A ; Restore old command line.
EX AF,AF'
LD A,(DE) ; Get first character of next param.
CP "/" ; Fiddle to make sure / options are
JR NZ,EXP10 ; separate parameters.
INC DE
EXP20: CALL WRCHAR ; Put character in buffer.
RET NZ ; return if error.
JR C,EXP10 ; Repeat for next character if not end.
RET ; Else return with no error.
SUBTTL REDIRECT
; This routine is called after all the other parameter expansion has
; been performed when parsing a command line. It removes all re-direction and