;	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