;
; SYSLIB Module Name:  SZFNAM
; Author:  Richard Conn
; SYSLIB Version Number:  2.2
; Module Version Number:  1.1
; Module Entry Points:
;       ZFNINIT         ZFNAME          ZDNAME          ZDNFIND
;       ZCPRQ2
; Module External References:
;       CAPS            FI3$CLOSE       FI3$OPEN        F3$GET
;       INITFCB         MOVEB           ZPFIND          BDOS
;
;*
;*  ZFNAME is a file name scanner.  Pointing to the first character
;*      of a file name specification of the form 'dir:filename.typ', where
;*      any part of this specification is optional, this routine fills in
;*      an FCB with zeros, properly initializes the FN and FT (File Name and
;*      File Type) fields if 'filename.typ' or any part thereof is present,
;*      and returns the value of disk and user if they are specified
;*      (or FFH if they are not).
;*  The directory indicator 'dir:', if specified, may be of one of two forms:
;*              DIRECT -- A named directory, of up to 8 chars in length
;*              d      -- A disk drive letter
;*              u      -- A user number
;*              du     -- A disk drive letter and a user number
;*      The named directory form is checked first, so if a directory named
;*              A or A10 exists, it matches, as opposed to Drive A or
;*              drive/user A/10.
;*      Examples:
;*              HELP:*.HLP
;*              A5:TEST.TXT
;*              C?:ABC.*
;*              PASCAL:*.COM
;*      NOTE:  It is the programmer's responsibility to have called and
;*              properly initialized the buffers in the ZCPRINIT routine
;*              in the SZCPR module
;*


;*
;*  EXTERNALS
;*
       EXT     BDOS    ; BDOS UTILITY
       EXT     MOVEB   ; COPY UTILITY
       EXT     INITFCB ; INIT FCB UTILITY
       EXT     CAPS    ; CAPITALIZE ROUTINE
       EXT     ZPFIND  ; FIND FILE ALONG PATH
       EXT     FI3$OPEN        ; BYTE-ORIENTED FILE I/O
       EXT     FI3$CLOSE
       EXT     F3$GET


;*
;*  BASIC EQUATES
;*
MAXDISK EQU     16      ; MAX NUMBER OF DISKS
MAXUSER EQU     31      ; MAX USER NUMBER
MAXNAME EQU     64      ; MAX NUMBER OF NAMES IN DIR FILE
CPM     EQU     0       ; CP/M ENTRY
BENTRY  EQU     CPM+5   ; BDOS ENTRY
CR      EQU     0DH
LF      EQU     0AH


;*
;*  INIT MODULE
;*      IF THE USER WISHES TO CHANGE THE NAME OF THE DIRECTORY FILE TO LOOK
;*      FOR, HE CALLS THIS ROUTINE, WITH THE FCB PTR IN DE
;*
;*      IF THE USER WISHES TO CHANGE THE ADDRESS OF THE MEMORY-RESIDENT
;*      DISK NAMES BUFFER, HE CALLS THIS ROUTINE WITH THE ADDRESS IN HL
;*
;*      IF THE USER WISHES TO CHANGE THE NUMBER OF DIRECTORY NAMES PERMITTED
;*      IN THE DIRECTORY FILE, HE CALLS THIS ROUTINE, WITH THE NEW NAME
;*      COUNT IN C
;*
;*      THE SPECIFIC BUFFERED VALUES TO CHANGE ARE INDICATED BY THE A
;*      REGISTER AS FOLLOWS:
;*              BIT 7 -- SET NAME OF DIR FILE TO FCB PTED TO BY DE
;*              BIT 6 -- SET MAX NUMBER OF DIRECTORY NAMES PERMITTED
;*              BIT 5 -- SET ADDRESS OF MEMORY-RESIDENT BUFFER
;*
;*      IF THIS MODULE IS NOT CALLED, THE FOLLOWING DEFAULT VALUES ARE
;*      ASSUMED:
;*              NAME OF DIRECTORY NAMES FILE -- NAMES.DIR
;*              MAX NUMBER OF DIRECTORY NAMES -- 64
;*              NO MEMORY-RESIDENT NAMES BUFFER
;*
;*      NO REGS ARE AFFECTED
;*      ON INPUT, DE PTS TO NEW FCB
;*                HL IS THE ADDRESS OF THE MEMORY-RESIDENT NAMES BUFFER
;*                C IS THE MAX NUMBER OF DIRECTORY NAMES
;*                A IS FLAG --
;*                      BIT 7 SET (1) - LOAD DEFAULT FCB FROM DE
;*                      BIT 6 SET (1) - LOAD NAME COUNT FROM C
;*
ZFNINIT::
       PUSH    H       ; SAVE REGS
       PUSH    D
       PUSH    B
       PUSH    PSW
       MOV     B,A     ; SAVE FLAG IN B
       ANI     20H     ; LOOK AT BIT 5
       JZ      ZFNIN0  ; SKIP ADDRESS LOAD IF BIT 5 = 0
       SHLD    NDADR   ; SET ADDRESS FROM HL
ZFNIN0:
       MOV     A,B     ; GET FLAG BYTE
       ANI     80H     ; LOOK AT BIT 7
       JZ      ZFNIN1  ; SKIP FCB LOAD IF BIT 7 = 0
       PUSH    B       ; SAVE FLAG IN B
       XCHG            ; HL PTS TO FCB
       LXI     D,FNFCB ; PT TO MY FCB
       MVI     B,12    ; COPY 12 BYTES
       CALL    MOVEB   ; COPY
       POP     B       ; GET FLAG IN B
ZFNIN1:
       MOV     A,B     ; GET FLAG
       ANI     40H     ; LOOK AT BIT 6
       JZ      ZFNIN2  ; SKIP NAMES LOAD IF BIT 6 = 0
       MOV     A,C     ; GET COUNT
       STA     MAXN    ; STORE IN BUFFER
ZFNIN2:
       POP     PSW     ; RESTORE REGS
       POP     B
       POP     D
       POP     H
       RET


;*
;*  ZCPRQ2 -- RETURN POINTER TO BUFFER AREA IN 2ND ZCPR2 MODULE
;*      ON RETURN, HL PTS TO FIRST BYTE OF BUFFER AREA
;*
ZCPRQ2::
       LXI     H,MAXN  ; 1ST BYTE
       RET

;
;  FCB USED BY ZFNAME
;
MAXN:
       DB      MAXNAME                         ; INIT TO MAXNAME
NDADR:
       DW      0                               ; INIT TO NO BUFFER
FNFCB:
       DB      0,'NAMES   DIR',0,0,0,0         ; INIT TO NAMES.DIR
       DS      16
       DS      4

;*
;*  MAIN MODULE
;*      ON ENTRY, DE PTS TO FCB TO BE FILLED AND HL PTS TO FIRST BYTE OF
;*              TARGET STRING; FCB IS 36 BYTES LONG
;*      ON EXIT, B=DISK NUMBER (1 FOR A, ETC) AND C=USER NUMBER
;*              HL PTS TO TERMINATING CHAR
;*              A=0 AND Z SET IF ERROR IN DISK OR USER NUMBERS, A=0FFH AND NZ
;*                      IF OK
;*
ZFNAME::
       PUSH    D       ; SAVE DE
       PUSH    H       ; SAVE HL
       XCHG            ; SAVE PTR TO FCB
       SHLD    FCBPTR
       XCHG            ; DE PTS TO FCB
       POP     H       ; GET HL
       MVI     A,0FFH  ; SET DEFAULT DISK AND USER
       STA     DISK
       STA     USER
;  FILL TARGET FCB WITH ZEROES
       MVI     B,36    ; INIT FCB
       XRA     A       ; A=0
FNINI:
       STAX    D       ; STORE ZERO
       INX     D       ; PT TO NEXT
       DCR     B       ; COUNT DOWN
       JNZ     FNINI
;  SCAN FOR COLON IN STRING
       PUSH    H       ; SAVE PTR TO TARGET STRING
COLON:
       MOV     A,M     ; SCAN FOR COLON OR SPACE
       CPI     ':'     ; COLON FOUND?
       JZ      COLON1
       CALL    DELCK   ; DELIMITER FOUND?
       JZ      GETF1
       INX     H       ; PT TO NEXT
       JMP     COLON   ; CONTINUE IF NOT END OF LINE
;  WE HAVE FOUND A COLON, SO THERE IS A DIR: PREFIX
COLON1:
       POP     H       ; GET PTR TO FIRST CHAR
       MVI     A,0FFH  ; ALLOW DU: FORM
       CALL    ZDNFIND ; SCAN FOR DIRECTORY NAME; RETURN DISK IN DISK,
       JNZ     GETFILE         ;   USER IN USER, NZ IF OK, HL PTS TO COLON
       XRA     A       ; ERROR INDICATOR
       POP     D       ; RESTORE DE
       RET

;  EXTRACT FILE NAME
GETF1:
       POP     H       ; GET PTR TO BYTE
GETFILE:
       XCHG            ; GET PTR TO FCB
       LHLD    FCBPTR
       XCHG            ; HL PTS TO TARGET STRING, DE PTS TO FCB
       MOV     A,M     ; PTING TO COLON?
       CPI     ':'
       JNZ     GFILE1
       INX     H       ; SKIP OVER COLON
GFILE1:
       CALL    DELCK   ; GET NEXT CHAR AND CHECK FOR A DELIMITER
       JNZ     GFILE2  ; PROCESS SINCE NOT A DELIMITER
       CPI     '.'     ; WAS DELIMITER A '.'?
       JZ      GFILE2  ; PROCESS SINCE FN MISSING
;  NO NAME SPECIFIED, SO MAKE IT WILD
GFQUES:
       INX     D       ; FILL WITH '?'
       MVI     B,11    ; 11 BYTES
       MVI     A,'?'
GFFILL:
       STAX    D       ; PUT ?
       INX     D       ; PT TO NEXT
       DCR     B       ; COUNT DOWN
       JNZ     GFFILL
;  EXIT ZFNAME
FNDONE:
       LDA     DISK    ; GET DISK NUMBER
       CPI     0FFH    ; CURRENT DISK?
       JZ      FNDN1
       INR     A       ; ADD 1 SO IN THE RANGE FROM 1 TO 16
FNDN1:
       MOV     B,A     ; ... IN B
       LDA     USER    ; GET USER NUMBER
       MOV     C,A     ; ... IN C
       POP     D       ; RESTORE REGS
       MVI     A,0FFH  ; NO ERROR
       ORA     A       ; SET FLAGS
       RET
;  GET FILE NAME FIELDS
GFILE2:
       MVI     B,8     ; AT MOST 8 BYTES FOR FN
       CALL    SCANF   ; SCAN AND FILL
       MVI     B,3     ; AT MOST 3 BYTES FOR FT
       MOV     A,M     ; GET DELIMITER
       CPI     '.'     ; FN ENDING IN '.'?
       JNZ     GFILE3
       INX     H       ; PT TO CHAR AFTER '.'
       CALL    SCANF   ; SCAN AND FILL
       JMP     FNDONE  ; DONE ... RETURN ARGS
;  FT FIELD NOT GIVEN, SO <SP> FILL FT FIELD IN FCB
GFILE3:
       CALL    SCANF4  ; FILL WITH <SP>
       JMP     FNDONE
;
;  SCANNER ROUTINE
;
SCANF:
       CALL    DELCK   ; CHECK FOR DELIMITER
       JZ      SCANF4  ; <SP> FILL IF FOUND
       INX     D       ; PT TO NEXT BYTE IN FN
       CPI     '*'     ; ? FILL?
       JNZ     SCANF1
       MVI     A,'?'   ; PLACE '?'
       STAX    D
       JMP     SCANF2
SCANF1:
       STAX    D       ; PLACE CHAR
       INX     H       ; PT TO NEXT POSITION
SCANF2:
       DCR     B       ; COUNT DOWN
       JNZ     SCANF   ; CONTINUE LOOP
SCANF3:
       CALL    DELCK   ; "B" CHARS OR MORE - SKIP TO DELIMITER
       RZ
       INX     H       ; PT TO NEXT
       JMP     SCANF3
SCANF4:
       INX     D       ; PT TO NEXT FN OR FT
       MVI     A,' '   ; <SP> FILL
       STAX    D
       DCR     B       ; COUNT DOWN
       JNZ     SCANF4
       RET

;*
;*  ZDNAME -- LOAD THE CONTENTS OF THE NAMES.DIR FILE INTO THE MEMORY
;*      BUFFER PTED TO BY HL
;*      ON ENTRY, HL PTS TO THE MEMORY BUFFER EXTENDING TO THE BASE OF
;*              THE BDOS
;*      ON EXIT, HL PTS TO THE FIRST ENTRY IN THE NAMES.DIR FILE, BC IS
;*              THE NUMBER OF VALID ENTRIES, A IS THE ERROR FLAG (A=0FFH
;*              AND NZ IF NO ERROR, A=0 AND Z IF ERROR)
;*                      ERRORS MAY BE EITHER MEMORY OVERFLOW OR NAMES.DIR
;*                      NOT FOUND
;*      EACH NAMES.DIR ENTRY IS 10 BYTES LONG, STRUCTURED AS FOLLOWS:
;*              BYTE 0: DISK NUMBER (A=0)
;*              BYTE 1: USER NUMBER
;*              BYTES 2-9: DIRECTORY NAME, 8 CHARS MAX, <SP> FILL AT END
;*
ZDNAME::
       PUSH    D       ; SAVE UNCHANGED REG
       SHLD    DIRNAME ; SAVE PTR TO BUFFER
       SHLD    CURNAME ; SAVE PTR TO FIRST ENTRY
       LXI     D,FNFCB ; PT TO FCB WHICH CONTAINS DIR FILE NAME
       CALL    INITFCB ; INIT FCB
       MVI     B,0FFH  ; SEARCH CURRENT USER
       CALL    ZPFIND  ; LOOK FOR NAMES.DIR FILE
       JZ      DIRNERR ; FILE NOT FOUND ERROR
;
;  FOUND NAMES.DIR, SO LOAD IT
;
       CALL    PUTUD   ; SAVE CURRENT USER/DISK
       CALL    LOGUD   ; LOG IN NEW USER/DISK
       LXI     D,FNFCB ; PT TO FCB
       CALL    FI3$OPEN        ; OPEN FOR INPUT
;
;  LOAD NAMES.DIR FILE
;
       MVI     C,0     ; SET ENTRY COUNT
       LDA     MAXN    ; GET MAX NUMBER OF NAMES
       MOV     B,A     ; ... IN B
ZDNA1:
       LXI     H,ENTRY ; PT TO ENTRY BUFFER
       CALL    GETNAME ; GET NAME FROM DISK
       JNZ     ZDNA3   ; DONE?
       LDA     ENTRY+2 ; LOOK AT FIRST LETTER OF DIR NAME
       ORA     A       ; NO ENTRY?
       JZ      ZDNA2
       LHLD    DIRNAME ; PT TO BUFFER ENTRY
       LXI     D,ENTRY ; PT TO NEW ENTRY
       INR     C       ; INCREMENT ENTRY COUNTER
       PUSH    B       ; SAVE COUNTERS
       XCHG            ; HL PTS TO NEW ENTRY, DE PTS TO DEST
       MOV     A,M     ; GET DISK NUMBER
       STAX    D       ; STORE DISK NUMBER
       INX     H       ; PT TO USER NUMBER
       INX     D
       MOV     A,M     ; GET USER
       STAX    D       ; PUT USER
       MVI     B,8     ; AT MOST 8 MORE BYTES
ZDNA1A:
       INX     H       ; PT TO NEXT BYTE
       INX     D
       MOV     A,M     ; GET NEXT BYTE
       ORA     A       ; END OF NAME?
       JZ      ZDNA1B  ; <SP> FILL
       STAX    D       ; PUT BYTE
       DCR     B       ; COUNT DOWN
       JNZ     ZDNA1A
       INX     D       ; PT TO FIRST BYTE OF NEXT ENTRY
       JMP     ZDNA1C
ZDNA1B:
       MVI     A,' '   ; <SP> FILL
       STAX    D       ; PLACE <SP>
       INX     D       ; PT TO NEXT
       DCR     B       ; COUNT DOWN
       JNZ     ZDNA1B
ZDNA1C:
       POP     B       ; RESTORE COUNTERS
       LHLD    BENTRY+1        ; PT TO BDOS ADDRESS
       MOV     A,H     ; CHECK FOR PAGE BEFORE BDOS
       DCR     A       ; PAGE BEFORE
       CMP     D       ; ARE WE THERE?
       JZ      DNSC2A  ; ERROR IF SO, BUT RESTORE UD
       JC      DNSC2A
       XCHG            ; HL PTS TO NEXT BUFFER POSITION
       SHLD    DIRNAME ; SAVE PTR
;
;  CONTINUE LOOPING
;
ZDNA2:
       DCR     B       ; COUNT DOWN
       JNZ     ZDNA1
;
;  COMPLETION EXIT
;
ZDNA3:
       CALL    FI3$CLOSE       ; CLOSE FILE
       CALL    GETUD   ; RESTORE USER/DISK
       MVI     B,0     ; SET HIGH-ORDER BYTE
       LHLD    CURNAME ; GET PTR TO FIRST ENTRY
       MVI     A,0FFH  ; SET NO ERROR
       ORA     A       ; SET FLAGS
       POP     D       ; RESTORE DE
       RET

;*
;*  ZDNFIND -- SCAN FOR POSSIBLE DISK DIRECTORY NAME
;*      THIS ROUTINE EXAMINES THE DIR: PREFIX FOR EITHER A DIRECTORY NAME
;*              OR THE DU FORM
;*      ON ENTRY, HL PTS TO DIRECTORY NAME ENDING IN ANY VALID DELIMITER
;*              AND A=0 IF DU: FORM NOT ALLOWED (JUST DIR: FORM ALLOWED)
;*      RETURN DISK IN B, USER IN C, NZ IF OK, HL PTS TO COLON
;*      DE IS NOT AFFECTED
;*
ZDNFIND::
       PUSH    D       ; SAVE DE
       SHLD    DIRNAME ; SAVE DIRECTORY NAME AWAY
       ORA     A       ; DU: FORM ALLOWED?
       JNZ     SVDISK  ; SCAN FOR DU: FORM FIRST
;
;  LOOK FOR DIR: FORM
;
NAME:
;
;  SCAN MEMORY-RESIDENT BUFFER IF ONE IS AVAILABLE
;
       LHLD    NDADR   ; GET ADDRESS
       MOV     A,L     ; CHECK FOR ZERO
       ORA     H
       JZ      NAME2
       INX     H       ; PT TO ENTRY COUNT
       MOV     A,M     ; GET ENTRY COUNT
       ORA     A       ; CHECK FOR NO ENTRIES
       JZ      NAME2   ; GOTO DISK IF NO ENTRIES IN MEMORY
       MOV     B,A     ; ENTRY COUNT IN B
       INX     H       ; PT TO FIRST ENTRY
       XCHG            ; DE PTS TO FIRST ENTRY IN MEMORY
;
;  MAIN SCANNING LOOP FOR MEMORY-RESIDENT BUFFER
;
NAME0:
       LHLD    DIRNAME ; HL PTS TO DIR NAME
       PUSH    D       ; SAVE PTR TO CURRENT MEMORY ENTRY
       MVI     C,8     ; SCAN UP TO 8 BYTES
       INX     D       ; SKIP DISK
       INX     D       ; SKIP USER
NAME1:
       CALL    DELCK   ; CHECK FOR DELIMITER IN DIR NAME
       JZ      NAME1B
       LDAX    D       ; GET CHAR IN BUFFER
       CMP     M       ; COMPARE AGAINST TARGET NAME
       JNZ     NAME1A
       INX     H       ; PT TO NEXT
       INX     D
       DCR     C       ; COUNT DOWN
       JNZ     NAME1
       JMP     NAME1C  ; FOUND
NAME1A:
       POP     H       ; GET PTR TO CURRENT BUFFER ENTRY
       LXI     D,10    ; SKIP TO NEXT ENTRY
       DAD     D       ; HL PTS TO NEXT ENTRY
       XCHG            ; DE PTS TO NEXT ENTRY
       DCR     B       ; COUNT DOWN
       JNZ     NAME0
       JMP     NAME2   ; COMPLETE FAILURE -- DISK SCAN NOW
;
;  DELIM ENCOUNTERED ON TARGET NAME, SO CHECK FOR SPACE ON BUFFER ENTRY
;    FOR A MATCH
;
NAME1B:
       LDAX    D       ; DELIM ENCOUNTERED, SO MUST BE <SP> TO MATCH
       CPI     ' '     ; CHECK FOR SPACE
       JNZ     NAME1A  ; NOT SPACE, SO SKIP TO NEXT
;
;  ENTRY FOUND
;
NAME1C:
       POP     H       ; GET PTR TO ENTRY
       JMP     DNSC3A  ; EXTRACT DATA AND CONTINUE
;
;  LOOK FOR DIR NAME FILE
;
NAME2:
       LXI     D,FNFCB ; PT TO FCB WHICH CONTAINS DIR FILE NAME
       CALL    INITFCB ; INIT FCB
       MVI     B,0FFH  ; SEARCH CURRENT USER
       CALL    ZPFIND  ; LOOK FOR NAME FILE
       JZ      DIRNERR ; ERROR IF NOT FOUND
;
;  FOUND DIR NAME FILE, SO LOAD IT AND SCAN IT FOR TARGET NAME
;
       CALL    PUTUD   ; SAVE CURRENT USER/DISK
       CALL    LOGUD   ; LOG IN NEW USER/DISK
       LXI     D,FNFCB ; PT TO FCB
       CALL    FI3$OPEN        ; OPEN FOR INPUT
;
;  LOAD DIR NAME FILE ENTRIES
;
       LDA     MAXN    ; GET MAXIMUM NUMBER OF NAMES
       MOV     B,A     ; ... IN B
DNSC1:
       LXI     H,ENTRY ; PT TO ENTRY BUFFER
       CALL    GETNAME ; GET NAME FROM DISK
       JNZ     DNSC2   ; ERROR EXIT?
       CALL    SCANAME ; SCAN FOR DIR NAME
       JZ      DNSC3   ; FOUND ENTRY, SO GET VALUES
       DCR     B       ; COUNT DOWN
       JNZ     DNSC1
DNSC2:
       CALL    FI3$CLOSE       ; CLOSE FILE
DNSC2A:
       CALL    GETUD   ; RESTORE CURRENT USER/DISK
       JMP     DIRNERR ; ERROR SINCE NO ENTRY FOUND
;
;  DIR NAME FOUND, SO GET DISK AND USER INFORMATION
;
DNSC3:
       CALL    FI3$CLOSE       ; CLOSE FILE
       CALL    GETUD   ; RESTORE CURRENT USER/DISK
DNSC3A:
       MOV     A,M     ; GET DISK (HL PTS TO CURRENT ENTRY)
       STA     DISK    ; SAVE DISK
       INX     H       ; PT TO USER
       MOV     A,M     ; GET USER
       STA     USER    ; SAVE USER
;
;  SKIP TO COLON AFTER DIR NAME
;
       LHLD    DIRNAME ; PT TO DIR NAME
DNSC4:
       CALL    DELCK   ; SKIP TO DELIMITER
       JZ      DIRNX   ; EXIT IF SO
       INX     H       ; PT TO NEXT
       JMP     DNSC4
;
;  LOOK AT START OF DU: FORM
;       ON ENTRY, HL PTS TO FIRST CHAR OF DIRECTORY NAME
;
SVDISK:
       MOV     A,M     ; GET DISK LETTER
       CALL    CAPS    ; CAPITALIZE LETTER
       CPI     'A'     ; DIGIT?
       JC      USERCK  ; IF NO DIGIT, MUST BE USER OR COLON
       SUI     'A'     ; CONVERT TO NUMBER
       CPI     MAXDISK ; LIMIT?
       JNC     NAME    ; NAME IF OUT OF LIMIT
       STA     DISK    ; SAVE FLAG
       INX     H       ; PT TO NEXT CHAR
;
;  CHECK FOR USER
;
USERCK:
       MOV     A,M     ; GET POSSIBLE USER NUMBER
       CPI     ':'     ; NO USER NUMBER
       JZ      DIRNX   ; EXIT IF SO
       CPI     '?'     ; ALL USER NUMBERS?
       JNZ     USERC1
       STA     USER    ; SET VALUE
       INX     H       ; PT TO AFTER
       MOV     A,M     ; MUST BE COLON
       CPI     ':'
       JZ      DIRNX   ; EXIT
       JMP     DIRNERR ; FATAL ERROR IF NOT COLON AFTER ?
USERC1:
       XRA     A       ; ZERO USER NUMBER
       MOV     B,A     ; B=ACCUMULATOR FOR USER NUMBER
USRLOOP:
       MOV     A,M     ; GET DIGIT
       INX     H       ; PT TO NEXT
       CPI     ':'     ; DONE?
       JZ      USRDN
       SUI     '0'     ; CONVERT TO BINARY
       JC      NAME    ; NAME IF USER NUMBER ERROR
       CPI     10
       JNC     NAME
       MOV     C,A     ; NEXT DIGIT IN C
       MOV     A,B     ; OLD NUMBER IN A
       ADD     A       ; *2
       ADD     A       ; *4
       ADD     B       ; *5
       ADD     A       ; *10
       ADD     C       ; *10+NEW DIGIT
       MOV     B,A     ; RESULT IN B
       JMP     USRLOOP
USRDN:
       MOV     A,B     ; GET NEW USER NUMBER
       CPI     MAXUSER+1       ; WITHIN RANGE?
       JNC     NAME    ; NAME IF OUT OF RANGE
       STA     USER    ; SAVE IN FLAG
;
;  VALID EXIT -- FOUND IT, SO LOAD BC AND EXIT FLAG; ON ENTRY, HL PTS TO :
;
DIRNX:
       LDA     USER    ; RETURN USER IN C, DISK IN B
       MOV     C,A
       LDA     DISK
       MOV     B,A
       INR     B       ; DISK A = 1
       MVI     A,0FFH  ; SET NO ERROR
       ORA     A       ; SET FLAGS
       POP     D       ; RESTORE DE
       RET
;
;  INVALID EXIT -- NOT FOUND OR ERROR
;       NO VALID RETURN PARAMETERS (BC, HL)
;
DIRNERR:
       XRA     A       ; ERROR CODE
       POP     D       ; RESTORE DE
       RET

;*
;*  BUFFERS
;*
FCBPTR: DS      2       ; PTR TO FCB
DISK:   DS      1       ; DISK NUMBER
USER:   DS      1       ; USER NUMBER
ENTRY:
       DS      11      ; ENTRY FROM DISK FILE (DISK, USER, DIR NAME)
DIRNAME:
       DS      2       ; PTR TO DIRECTORY NAME
CURNAME:
       DS      2       ; PTR TO CURRENT LOCATED NAME

;
;  SCAN DIRECTORY ENTRY PTED TO BY HL FOR DIRECTORY NAME STARTING AT DIRNAME
;       RETURN WITH Z IF FOUND; DO NOT AFFECT BC
;
SCANAME:
       PUSH    B       ; SAVE BC
       SHLD    CURNAME ; SET CURRENT NAME PTR
       INX     H       ; SKIP DISK
       INX     H       ; SKIP USER
       MVI     B,9     ; UP TO 9 CHARS
       XCHG            ; CURRENT PTR IN DE
       LHLD    DIRNAME ; PT TO TARGET NAME
SCANL:
       LDAX    D       ; GET CHAR
       ORA     A       ; END OF STRING?
       JZ      SCANL2
       MOV     C,A     ; SAVE IN C
       MOV     A,M     ; GET TARGET NAME CHAR
       CALL    CAPS    ; CAPITALIZE IT
       CMP     C       ; COMPARE
       JNZ     SCANL1  ; ABORT IF NO MATCH
       CALL    DELCK   ; END OF TARGET NAME?
       JZ      SCANL1  ; ABORT IF SO
       INX     H       ; PT TO NEXT
       INX     D
       DCR     B       ; COUNT DOWN
       JNZ     SCANL
;  NOT FOUND RETURN
SCANL1:
       LHLD    CURNAME ; PT TO CURRENT ENTRY
       LXI     B,11    ; SKIP 11 BYTES
       DAD     B       ; PT TO NEXT ENTRY
       POP     B       ; RESTORE BC
       MVI     A,0FFH  ; NOT FOUND FLAG
       ORA     A       ; SET FLAGS
       RET
;  FOUND RETURN
SCANL2:
       CALL    DELCK   ; MUST PT TO DELIMITER IF MATCH
       JNZ     SCANL1  ; ABORT IF NOT
       LHLD    CURNAME ; PT TO FOUND ENTRY
       POP     B       ; RESTORE BC
       XRA     A       ; FOUND
       RET

;
;  GET NAME FROM FILE 3 INTO BUFFER PTED TO BY HL
;       DO NOT AFFECT BC OR HL; RET W/NZ IF ERROR
;
GETNAME:
       PUSH    B       ; SAVE BC
       PUSH    H       ; SAVE HL
       CALL    F3$GET  ; GET DISK LETTER
       JNZ     GNERR   ; ERROR?
       SUI     'A'     ; CONVERT TO NUMBER
       MOV     M,A     ; STORE IT
       INX     H       ; PT TO NEXT
       MVI     B,10    ; GET USER AND DIRECTORY NAME
GETN1:
       CALL    F3$GET  ; GET BYTE
       JNZ     GNERR   ; ERROR?
       MOV     M,A     ; STORE IT
       INX     H       ; PT TO NEXT
       DCR     B       ; COUNT DOWN
       JNZ     GETN1
       XRA     A       ; OK
GNERR:
       POP     H       ; RESTORE HL
       POP     B       ; RESTORE BC
       RET

;
;  CHECK CHAR PTED TO BY HL FOR A DELIMITER
;    RET WITH Z FLAG SET IF DELIMITER
;
DELCK:
       MOV     A,M     ; GET CHAR
       CALL    CAPS    ; CAPITALIZE
       ORA     A       ; 0=DELIM
       RZ
       CPI     ' '+1   ; <SP>+1
       JC      DELCK1  ; <SP> OR LESS
       CPI     '='
       RZ
       CPI     5FH     ; UNDERSCORE
       RZ
       CPI     '.'
       RZ
       CPI     ':'
       RZ
       CPI     ';'
       RZ
       CPI     ','
       RZ
       CPI     '<'
       RZ
       CPI     '>'
       RET
DELCK1:
       CMP     M       ; COMPARE WITH SELF FOR OK
       RET

;
;  PUTUD -- SAVE CURRENT USER/DISK FOR LATER RESTORE
;       NO REGS AFFECTED
;
PUTUD:
       PUSH    B       ; SAVE REGS
       PUSH    PSW
       PUSH    D
       PUSH    H
       MVI     C,25    ; GET CURRENT DISK
       CALL    BDOS
       STA     CDISK   ; SET CURRENT DISK
       MVI     E,0FFH  ; GET USER
       MVI     C,32    ; GET CURRENT
USER
       CALL    BDOS
       STA     CUSER   ; SET CURRENT USER
       POP     H       ; RESTORE REGS
       POP     D
       POP     PSW
       POP     B
       RET

;
;  BUFFERS
;
CDISK:
       DS      1       ; CURRENT DISK
CUSER:
       DS      1       ; CURRENT USER

;
;  GETUD -- RESTORE USER/DISK FROM PREVIOUS PUTUD
;
GETUD:
       PUSH    H       ; SAVE REGS
       PUSH    D
       PUSH    B
       PUSH    PSW
       LDA     CDISK   ; SELECT DISK
       MOV     E,A
       MVI     C,14    ; SELECT
       CALL    BDOS
       LDA     CUSER   ; SELECT USER
       MOV     E,A
       MVI     C,32    ; SELECT
       CALL    BDOS
DONE:
       POP     PSW     ; GET REGS
       POP     B
       POP     D
       POP     H
       RET

;
;  LOGUD -- LOG IN USER/DISK, WHICH C=USER AND B=DISK
;
LOGUD:
       PUSH    H       ; SAVE REGS
       PUSH    D
       PUSH    B
       PUSH    PSW
       MOV     E,C     ; SELECT USER
       MVI     C,32
       CALL    BDOS
       MOV     E,B     ; SELECT DISK
       MVI     C,14
       CALL    BDOS
       JMP     DONE


       END