; ZCPR34-3.Z80 ; Revisions to ZCPR Version 3.3 to make Version 3.4 (C) Copyright Jay P. Sage, ; 1988, 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 ld (duerrflag),a ; Store zero (INITFCB ends with A=0) into flag call scanner ; Parse first token on command line into FCB jr nz,badcmd ; Invoke error handler if '?' in command duerrflag equ $+1 ; Pointer for in-the-code modification ld a,0 ; 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,baddirerr ; 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 (tmpcolon),a ld a,' ' ; Set to invoke ECP directly ld (firstchar),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 TMPCOLON flag, but only when passing here does ; the flag get transferred to COLON. tmpcolon equ $+1 ; Pointer for in-the-code modification ld a,0 ; ..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. parsetail: 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 (parseptr),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: xor a ; Initialize various flags ld (tmpcolon),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 (tempusr),bc ; Initialize temporary DU call scanfld8 ; Extract possible file name cp ':' ; Was terminating character a colon? jr nz,scantype ; If not, go on to extract file type ld (tmpcolon),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 (duerrflag),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,(tempdr) ; Set designated drive ld (de),a ; ..into FCB call scanfld8 ; 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 scanfld_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,(tempusr) ; Get specified user number ld (hl),a ; ..and store in S1 byte of FCB ex de,hl ; Swap pointers back scan3: ; Skip to space character, character after an ; ..equal sign, or to end of command 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 scan4: ; Set zero flag if '?' in filename.typ qmcnt equ $+1 ; Pointer for in-the-code modification ld a,0 ; 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. scanfld8: xor a ; Initialize question mark count ld (qmcnt),a ld b,8 ; Scan up to 8 characters scanfld_B: ; Parse field of (B) characters into FCB push de ; Save pointer to FCB call scanfield 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,scanfld1 ; Skip if so inc hl ; Point to next character in command line cp '?' ; Was character a '?' wild card? jr nz,scanfld2 ; If not, skip ahead scanfld1: ; Increment the count of question mark characters in the parsed file name. push hl ld hl,qmcnt ; Point to count inc (hl) ; Increment it pop hl ld a,'?' ; Process '*' by filling with '?'s scanfld2: inc de ld (de),a djnz scanfield ; Loop through allowed number of characters scanfld3: call sdelm ; Skip until delimiter ret z ; Zero flag set if delimiter found inc hl ; Pt to next char in command line jr scanfld3 ;----------------------------------------------------------------------------- ; Validate the password pointed to by HL. Prompt user for password entry ; and return zero if it is correct. if pwcheck passck: push hl ; Save pointer to password call printc ; Prompt user dc 'PW? ' 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 endif ; pwcheck ;----------------------------------------------------------------------------- ; 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 TEMPDR and TEMPUSR, 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 REPARSE). 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 dirscan1 dirscan0: pop de pop hl ; Restore pointers ld bc,18 ; 2 bytes for DU + 8 bytes for name add hl,bc ; + 8 bytes for password dirscan1: 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 dirscan2: ; Point to name of directory (1st time thru) inc hl ; Point to next character to compare ld a,(de) cp (hl) jr nz,dirscan0 ; If no match, quit and go on to next DIR inc de ; Point to next character to compare djnz dirscan2 ; Count down dirscan3: ; 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 if pwcheck ; Otherwise, we drop through to setdu call dircheck ; 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 success without ; ..checking password (but TEMPDR/TEMPUSR 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 ;accptdir if accptdu or accptdir 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 (tempusr),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 not accptdu ; i.e., accptdir and not accptdu direrr: ; No match found dec a ret endif ;not accptdu endif ;accptdu or accptdir ;----------------------------------------------------------------------------- ; 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 TEMPDR and TEMPUSR, 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 dircheck ; See if directory acceptable jr nc,setdu ; If so, branch direrr: ; This code may do double duty for DIRSCAN ; ..above duerr: or 255 ; Return NZ to show failure ret endif ; accptdu ;---------------------------------------- ; 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 accptdu or [ accptdir and pwcheck ] dircheck: ; If the specified directory is the currently logged directory, accept it ; even if it is out of range and/or password protected. ld hl,(curusr) ; Get current drive/user into HL inc h ; Shift drive to range 1..16 ; Carry flag cleared by "or a" in duscan ; ..or "cp (hl)" in dirscan (at dirscan2) 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 ; End ZCPR34-3.Z80