;
;  PROGRAM:  PATH
;  VERSION:  1.0
;  AUTHOR:  RICHARD CONN
;  DATE:  12 JAN 83
;  PREVIOUS VERSIONS:  NONE
;
VERS    EQU     10

;
;       PATH allows the user to do two things -- display the current path
; and set a new path.  Named directories may be used in the definition of
; the new path.
;
;       PATH is invoked by the following forms:
;               PATH                    <-- Display Path
;               PATH path-expression    <-- Set Path
;               PATH //                 <-- Print Help
;

;
;  CP/M Constants
;
cpm     equ     0       ;base
bdose   equ     cpm+5
fcb     equ     cpm+5ch
tbuff   equ     cpm+80h
cr      equ     0dh
lf      equ     0ah

;
;  SYSLIB Routines
;
       ext     cline,zgpins,zdname,print,codend,eval10
       ext     cout,pstr,padc,retud

;
;       This program is Copyright (c) 1982, 1983 by Richard Conn
;       All Rights Reserved
;
;       ZCPR2 and its utilities, including this one, are released
; to the public domain.  Anyone who wishes to USE them may do so with
; no strings attached.  The author assumes no responsibility or
; liability for the use of ZCPR2 and its utilities.
;
;       The author, Richard Conn, has sole rights to this program.
; ZCPR2 and its utilities may not be sold without the express,
; written permission of the author.
;

;
;  Branch to Start of Program
;
       jmp     start

;
;******************************************************************
;
;  SINSFORM -- ZCPR2 Utility Standard General Purpose Initialization Format
;
;       This data block precisely defines the data format for
; initial features of a ZCPR2 system which are required for proper
; initialization of the ZCPR2-Specific Routines in SYSLIB.
;

;
;  EXTERNAL PATH DATA
;
EPAVAIL:
       DB      0FFH    ; IS EXTERNAL PATH AVAILABLE? (0=NO, 0FFH=YES)
EPADR:
       DW      40H     ; ADDRESS OF EXTERNAL PATH IF AVAILABLE

;
;  INTERNAL PATH DATA
;
INTPATH:
       DB      0,0     ; DISK, USER FOR FIRST PATH ELEMENT
                       ; DISK = 1 FOR A, '$' FOR CURRENT
                       ; USER = NUMBER, '$' FOR CURRENT
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0
       DB      0,0     ; DISK, USER FOR 8TH PATH ELEMENT
       DB      0       ; END OF PATH

;
;  MULTIPLE COMMAND LINE BUFFER DATA
;
MCAVAIL:
       DB      0FFH    ; IS MULTIPLE COMMAND LINE BUFFER AVAILABLE?
MCADR:
       DW      0FF00H  ; ADDRESS OF MULTIPLE COMMAND LINE BUFFER IF AVAILABLE

;
;  DISK/USER LIMITS
;
MDISK:
       DB      4       ; MAXIMUM NUMBER OF DISKS
MUSER:
       DB      31      ; MAXIMUM USER NUMBER

;
;  FLAGS TO PERMIT LOG IN FOR DIFFERENT USER AREA OR DISK
;
DOK:
       DB      0FFH    ; ALLOW DISK CHANGE? (0=NO, 0FFH=YES)
UOK:
       DB      0FFH    ; ALLOW USER CHANGE? (0=NO, 0FFH=YES)

;
;  PRIVILEGED USER DATA
;
PUSER:
       DB      10      ; BEGINNING OF PRIVILEGED USER AREAS
PPASS:
       DB      'chdir',0       ; PASSWORD FOR MOVING INTO PRIV USER AREAS
       DS      41-($-PPASS)    ; 40 CHARS MAX IN BUFFER + 1 for ending NULL

;
;  CURRENT USER/DISK INDICATOR
;
CINDIC:
       DB      '$'     ; USUAL VALUE (FOR PATH EXPRESSIONS)

;
;  DMA ADDRESS FOR DISK TRANSFERS
;
DMADR:
       DW      80H     ; TBUFF AREA

;
;  NAMED DIRECTORY INFORMATION
;
NDRADR:
       DW      00000H  ; ADDRESS OF MEMORY-RESIDENT NAMED DIRECTORY
NDNAMES:
       DB      64      ; MAX NUMBER OF DIRECTORY NAMES
DNFILE:
       DB      'NAMES   '      ; NAME OF DISK NAME FILE
       DB      'DIR'           ; TYPE OF DISK NAME FILE

;
;  REQUIREMENTS FLAGS
;
EPREQD:
       DB      0FFH    ; EXTERNAL PATH?
MCREQD:
       DB      000H    ; MULTIPLE COMMAND LINE?
MXREQD:
       DB      0FFH    ; MAX USER/DISK?
UDREQD:
       DB      000H    ; ALLOW USER/DISK CHANGE?
PUREQD:
       DB      000H    ; PRIVILEGED USER?
CDREQD:
       DB      0FFH    ; CURRENT INDIC AND DMA?
NDREQD:
       DB      0FFH    ; NAMED DIRECTORIES?
Z2CLASS:
       DB      0       ; CLASS 0
       DB      'ZCPR2'
       DS      10      ; RESERVED

;
;  END OF SINSFORM -- STANDARD DEFAULT PARAMETER DATA
;
;******************************************************************
;

;
;  Start of Program
;
start:
       call    zgpins  ; init ZCPR2 buffers
       lxi     h,tbuff ; pt to buffer
       call    cline   ; save it as string
       shld    cmdline ; save ptr to command line
       call    retud   ; get current disk and user
       mov     a,b
       sta     cdisk   ; set disk
       mov     a,c
       sta     cuser   ; set user
;
;  Print Banner
;
       call    print
       db      'PATH  Version '
       db      vers/10+'0','.',(vers mod 10)+'0',0

;
;  Check for Help
;
       lda     fcb+1   ; get first char
       cpi     '/'     ; help?
       jnz     start1
       call    print
       db      cr,lf,' PATH allows the user to display his current path'
       db      cr,lf,'and set a new path.  It is invoked by one of the forms:'
       db      cr,lf
       db      cr,lf,'         PATH                    <-- Display Path'
       db      cr,lf,'         PATH path-expression    <-- Set Path'
       db      cr,lf,'         PATH //                 <-- Print Help'
       db      cr,lf,0
       ret

;
;  Check for Error and Continue if not:
;       Load NAMES.DIR and check for function
;
start1:
       lda     epavail ; external path available?
       ora     a       ; 0=no
       jnz     start2
       call    print
       db      cr,lf,'Error -- External Path Not Defined -- Aborting',0
       ret
start2:
       call    codend  ; pt to buffer
       call    zdname  ; load NAMES.DIR
       jnz     start3
       mvi     c,0     ; if no NAMES.DIR or overflow, set no entries
start3:
       shld    ddir    ; save ptr to disk buffer
       mov     a,c     ; set entry count
       sta     dentry
       lxi     h,tempath       ; pt to temporary path
       shld    pathptr ; set path ptr
       lhld    cmdline ; check command line for text
       call    sblank  ; skip to non-blank
       shld    cmdline ; set ptr to next element
       ora     a       ; EOL=display function
       jz      pdisp

;
;  **** Set New Path ****
;       HL pts to next element
;
pbuild:
       lhld    cmdline ; pt to next element
       call    sblank  ; skip to non-blank
       mov     a,m     ; get first char of next element
       ora     a       ; EOL?
       jz      pbdone  ; done if so, store path and display
       shld    token   ; save ptr to first byte
       lda     cindic  ; get current disk indicator
       cmp     m       ; is it current?
       jz      pbdu    ; DU: form
       mov     a,m     ; get first char again
       sui     'A'     ; convert to number
       jc      pbdir   ; DIR: form
       mov     b,a     ; save number
       lda     mdisk   ; compare to max disk number
       mov     c,a
       mov     a,b
       cmp     c       ; in range?
       jnc     pbdir   ; DIR: form if not
       inx     h       ; pt to next char -- may be DU or DIR
       lda     cindic  ; get current indicator
       mov     b,a     ; ... in B
       mov     a,m     ; get next part of element
       cmp     b       ; current?
       jz      pbdu    ; is a DU: form
digtst:
       cpi     ':'     ; colon ends it
       jz      pbdu    ; is a DU: form
       cpi     ' '     ; space ends it
       jz      pbdu
       ora     a       ; EOL ends it
       jz      pbdu
       cpi     '0'     ; must be a digit
       jc      pbdir   ; DIR: form if not in range
       cpi     '9'+1
       jnc     pbdir
       inx     h       ; pt to next
       mov     a,m     ; get it
       jmp     digtst
;
;  It is a DU: form
;
pbdu:
       lhld    pathptr ; pt to path entry
       xchg            ; ... in DE
       lhld    token   ; pt to token
       lda     cindic  ; get current indicator
       mov     b,a
       mov     a,m     ; current?
       cmp     b
       jz      pbdu1
       sui     'A'-1   ; convert to number from 1 to n
pbdu1:
       stax    d       ; save disk element
       inx     h       ; pt to next
       inx     d
       mov     a,m     ; current user?
       inx     h       ; pt to after user in case of match to cindic
       cmp     b       ; match cindic?
       jz      pbdu2
       dcx     h       ; pt to first digit
       push    d       ; save ptr to path
       call    eval10  ; convert to number
       mov     a,d     ; range error?
       ora     a
       jnz     rangerr
       lda     muser   ; check for max user
       inr     a
       mov     b,a
       mov     a,e
       cmp     b
       jnc     rangerr
       pop     d       ; get ptr to path
pbdu2:
       stax    d       ; store user number
       inx     d
       mov     a,m     ; ending with colon?
       cpi     ':'
       jnz     pbdu3
       inx     h       ; skip over colon
pbdu3:
       shld    cmdline ; save ptr to next command line entry
       xchg
       shld    pathptr ; save ptr to next path entry
       jmp     pbuild  ; continue processing
;
;  Build DIR: form
;
pbdir:
       lhld    ndradr  ; memory-based directory available?
       mov     a,h
       ora     l
       jz      ddscan  ; scan disk dir if not
       inx     h       ; pt to entry count
       mov     c,m     ; get entry count in C
       inx     h       ; pt to first entry
       call    dirscan ; scan directory
       jnz     gotud   ; store disk/user
ddscan:
       lhld    ddir    ; pt to disk dir
       lda     dentry  ; get entry count
       mov     c,a     ; ... in C
       call    dirscan ; scan directory
       jnz     gotud
;
;  Entry not found
;
rangerr:
       call    print
       db      cr,lf
       db      cr,lf,'Invalid Path Expression Element -- Error Flagged at:'
       db      cr,lf,' -->',0
       lhld    token   ; print string starting at token
       call    pstr
       call    print
       db      cr,lf,'This may be an invalid DU: form (disk or user out of '
       db      'range)'
       db      cr,lf,'or an undefined named directory.'
       db      cr,lf
       db      cr,lf,'Aborting to CP/M',0
       jmp     cpm
;
;  Got User and Disk -- Store in Path
;
gotud:
       lhld    pathptr ; get ptr to path
       mov     m,b     ; store disk
       inx     h
       mov     m,c     ; store user
       inx     h       ; pt to next
       shld    pathptr
       lhld    token   ; skip over token
gotud1:
       mov     a,m     ; skip to space or EOL
       inx     h       ; pt to next
       ora     a       ; EOL?
       jz      gotud2
       cpi     ' '     ; space?
       jnz     gotud1
gotud2:
       dcx     h       ; pt to EOL or space
       shld    cmdline ; set ptr to next element
       jmp     pbuild  ; continue building
;
;  Path Building is Done -- TEMPATH contains new path
;
pbdone:
       lhld    pathptr ; store ending zero in path
       mvi     m,0
       lhld    epadr   ; pt to external path
       xchg            ; ... in DE
       lxi     h,tempath       ; copy tempath into external path
pcopy:
       mov     a,m     ; get disk
       stax    d       ; put disk
       ora     a       ; end of path?
       jz      pdisp   ; done if so and display
       inx     h       ; pt to user
       inx     d
       mov     a,m     ; get user
       stax    d       ; put user
       inx     h       ; pt to next disk
       inx     d
       jmp     pcopy

;
;  **** Display Path Function ****
;
pdisp:
       call    print
       db      cr,lf,'Current Path in Symbolic Form -- ',cr,lf,'       ',0
       lhld    epadr   ; pt to external path
       lda     cindic  ; get current indicator
       mov     d,a     ; ... in D
pdisp1:
       mov     a,m     ; get disk
       ora     a       ; done?
       jz      adisp
       cmp     d       ; current?
       jz      pdisp2
       adi     '@'     ; convert to letter
pdisp2:
       call    cout    ; print disk letter
       inx     h       ; pt to user
       mov     a,m     ; get user number
       cmp     d       ; current?
       jnz     pdisp3
       call    cout    ; print cindic
       jmp     pdisp4
pdisp3:
       call    padc    ; print user number
pdisp4:
       mvi     a,':'   ; print colon
       call    cout
       inx     h       ; pt to next element
       mov     a,m     ; done?
       ora     a       ; 0=yes
       jz      adisp
       call    print
       db      ' --> ',0
       jmp     pdisp1
;
;  Print Absolute Path
;
adisp:
       call    print
       db      cr,lf,'Current Path in Absolute Form --',cr,lf,'        ',0
       call    curud   ; get current user/disk
       lda     cindic  ; get current indicator
       mov     d,a     ; ... in D
       lhld    epadr   ; pt to path
adisp1:
       mov     a,m     ; get disk
       ora     a       ; done?
       jz      ndisp
       cmp     d       ; current?
       jnz     adisp2
       mov     a,b     ; get current disk
       inr     a       ; adjust to 1 to n
adisp2:
       adi     '@'     ; convert to letter
       call    cout    ; print disk letter
       inx     h       ; pt to user
       mov     a,m     ; get user
       cmp     d       ; current?
       jnz     adisp3
       mov     a,c     ; get current user
adisp3:
       call    padc    ; print user
       mvi     a,':'
       call    cout
       inx     h       ; pt to next
       mov     a,m     ; done?
       ora     a
       jz      ndisp
       call    print
       db      ' --> ',0
       jmp     adisp1
;
;  Print Named Path
;
ndisp:
       call    print
       db      cr,lf,'Current Path in Named Directory Form --',cr,lf,' ',0
       lhld    epadr   ; pt to external path
ndisp1:
       lda     cindic  ; get current indicator
       mov     d,a     ; ... in D
       call    curud   ; get current user and disk in C and B
       mov     a,m     ; get disk
       ora     a       ; done?
       rz
       cmp     d       ; current?
       jz      ndisp2
       mov     b,a     ; disk in B
       dcr     b       ; adjust to 0 to n-1
ndisp2:
       inx     h       ; pt to user
       mov     a,m     ; get it
       cmp     d       ; current?
       jz      ndisp3
       mov     c,a     ; user in C
ndisp3:
       inx     h       ; pt to next
       push    h       ; save ptr
       call    udscan  ; scan dirs for user/disk and print its name
       pop     h       ; get ptr
       mvi     a,':'
       call    cout
       mov     a,m     ; done?
       ora     a
       rz
       call    print
       db      ' --> ',0
       jmp     ndisp1


;
;  Utilities
;

;
;  Scan Memory-Based Directory for Name Pted to by TOKEN
;       On entry, HL pts to first entry and C=number of entries
;       On exit, return with NZ and BC=disk/user if found
;
dirscan:
       mov     a,c     ; any entries?
       ora     a
       rz              ; error return
dirsl:
       push    h       ; save ptr to entry
       push    b       ; save char count
       call    tokscan ; check for token match
       pop     b       ; get char count
       pop     h       ; get entry ptr
       jz      dirsfnd ; found?
       lxi     d,10    ; skip to next entry
       dad     d
       dcr     c       ; count down
       jnz     dirsl
       ret             ; return with Z if not found
dirsfnd:
       mov     b,m     ; entry found, so get disk number
       inr     b       ; adjust to 1 to n
       inx     h       ; pt to user
       mov     c,m     ; get user
       mvi     a,0ffh  ; OK return
       ora     a
       ret
;
;  Scan entry pted to by HL to see if it contains the TOKEN
;
tokscan:
       xchg            ; save ptr in DE
       lhld    token   ; pt to token
       xchg            ; HL pts to entry, DE pts to token
       inx     h       ; pt to entry in dir
       inx     h
       mvi     b,8     ; up to 8 bytes
toks1:
       ldax    d       ; get token char
       cpi     ':'     ; end of token?
       jz      toks2
       cpi     ' '     ; space?
       jz      toks2
       ora     a       ; EOL?
       jz      toks2
       cmp     m       ; match?
       rnz             ; error abort if no match
       inx     h       ; pt to next
       inx     d
       dcr     b       ; count down
       jnz     toks1
       ldax    d       ; full 8 chars, so token char must be a delim
       cpi     ':'     ; ok?
       rz
       cpi     ' '     ; ok if space
       rz
       ora     a       ; ok if EOL
       ret
toks2:
       mov     a,m     ; this must be a space for match
       cpi     ' '
       ret
;
;  Skip to non-blank
;
sblank:
       mov     a,m     ; get char
       inx     h       ; pt to next
       cpi     ' '     ; space?
       jz      sblank
       dcx     h       ; pt to non-blank
       ret
;
;  Scan directories for user and disk in C and B
;       Print name if found or Noname if not
;
udscan:
       lhld    ndradr  ; check memory-based first
       mov     a,h
       ora     l
       jz      udscan1 ; none
       inx     h       ; get entry count
       mov     d,m     ; ... in D
       inx     h       ; pt to first element
       call    udndscn ; scan named dir
       jz      udscan2 ; match
udscan1:
       lhld    ddir    ; pt to disk-based dir
       lda     dentry  ; number of entries
       mov     d,a     ; entry count in D
       call    udndscn ; scan named dir
       jnz     udscan3 ; return no name
udscan2:
       mvi     b,8     ; 8 chars max
udsprn:
       mov     a,m     ; get name char
       cpi     ' '     ; done?
       rz
       call    cout    ; print char
       inx     h       ; pt to next
       dcr     b       ; count down
       jnz     udsprn
       ret
udscan3:
       call    print
       db      'Noname',0
       ret
;
;  Scan dir pted to by HL for D elements
;       BC=Disk/User
;       Return with Z set and HL pting to name if found
;
udndscn:
       mov     a,m     ; get disk
       inx     h       ; pt to user
       cmp     b       ; match so far?
       jnz     udnd1
       mov     a,m     ; get user
       cmp     c       ; match?
       jnz     udnd1
       inx     h       ; pt to name
       xra     a       ; return with zero for found
       ret
udnd1:
       push    d       ; save count
       lxi     d,9     ; pt to next element
       dad     d
       pop     d       ; get count
       dcr     d       ; count down
       jnz     udndscn
       mvi     a,0ffh  ; not found
       ora     a
       ret

;
;  Return Current Disk and User (in B and C)
;
curud:
       lda     cdisk   ; get disk
       mov     b,a     ; ... in B
       lda     cuser   ; get user
       mov     c,a     ; ... in C
       ret

;
;  Buffers
;
cdisk:
       ds      1       ; current disk
cuser:
       ds      1       ; current user
cmdline:
       ds      2       ; ptr to next char in command line
token:
       ds      2       ; ptr to current token
pathptr:
       ds      2       ; ptr to next path entry
ddir:
       ds      2       ; ptr to disk dir in memory
dentry:
       ds      1       ; number of entries in disk dir
tempath:
       ds      50      ; allow for 25 element path

       end