;       title   'dupusr - duplicate a directory entry under a new user'
;       by Bruce R. Ratoff - first version 5/17/80
;       modified 5/17/80 for wildcard processing
;       modified 5/18/80 to zero drive number before making file.
;       modified 5/18/80 to operate from originating user #
;       modified 6/3/80  to fix problem w/ multi-extent files
;
; The purpose of this program is to create extra entries in a CP/M 2.x
; directory that "point to" files which were actually created in a
; different directory.  This way, you have access to the file from
; both user numbers without having to keep multiple copies of the
; actual file itself.  To create duplicate entries on drive "d" for
; user "x" from files which currently reside at user "y", type:
;       A>USER y                        ;log in to originating user y
;       A>DUPUSR d:filename.typ x       ;create files at destination user x
; Note that this program will totally duplicate the directory entry in
; all respects (except the user number, of course).  This means that
; both entries will show the file with the same attributes, such as
; "read-only" or "system".  The filename.typ may contain "?" and "*".
;
; The only known hazard in the use of this program occurs when erasing
; one of the duplicate entries.  You must type control-c immediately
; after erasing the entry, so that cp/m is forced to rebuild the allocation
; vector for that drive.  This is because the erase command frees the
; blocks shown for the erased file without checking if they are in use
; elsewhere.  If you didn't type control-c, the next disk write would
; clobber these blocks, voiding all other pointers to the file.
; Unfortunately this would only be apparent the next time you tried
; to read the file from another user number, at which time you would
; read garbage.
;
; Please forward all comments, suggestions and improvements to:
;       Bruce R. Ratoff
;       80 Gill Lane, Apt 1B
;       Iselin, New Jersey 08830
;
;
;
bdos    equ     5               ;cp/m entry point
exit    equ     0               ;cp/m exit point
dfcb    equ     5ch             ;cp/m default fcb
dbuff   equ     80h             ;default disk buffer
;
pmessg  equ     9               ;print message function
seldsk  equ     14              ;select drive function
open    equ     15              ;open file function
close   equ     16              ;close file function
srchfst equ     17              ;search for first file match
srchnxt equ     18              ;search for next file match
delete  equ     19              ;delete file function
make    equ     22              ;make file function
attrib  equ     30              ;set file attributes function
gsuser  equ     32              ;get/set user function
;
;
       org     100h
begin:
       lhld    bdos+1          ;set up a stack
       sphl                    ;at top of tpa
       mvi     c,gsuser        ;get our user #
       mvi     e,0ffh
       call    bdos
       sta     ourusr          ;save for later
       lda     dfcb            ;check for specific drive
       dcr     a
       mov     e,a             ;set up for select disk call
       mvi     c,seldsk
       inr     a               ;if no specified drive, skip call
       cnz     bdos
       sub     a               ;now zap out drive spec
       sta     dfcb
       mvi     a,'?'           ;force extent number wild
       sta     dfcb+12
       lxi     h,dfcb+17       ;point to originating user # in cmd line
       mvi     e,0
numlup:
       mov     a,m             ;get numeric (i hope) character
       inx     h               ;bump char pointer
       sui     '0'             ;remove ascii bias
       jc      numdone
       cpi     10              ;check if past 9
       jnc     numdone         ;any invalid char ends number
       mov     d,a
       mov     a,e             ;get accumulated number
       add     a               ;times two
       add     a               ;times four
       add     e               ;times five
       add     a               ;times ten
       add     d               ;plus new digit
       mov     e,a             ;save accumulation
       jmp     numlup          ;loop back for next char
numdone:
       lda     ourusr          ;make sure not same as us
       cmp     e
       jz      badusr
       mov     a,e             ;get destination user number
       sta     dstusr          ;save it
       cpi     16              ;legal?
       jc      userok
badusr:
       lxi     d,ilgusr
       mvi     c,pmessg        ;bitch about illegal user #
       call    bdos
       jmp     exit
ilgusr:
       db      'Invalid destination user #$'
userok:
       sub     a               ;zero out file count
       sta     filcnt
       lxi     d,dfcb          ;find the first file and get its block map
       mvi     c,srchfst
       call    bdos
       inr     a               ;search successful?
       jnz     gotfile         ;yes, go process rest
       lxi     d,nofile
       mvi     c,pmessg        ;say "no file"
       call    bdos
       jmp     exit
nofile:
       db      'Originating file not found$'
gotfile:
       dcr     a               ;compensate for inr above
       rrc                     ;file offset to bits 5 and 6
       rrc
       rrc
       ani     60h
       lxi     h,dbuff         ;point to base of buffer
       mov     c,a
       mvi     b,0
       dad     b               ;index by file offset
       push    h               ;save for the moment
       lxi     b,filetable
       call    filepoint       ;get table pointer to hl
       xchg                    ;de now points to place in table
       lda     filcnt
       inr     a
       sta     filcnt          ;bump file count
       pop     h               ;hl points to directory entry
       mvi     b,32
       call    blkmov          ;copy entry into table
       mvi     c,srchnxt       ;search for another entry
       lxi     d,dfcb
       call    bdos
       inr     a               ;returns 0ffh at end of search
       jnz     gotfile         ;got another one...go save it
;
; end of directory encountered, now process them
;
       lda     dstusr          ;set to dest user
       mov     e,a
       mvi     c,gsuser
       call    bdos
;
; main loop to set up one duplicate entry
;
makefile:
       lxi     b,filetable-32  ;allow for filcnt one greater than desired
       call    filepoint
       push    h               ;save pointer
       lxi     d,dfcb          ;copy next name to default fcb
       mvi     b,32
       call    blkmov
       sub     a
       sta     dfcb            ;clear drive number
       lxi     d,-20           ;point back to extent field
       dad     d
       mvi     m,'$'           ;tag end of print here
       pop     d               ;get back pointer to start of entry
       inx     d               ;bump fwd to name
       mvi     c,pmessg
       call    bdos            ;say what we're working on
       lxi     h,dfcb
       lxi     d,ourfcb        ;copy name data to work fcb
       mvi     b,14
       call    blkmov
       lxi     h,0
       shld    ourfcb+14       ;zap frebyt, extlen
       lda     ourfcb+9
       ani     7fh             ;clear r/o flag for create
       sta     ourfcb+9
       sub     a
       sta     ourfcb          ;clear drive number
       lxi     d,ourfcb
       mvi     c,open
       call    bdos            ;check for existing file of same name
       inr     a
       jnz     makeok          ;skip create if already there
       lxi     d,ourfcb
       mvi     c,make          ;create the file
       call    bdos
       inr     a               ;check for errors
       jnz     makeok
       lxi     d,makerr
       mvi     c,pmessg        ;say can't make file
       call    bdos
       jmp     exit
makerr:
       db      ', Cannot create file$'
makeok:
       lxi     h,dfcb+12       ;copy bookkeeping stuff from orig file
       lxi     d,ourfcb+12
       mvi     b,20
       call    blkmov
       lda     ourfcb+14       ;set update flag so that altered fcb
       ani     1fh             ;gets written by close function
       sta     ourfcb+14       ;(retain module # in low 5 bits)
       lxi     d,ourfcb
       mvi     c,close         ;do a close to set size and block map
       call    bdos
       inr     a
       jnz     closok          ;check for errors
       lxi     d,closerr
       mvi     c,pmessg
       call    bdos            ;say close error
       jmp     exit
closerr:
       db      ', Cannot close file$'
closok:
       lda     dfcb+9          ;was original file r/o?
       ora     a               ;set sign bit if yes
       jp      nextfile        ;done if no
       sta     ourfcb+9        ;make our new file r/o to match
       lxi     d,ourfcb
       mvi     c,attrib        ;do set attributes call
       call    bdos
nextfile:
       lxi     h,filcnt        ;point to file counter
       dcr     m               ;count it down
       jz      exit            ;exit if done
       lxi     d,crlf
       mvi     c,pmessg        ;else do a crlf
       call    bdos
       jmp     makefile        ;and go work on next one
crlf:
       db      13,10,'$'
;
;
; subroutine to do block moves
blkmov:
       mov     a,m             ;copy byte from m(hl) to m(de)
       stax    d
       inx     h               ;bump pointers
       inx     d
       dcr     b               ;loop for count in b
       jnz     blkmov
       ret
;
;
; subroutine to index bc by file counter
filepoint:
       lhld    filcnt          ;get file counter
       mvi     h,0             ;force hi ord to 0
       dad     h               ;multiply by 32
       dad     h
       dad     h
       dad     h
       dad     h
       dad     b               ;use as index to file table
       ret
;
;
;
;
filcnt: ds      1               ;count of files in filetable
ourusr: ds      1               ;storage for our user number
dstusr: ds      1               ;storage for destination user number
ourfcb: ds      32              ;file control block for create function
;
filetable       equ     $       ;start table here, take all avail memory
;
       end