; Z40-3.Z80 ; Revisions to ZCPR Version 3.4 to make Version 4.0 (C) Copyright Harold Bower, ; 1992, all rights reserved. ;============================================================================= ; C O M M A N D L I N E P A R S I N G C O D E ;============================================================================= ; This code parses the command line pointed to by HL. The command verb is ; parsed, placing the requested program name into the command file control ; block. The drive and user bytes are set. If an explicit DU or DIR was ; given, the COLON flag is set so that the processor knows about this later ; when the command search path is built. PARSER: LD DE,CMDFCB ; Point to the command FCB PUSH DE CALL INITFCB ; Initialize the FCB POP DE IF NOT ZSDOS2 LD (DUERFLG),A ; Store zero (INITFCB ends with A=0) into flag ENDIF CALL SCANNER ; Parse first token on command line into FCB JR NZ,BADCMD ; Invoke error handler if '?' in command LD A,(DUERFLG) ; See if bad DU/DIR specified with command verb OR A IF badduecp JR Z,PARSER1 ; If DU/DIR is OK, skip ahead LD A,(CMDFCB+1) ; See if it's a directory change command SUB ' ' JR Z,BADCMD ; If so, give ECP a shot at it ENDIF ; badduecp JR NZ,BADDIR ; Invoke error handler PARSER1: LD DE,CMDFCB+9 ; Make sure no explicit file type was given LD A,(DE) ; Get first character of file type CP ' ' ; Must be blank JR Z,PARSER2 BADCMD: IF badcmdecp XOR A ; Pretend there is no colon LD (TMPCOLN),A LD A,' ' ; Set to invoke ECP directly LD (FIRSTCH),A ELSE ; not badcmdecp LD A,ECBADCMD ; Error code for illegal command form JR ERROR ENDIF PARSER2: PUSH HL ; Save pointer to next byte of command LD HL,COMMSG ; Place default file type (COM) into FCB LD BC,3 LDIR POP HL ; Get command line pointer back ; The following block of code is arranged so that the COLON flag is set only ; when an explicit directory specification is detected in the command verb. ; Other parses also change the TMPCOLN flag, but only when passing here does ; the flag get transferred to COLON. LD A,(TMPCOLN) ; Get Temp Colon Flag set by SCANNER routine LD (COLON),A ; If explicit DU/DIR, set COLON flag ; This block of code parses two tokens in the command line into the two ; default FCBs at 5Ch and 6Ch. It also sets a pointer to the command tail ; for later copying into the command tail buffer at 80h. This code is used ; first when attempting to parse a normal command line and possibly again ; later when the entire user's command is treated as a tail to the extended ; command processor. The resident JUMP and SAVE commands use it also, and ; the entry point is available at location CCP+16 for use by other programs. PRSTAIL: LD (TAILSV),HL ; Save pointer to command tail ; Process first token LD DE,TFCB ; Point to first default FCB PUSH DE ; Save pointer while initializing CALL INITFCB ; Initialize both default FCBs POP DE CALL SKSP ; Skip over spaces in command line CALL NZ,SCANNER ; If not end of line, parse the token ; ..into first FCB LD (PARSPTR),HL ; Save pointer to second token for reparsing ; Process second token CALL SKSP ; Skip over spaces RET Z ; Done if end of line or end of command LD DE,TFCB2 ; Point to second default FCB ; ..and fall through to SCANNER routine ;----------------------------------------------------------------------------- ; This routine processes a command line token pointed to by HL. It attempts ; to interpret the token according to the form [DU:|DIR:]NAME.TYP and places ; the corresponding values into the FCB pointed to by DE. On exit, HL points ; to the delimiter encountered at the end of the token. The Z flag is reset ; if a wild card was detected in the token. SCANNER: IF ZSDOS2 ;4.0E EX DE,HL ;4.0E Put command line addr in DE CALL DMASET ;4.0E .set DMA addr here EX DE,HL ;4.0E ..restore FCB addr to DE LD C,B_PARS ;4.0E ZsDos2 Parse Name Function PUSH DE ;4.0E .save FCB ptr CALL BDOS ;4.0E Parse it! POP HL ;4.0E Restore FCB ptr to HL PUSH AF ;4.0E ..and save Question Mark count LD A,(HL) ;4.0E Get Drive LD (TMPCOLN),A ;4.0E . (Saving to show if DU/DIR used) LD BC,13 ;4.0E .offset to User # ADD HL,BC ;4.0E LD C,(HL) ;4.0E ..and get to C LD B,A ;4.0E ...Drive to B INC HL ;4.0E Advance to DU Parse flag INC HL ;4.0E .at +15 LD A,(HL) ;4.0E LD (DUERFLG),A ;4.0E ..and store for later LD A,(DELIMT) ;4.0E Get old temp delimiter LD (DELIM2),A ;4.0E ..and store as last LD A,(DE) ;4.0E Get delimiter char from Command Line LD (DELIMT),A ;4.0E ..and store as current CP '=' ;4.0E Is it an Equal Sign? JR NZ,NOEQUL ;4.0E .jump if not INC DE ;4.0E ..else bump ptr NOEQUL: PUSH DE ;4.0E Save Command line ptr CALL ZS2PASS ;4.0E Check for password POP HL ;4.0E Move command line ptr to HL CALL DEFLTDMA ;4.0E ..while we restore default DMA POP AF ;4.0E Restore Question Mark count OR A ;4.0E ..and set flags on # of "?" in A RET ;4.0E ELSE ;4.0E if NOT zsdos2.. XOR A ; Initialize various flags LD (TMPCOLN),A ; Set no colon LD BC,(CURUSR) ; Get current drive and user into BC INC B ; Shift drive range from 0..15 to 1..16 LD (TMPUSR),BC ; Initialize temporary DU CALL SCANF8 ; Extract possible file name CP ':' ; Was terminating character a colon? JR NZ,SCANTYPE ; If not, go on to extract file type LD (TMPCOLN),A ; Otherwise, set colon and process DU/DIR INC HL ; Point to character after colon ; Code for resolving directory specifications. Ends with a nonzero value and ; Z flag reset if the DU/DIR specification cannot be resolved. There are ; quite a few possibilities here. PUSH DE ; Save pointer to FCB INC DE ; Point to first character of name IF accptdir OR accptdu ;------------------------------| ; Cases of EITHER form accepted PUSH HL ; Save pointer to command string PUSH DE ; Save pointer to FCB+1 if accptdir AND accptdu ;------------------------------| ; Cases where BOTH forms are accepted PUSH DE ; Save pointer to FCB+1 for second scan IF dufirst ; Check DU: form before DIR: form EX DE,HL CALL DUSCAN POP DE ; Restore pointer to FCB+1 for DIRSCAN CALL NZ,DIRSCAN ELSE ; Check DIR: form before DU: form CALL DIRSCAN POP HL ; Restore pointer to FCB+1 for duscan CALL NZ,DUSCAN ENDIF ;dufirst ELSE ;not [accptdir AND accptdu]--------------------| ; Cases of ONLY ONE form accepted IF accptdu ; Check only DU: form EX DE,HL CALL DUSCAN ELSE ;accptdir ; Check only DIR: form CALL DIRSCAN ENDIF ;accptdu ENDIF ;accptdir AND accptdu--------------------------| POP DE ; Restore pointer to FCB+1 POP HL ; Restore pointer to command string ELSE ;not [accptdir OR accptdu]---------------------| ; Case of NEITHER form accepted LD A,(DE) ; Get it SUB ' ' ; If no name is there, A=0 and Z flag set ENDIF ;accptdir OR accptdu---------------------------| LD (DUERFLG),A ; Save flag in parser code LD C,A ; Save bad directory flag CALL IFCB ; Perform partial init (set user code) LD A,C ; Get bad directory flag back DEC DE ; Back up to record count byte DEC DE LD (DE),A ; Store error flag there NZ if error SCANNER1: POP DE ; Get FCB pointer back LD A,(TMPDRV) ; Set designated drive LD (DE),A ; ..into FCB CALL SCANF8 ; Scan for file name ; This code processes the file type specification in the token SCANTYPE: EX DE,HL ; Switch FCB pointer into HL LD BC,8 ; Offset to file type field ADD HL,BC cp '.' ; See if file type specified jr nz,scantype2 ; If not, skip over file type parsing ex de,hl ; Swap pointers back inc hl ; Point to character after '.' ld b,3 ; Maximum characters in file type call SCANF_B ; Parse file type into FCB ex de,hl ; Swap pointers again scantype2: ld bc,5 ; Offset from file type to S1 field in FCB add hl,bc ld a,(tmpusr) ; Get specified user number ld (hl),a ; ..and store in S1 byte of FCB ex de,hl ; Swap pointers back ; Skip to space char, char after an equal sign, or to end of command scan3: ld a,(hl) ; Get next character cp ' '+1 ; Done if less than or equal to space jr c,scan4 ; ..(including end-of-line (= 0)) cp cmdsep ; Done if end of command jr z,scan4 inc hl ; Skip on to next character cp '=' ; If not equal sign jr nz,scan3 ; ..keep scanning ; Set zero flag if '?' in filename.typ scan4: ld a,(qmcnt) ; Number of question marks or a ; Set zero flag ret ; This routine invokes SCANFIELD for a file name field. It initializes the ; question mark count and preserves the FCB pointer. SCANF8: xor a ; Initialize question mark count ld (qmcnt),a ld b,8 ; Scan up to 8 characters SCANF_B: push de ; Save pointer to FCB call scanfield ; Parse field of (B) characters into FCB pop de ; Restore pointer to FCB ret ; This routine scans a command-line token pointed to by HL for a field whose ; maximum length is given by the contents of the B register. The result is ; placed into the FCB buffer pointed to by DE. The FCB must have had its name ; and type fields initialized before this routine is called. Wild cards of ; '?' and '*' are expanded. On exit, HL points to the terminating delimiter. scanfield: call sdelm ; Done if delimiter encountered ret z cp '*' ; Is character a wild card? jr z,SCANF1 ; Skip if so inc hl ; Point to next character in command line cp '?' ; Was character a '?' wild card? jr nz,SCANF2 ; If not, skip ahead ; Increment the count of question mark characters in the parsed file name. SCANF1: push hl ld hl,qmcnt ; Point to count inc (hl) ; Increment it pop hl ld a,'?' ; Process '*' by filling with '?'s SCANF2: inc de ld (de),a djnz scanfield ; Loop through allowed number of characters SCANF3: call sdelm ; Skip until delimiter ret z ; Zero flag set if delimiter found inc hl ; Pt to next char in command line jr SCANF3 ENDIF ;4.0E not zsdos2 ;----------------------------------------------------------------------------- ; Validate the password pointed to by HL. Prompt user for password entry ; and return zero if it is correct. IF pwcheck PASSCK: IF BANKED ;4.0E LD HL,BPASSCK ;4.0E Point to banked routine JP DOIT ;4.0E ..and execute COMMON /BANK2/ ;4.0E BPASSCK: ;4.0E ENDIF ;4.0E banked PUSH HL ; Save pointer to password CALL PRINTC ; Prompt user DEFC 'PW? ' LD HL,(Z3ENV+26) ;4.0E Set up buffer for user input ;4.0E LD HL,PWLIN ; Set up buffer for user input LD BC,90AH ; Set 0ah (BDOS readln function) in C LD (HL),B ; ..and 9 (max character count) in B EX DE,HL ; Switch buffer pointer to DE IF pwnoecho LD HL,BIOS+0CH ; Disable BIOS conout routine LD (HL),0C9H ; ..by putting RET code there CALL BDOSSAVE ; Get user input LD (HL),0C3H ; Reenable BIOS conout routine ELSE ; not pwnoecho CALL BDOSSAVE ; Get user input ENDIF ; pwnoecho INC DE ; Point to count of characters entered LD A,(DE) ; Get character count INC DE ; Point to first character LD H,D LD L,E ; Get pointer into HL to mark end of input CALL ADDAH ; Advance HL to just past last character LD (HL),' ' ; Place space there POP HL ; Restore pointer to password from NDR DEC B ; Reduce B to 8 (maximum characters to compare) PWCK: LD A,(DE) ; Get next user character CALL UCASE ; Capitalize it CP (HL) ; Compare to NDR RET NZ ; No match CP ' ' ; If last user character matched space in RET Z ; ..NDR, then we have a complete match INC HL ; If not done, point to next characters INC DE DJNZ PWCK ; (flags not affected by DJNZ) XOR A ; Set zero flag and RET ; ..return Z to show success IF BANKED ;4.0E CSEG ;4.0E ENDIF ;4.0E banked ENDIF ; pwcheck IF NOT ZSDOS2 ;4.0E ;----------------------------------------------------------------------------- ; This code attempts to interpret the token in the FCB pointed to by register ; pair DE as a DIR (named directory) prefix. If it is successful, the drive ; and user values are stored in TMPDRV and TMPUSR, the zero flag is set, and ; a value of zero is returned in register A. ; If the named directory is found to be password restricted, then the user is ; asked for the password (unless the directory is the one currently logged or ; the current IF state is false). If an incorrect password is entered, the ; error handler is generally invoked directly. The exception to this is when ; the transient program bit is set in the command status flag (this bit would ; be set by a non-CPR program that calls REPARS). In this case the default ; directory is returned, the zero flag is reset, and a nonzero value in ; returned in register A to show a bad directory. In addition, the code in ; SCANNER will set record-count byte in the FCB to a nonzero value so that ; the calling program can detect the error. [Note: if DU processing is also ; allowed and it follows DIR processing, DUSCAN will also be called. Unless ; there is a passworded directory with a DU form, this will cause no trouble.] IF accptdir DIRSCAN: ; If the DU form is not allowed, we have to detect a colon-only condition here. ; Otherwise DUSCAN will take care of it. IF NOT accptdu ld a,(de) ; Get first character of directory sub ' ' ; If it is a blank space ret z ; ..we have a successful directory resolution ENDIF ; not accptdu IF ndrenv ; If getting NDR address from Z3ENV ld hl,(z3env+15h) ; Offset to NDR address ld a,h or l ; Test for NDR in Z3ENV jr z,DIRERR ; Branch if no NDR implemented ELSE ; using fixed address of NDR buffer ld hl,z3ndir ; Point to first entry in NDR ENDIF ; ndrenv jr DIRSCN1 DIRSCN0: pop de pop hl ; Restore pointers ld bc,18 ; 2 bytes for DU + 8 bytes for name add hl,bc ; + 8 bytes for password DIRSCN1: ld a,(hl) ; Get next character or a ; Zero if end of NDR jr z,DIRERR push hl ; Save pointer to NDR entry push de ; Save pointer to name we are looking for ld b,8 ; Number of characters to compare inc hl ; Point to name of directory (1st time thru) DIRSCN2: inc hl ; Point to next character to compare ld a,(de) cp (hl) jr nz,DIRSCN0 ; If no match, quit and go on to next DIR inc de ; Point to next character to compare djnz DIRSCN2 ; Count down ; Match found pop de ; Straighten out stack (DE not used hereafter) pop hl ; Point to drive corresponding to the DIR ld b,(hl) ; Get it into B inc hl ; Point to user ld c,(hl) ; Get user value into C ENDIF ;accptdir ENDIF ;4.0E not zsdos2 IF accptdir OR ZSDOS2 ;4.0E ZS2PASS: ;4.0E IF BANKED ;4.0E LD HL,BZS2PAS ;4.0E Point to banked routine JP DOIT ;4.0E ..and execute COMMON /BANK2/ ;4.0E BZS2PAS: ;4.0E ENDIF ;4.0E IF pwcheck ; Otherwise, we drop through to setdu CALL DIRCHK ; See if directory automatically acceptable JR NC,SETDU ; Set up the directory if OK ; ..else fall through ; This code is a bit tricky. We do not want to be asked for passwords for ; named directory references in commands when the current IF state is false. ; So, first we check to see if there is a password on the directory. If not, ; we proceed to set the temporary DU to the specified directory. If there is ; a password, we check the current IF state. If it is false, we do not check ; passwords and pretend there was no password. However, we leave the current ; directory in effect. This will work properly in all but one rare ; circumstance. When the command is an 'OR' command with a reference to a ; passworded named directory (e.g., "OR EXIST SECRET:FN.FT"), the password ; will not be requested and the current directory will be used instead of the ; specified one. IF fcps NE 0 ; If FCP implemented ... CALL IFTEST ; ..test current IF state RET Z ; If false IF in effect, fake Ok w/o checking ; ..password (but TMPDRV/TMPUSR not set) ENDIF ; fcps ne 0 PUSH BC ; Save drive/user CALL PASSCK ; Perform password checking POP BC ; Restore requested drive/user JR Z,SETDU ; If not bad password, set it up LD A,(CMDSTATFL) ; See if external invocation (disable BIT 3,A ; ..error handling if so) RET NZ ; Return NZ to show bad directory LD A,ECBADPASS ; Error code for bad password JP ERROR ENDIF ; pwcheck ENDIF ;4.0E accptdir or zsdos2 IF accptdu OR accptdir OR ZSDOS2 ;4.0E SETDU: LD A,16+1 SUB B ; A has 16 for drive A, 1 for drive P LD HL,(DRVEC) ; Load drive vector into HL DRCHK: DEC A ; Reduce drive count (sets/clears Z flag) ADD HL,HL ; Shift HL (sets/clears carry flag) JR NZ,DRCHK ; Loop if more shifts required JR NC,DIRERR ; If carry not set, drive is not in vector LD (TMPUSR),BC RET ; Return with A=0 and Z flag set ; If ACCPTDU is enabled, we can share similar code in DUSCAN and do not need ; the code here. IF ZSDOS2 OR [NOT accptdu] ; i.e., accptdir and not accptdu DIRERR: DEC A ; No match found RET ENDIF ; not accptdu ENDIF ;4.0E accptdu or accptdir or zsdos2 IF NOT ZSDOS2 ;4.0E ;----------------------------------------------------------------------------- ; This code attempts to interpret the token in the FCB pointed to by register ; pair DE as a DU (drive/user) prefix. If it is successful, the drive and ; user values are stored in TMPDRV and TMPUSR, the zero flag is set, and a ; value of zero is returned in register A. Otherwise the zero flag is reset ; and a nonzero value is returned in register A. ; The ADUENV option allows acceptance of the DU form to be controlled by the ; DUOK flag in the environment descriptor. An additional feature of this code ; when the ADUENV option is enabled is that a DU value is always accepted, ; even if DUOK is off and even if it is outside the normally allowed range, ; if it corresponds to a named directory with no password. The currently ; logged directory is unconditionally acceptable (if you got there once, you ; can stay as long as you like without further hassles). IF accptdu ; Allow DU: form DUSCAN: ld bc,(curusr) ; Preset C to current user, B to current drive ld a,(hl) ; Get possible drive specification sub 'A' ; Otherwise convert to number 0..15 jr c,DUSCAN1 ; If < 0, leave B as is cp 16 jr nc,DUSCAN1 ; If > 15, leave B as is ld b,a ; Otherwise use value given inc hl ; ..and point to next character DUSCAN1: inc b ; Shift drive to range 1..16 ld a,(hl) ; Get possible user specification cp ' ' jr z,DUSCAN2 ; If none present, leave C as is push bc ; Save DU values in BC call decimal1 ; Get possible user number from cmd line to DE pop bc ; Restore values to BC jr c,DUERR ; Return NZ if invalid decimal conversion ld a,d ; Get high byte of result or a ; Make sure it is zero ret nz ; If not, return NZ to show bad user number ld c,e ; Get specified decimal user number into C DUSCAN2: CALL DIRCHK ; See if directory acceptable JR NC,SETDU ; If so, branch DIRERR: ; This may do double duty for DIRSCAN above DUERR: OR 255 ; Return NZ to show failure RET ENDIF ; accptdu ENDIF ;4.0E not zsdos2 ;---------------------------------------- ; This routine checks the directory request for automatic acceptance. DU and ; DIR references are automatically allowed if they: ; (1) refer to the currently logged directory ; (2) refer to a directory within the MAXDU range ; (3) have a directory name with no password ; If the directory passes these tests, the routine returns with the carry flag ; reset. Otherwise the carry flag is set. ; If the specified directory is the currently logged directory, accept it ; even if it is out of range and/or password protected. IF accptdu OR [ accptdir AND pwcheck ] OR ZSDOS2 ;4.0E DIRCHK: LD HL,(CURUSR) ; Get current drive/user into HL INC H ; Shift driv to 1..16. Carry cleared by "or a" ; in duscan or "cp (hl)" in DIRSCAN (@ DIRSCN2) SBC HL,BC ; Compare values RET Z ; Return with carry reset if current drive/user ; If the specified DU corresponds to a named directory with no password, or ; if WPASS is enabled so that password checking is not performed when the ; wheel byte is set, then accept it. IF z3ndirs NE 0 CALL DU2DIR ; See if there is a matching named directory IF pwcheck ; If passwords are being checked... JR Z,DIRCHK1 ; If no directory with that name, skip on IF wpass CALL WHLCHK ; If wheel set, accept the DU values RET NZ ; (carry is reset) ENDIF ; wpass LD DE,9 ; Advance to password ADD HL,DE LD A,(HL) ; Get first character of password CP ' ' RET Z ; If none, we have a valid DU (carry clear) ELSE ; not pwcheck RET NZ ; Accept if there is a corresponding DIR ENDIF ; pwcheck ENDIF ; z3ndirs ne 0 DIRCHK1: IF aduenv ; Check DUOK flag in ENV LD A,(DUOKFL) ; Get flag SUB 1 ; Force carry set if flag is zero RET C ; If so, DU not accepted ENDIF ; aduenv IF duenv ; If getting max drive and user from ENV LD DE,(MAXDRENV) ; E=maxdrive, D=maxuser LD A,E ; Check drive first CP B RET C ; Return if out of range LD A,D ELSE ; Using fixed values of max DU LD A,MAXDISK CP B RET C ; Return with carry set if out of range LD A,MAXUSR ENDIF ; duenv CP C RET ; Return with carry flag in appropriate state ENDIF ; accptdu or [accptdir and pwcheck] or zsdos2 ;4.0E IF BANKED ;4.0E CSEG ;4.0E ENDIF ;4.0E ; End Z40-3.Z80