;	Title	Share_2
;				   $SALUT (0,36,41,44)
				   %include "sharehdr.mac"
;
;     Label: "The DOS SHARE Utility"
;	     "Version 4.00 (C) Copyright 1988 Microsoft"
;	     "Licenced Material - Program Property of Microsoft"
;
;******************* END OF SPECIFICATIONS *************************************

section SHARE align=1 PUBLIC class=SHARE

;				   NAME Sharer2

					   ;  INCLUDE DOSSYM.INC
					   ;  INCLUDE SYSMSG.INC
				   [list -]
;				   .xcref
				   %include "dossym.mac"
				   %include "sysmsg.mac"
				   %include "amis.mac"
;				   .cref
				   [list +]
;				   page 80,132

				   MSG_UTILNAME <SHARE>

ShareDataVersion		   equ	1

%ifndef IBM
 %iassign IBM 0
%endif
%ifndef Installed
 %iassign Installed 0
%endif

%iassign Installed	TRUE ; for installed version

; NASM original macros

%imacro OFF 2.nolist
 %if Installed
	mov %1, offset %2
 %else
	mov %1, offset %2 wrt DOSGROUP
 %endif
%endmacro

%unimacro detectstripangles 4.nolist

%imacro detectstripangles 4.nolist
%defstr %%param %4
%assign ?%2 0
%assign ?%3 0
%rep 16
 %substr %%opening %%param 1
 %ifidn %%opening, '<'
  %substr %%param %%param 2,-1
  %assign ?%2 ?%2 + 1
 %endif
%endrep
%rep 16
 %strlen %%length %%param
 %substr %%closing %%param %%length
 %ifidn %%closing, '>'
  %substr %%param %%param 1,-2
  %assign ?%3 ?%3 + 1
 %endif
%endrep
%deftok %%token %%param
%define ?%1 %%token
%endmacro

%unimacro errnz 1.nolist

%imacro errnz 1.nolist
detectstripangles %%token, %%opening, %%closing, %1
%if ? %+ %%token
    %error %1 <> 0, ERRNZ failed
%endif
%endmacro

; end of NASM original macros

mft equ MFT	; NASM port label
quux equ mft

					   ;---------------------------------------
					   ; if we are installed, then define the
					   ; base code segment of the sharer first
					   ;---------------------------------------
;	$SALUT	(4,9,17,36)

	%IF	Installed

section SHARE
; (no prior section) ; Share   ENDS

	%ENDIF

				   ;---------------------------------------
				   ; include the rest of the segment
				   ;  definitions for normal msdos

				   ; segment ordering for MSDOS

				   ;---------------------------------------
	%include "dosseg.nas"

section CONSTANTS

	extrn	DataVersion:BYTE   ; version number of DOS data.
	extrn	JShare:BYTE	   ; location of DOS jump table.
	extrn	sftFCB:DWORD	   ; [SYSTEM] pointer to FCB cache table
	extrn	KeepCount:WORD	   ; [SYSTEM] LRU count for FCB cache
	extrn	CurrentPDB:WORD

; (no prior section) ; CONSTANTS ENDS

section DATA

	extrn	ThisSFT:DWORD	   ; pointer to SFT entry
	extrn	WFP_start:WORD	   ; pointer to name string
	extrn	User_ID:WORD
	extrn	Proc_ID:WORD
	extrn	SFT_addr:DWORD
	extrn	Arena_Head:WORD
	extrn	fshare:BYTE
	extrn	pJFN:DWORD
	extrn	JFN:WORD

	%IF	DEBUG

	    extrn   BugLev:WORD
	    extrn   BugTyp:WORD
	    %include "bugtyp.nas"

	%ENDIF


; (no prior section) ; DATA	ENDS

				   ;---------------------------------------
				   ; if we are not installed, then the
				   ; code here is just part of the normal
				   ; MSDOS code segment otherwise,
				   ; define our own code segment
				   ;---------------------------------------

;	.sall
;	%IF	INSTALLED

section SHARE

	    ASSUME  SS:DOSGROUP,CS:SHARE

;	%ELSE
;
;section CODE align=1 PUBLIC class=CODE
;
;	    ASSUME  SS:DOSGROUP,CS:DOSGROUP
;
;	%ENDIF

	Extrn	FreLock:WORD,Serial:WORD
	Extrn	MFT_Enter:NEAR,MFTClose:NEAR,MFTClu:NEAR,MFTCloseP:NEAR
	Extrn	MFTCloN:NEAR
	Extrn	Set_Mult_Block:NEAR,Clr_Mult_Block:NEAR,Chk_Block:NEAR
	Extrn	MFT_Get:NEAR

[list -]
	%include "mft.mac"
[list +]

;  $SALUT (4,4,9,41)

   BREAK <FNM - Find name in MFT>

;******************* START OF SPECIFICATIONS ***********************************
;
;	FNM - Find name in MFT
;
;	FNM searches the MFT for a name record.
;
;	ENTRY	(DS:SI) = pointer to name string (.asciz)
;		(al) = 1 to create record if non exists
;		     = 0 otherwise
;	EXIT	'C' clear if found or created
;		  (DS:BX) = address of MFT name record
;		'C' set if error
;		  If not to create, item not found
;		    (DS:SI) unchanged
;		  If to create, am out of space
;		    (ax) = error code
;	USES	ALL
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure FNM,NEAR

   push ds				; save string address
   push si
   xchg bh,al				; (bh) = create flag
   or	bh,bh				; if not creating
   jz	fnm01				; skip sft test

					;---------------------------------------
					; run down through string counting
					;  and summing
					;---------------------------------------

fnm01: sub dx,dx			; (dx) = byte count
   sub	bl,bl				; (bl) = sum

fnm1: lodsb				; (al) = next char
   add	bl,al
   adc	bl,0
   inc	dx
   and	al,al
   jnz	fnm1				; terminate after null char

					;---------------------------------------
					; Info computed.
					;  Start searching name list

					;  (bh) = create flag
					;  (bl) = sum byte
					;  (dx) = byte count
					;  (TOS+2:TOS) = name string address
					;---------------------------------------
	; Magic code for DOSREINIT start !
   push cs
   pop	ds

   Off	SI,mft

fnm2: cmp byte [si + mft_flag],MFLG_FRE
	; Magic code for DOSREINIT end !
   jl	fnm10				; at end - name not found
   jz	fnm4				; is free, just skip it
   cmp	bl,[si + mft_sum] 		; do sums compare?
   jz	fnm5				; its a match - look further
fnm4: add si,[si + mft_len]		; not a match... skip it
   JMP	SHORT fnm2
					;---------------------------------------
					; name checksums match
					;   - compare the actual strings
					;
					;   (dx)	= length
					;   (ds:si	= MFT address
					;   (bh)	= create flag
					;   (bl)	= sum byte
					;   (dx)	= byte count
					;   (TOS+2:TOS) = name string address
					;---------------------------------------

fnm5: mov cx,dx 			; (cx) = length to match
   pop	di
   pop	es				; (ES:DI) = fba given name
   push es
   push di
   push si				; save MFT offset
   add	si,mft_name			; (ds:si) = fwa string in record
   repz cmpsb
   pop	si				; (ds:si) = fwa name record
   jnz	fnm4				; not a match

					;---------------------------------------
					; Yes, we've found it.  Return the info
					;
					;  (TOS+2:TOS) = name string address
					;---------------------------------------

   fmt	TypShare,LevMFTSrch,<"FNM found name record at $x\n">,<si>
   pop	ax				; discard unneeded stack stuff
   pop	ax
   mov	bx,si				; (ds:bx) = fwa name record
   clc

   ret
					;---------------------------------------
					;**
					;**  Its not in the list
					;**  - lets find a free spot and put
					;**    it there
					;
					;  (bh)        = create flag
					;  (bl)        = sum byte
					;  (dx)        = string length
					;  (TOS+2:TOS) = ASCIZ string address
					;  (ds)        = SEG CODE
					;---------------------------------------
fnm10:
   and	bh,bh
   jnz	fnm10$5 			; yes, insert it
   pop	si
   pop	ds				; no insert, its a "not found"
   stc

   fmt	TypShare,LevMFTSrch,<"FNM failing\n">

   mov	ax,error_path_not_found

   ret

fnm10$5:
   add	dx,mft_name			; (dx) = minimum space needed

   off	SI,mft

fnm11: cmp byte [si + mft_flag],MFLG_FRE

   %IFN  DEBUG
       jl   fnm20			; at END, am out of space
   %ELSE
       jl   fnm20j
   %ENDIF

   jz	fnm12				; is a free record
   add	si,[si + mft_len] 		; skip name record
   JMP	SHORT fnm11

   %IF	DEBUG
fnm20j: jmp fnm20
   %ENDIF

fnm12: mov ax,[si + mft_len]		; Have free record, (ax) = total length
   cmp	ax,dx
   jnc	fnm13				; big enough
   add	si,ax
   JMP	SHORT fnm11			; not large enough - move on

					;---------------------------------------
					; OK, we have a record which is big
					;  enough.  If its large enough to hold
					;  another name record of 6 characters
					;  than we'll split the block, else
					;  we'll just use the whole thing
					;
					; (ax)	      = size of free record
					; (dx)	      = size needed
					; (ds:si)     = address of free record
					; (bl)	      = sum byte
					; (TOS+2:TOS) = name string address
					;---------------------------------------

fnm13: sub ax,dx			; (ax) = total size of proposed fragment
   cmp	ax,mft_name+6
   jc	fnm14				; not big enough to split
   push bx				; save sum byte
   mov	bx,dx				; (bx) = offset to start of new name record
   mov	byte [bx + si + mft_flag],MFLG_FRE
   mov	[bx + si + mft_len],ax		; setup tail as free record
   sub	ax,ax				; don't extend this record
   pop	bx				; restore sum byte
fnm14: add dx,ax			; (dx) = total length of this record
   mov	[si + mft_len],dx
   mov	[si + mft_sum],bl
   mov	byte [si + mft_flag],MFLG_NAM

   fmt	TypShare,LevMFTSrch,<"FNM creating record at $x\n">,<si>

   push ds
   pop	es				; (es) = MFT segment for "stow"
   sub	ax,ax
   mov	di,si
   add	di,mft_lptr
   stosw				; zero LCK pointer
   ERRNZ mft_sptr-mft_lptr-2
;	add	di,mft_sptr-mft_lptr-2
   stosw				; zero SFT pointer
   stosw				; zero SFT pointer
serial equ Serial	; NASM port label
   inc	word [cs:serial]				; bump serial number
   mov	ax,[cs:serial]
   ERRNZ mft_serl-mft_sptr-4
;	ADD	di,mft_serl-mft_sptr-4
   stosw
					;---------------------------------------
					; We're all setup except for the name.
					;  Note that we'll block copy the whole
					;  name field, even though the name may
					;  be shorter than that (we may have
					;  declined to fragment this memory block)
					;
					;	(dx) = total length of this record
					;	(ds:si) = address of working record
					;	(es) = (ds)
					;	(TOS+2:TOS) = name string address
					;---------------------------------------
   mov	cx,dx
   sub	cx,mft_name			; compute total size of name area
   ERRNZ mft_name-mft_serl-2
;	add	di,mft_name-mft_serl-2	; (ES:DI) = target address
   mov	ax,si				; save name record offset
   pop	si
   pop	ds
   rep	movsb
   mov	bx,ax				; (bx) = name record offset
   push es
   pop	ds				; (DS:BX) = name record offset
   clc

   ret

;**
;**	OUT OF FREE SPACE
;**
;**	This is tough, folks.  Lets trigger a garbage collection and see if
;**	there is enough room.  If there is, we'll hop back and relook for a
;**	free hunk; if there isnt enough space, its error-city!
;
;	WARNING: it is important that the garbage collector be told how big a
;		name record hole we're looking for...  if the size given GCM
;		is too small we'll loop doing "no space; collect; no space;
;		...)
;
;	(dx) = total length of desired name record
;	(ds) = SEG CODE
;	(bl) = sum byte
;	(TOS+2:TOS) = name string address

fnm20:
   mov	ax,dx				; (ax) = size wanted
   sub	dx,mft_name			; (dx) = string length for reentry at fnm10
   push dx
   push bx
   call GCM				; garbage collect MFT
   pop	bx
   pop	dx

   %IF	DEBUG
       jnc  fnm10j
   %ELSE
       jnc  fnm10			; go back and find that space
   %ENDIF

					;---------------------------------------
					; no space, return w/error
					;---------------------------------------

fnm50: pop ax
   pop	ax				; clean stack
   mov	ax,error_sharing_buffer_exceeded
   stc

   ret

   %IF	DEBUG
fnm10j: jmp fnm10
   %ENDIF

   EndProc FNM

   BREAK <GCM - Garbage Collect MFT>

;******************* START OF SPECIFICATIONS ***********************************
;
;	GCM - Garbage Collect MFT
;
;	GCM runs down the MFT structure squeezing out the free space and
;	putting it into one free block at the end.  This is a traditional heap
;	collection process.  We must be sure to update the pointer in the
;	SFTs.  This presumes no adjacent free blocks.
;
;	ENTRY	(ax) = space desired in last free block
;		(DS) + SEG CODE
;	EXIT	'C' clear if enough space in block
;		'C' set if not enough space
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure GCM,NEAR

   push ax				; save target
   off	si,mft				; (si) = from pointer
   mov	di,si				; (di) = to pointer

					;---------------------------------------
					; (DI) points to the beginning of
					;	a free space block
					; (SI) points to the next block.
					;---------------------------------------

gcm1: mov cx,[si + mft_len]		; (cx) = size of whatever it is
   cmp	byte [si + mft_flag],MFLG_FRE
   jl	gcm10				; END marker
   jnz	gcm2				; have a name record

					;---------------------------------------
					; (SI) points to a free block.
					;    We coalesce it by changing the size.
					;---------------------------------------
   cmp	si,di
   jz	gcm15				; do NOT coalesce a block with itself
   add	[di + mft_len],cx 		; coalesce
gcm15:
   add	si,cx				; skip the empty one
   JMP	SHORT gcm1
					;---------------------------------------
					; (SI) points to a non-free,
					;	non-last block.
					; (DI) points to the beginning of a
					;	 free block.
					;
					; We move the non-free block down over
					;   the free block
					;---------------------------------------
gcm2: cmp si,di
   jnz	gcm3				; have to copy

					;---------------------------------------
					; SI = DI => we are at a boundary
					;	     between allocated blocks.
					;	     We do no copying.
					;---------------------------------------
   add	si,cx
   mov	di,si				; no emptys yet... no need to copy
   JMP	SHORT gcm1
					;---------------------------------------
					; CX is length of allocated block.
					;      - Move it
					;---------------------------------------

gcm3: mov bx,di 			; (DS:BX) = new home for this record
   mov	ax,ds
   mov	es,ax
   rep	movsb
					;---------------------------------------
					; We've moved the record, now fix up
					;  the pointers in the SFT chain
					;
					;  (si) = address of next record
					;  (di) = address of next free byte
					;  (bx) = address of record in its new home
					;  (TOS) = needed space
					;---------------------------------------
   push di
   push ds
   lds	di,[bx + mft_sptr]		; (ds:di) = chain of SFT
gcm4: or di,di
   jz	gcm5				; no more SFT
sf_mft equ sf_MFT	; NASM port equate
   mov	[di + sf_mft],bx			; install new MFT position
   lds	di,[di + sf_chain]		; link to next
   JMP	gcm4				; fix next SFT

gcm5: pop ds
   pop	di
					;---------------------------------------
					; (DI) points to beginning of
					;	new free record (moved)
					; (SI) points to next record
					;
					; Make sure that the (DI) record
					;  has correct format
					;---------------------------------------

   mov	byte [di + mft_flag],MFLG_FRE		; indicate free record
   mov	[di + mft_len],si 		; calculate correct length
   sub	[di + mft_len],di
					;---------------------------------------
					; MFT now has correct record structure.
					;  Go find more free blocks
					;---------------------------------------
   JMP	SHORT gcm1
					;---------------------------------------
					; We have scanned the entire table,
					;  compacting all empty records together.
					;
					;   (di) = first free byte in table
					;   (si) = address of END record
					;   (TOS) = size needed
					;
					; Be extra careful!!!
					;---------------------------------------
gcm10: mov ax,si
   sub	ax,di				; (ax) = free space
   pop	bx				; (bx) = space wanted
   sub	ax,bx

   ret

   EndProc GCM

   BREAK <RMN - Remove MFT Name record>

;******************* START OF SPECIFICATIONS ***********************************
;
;	RMN - Remove MFT Name record
;
;	RMN removes a name record from the MFT list.  The record is marked
;	free and all free space is coalesced.
;
;	ENTRY	(DS:BX) = FBA MFT name record
;	EXIT	to INTERR if lock and SFT chains are not empty
;	USES	ALL
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure RMN,NEAR

   mov	si,bx
   mov	ax,word ptr [si + mft_sptr]
   or	ax,word ptr [si + mft_lptr]
   jnz	RMNIER1 			; not clean - internal error
   mov	si,bx				; (ds:si) = fwa name record

   mov	byte [si + mft_flag],MFLG_FRE		; mark free

mrg equ MRG	; NASM port label
   call mrg				; coalesce all free space

   ret

RMNIER1:push ax
   off	ax,rmnerr1

RMNIER: call INTERR			; internal error

rmnerr1 db "RMN: SFT LCK fields not 0", 13, 10, 0

   EndProc RMN

   Break <MRG - merge all free space>

;******************* START OF SPECIFICATIONS ***********************************
;
;   MRG - merge all free space
;
;   MRG - walk through mft merging adjacent free space.
;
;   Inputs:	ds = CS
;   Outputs:	none (all free space coalesced)
;   Registers Revised: none
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure MRG,near

   assume ds:nothing,es:nothing

   push si
   push bx

   off	si,mft				; start at beginning
mrg1: mov bx,[si + mft_len]		; get length
   cmp	byte [si + mft_flag],MFLG_FRE		; is record free?
   jl	mrg9				; done.
   jz	mrg2				; yes, try to merge with next
mrg15: add si,bx			; advance to next
   jmp	mrg1
					;---------------------------------------
					; (si) points to free record.
					;  - See if next is free
					;---------------------------------------
mrg2: cmp byte [bx + si + mft_flag],MFLG_FRE
   jnz	mrg15				; not free, go scan again
   mov	bx,[bx + si + mft_len]		; get length of next guy
   add	[si + mft_len],bx 		; increase our length
   jmp	mrg1				; and check again
mrg9: pop bx
   pop	si

   ret

   EndProc MRG

   BREAK <RSC - Remove SFT from SFT chain>

;******************* START OF SPECIFICATIONS ***********************************
;
;	RSC - Remove SFT from SFT chain
;
;	RSC removes a given SFT from its chain.  The caller must insure that
;	any locks have been cleared and that the SFT is indeed free.  The
;	sf_mft field is zeroed to indicate that this SFT is no longer chained.
;
;	NOTE - RSC does NOT remove the name record if this was the last SFT on
;		it.  The caller must check for this and remove it, if
;		necessary.
;
;	ENTRY	(ES:DI) = SFT address
;	EXIT	(DS:BX) = FBA name record for this SFT
;		'Z' set if this is the last SFT
;	USES	ALL
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure RSC,NEAR

   push cs
   pop	ds

   mov	ax,es				; easy spot for compare
   mov	bx,[es:di + sf_mft]
   lea	si,[bx + mft_sptr - sf_chain]	; [ds:si].sf_chain point to prev link
rsc1: or si,si
   jz	rscier
   cmp	word ptr [si + sf_chain],di
   jnz	rsc15
   cmp	word ptr [si + sf_chain + 2],ax
   jz	rsc2
rsc15: lds si,[si + sf_chain]
   jmp	rsc1
					;---------------------------------------
					; (es:di) is sft
					; (ds:si) is prev sft link
					;---------------------------------------
rsc2: mov ax,word ptr [es:di + sf_chain]
   mov	word ptr [si + sf_chain],ax
   mov	ax,word ptr [es:di + sf_chain + 2]
   mov	word ptr [si + sf_chain + 2],ax

   push cs
   pop	ds
   xor	bx,bx
   xchg bx,[es:di + sf_MFT]		; (DS:bx) = MFT address
					;    and 0 MFT pointer (show free)
   cmp	word ptr [bx + mft_sptr],0	; set z flag if no more sft

   ret

rscier: push ax
   off	ax,rscerr

interr equ INTERR	; NASM port label
   call interr

rscerr db "RSC: SFT not in SFT list", 13, 10, 0

   EndProc RSC

   BREAK <SLE - Scan for Lock Entry>

;******************* START OF SPECIFICATIONS ***********************************
;
;	SLE - Scan for Lock Entry
;
;	SLE scans a lock list looking for a lock range that overlaps the
;	caller-supplied range.	SLE indicates:
;
;		no overlap
;		partial overlay
;		1-to-1 match
;
;	ENTRY	(AX:BX) = FBA of area
;		(CX:DX) = LBA of area
;		(DS:SI) = address of name record
;		(DI)	= 0 to ignore locks by User_ID Proc_ID ThisSFT
;			= 1 to consider all locks
;	EXIT	'C' clear if no overlap
;		  AX,BX,CX,DX preserved
;		'C' set if overlap
;		  (di) = address of pointer to found record
;			 (i.e., DS:((di)) = address of lock record)
;		  'Z' set if 1-to-1 match
;	USES	ALL but (ds), (es) (also see EXIT)
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure SLE,NEAR

   push es
   and	di,di
   pushf				; Z set to ignore own locks
   lea	di,[si + mft_lptr]		; (ds:di) = addr of ptr to lock record
   mov	si,[di] 			; (ds:si) = address of 1st lock record

					;---------------------------------------
					; check out next lock
					;
					; (ds:si) = address of next lock record
					; (ds:di) = address of pointer to next
					;	     lock record
					; (TOS)   = flags (Z set to ignore
					;	      own locks)
					; (TOS+1) = Saved ES
					;---------------------------------------
sle1: and si,si
   jz	sle9				; list exhaused, ergo no overlap
   popf 				;
   pushf
   jnz	sle2				; am to check all locks

					;---------------------------------------
					; am to ignore own locks...
					;  check the user and proc IDs on this one
					;---------------------------------------

;dcl - this code used to compare the process id in the sft pointed to by the
;  lock.  now we compare the lock process id to the current process id.  this
;  allows a child process to lock an area and then do i/o with it.  before,
;  the child could lock it, but then could not access it


   mov	bp,[si + rlr_pid] 		;dcl
Proc_id equ Proc_ID	; NASM port label
   cmp	bp,[ss:Proc_id]			;dcl
   jnz	sce1$5				;dcl
   les	si,[si + rlr_sptr]		; (si) = sft address		;dcl
   mov	bp,[es:si + sf_UID]		;dcl
   cmp	bp,[ss:User_ID]			;dcl
   jnz	sce1$5				; doesn't belong to user        ;dcl
   mov	bp,es				;dcl
   cmp	bp,WORD PTR [ss:ThisSFT+2]
   jnz	sce1$5
   cmp	si,WORD PTR [ss:ThisSFT]
sce1$5: mov si,[di]			; (ds:si) = address of next lock record
   jz	sle3				; owned by user - ignore

sle2: mov bp,dx
   sub	bp,[si + rlr_fba] 		; compare proposed last to first of record
   mov	bp,cx
   sbb	bp,[si + rlr_fba + 2]
   jc	sle3				; proposed is above current
   mov	bp,[si + rlr_lba]
   sub	bp,bx				; compare proposed first to last of record
   mov	bp,[si + rlr_lba + 2]
   sbb	bp,ax
   jnc	sle5				; we have a hit

					;---------------------------------------
					; This entry is harmless...
					;    chain to the next one
					;---------------------------------------
   ERRNZ rlr_next

sle3: mov di,si 			; save addr of pointer to next
   mov	si,[di]
   JMP	SHORT sle1
					;---------------------------------------
					; We have an overlap.
					;  - See if its an exact match
					;
					; (ds:di) = address of pointer
					;	    (offset only) to the lock record
					; (ds:si) = address of lock record
					; (TOS) = flags ('Z' set if to ignore
					;	  own locks)
					; (TOS+1) = saved (es)
					;---------------------------------------

sle5: xor ax,[si + rlr_fba + 2]		; require a 4-word match
   xor	bx,[si + rlr_fba]
   xor	cx,[si + rlr_lba + 2]
   xor	dx,[si + rlr_lba]
   or	ax,bx
   or	ax,cx
   or	ax,dx				; 'Z' set if exact match
   stc					; flag an overlap
   mov	ax,error_lock_violation
sle9: pop bp				; discard flags (pushf)
   pop	es				; restore (es)

					;---------------------------------------
					; (ds:si) = address of lock record
					;	    for Chk_Block
					;---------------------------------------
   ret

   EndProc SLE

   BREAK <OFL - obtain free lock-record>

;******************* START OF SPECIFICATIONS ***********************************
;
;	OFL - obtain free lock-record
;
;	OFL returns a free lock-record, if one can be had.
;
;	ENTRY	(DS) = MFT Segment
;	EXIT	'C' clear if OK
;		  (DI) = FBA lock record
;		'C' set if no space
;		  (ax) = error code
;	USES	DI, FLAGS
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure OFL,NEAR

Frelock equ FreLock	; NASM port label
   mov	di,[cs:Frelock]
   and	di,di

;  $if	nz				; if something there
   JZ D_$IF1

       push word [di + rlr_next]
       pop  word [cs:Frelock]			; chain off of the list
					; exit with 'C' clear

;  $else				; none on free list
   JMP SHORT D_$EN1
D_$IF1:

       mov  ax,error_sharing_buffer_exceeded ; None on free list, give up until
       stc				;  garbage collector is ready

;  $endif
D_$EN1:

   ret

   EndProc OFL

   Break <CPS - close process SFT>

;******************* START OF SPECIFICATIONS ***********************************
;
;   CPS - close process SFT.
;
;	During maintenance, it is necessary to close a
;	file given ONLY the SFT.  This necessitates walking all PDB's JFN
;	tables looking for the SFN.  The difficult part is in generating the
;	SFN from the SFT.  This is done by enumerating SFT's and comparing for
;	the correct SFT.  Finding all PDBs is easy:  walk arena and check
;	owner fields
;
;   Inputs:	ThisSFT points to SFT of interest
;   Outputs:	Handle is closed on user
;   Registers Revised: none
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure CPS,NEAR

   ASSUME DS:NOTHING,ES:NOTHING

   SaveReg <DS,SI,ES,DI,AX,BX,CX>

   lds	si,[ss:ThisSFT]
   xor	bx,bx
cps01:

multDOS equ MultDOS	; NASM port equate
   CallInstall SFFromSFN,multDOS,22,bx,bx

   jc	cps31				; no more SFN's.  Must be FCB.

   CallInstall PointComp,multDOS,20

   jz	cps02				; found matching SFN, go scan.
   inc	bx				; did not match, go back for more
   jmp	cps01
					;---------------------------------------
					; BL is the sfn we want to find.  Walk
					;  the memory arena enumerating all PDB's
					;  and zap the handle tables for the
					;  specified sfn.
					;---------------------------------------
cps02:
   mov	al,bl
   xor si, si
   mov	ds,[ss:Arena_Head]			; get first arena pointer

					;---------------------------------------
					; [DS:0] is the arena header.
					; AL is sfn to be closed
					;---------------------------------------
cps1:
   inc si				; loop detection
   jz cps3				; stop if looped -->
   mov	cx,[arena_owner]
   mov	bx,ds
   inc	bx				; is the owner the same as the current
   cmp	cx,bx				; block?
   jnz	cps2				; no, go skip some more...

					;---------------------------------------
					; CX:0 is the correct pointer to a PDB.
					;---------------------------------------
   push ds
   mov	ds,cx
					;---------------------------------------
					; Given a PDB at DS:0, scan his handle
					;  table and then loop through the next
					;  PDB link.
					;---------------------------------------
cps15:
   cmp word [PDB_Exit_Call], 20CDh	; harden: is this a valid PSP ?
   jne .skip				; no -->
   call CPJ				; free for this PDB
   lds	cx,[PDB_Next_PDB]		; advance to next
   cmp	cx,-1
   jnz	cps15				; there is another link to process
.skip:
   pop	ds
					;---------------------------------------
					; We have processed the current
					;  allocation block pointed to by DS.
					;  [DS:0] is the allocation block
					;---------------------------------------
cps2:
   cmp	byte [arena_signature], 'Z'
   jne .not_Z
	 push ax
	mov ax, 1261h
	int 2Fh				; get first UMCB
	cmp ax, 1			; none ?
	jbe .pop
	cmp ax, -1			; none ?
	je .pop
	call nextmcb			; bx => next MCB
	cmp ax, bx			; is it the first UMCB ?
	 pop ax
	je cps1				; yes, so we continue -->
	db 0A8h				; skip pop
.pop:
	 pop ax
   jmp	cps3				; no more blocks to do

.not_Z:
   cmp	byte [arena_signature], 'M'
   jne cps3				; corrupted -->
	call nextmcb
   jmp	cps1


nextmcb:
   mov	bx,ds				; get current address
Arena_size equ arena_size	; NASM port equate
   add	bx,[Arena_size]		; add on size of block
   inc	bx				; remember size of header
   mov	ds,bx				; link to next
   retn
					;---------------------------------------
					; Just for good measure, use CurrentPDB
					;  and clean off him
					;---------------------------------------
cps3:
   mov	ds,[ss:CurrentPDB]

   call CPJ

cps31:

   RestoreReg <CX,BX,AX,DI,ES,SI,DS>

   ret

   EndProc CPS

;******************* START OF SPECIFICATIONS ***********************************
;
; CPJ -
;
; Scan JFN table for SFT # and put in -1 if found
;
; Input: DS:0 is PDB
;	 AL is SFT index # of interest
;
; Output: None
;
; Uses: Flags,CX,ES,DI
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure CPJ,NEAR

   assume ds:nothing,es:nothing

PDB_JFN_length equ PDB_JFN_Length	; NASM port equate
   mov	cx,[PDB_JFN_length]
PDB_JFN_pointer equ PDB_JFN_Pointer	; NASM port equate
   les	di,[PDB_JFN_pointer]
   cld
cpj1:
   jcxz CPJret				; no more entries
   repne scasb

   retnz				; none found

   mov	byte ptr [es:di-1],-1		; free this
   jmp	cpj1				; keep looking
CPJret:
   ret

   EndProc CPJ

   Break <SFM - convert an mft pointer into a serial number>

;******************* START OF SPECIFICATIONS ***********************************
;
;   SFM - convert a pointer to a mft entry into the serial number for that
;   entry.  We keep these around to see if a FCB really points to the correct
;   SFT.
;
;   Inputs:	BX is the mft pointer
;   Outputs:	BX is the serial number
;   Registers Revised: none
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure SFM,NEAR

   ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:DOSGROUP

   mov	bx,[cs:bx + mft_serl]

   ret

   EndProc SFM

   Break <ShChk - check a fcb for share related information>

;******************* START OF SPECIFICATIONS ***********************************
;
;   ShChk - check a fcb for share related information
;
;   ShChk - checks the reserved field contents of an FCB with a SFT to see
;   if they represent the same file.  The open ref count must be > 0.
;
;   Inputs:	DS:SI point to FCB
;		ES:DI point to SFT
;   Outputs:	Carry Set if contents do not match
;		Carry clear if contents match
;		    BX has first cluster
;   Registers Revised: none
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure ShChk,NEAR

   ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:DOSGROUP

   CMP	word [ES:DI + sf_ref_count],0
   JZ	BadSFT
   MOV	BX,[ES:DI + sf_mft]		; Local file or dev with sharing

   call SFM

   CMP	BX,[SI + fcb_l_mfs]
   JNZ	BadSFT
   MOV	BX,[SI + fcb_l_firclus]

   ret

BadSFT: stc

   ret

   EndProc ShChk

   Break <ShSave - save information from SFT into an FCB>

;******************* START OF SPECIFICATIONS ***********************************
;
;   ShSave - save information from SFT into an FCB
;
;   ShSave - copy information into the reserved area of an FCB from a SFT.
;   This is so that we can later match the SFT with the FCB.
;
;   Inputs:	ES:DI point to SFT
;		DS:SI point to FCB
;   Outputs:	FCB reserved field is filled in
;		BL = FCBSHARE
;   Registers Revised: AX,BX
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure ShSave,NEAR

   ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:DOSGROUP

   MOV	AL,[ES:DI + sf_attr]		; move attribute (for reopen)
   MOV	[SI + FCB_l_attr],AL
   MOV	AX,[ES:DI + sf_firclus]		; get first cluster
   MOV	[SI + FCB_l_firclus],AX
   MOV	BX,[ES:DI + sf_mft]		; get sharing pointer

   call SFM

   MOV	[SI + FCB_l_mfs],BX
   MOV	BL,FCBSHARE

   ret

   EndProc ShSave

   Break <ShCol - collapse identical handle SFTs in mode 70 only>

;******************* START OF SPECIFICATIONS ***********************************
;
;   ShCol - collapse identical handle SFTs in mode 70 only
;
;   ShCol - collapse same 70-mode handles together.  This represents network
;   originated FCBs.  Since FCB's are incredibly mis-behaved, we collapse the
;   SFT's for identical files, thus using a single sft for each file instead
;   of a separate sft for each instance of the file.
;
;   Note that the redirectors will collapse multiple instances of these
;   files together.  FCB's are pretty misbehaved, so the redirector will
;   inform us of EACH close done on an FCB.  Therefore, we must increment
;   the ref count each time we see a collapse here.
;
;   Inputs:	DS:SI ThisSFT has new sft to find.
;   Outputs:	Carry set - no matching SFT was found
;		Carry clear - matching SFT was found and all collapsing done.
;		    AX has proper handle
;   Registers Revised: all.
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure ShCol,NEAR

   ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:DOSGROUP

					;---------------------------------------
					; Collapse the files ONLY if
					;  the mode is for net FCB's
					;---------------------------------------

   MOV	AL,BYTE PTR [SI + sf_mode]
   AND	AL,sharing_mask
   CMP	AL,sharing_net_FCB
   JNZ	UseJFN

					;---------------------------------------
					; In share support
					;---------------------------------------

   XOR	BX,BX				;   for (i=0; sffromsfn(i); i++) {
OpenScan:

   CallInstall SFFromSFN,multDOS,22,bx,bx

   JC	UseJFN

   CallInstall PointComp,multDOS,20	;	if (!pointcomp (s,d))

   JZ	OpenNext
   CMP	word [ES:DI + sf_ref_count],0
   JZ	OpenNext
   MOV	AX,[ES:DI + sf_mode]
   CMP	AX,[SI + sf_mode]
   JNZ	OpenNext
   MOV	AX,[ES:DI + sf_mft]
   CMP	AX,[SI + sf_mft]
   JNZ	OpenNext
   MOV	AX,WORD PTR [ES:DI + sf_UID]
sf_uid equ sf_UID	; NASM port equate
   CMP	AX,WORD PTR [SI + sf_uid]
   JNZ	OpenNext
sf_pid equ sf_PID	; NASM port equate
   MOV	AX,WORD PTR [ES:DI + sf_pid]
   CMP	AX,WORD PTR [SI + sf_pid]
   JZ	OpenFound
OpenNext:
   INC	BX
   JMP	OpenScan
					;--------------------------------------
					; DS:SI points to an sft which is a
					;	 duplicate of that found in
					; ES:DI is the older one.
					;
					; We call mftclose to release the
					;   appropriate info.
					;--------------------------------------
OpenFound:
   MOV	word [SI + sf_ref_count],0		; free 'new' sft

   SaveReg <DS,SI,ES,DI,BX>

   Context DS

   LES	DI,[ThisSFT]

   call MFTClose

   RestoreReg <AX,DI,ES,SI,DS>

   ASSUME DS:NOTHING

   INC	word [ES:DI + sf_ref_count]		;   d->refcount++;
   XOR	BX,BX				; find jfn with sfn as contents
JFNScan:

   CallInstall pJFNFromHandle,multDOS,32,AX,AX

   JC	UseJFN				; ran out of handles?
   CMP	AL,BYTE PTR [ES:DI]		; does JFN have SFN?
JFNfound equ JFNFound	; NASM port label
   jz	JFNfound			; YES, go return JFN
   INC	BX				; no, look at next
   JMP	JFNScan
JFNFound:
   LDS	SI,[ss:pJFN]
   MOV	BYTE PTR [SI],0FFh		; free JFN
   MOV	AX,BX				; return JFN

   ret

UseJFN:
   MOV	AX,[ss:JFN]

   ret

   EndProc ShCol

   Break <ShCloseFile - close a particular file for a particular UID/PID>

;******************* START OF SPECIFICATIONS ***********************************
;
;   ShCloseFile - close a particular file for a particular UID/PID
;
;   ShCloseFile - Compatability mode programs will often delete files that
;   they had open.  This was perfectly valid in the 2.0 days, but this
;   presents a reliability problem in the network based operating environment.
;   As a result, both RENAME and DELETE will call us to see if the file is
;   open by is only.  If it is not open or is open by us only, we close it.
;   Note that we will ONLY close compatability SFTs.
;   Otherwise, we signal and error.
;
;   Inputs:	WFT_Start has a DOSGROUP offset to the file name
;		DS is DOSGroup
;   Outputs:	nothing relevant.
;   Registers Revised: None.
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure ShCloseFile,NEAR

   ASSUME DS:DOSGroup,ES:NOTHING,SS:DOSGroup

   SaveReg <AX,BX,CX,DX,SI,DI,BP,DS,ES>

   EnterCrit critShare

ShCl:
WFP_Start equ WFP_start	; NASM port label
   MOV	SI,[WFP_Start]
   XOR	AL,AL

   call FNM				; attempt to find name in list

   ASSUME DS:NOTHING

   JC	ShCloseDone			; can't find, signal success

					;--------------------------------------
					; We have found a file in the MFT.
					;  Walk the open sft list to find
					;  the SFTs for the current UID/PID.
					;--------------------------------------
   MOV	CX,DS
   LDS	SI,[BX + mft_sptr]
ShClCheck:
   MOV	AX,[ss:Proc_ID]
   CMP	[SI + sf_PID],AX
   JNZ	ShCloseDone
   MOV	AX,[ss:User_ID]
   CMP	[SI + sf_UID],AX
   JNZ	ShCloseDone
   MOV	AX,[SI + sf_mode]
   AND	AX,sharing_mask
sharing_net_fcb equ sharing_net_FCB	; NASM port equate
   CMP	AX,sharing_net_fcb
   jz	ShClNext
   CMP	AX,sharing_compat
ShCloseDOne equ ShCloseDone	; NASM port label
   jnz	ShCloseDOne
ShClNext:
   LDS	SI,[SI + sf_chain]
   OR	SI,SI
   JNZ	ShClCheck
   MOV	DS,CX
   LDS	SI,[BX + mft_sptr]
					;--------------------------------------
					; Everything matches.  Set up ThisSFT
					;  and walk the chain from the beginning.
					;--------------------------------------
   MOV	WORD PTR [ss:ThisSFT],SI
   MOV	WORD PTR [ss:ThisSFT+2],DS
					;--------------------------------------
					; Close all handles for this SFT
					;--------------------------------------
   call CPS
					;--------------------------------------
					; Close the sft itself.
					;--------------------------------------
   Context DS

multDos equ MultDOS	; NASM port equate
   CallInstall DOS_Close,multDos,1
					;--------------------------------------
					; The SFT may be free and we have no
					;  idea where the next is.  Go and loop
					;  all over.
					;--------------------------------------
   JMP	ShCl
					;--------------------------------------
					; There are no more SFTs to close. Leave
					;---------------------------------------
ShCloseDone:

   LeaveCrit critShare

   STC

   RestoreReg <ES,DS,BP,DI,SI,DX,CX,BX,AX>

   ret

   EndProc ShCloseFile

;   .xall
   Break <ShSU - update all SFTs for a specified change>
;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	   ShSU - update all SFTs for a specified change>
;
;  FUNCTION:	   In a shared environment, we want to propogate the SFT
;		   changes for a particular file to all other SFTs for that
;		   file.  The types of things we propogate are:
;
;		   - Time of last write - we only do this on CLOSE and on
;		     FILETIMES.
;
;		   - Size and allocation information - we do this ONLY when
;		     we change sf_size.
;
;		   We achieve this by walking the linked list of SFTs for the
;		   file. See PSEUDOCODE below
;
;  INPUT:	   ES.DI  has SFT that was just Revised.
;		   AX = 0 for updating of time from ES:DI into old sfts
;		   AX = 1 for updating of size/allocation for growth from ES:DI
;		   AX = 2 for updating of size/allocation for shrink from ES:DI
;		   AX = 3 for new instance copy into ES:DI
;		   AX = 4 for update of codepage and high attribute
;
;  OUTPUT:	   All relevant SFTs are updated.
;
;  REGISTERS USED: All except ES:DI and DS:SI
;  (NOT RESTORED)
;
;  LINKAGE:	   DOS Jump Table
;
;  EXTERNAL	   Invoke: New_Sft, Call_IFS
;  REFERENCES:	   Callinstall
;
;  NORMAL	   -
;  EXIT:
;
;  ERROR	   -
;  EXIT:
;
;  CHANGE	   04/15/87 - Major overhaul and IFS support
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************
;******************+ START OF PSEUDOCODE +**************************************
;
;	START ShSU
;
;	if not a device and
;	if not a network
;		search
;			if our SFT
;				advance to next SFT
;			endif
;		leave if no more SFT's
;		exitif cx = 3
;			invoke New_Sft
;		orelse
;			if cx = 0
;				update time
;				update date
;				if non - FAT file system
;					call IFSFUNC
;				endif
;			else cx = 1 or 2
;				update size
;				if non - FAT file system
;					call IFSFUNC
;				else
;					update first cluster
;					if cx = 2 or
;					if lstclus un-set from create
;						update cluster position
;						update last cluster
;					endif
;				endif
;			endif
;			advance to next SFT
;		endloop
;		endsearch
;	endif
;	return
;
;	END ShSU
;
;******************+  END  OF PSEUDOCODE +**************************************

   Procedure ShSU,near

   ASSUME DS:NOTHING,ES:NOTHING

   nop
;  int 3
   nop

ifs_flag equ 8000h			;				       ;AN000;
					;---------------------------------------
					; Do nothing for device or network
					;---------------------------------------
   mov	bx,[es:di + sf_mode]
   and	bx,sf_isnet + devid_device

;  $if	z,and,long			; if not device and		       ;AC000;
   JZ D_$XL1
   JMP D_$IF4
D_$XL1:

   mov	bx,[es:di + sf_MFT]
   or	bx,bx

;  $if	nz,,long			; if not network		       ;AC000;
   JNZ D_$XL2
   JMP D_$IF4
D_$XL2:

       EnterCrit critShare
					;---------------------------------------
					; Walk the sft chain for this file and
					;  skip the current SFT (ES:DI)
					;---------------------------------------
       SaveReg <DS,SI>

MFT_SPTR equ mft_sptr	; NASM port equate
       lds  si,[cs:bx + MFT_SPTR]
       mov  cx,ax

;      $search				;				       ;AC000;
D_$DO5:

	   CallInstall PointComp,multDOS,20 ; pointers different?

;	   $if	z			; if ourselves			       ;AC000;
	   JNZ D_$IF6

	       lds  si,[si + sf_chain]	; move to next			       ;AC000;

;	   $endif			; endif - ourselves		       ;AC000;
D_$IF6:

	   or	si,si

;      $leave z 			;				       ;AC000;
       JZ D_$EN5

					;---------------------------------------
					; CX = 0 for updating of time
					; CX = 1 for updating of size/allocation
					;	   for growth
					; CX = 2 for updating of size/allocation
					;	   for shrink
					; CX = 3 for new instance copy.
					;---------------------------------------
	   cmp	cx,2			;				       ;AC000;

;      $exitif a			;				       ;AC000;
       JNA D_$IF5
					;---------------------------------------
					; CX = 3 for new instance copy.
					; CX = 4 for codepage and high attrib update
					;---------------------------------------
	   cmp	cx,3			; cx = 3 ?			       ;an000;
;	   $if	e			; yes				       ;an000;
	   JNE D_$IF10
	       call New_Sft		;				       ;AN000;
;;	   $else			; cx = 4			       ;an000;
;;	       call New_CP_Attrib	; update codepage and high attrib      ;an000;
;	   $endif			;				       ;an000;
D_$IF10:

;      $orelse				;				       ;AC000;
       JMP SHORT D_$SR5
D_$IF5:

	   or	cx,cx

;	   $if	z			; if cx = 0 then		       ;AC000;
	   JNZ D_$IF13
					;---------------------------------------
					; CX = 0 for updating of time
					;
					; Copy time from ES:DI into DS:SI
					;---------------------------------------
	       mov  bx,[es:di + sf_time]
	       mov  [si + sf_time],bx
	       mov  bx,[es:di + sf_date]
	       mov  [si + sf_date],bx
	       test word [si + sf_flags],ifs_flag ;				       ;AN000;

;	       $if  nz			; if non-FAT			       ;AC003;
	       JZ D_$IF14

		   call Call_IFS	; tell IFS of SFT change	       ;AN000;

;	       $endif			; endif non- FAT		       ;AN000;
D_$IF14:

;	   $else			; else - must be >0 and <2	       ;AC000;
	   JMP SHORT D_$EN13
D_$IF13:
					;---------------------------------------
					; CX = 1 for updating of size/allocation
					;	  for growth
					; CX = 2 for updating of size/allocation
					;	  for shrink
					;
					; We always copy size and firclus
					;---------------------------------------
	       mov  bx,word ptr [es:di + sf_size]
	       mov  word ptr [si + sf_size],bx
	       mov  bx,word ptr [es:di + sf_size + 2]
	       mov  word ptr [si + sf_size + 2],bx
	       test word [si + sf_flags],ifs_flag ;				       ;AN000;

;	       $if  nz			; if non-FAT			       ;AC003;
	       JZ D_$IF17

		   invoke Call_IFS	; tell IFS of SFT change	       ;AN000;

;	       $else			; else - its FAT		       ;AN000;
	       JMP SHORT D_$EN17
D_$IF17:

		   mov	bx,[es:di + sf_firclus]
		   mov	[si + sf_firclus],bx
		   cmp	cx,2		;				       ;AC000;

;		   $if	z,or		; if SFT is shrinking or	       ;AC000;
		   JZ D_$LL19

		   cmp	word [si + sf_lstclus],0 ; lstclus UN-set from a create?      ;AC000;

;		   $if	z		; If it is, set lstclus and cluspos too;AC000;
		   JNZ D_$IF19
D_$LL19:
					;---------------------------------------
					; Shrink the file, move in new cluspos
					;  and lstclus
					;---------------------------------------
		       mov  word [si + sf_cluspos],0 ; retrace from start
		       mov  [si + sf_lstclus],bx ; ditto

;		   $endif		; endif - set lstclus and cluspos      ;AC000;
D_$IF19:

;	       $endif			; endif  FAT			       ;AN000;
D_$EN17:

;	   $endif			; enndif - > 0			       ;AC000;
D_$EN13:
					;---------------------------------------
					; Link to next SFT
					;---------------------------------------
	   lds	si,[si + sf_chain]

;      $endloop 			;				       ;AC000;
       JMP SHORT D_$DO5
D_$EN5:

;      $endsrch 			;				       ;AC000;
D_$SR5:
					;---------------------------------------
					; All Done
					;---------------------------------------
       RestoreReg <SI,DS>

       LeaveCrit critShare

;  $endif				; endif - device and network	       ;AC000;
D_$IF4:

   ret

   EndProc ShSU

   Break <New_Sft - update a new SFT>

;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	   New_Sft - update a new SFT
;
;  FUNCTION:	   Copy all SFT information into a NEW sft of a SHARED file.
;
;
;  INPUT:	   ES.DI  has SFT that was just Revised.
;		   DS:SI  has SFT that is to be updated
;
;  OUTPUT:	   SFT is updated.
;
;  REGISTERS USED: AX, BX
;  (NOT RESTORED)
;
;  LINKAGE:	   Invoked by: ShSU
;
;  EXTERNAL	   Invoke: Call_IFS
;  REFERENCES:
;
;  CHANGE	   04/15/87 - First release
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************
;******************+ START OF PSEUDOCODE +**************************************
;
;	START New_Sft
;
;	update time
;	update date
;	update size
;	if   non - FAT file system
;		call IFSFUNC
;	else
;		update first cluster
;		update cluster position
;		update last cluster
;	endif
;	return
;
;	END New_Sft
;
;******************+  END  OF PSEUDOCODE +**************************************

   Procedure New_Sft,near		;				       ;AN000;

   mov	bx,[si + sf_time] 		; update time
   mov	[es:di + sf_time],bx
   mov	bx,[si + sf_date] 		; update date
   mov	[es:di + sf_date],bx
   mov	bx,word ptr [si + sf_size]	; update size
   mov	word ptr [es:di + sf_size],bx
   mov	bx,word ptr [si + sf_size + 2]
   mov	word ptr [es:di + sf_size + 2],bx
   test word [es:di + sf_flags],ifs_flag	;				       ;AN000;

;  $if	nz				; if non-FAT			       ;AC003;
   JZ D_$IF26

       call Call_IFS			; tell IFS of SFT change	       ;AN000;

;  $else				; else - its FAT		       ;AN000;
   JMP SHORT D_$EN26
D_$IF26:

       mov  bx,[si + sf_firclus]		; update first cluster
       mov  [es:di + sf_firclus],bx
       mov  word [es:di + sf_cluspos],0	; retrace from start
       mov  [es:di + sf_lstclus],bx	; ditto

;  $endif				; endif  FAT			       ;AN000;
D_$EN26:

   ret					; we'er done                           ;AN000;

   EndProc New_Sft			;				       ;AN000;

   Break <New_CP_Attrib - update the codepage and attrib in SFT>

;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	   New_CP_Attrib - Update codepage and attrib in SFT
;
;  FUNCTION:	   Copy all codepage and attrib into SFT of a SHARED file.
;
;
;  INPUT:	   ES.DI  has SFT that was just Revised.
;		   DS:SI  has SFT that is to be updated
;
;  OUTPUT:	   SFT is updated.
;
;  REGISTERS USED: AX, BX
;  (NOT RESTORED)
;
;  LINKAGE:	   Invoked by: ShSU
;
;  EXTERNAL	   Invoke: Call_IFS
;  REFERENCES:
;
;  CHANGE	   10/06/87 - First release	- D. M. Sewell
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************
;******************+ START OF PSEUDOCODE +**************************************
;
;	START New_CP_Attrib
;
;	Update codepage
;	Update high attribute
;	$if ifs_flag
;		call Call_IFS
;	$endif
;	return
;
;	END New_CP_Attrib
;
;******************+  END  OF PSEUDOCODE +**************************************

;; Procedure New_CP_Attrib,near 	;				       ;AN000;

;; mov	bx,[es:di].SF_Codepage		; update codepage		       ;an000;
;; mov	[si].SF_Codepage,bx		;an000; dms;
;; mov	bl,[es:di].SF_Attr_Hi		; update high attribute 	       ;an000;
;; mov	[si].SF_Attr,bl 		;an000; dms;
;; test [es:di].sf_flags,ifs_flag	;				       ;AN000;

;; $if	nz				; if non-FAT			       ;AC003;

;;     call Call_IFS			; tell IFS of SFT change	       ;AN000;

;; $endif				; endif  FAT			       ;AN000;

;; ret					; we'er done                           ;AN000;

;; EndProc New_CP_Attrib		;				       ;AN000;


   Break <Call_IFS - warn IFS that SFT has changed>

;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	   Call_IFS - warn IFS that SFT has changed
;
;  FUNCTION:	   Call IFS thru 2F interupt.
;
;  INPUT:	   DS.SI  points to SFT that was just Revised.
;
;  OUTPUT:	   none
;
;  REGISTERS USED: AX
;  (NOT RESTORED)
;
;  LINKAGE:	   Invoked by: ShSU, New_SFT
;
;  EXTERNAL	   Callinstall
;  REFERENCES:
;
;  CHANGE	   04/15/87 - First release
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************
;******************+ START OF PSEUDOCODE +**************************************
;
;	START Call_IFS
;
;	set up for INT
;	INT 2F
;	return
;
;	END Call_IFS
;
;******************+  END  OF PSEUDOCODE +**************************************

   Procedure Call_IFS,near		;				       ;AN000;

   CallInstall BlockUpdate,MultIFS,44,CX,CX ;				       ;AC005;

   ret					;				       ;AN000;

   EndProc Call_IFS			;				       ;AN000;

   Break <Internal error routines>

;******************* START OF SPECIFICATIONS ***********************************
;
; INTERR - INTernal ERRor routines
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure INTERR,NEAR

   ASSUME DS:NOTHING,ES:NOTHING,SS:NOTHING

   SaveReg <BX,SI,DS>			; save registers that get clobbered

   push cs				; gain addressability
   pop	ds
   mov	si,ax				; get message to print

   call gout

   off	si,IntErrMsg

   call gout

   RestoreReg <ds,si,bx>

INTERRL:jmp INTERRL			; hang here - we're sick

gout: lodsb
   or	al,al
   retz
   mov	ah,14
   int	10h
   jmp	gout

IntErrMsg DB "Share: Internal error", 13, 10, 0

   EndProc INTERR

   Break <INT 2F handler>

   %IF	installed

       public skip_check

skip_check db 0 			; start with do checking

state_change db 0			; SHARE change in state flag
					; 0 - no change in state
					; 1 - SHARE load state has changed

iispentry i2F

       ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:NOTHING
multSHARE equ MultSHARE	; NASM port equate
       cmp  ah,multSHARE
       jnz strict short ContJ

; Its for SHARE!  Check to see who is calling:

; AL =
;      81h its us, with /NC - set skip_check
;			    - return 0F0h - end init
;      80h its us
;	if skip_check is reset
;			    - return 0FFh - loaded
;	if skip_check is set
;			    - reset skip_check
;			    - return 0F0h - end init
;
;      40h its IFSFUNC	    - return 0FFh - loaded
;
;      00h its anyone else  - clear skip_check
;			    - return 0FFh - loaded

       test al,80h			; is it share?			       ;AN010;
;      $if  nz				; if it is			       ;AN010;
       JZ D_$IF29
	   and	al,1			; is /NC set			       ;AN010;
	   mov	al,0F0H 		; assume a quiet return 	       ;AN010;
;	   $if	nz			; if it is			       ;AN010;
	   JZ D_$IF30
	       cmp  byte [cs:skip_check],1	; is skip_check set ?		       ;AN011;
;	       $if  ne			; if it is			       ;AN011;
	       JE D_$IF31
		   mov	byte [cs:state_change],1	; set the change state flag	       ;AN011;
;	       $endif			;				       ;AN011;
D_$IF31:
	       mov  byte [cs:skip_check],1	; set skip_check		       ;AN010;
;	   $else			;  /NC not requested		       ;AN010;
	   JMP SHORT D_$EN30
D_$IF30:
	       cmp  byte [cs:skip_check],1	; is skip_check set ?		       ;AN010;
;	       $if  e			; if it is			       ;AN010;
	       JNE D_$IF34
		   mov	byte [cs:state_change],1	; set the change state flag	       ;AN011;
		   mov	byte [cs:skip_check],0	; reset skip_check		       ;AN010;
;	       $else			; else , its already clear	       ;AN010;
	       JMP SHORT D_$EN34
D_$IF34:
		   mov	al,0FFH 	;    and we are loaded		       ;AN010;
;	       $endif			;				       ;AN010;
D_$EN34:
;	   $endif			;				       ;AN010;
D_$EN30:

;      $else				;				       ;AN010;
       JMP SHORT D_$EN29
D_$IF29:
	   cmp	al,40h			; is it IFSFUNC?		       ;AN010;
;	   $if	ne			; if it is not			       ;AN010;
	   JE D_$IF39

	       test al, al
	       jnz ContJ		; lDOS: chain on unknown values
	       cmp  byte [cs:skip_check],1	; is skip_check set ?		       ;AN010;
;	       $if  e			; if it is			       ;AN011;
	       JNE D_$IF40
		   mov	byte [cs:state_change],1	; set the change state flag	       ;AN011;
;	       $endif			;				       ;AN011;
D_$IF40:
	       mov  byte [cs:skip_check],0	;    and believe it !		       ;AN011;

;	   $endif			;				       ;AN010;
D_$IF39:
	   mov	al,0FFH 		;  else - say we are here	       ;AN010;
;      $endif				;				       ;AN010;
D_$EN29:
       cmp  byte [cs:state_change],1		; SHARE installed state may have change;AN011;d
;      $if  e				;    - update DOS		       ;AN011;
       JNE D_$IF44
	   push ax			;				       ;AN011;
	   push es			;  this is interesting -	       ;AN011;
	   MOV	AH,Get_In_Vars		;    if SHARE =1 and DOS =1 - no change;AN011;
	   INT	21h			;    if SHARE = 		       ;AN011;

	   ASSUME ES:DOSGROUP

	   mov	al,[cs:skip_check]		; get the SHARE operating mode	       ;AN011;
	   cmp	al,1			;  is it a /nc	  -  tell DOS  " 1 "   ;AN011;
;	   $if	ne			; if not			       ;AN011;
	   JE D_$IF45
	       dec  al			;   "full" SHARE  -  tell DOS  " -1 "  ;AN011;
;	   $endif			;				       ;AN011;
D_$IF45:
fShare equ fshare	; NASM port label
	   MOV	[es:fShare],al		; tell DOS we are here		       ;AN011;
	   pop	es			;				       ;AN011;
	   pop	ax			;				       ;AN011;
	   mov	byte [cs:state_change],0		; REset the change state flag	       ;AN011;
;      $endif				;				       ;AN011;
D_$IF44:

       ASSUME ES:nothing

       iret
ContJ:
       JMP  far [cs:i2F.next]


       ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:DOSGroup

%macro shareentry 1-*
 %rep %0
J%1 proc far
	call %1
	ret
J%1 endp
  %rotate 1
 %endrep
%endmacro

shareentry MFT_Enter, MFTClose, MFTClu, MFTCloseP, MFTCloN, Set_Mult_Block, Clr_Mult_Block
shareentry Chk_Block, MFT_Get, ShSave, ShChk, ShCol, ShCloseFile, ShSU

       Procedure EcritShare,NEAR
       PUSH AX
       MOV  AX,8000h+critShare
int_ibm equ int_IBM	; NASM port equate
       INT  int_ibm
       POP  AX
       ret
       EndProc EcritShare

       Procedure LcritShare,NEAR
       PUSH AX
       MOV  AX,8100h+critShare
       INT  int_ibm
       POP  AX
       ret
       EndProc LcritShare

   %ENDIF

   BREAK <MFT and Lock Record Data Area>

;******************* START OF SPECIFICATIONS ***********************************
;
;	first MFT record
;
;	Note that the name field can have garbage after the trailing
;	00 byte.  This is because the field might be too long, but
;	not long enough (at least 16 extra bytes) to fragment.
;	in this case we copy the length of the string area, not
;	the length of the string and thus may copy tailing garbage.
;
;******************* END OF SPECIFICATIONS *************************************

PoolSize equ 2048

   PUBLIC MFT

MFT DB	0				; free
   DW	PoolSize			; PoolSize bytes long

;   %IFn  Installed
;
;       DB   (PoolSize-3) DUP(0) 	; leave rest of record
;MEND   DB   -1				; END record
;
;lck1   DW   0				; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck2   DW   OFFSET lck1 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck3   DW   OFFSET lck2 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck4   DW   OFFSET lck3 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck5   DW   OFFSET lck4 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck6   DW   OFFSET lck5 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck7   DW   OFFSET lck6 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;lck8   DW   OFFSET lck7 wrt DOSGROUP	; link
;       DB   RLR_entry_struc_size-2 DUP(0)
;
;section SHARE	; CODE ENDS
;
;       %warning out: Ignore this END error (blasted assembler)
;
;   %ENDIF

%IF Installed

   ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:NOTHING

shareinit equ SHAREINIT	; NASM port label
shareinit_minus_MFT equ shareinit - MFT
       %IF   shareinit_minus_MFT LT PoolSize
InitSpace  DW	PoolSize
       %ELSE
InitSpace  DW	shareinit_minus_MFT
       %ENDIF
InitLocks DW 20


JTable LABEL BYTE
   DD	?
   dw	JMFT_Enter, seg JMFT_Enter			;   1	MFT_enter
   dw	JMFTClose, seg JMFTClose			;   2	MFTClose
   dw	JMFTClu, seg JMFTClu 			;   3	MFTClu
   dw	JMFTCloseP, seg JMFTCloseP			;   4	MFTCloseP
   dw	JMFTCloN, seg JMFTCloN			;   5	MFTCloN
   dw	JSet_Mult_Block, seg JSet_Mult_Block 		;   6	Set_Mult_Block
   dw	JClr_Mult_Block, seg JClr_Mult_Block 		;   7	Clr_Mult_Block
   dw	JChk_Block, seg JChk_Block			;   8	Chk_Block
   dw	JMFT_Get, seg JMFT_Get			;   9	MFT_get
   dw	JShSave, seg JShSave 			;   10	ShSave
   dw	JShChk, seg JShChk				;   11	ShChk
   dw	JShCol, seg JShCol				;   12	ShCol
   dw	JShCloseFile, seg JShCloseFile			;   13	ShCloseFile
   dw	JShSU, seg JShSU				;   14	ShSU
JTableLen equ $ - JTable

;	$SALUT	(4,9,17,36)
				   ;---------------------------------------
				   ;  STRUCTURE TO DEFINE ADDITIONAL
				   ;  COMMAND LINE PARAMETERS
				   ;---------------------------------------
PARMS	LABEL	DWORD
	DW	OFFSET PARMSX	   ; POINTER TO PARMS STRUCTURE
	DB	0		   ; NO DELIMITER LIST FOLLOWS
	DB	0		   ; NUMBER OF ADDITIONAL DELIMITERS

				   ;---------------------------------------
				   ;  STRUCTURE TO DEFINE SORT
				   ;  SYNTAX REQUIREMENTS
				   ;---------------------------------------
PARMSX	LABEL	BYTE
	DB	0,0		   ; THERE ARE NO POSITIONAL PARAMETERS
	DB	1		   ; THERE ARE ONLY ONE TYPE OF SWITCH
	DW	OFFSET SW	   ; POINTER TO THE SWITCH DEFINITION AREA
	DW	0		   ; THERE ARE NO KEYWORDS IN SHARE SYNTAX

				   ;---------------------------------------
				   ;  STRUCTURE TO DEFINE THE SWITCHES
				   ;---------------------------------------

SW	LABEL	WORD
	DW	08001H		   ; MUST BE NUMERIC
	DW	0		   ; NO FUNCTION FLAGS
	DW	OFFSET SWITCH_BUFF ; PLACE RESULT IN SWITCH BUFFER
	DW	OFFSET VALUES	   ; NEED VALUE LIST
	DB	3		   ; TWO SWITCHES IN FOLLOWING LIST
F_SW	DB	"/F",0		   ; /F: INDICATES n FILESPACE REQUESTED
L_SW	DB	"/L",0		   ; /L: INDICATES m LOCKS REQUESTED
N_SW	DB	"/NC",0 	   ; /NC: INDICATES no checking required


				   ;---------------------------------------
				   ;  VALUE LIST DEFINITION FOR n
				   ;---------------------------------------

VALUES	LABEL	BYTE
	DB	1		   ; ONE VALUE ALLOWED
	DB	1		   ; ONLY ONE RANGE
	DB	FILE_SWITCH	   ; IDENTIFY IT AS n
	DD	1,65535 	   ; USER CAN SPECIFY /+1 THROUGH /+65535

				   ;---------------------------------------
				   ;  RETURN BUFFER FOR SWITCH INFORMATION
				   ;---------------------------------------
;		$SALUT	  (4,17,27,36)

SWITCH_BUFF	LABEL	  BYTE
SW_TYPE 	DB	  ?	   ; TYPE RETURNED
SW_ITEM_TAG	DB	  ?	   ; SPACE FOR ITEM TAG
SW_SYN		DW	  ?	   ; POINTER TO SWITCH LIST ENTRY
SW_VALUE	DD	  ?	   ; SPACE FOR VALUE

;  $SALUT (4,4,9,41)

   Break <INIT - INITalization routines>

;******************* START OF SPECIFICATIONS ***********************************
;
; INIT - INITalization routines
;
;******************* END OF SPECIFICATIONS *************************************

   Procedure Init,NEAR

   PUSH CS
   POP	DS

   ASSUME DS:SHARE

   MOV	BX,[InitSpace]

   SUB	BX,3
   MOV	SI,OFFSET MFT
   MOV	WORD PTR [SI+1],BX		; length of first item
   ADD	SI,BX				; link to end of structure
   MOV	BYTE PTR [SI],-1		; signal end
   INC	SI				; point to next free byte

initlocks equ InitLocks	; NASM port label
   MOV	CX,[initlocks]			; count for loop
   MOV	AX,0

;  $do					;				       ;AC000;
D_$DO48:

RLR_next equ rlr_next	; NASM port equate
       MOV  [SI + RLR_next],AX		; link in previous
       MOV  AX,SI			; this is now previous
RLR_Entry_struc_size equ RLR_entry_struc_size	; NASM port equate
       ADD  SI,RLR_Entry_struc_size		; move to next object

;  $enddo loop				;				       ;AC000;
   LOOP D_$DO48

   MOV	[FreLock],AX			; point to beginning of free list

   MOV	DX,CS
   MOV	BX,ES
   SUB	DX,BX
   ADD	SI,15
   RCR	SI,1
   SHR	SI,1
   SHR	SI,1
   SHR	SI,1

   ADD	SI,DX
   PUSH SI				; # of paras for share on stack

   MOV	AX,(Get_Interrupt_Vector << 8) + 2Fh
   INT	21h
   MOV	WORD PTR [i2F.next],BX
   MOV	WORD PTR [i2F.next + 2],ES
   MOV	AX,(Set_Interrupt_Vector << 8) + 2Fh
   MOV	DX, i2F
   INT	21h
					;---------------------------------------
					; Notify the DOS that we are around so that
					; the DOS can make expensive calls to us.
					;---------------------------------------
   MOV	AH,Get_In_Vars
   INT	21h

   ASSUME ES:DOSGROUP

   mov	al,[skip_check]			; get the SHARE operating mode	       ;AN011;
   cmp	al,1				;  is it a /nc	  -  tell DOS  " 1 "   ;AN011;

;  $if	ne				; if not			       ;AN011;
   JE D_$IF50
       dec  al				;   "full" SHARE  -  tell DOS  " -1 "  ;AN011;
;  $endif				;
D_$IF50:

   MOV	[es:fShare],al			; tell DOS we are here		       ;AC011;
					;---------------------------------------
					; Cram in the new jump table
					;---------------------------------------
   CLI
   MOV	SI,OFFSET JTable
   MOV	DI,OFFSET JShare
   MOV	CX,JTableLen/2
   REP	MOVSW
					;---------------------------------------
					; Examine the size of the FCB cache.
					; If it is NOT the system default of 4,0
					; change it (via reallocation) to 16,8.
					; The old table is lost.
					;---------------------------------------
   ASSUME DS:NOTHING

   CMP	word [es:KeepCount],0

;  $if	z,and				; if the ",0"  part and 	       ;AC000;
   JNZ D_$IF52

   LDS	SI,[ES:BX + SYSI_FCB]		; point to the existing cache
sfCount equ SFCount	; NASM port equate
   CMP	word [SI + sfCount],4

;  $if	z				; if the "4,"  part then	       ;AC000;
   JNZ D_$IF52

					;---------------------------------------
					; Whammo, we need to allocate 16 * size
					; of SF_entry + size of sfTable.
					; Compute this size in paragraphs
					;---------------------------------------
       MOV  AX,16
       MOV  CX,sf_entry_struc_size
       MUL  CX
sf_struc_size equ SF_struc_size	; NASM port equate
       ADD  AX,(sf_struc_size) - 2
					;---------------------------------------
					; This size is in bytes...
					; Round up to paragraph size
					;---------------------------------------
       ADD  AX,0Fh
       RCR  AX,1
       SHR  AX,1
       SHR  AX,1
       SHR  AX,1
					;---------------------------------------
					; AX is the number of paragraphs to add.
					; Word on stack is current TNR size.
					; Make dos point to new table
					;---------------------------------------
       MOV  WORD PTR [ES:BX + SYSI_FCB],0
       MOV  WORD PTR [ES:BX + SYSI_FCB + 2],SS
       POP  SI
       ADD  WORD PTR [ES:BX + SYSI_FCB + 2],SI
					;---------------------------------------
					; Initialize table parts, next link
					;   and size
					;---------------------------------------
       MOV  DS,WORD PTR [ES:BX + SYSI_FCB + 2]
sfLink equ SFLink	; NASM port equate
       MOV  WORD PTR [sfLink],-1
       MOV  WORD PTR [sfLink+2],-1
sfcount equ SFCount	; NASM port equate
       MOV  word [sfcount],16
					;---------------------------------------
					; Set up succeeding LRU size
					;---------------------------------------
       MOV  word [es:KeepCount],8

       ADD  SI,AX
       PUSH SI

;  $endif				; endif - "4,0" 		       ;AC000;
D_$IF52:

					;---------------------------------------
					; Clean out the FCB Cache
					;---------------------------------------
   LES	DI,[ES:BX + SYSI_FCB]

   ASSUME ES:Nothing

   MOV	CX,[ES:DI + SFCount]
   LEA	DI,[DI + SFTable]

;  $do					;				       ;AC000;
D_$DO54:

       MOV  word [ES:DI + sf_ref_count],0
       MOV  WORD PTR [ES:DI + sf_position],0
       MOV  WORD PTR [ES:DI + sf_position + 2],0
       ADD  DI,sf_entry_struc_size

;  $enddo loop				;				       ;AC000;
   LOOP D_$DO54

   STI

   ASSUME ES:NOTHING

   XOR	BX,BX
   MOV	CX,5				; StdIN,StdOUT,StdERR,StdAUX,StdPRN

;  $do					; Close STD handles before	       ;AC000;
D_$DO56:
					; keep process
CLOSE equ Close	; NASM port equate
       MOV  AH,CLOSE
       INT  21H
       INC  BX

;  $enddo loop				;				       ;AC000;
   LOOP D_$DO56

   POP	DX				; T+R size in DX
   MOV	AX,(Keep_Process << 8) + 0
   INT	21h
EXIT equ Exit	; NASM port equate
   MOV	AX,(EXIT << 8) + 1
   INT	21h				; We'er now resident, return to DOS

   EndProc Init

   Break <SHAREINIT - Share initialization entry point>

;******************* START OF SPECIFICATIONS ***********************************
;
; SHAREINIT - Share initialization entry point
;
;******************* END OF SPECIFICATIONS *************************************

..start:
   Procedure SHAREINIT,NEAR

   ASSUME CS:SHARE,DS:NOTHING,ES:NOTHING,SS:STACK

;  int 3
   nop
   nop


   PUSH DS				; save PSP segment for later stack     ;AC001;
					;     relocation

					;---------------------------------------
					; Load Messages
					;---------------------------------------
   call ShLoadMsg			;				       ;AN000;
					;---------------------------------------
					; At this point, the DOS version is OK.
					;  (checked by SYSLOADMSG)
					;  Now - Check the DOS data version
					;---------------------------------------
;  $if	c,or				; if not same as us			;AC009;
   JC D_$LL58

   MOV	AH,Get_In_Vars
   INT	21h

   ASSUME ES:DOSGROUP

   CMP	byte [es:DataVersion],ShareDataVersion

   ASSUME ES:NOTHING

;  $if	ne				; if not same as us			;AC000;
   JE D_$IF58
D_$LL58:
Utility_Msg_CLASS equ UTILITY_MSG_CLASS	; NASM port equate
       mov  ax,(Utility_Msg_CLASS << 8) + Bad_DOS_Ver ;			;AN000;
       call ShDispMsg			;					;AN000;
;  $endif				; endif - not same as us		;AC000;
D_$IF58:

					;---------------------------------------
					; Deallocate memory if possible
					;---------------------------------------
pdb_environ equ PDB_environ	; NASM port equate
   mov	ax,[pdb_environ]
   or	ax,ax

;  $if	nz				; if > 0 deallocate memory	       ;AC000;
   JZ D_$IF60
       mov  es,ax
dealloc equ Dealloc	; NASM port equate
       mov  ah,dealloc
       int  21h
;  $endif				; endif - > 0 deallocate memory        ;AC000;
D_$IF60:

					;---------------------------------------
					; Parse the command line
					;---------------------------------------
   call ShComndParse			;				       ;AN000;
					;---------------------------------------
					; Check to see if share already installed.
					;---------------------------------------
   mov	al,[cs:skip_check]			;				       ;AN010;
   or	al,80h				; signal its SHARE calling	       ;AN010;
multShare equ MultSHARE	; NASM port equate
   mov	ah,multShare			;				       ;AC010;
   INT	2Fh				;				       ;AC010;
   CMP	AL,0FFh 			;				       ;AC010;

;  $if	z				; if we'er already loaded              ;AC010;
   JNZ D_$IF62
       mov  ax,(UTILITY_MSG_CLASS << 8) + Sh_Already_Loaded ;		       ;AC010;
       call ShDispMsg			;				       ;AC010;
;  $endif				; endif - we'er already loaded         ;AC010;
D_$IF62:

					;---------------------------------------
					; Check to see if share installed and
					; a toggle was just performed
					;---------------------------------------
   CMP	AL,0F0h 			;				       ;AN010;

;  $if	z				; if we'er already loaded              ;AN010;
   JNZ D_$IF64

       MOV  AX,(EXIT << 8)		;				       ;AN010;
       INT  21h 			; Return to DOS with RC = 0	       ;AN010;

;  $endif				; endif - we'er already loaded         ;AN010;
D_$IF64:

					;---------------------------------------
					; All set to initialize the world.
					; Make sure that we have enough memory
					; for everything in our little 64K here.
					; First get avail count of paras.
					;---------------------------------------
   pop	es				; recover PSP segment		       ;AC002;
   push es				;				       ;AC002;
   MOV	BX,CS
PDB_Block_Len equ PDB_block_len	; NASM port equate
   MOV	AX,[ES:PDB_Block_Len]
   SUB	AX,BX
					;---------------------------------------
					; AX has the number of paragraphs
					; available to us after the beginning
					; of CS.  Max this out at 64K.
					;---------------------------------------
   CMP	AX,1000h

;  $if	a				; if more than we can handle	       ;AC000;
   JNA D_$IF66
       MOV  AX,1000h			;  force it
;  $endif				; endif - more than we can handle      ;AC000;
D_$IF66:

					;---------------------------------------
					; Take AX paragraphs and convert them
					; into BX:CX bytes.
					;---------------------------------------
   XOR	BX,BX
   SHL	AX,1
   SHL	AX,1
   SHL	AX,1
   SHL	AX,1
   ADC	BX,0
   MOV	CX,AX
					;---------------------------------------
					; compute in DX:AX, the size
					; requested by the user
					;---------------------------------------
   MOV	AX,[cs:initlocks]
   MOV	SI,RLR_Entry_struc_size
   MUL	SI
   ADD	AX,OFFSET MFT
   ADC	DX,0
   ADD	AX,[cs:InitSpace]
   ADC	DX,0
					;---------------------------------------
					; Compare the 32 bit sizes DX:AX and BX:CX.
					; If BX:CX is smaller, then we
					; are out of memory.
					;---------------------------------------

   CMP	DX,BX				; try upper half first

;  $if	a,or				; if most significant is bigger or     ;AC000;
   JA D_$LL68

;  $if	e,and				; if equal and			       ;AC000;
   JNE D_$IF68

   CMP	AX,CX				;

;  $if	a				; if least significant is bigger       ;AC000;
   JNA D_$IF68
D_$LL68:

       mov  ax,(EXT_ERR_CLASS << 8) + No_Mem_Error ; issue error message      ;AN000;

       call ShDispMsg			;				       ;AN000;

;  $endif				; endif - bigger		       ;AC000;
D_$IF68:

					;--------------------------------------
					; Move stack to PSP area.  Otherwise we
					; will run into problems with growing
					; the stack into the lock records.
					;---------------------------------------
   POP	AX				; this is the entry value for DS (PSP) ;AC001;
   MOV	SS,AX				;				       ;AC001;
   MOV	SP,100h 			;				       ;AC001;

   ASSUME SS:NOTHING
					;---------------------------------------
					; Continue with rest of initialization
					;---------------------------------------
INIT equ Init	; NASM port label
   JMP	INIT

   EndProc SHAREINIT

 global amis_sign
	even
amis_sign:
.ven:	fill 8, 32, db "ecm"
.prod:	fill 8, 32, db "lDOS"
amis_id:			; must be directly after amis_sign
	dw 0
.seq:	dw 0			; sequential number
.us:	counted "sharer"	; our id name


   Break <ShLoadMsg  -	Share Load Message>

;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	ShLoadMsg  -  Share Load Message
;
;  FUNCTION:	Load the Share messages into the message buffer.
;
;  INPUT:	None
;
;  OUTPUT:	Messages loaded into the message buffer and Message
;		Sevices code initalized
;
;  REGISTERS USED:  DI AX CX DX
;  (NOT RESTORED)
;
;  LINKAGE:	Call near
;
;  NORMAL	CF = O
;  EXIT:
;
;  ERROR	CF = 1
;  EXIT:
;
;  CHANGE	04/15/87 - First release
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************

					;---------------------------------------
					; Message Equates
					;---------------------------------------

;			  $SALUT (4,27,34,41)

Bad_DOS_Ver		  equ	 1	; Incorrect DOS version 	       ;AN000;
Sh_Already_Loaded	  equ	 2	; SHARE already loaded message number  ;AN000;
No_Mem_Error		  equ	 8	; insufficient memory message number   ;AN000;

;  $SALUT (4,4,9,41)

   Procedure ShLoadMsg,near		;				       ;AN000;
					;---------------------------------------
					; Load the Messages
					;---------------------------------------
EXTRN SYSLOADMSG:NEAR			;				       ;AN000;

   call SYSLOADMSG			;				       ;AN000;

;  $IF	C				; if we have a MAJOR problem	       ;AN000;
   JNC D_$IF70
       mov  ah,dh			; save the class
       call ShDispMsg			;				       ;AN000;
					; For pre DOS 2.0, we may come back
       xor  ax,ax			;   here - so do it the old way
       push ss				;   just in case
       push ax				;

xxx    proc far 			;				       ;AN000;
       ret				;				       ;AN000;
xxx    endp				;				       ;AN000;

;  $ENDIF				; endif - we have a MAJOR problem      ;AN000;
D_$IF70:


   ret					;				       ;AN000;

   EndProc ShLoadMsg			;

   Break <ShDispMsg  -	Share Display Message>

;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	ShDispMsg  -  Share Display Message
;
;  FUNCTION:	Display the messages for share
;
;  INPUT:	AX = message number - AH - Class
;				      AL - Number
;
;  OUTPUT:	- Messages output to Output Device
;		- Exit to DOS
;
;  REGISTERS USED:  CX DX
;  (NOT RESTORED)
;
;  LINKAGE:	Call near
;
;  NORMAL	CF = O
;  EXIT:
;
;  ERROR	CF = 1
;  EXIT:	CX = 0 - INCORRECT DOS VERSION
;
;  CHANGE	04/15/87 - First release
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************

;			  $SALUT (4,27,34,41)

					; The following structure is a
					;   SYSMSG SUBLIST control block.
					;   It is initalized for the "already
					;   installed " message.  The parse
					;   routine will set it up to work
					;   for parseing.
SUBLIST 		  LABEL  WORD

			  db	 sub_size ; size of sublist
			  db	 0	; reserved
msg_offset		  dw	 offset SHARE_Name ; insert 'SHARE'

msg_segment		  LABEL  WORD

%IFN	 INSTALLED

			  dw	 CODE

%ELSE

			  dw	 SHARE

%ENDIF

num_ins 		  db	 1	; only one insert
			  db	 Char_Field_ASCIIZ ; data type flag - ascii z string
max_ins 		  db	 SHARE_Name_Size ; maximum field size
min_ins 		  db	 SHARE_Name_Size ; minimum field size
			  db	 " "	; pad character

sub_size		  equ	 $ - SUBLIST

SHARE_Name		  LABEL  WORD

			  db	 "SHARE"

Share_Name equ SHARE_Name	; NASM port label
SHARE_Name_Size 	  equ	 $ - Share_Name

			  db	 0	; make it a Z string
;  $SALUT (4,4,9,41)

   Procedure ShDispMsg,near		;				       ;AN000;
					;---------------------------------------
					; Set up required parameters
					;--------------------------------------
   MOV	BX,STDERR			;display message on STD ERROR	       ;AN000;
   XOR	CX,CX				;no substitution required	       ;AN000;
   XOR	DX,DX				;set flags to 0 		       ;AN000;
   DEC	DH				;and class to utility		       ;AN000;
   cmp	ah,PARSE_ERR_CLASS		;
;  $if	be,and				;				       ;AC009;
   JNBE D_$IF72
   mov	dh,ah				;
;  $if	e				; set up implied substitution	       ;AC009;
   JNE D_$IF72

       ASSUME DS:nothing,ES:DOSGROUP

       mov  [cs:num_ins],cl			; set number of inserts to 0	       ;AN009;
       mov  BYTE [cs:max_ins],030h	; set maximum size of insert	       ;AN009;
       mov  BYTE [cs:min_ins],1		; set minimum size of insert	       ;AN009;
       push ds				; set up segment		       ;AN009;
       pop  word [cs:msg_segment]		;				       ;AN009;
       mov  BYTE PTR [si],0		; turn it into a ASCIIZ string	       ;AN009;
       cmp  si,[cs:msg_offset]		; is there something there?	       ;AN009;
;      $if  a				; if it is...			       ;AN009;
       JNA D_$IF73
	   inc	cx			;				       ;AN009;
;      $endif				;				       ;AN009;
D_$IF73:
;  $endif				;
D_$IF72:
   cmp	al,Sh_Already_Loaded		; SHARE already loaded message ?       ;AN000;
;  $if	e				; if it is...			       ;AN000;
   JNE D_$IF76
       inc  cx				;
SHARE_name equ SHARE_Name	; NASM port label
       mov  word [cs:msg_offset],OFFSET SHARE_name ; ensure the pointer is right	       ;AN010;
;  $endif				;
D_$IF76:
   push cs				; ensure that SYSMSG has proper        ;AC009;
   pop	ds				;	   addressability	       ;AC009;
   lea	si,[SUBLIST]			; point to sublist		       ;AC009;
   xor	ah,ah				;				       ;AN000;

					;--------------------------------------
					; Output the Message
					;---------------------------------------
EXTRN SYSDISPMSG:NEAR			;				       ;AN000;

   CALL SYSDISPMSG			;				       ;AN000;

;  $IF	C				; if error occured		       ;AN000;
   JNC D_$IF78

       CALL Get_DOS_Error		; a DOS extended error occured	       ;AN000;
       CALL SYSDISPMSG			; try to issue it		       ;AN000;

;  $ENDIF				; endif - error occured 	       ;AN000;
D_$IF78:

   MOV	AX,(EXIT << 8) + 0FFH		; exit to DOS			       ;AN000;
   INT	21h				;				       ;AN000;

   ret					; may return if pre DOS 2.0	       ;AN000;

   EndProc ShDispMsg			;				       ;AN000;

   BREAK < Get_DOS_Error >

;******************* START OF SPECIFICATIONS ***********************************
;Routine name: Get_DOS_Error
;*******************************************************************************
;
;Description:  Call DOS to obtain DOS extended error #
;
;Called Procedures: None
;
;Input: 	    None
;
;Output:	    AX = error number
;		    DH = DOS extended error class
;
;Change History:    Created	   5/01/87	   FG
;
;******************* END OF SPECIFICATIONS *************************************
;******************+ START OF PSEUDOCODE +**************************************
;
;	START  Get_DOS_Error
;
;	call DOS for extended error (INT21 GetExtendedError + 00 <5900>)
;	set up registers for return
;	ret
;
;	END  Get_DOS_Error
;
;******************-  END  OF PSEUDOCODE -**************************************

   public Get_DOS_Error

   Get_DOS_Error PROC NEAR

   mov	ax,(GetExtendedError << 8)	; DOS ext. error		       ;AN000;
   xor	bx,bx
   push es				;				       ;AN000;
   INT	21h				;    GetExtendedError + not_used <5900>;AN000;
   pop	es
   mov	bx,STDERR			; fix up bx			       ;AN000;
   xor	cx,cx				; fix up cx			       ;AN000;
   mov	dh,EXT_ERR_CLASS		; set class to dos error

   ret					;				       ;AN000;

   ENDPROC Get_DOS_Error

   Break <ShComndParse	-  Share Command line Parser>

;******************* START OF SPECIFICATIONS ***********************************
;
;  NAME:	ShComndParse  -  Share Command line Parser
;
;  FUNCTION:	Call the DOS PARSE Service Routines to process the command
;		line. Search for valid switches (/F:n and /L:m) and
;		update the values for file size and number of locks accordingly
;
;  INPUT:	Parameter string from command line in the PSP
;
;  OUTPUT:	INITspace and INITlocks are updated.
;
;  REGISTERS USED: ES DI AX BX CX DX
;  (NOT RESTORED)
;
;  LINKAGE:	Call
;
;  NORMAL	- If /F:n specified, then INITspace is updated.
;  EXIT:	- If /L:m specified, then INITlocks is updated.
;
;  ERROR	If user enters:
;  EXIT:	- any parameter or switch other than /F:n or /L:m
;		- an invalid value for "n" or "m"
;		then this routine will display the "Invalid Parameter"
;		error message and terminate.
;
;  EXTERNAL	- System parse service routines
;  REFERENCES:	- INT21 - GET PSP Function Call 062h
;
;  CHANGE	04/15/87 - First release
;  LOG:
;
;******************* END OF SPECIFICATIONS *************************************
;******************+ START OF PSEUDOCODE +**************************************
;
;	START
;
;	return
;
;	END
;
;******************-  END  OF PSEUDOCODE -*************************************

;			  $SALUT (4,27,34,41)

					;--------------------------------------
					; Parse Equates
					;--------------------------------------

EOL			  equ	 -1	; Indicator for End-Of-Line	       ;AN000;
NOERROR 		  equ	 0	; Return Indicator for No Errors       ;AN000;
FILE_SWITCH		  equ	 1	; this is a file switch 	       ;AN000;
LOCK_SWITCH		  equ	 2	; this is a lock switch 	       ;AN000;
Syntax_Error		  equ	 9	; maximum PARSE error # 	       ;AN000;

;  $SALUT (4,4,9,41)

   Procedure ShComndParse,near		;				       ;AN000;
					;--------------------------------------
					;  Get address of command line
					;--------------------------------------
EXTRN SYSPARSE:NEAR			;				       ;AN000;

   MOV	SI,0081H			; OFFSET OF COMMAND LINE IN PSP        ;AN000;
   MOV	AH,62H				; AH=GET PSP ADDRESS FUNCTION CALL     ;AN000;
   INT	21H				; PSP SEGMENT RETURNED IN BX	       ;AN000;
   MOV	DS,BX				; PUT PSP SEG IN DS		       ;AN000;
   MOV	CX,0				; NUMBER OF PARMS PROCESSED SO FAR     ;AN000;
   PUSH CS				;				       ;AN000;
   POP	ES				;				       ;AN000;

   ASSUME ES:SHARE			;				       ;AN000;

					;--------------------------------------
					;  Loop for each operand at DS:SI
					;--------------------------------------
;  $do					;				       ;AN000;
D_$DO80:

       LEA  DI,[PARMS]			; ADDRESS OF PARSE CONTROLS	       ;AN000;
       MOV  DX,0			; RESERVED			       ;AN000;
       mov  [es:msg_offset],si		; save the start scan point	       ;AC009;
       CALL SYSPARSE			; PARSE IT!			       ;AN000;
       CMP  AX,EOL			; ARE WE AT END OF COMMAND LINE ?      ;AN000;

;  $leave e				;				       ;AN000;
   JE D_$EN80

       CMP  AX,NOERROR			; ANY ERRORS?			       ;AN000;

;      $if  ne,or			; if parse says error or	       ;AN000;
       JNE D_$LL82

       MOV  AX,Syntax_Error		; Parse syntax error - just in case    ;AN000;
       MOV  BX,DX			; PLACE RESULT ADDRESS IN BX	       ;AN000;
       CMP  BX,OFFSET SWITCH_BUFF	;				       ;AN000;

;      $if  ne				; if no pointer 		       ;AN000;
       JE D_$IF82
D_$LL82:

	   call PARSE_ERROR		;   call error routine		       ;AN000;

;      $endif				; endif - error 		       ;AN000;
D_$IF82:

       MOV  AX,WORD PTR [es:SW_VALUE]	; load the value		       ;AN000;
       MOV  BX,[es:SW_SYN]			; load pointer to synonym	       ;AN000;

					;--------------------------------------
					;  If user said  /F:n, then
					;--------------------------------------

       CMP  BX,OFFSET F_SW		; IF USER SPECIFIED /F		       ;AN000;

;      $if  e				;				       ;AN000;
       JNE D_$IF84

INITspace equ InitSpace	; NASM port label
	   CMP	[es:INITspace],AX		; is default < requested ?	       ;AN000;

;	   $if	b			; if default is <		       ;AN000;
	   JNB D_$IF85
	       MOV  [es:INITspace],AX	; save the new value		       ;AN000;
;	   $endif			; endif   (else leave it alone)        ;AN000;
D_$IF85:

;      $else				; else - CHECK FOR LOCKS	       ;AN000;
       JMP SHORT D_$EN84
D_$IF84:

					;---------------------------------------
					;  If user said /L:m, then update INITlocks
					;---------------------------------------
	   CMP	BX,OFFSET L_SW		; IF USER SPECIFIED /L		       ;AN000;

;	   $if	e			; if it is				;AN000;
	   JNE D_$IF88

INITlocks equ InitLocks	; NASM port label
	       CMP  [es:INITlocks],AX	; is default < requested ?	       ;AN000;

;	       $if  b			; if default is <		       ;AN000;
	       JNB D_$IF89
		   MOV	[es:INITlocks],AX	;   save the value		       ;AN000;
;	       $endif			; endif      (else leave it alone)     ;AN000;
D_$IF89:

;	   $else			; else - CHECK FOR TOGGLE	       ;AN010;
	   JMP SHORT D_$EN88
D_$IF88:

					;---------------------------------------
					;  If user said /NC, then update check_flag
					;---------------------------------------
	       CMP  BX,OFFSET N_SW	; IF USER SPECIFIED /NC 	       ;AN010;
;	       $if  ne			; if error			       ;AC010;
	       JE D_$IF92
		   MOV	AX,Syntax_Error ; Parse syntax error		       ;AN000;
		   call PARSE_ERROR	;   call error routine		       ;AN000;
;	       $endif			; endif - error 		       ;AC010;
D_$IF92:

	       mov  byte [es:skip_check],1	; set the skip check flag	       ;AN010;

;	   $endif			; endif - CHECK FOR TOGGLE	       ;AN010;
D_$EN88:

;      $endif				; endif - CHECK FOR LOCKS	       ;AN000;
D_$EN84:

;  $enddo				; CHECK FOR NEXT PARM		       ;AN000;
   JMP SHORT D_$DO80
D_$EN80:

   ret					; NORMAL RETURN TO CALLER	       ;AN000;

					;---------------------------------------
					;  If any other parameter specified,
					;  display message and quit
					;---------------------------------------
PARSE_ERROR:				;				       ;AN000;

   cmp	al,Syntax_Error 		; error 1 to 9 ?		       ;AN000;

;  $if	a				; if parse error		       ;AN000;
   JNA D_$IF97

       mov  al,Syntax_Error		; Parse syntax error

;  $endif				; endif errors			       ;AN000;
D_$IF97:

   lea	bx,[Parse_Ret_Code]
   cs xlatb
   mov	ah,PARSE_ERR_CLASS		; set class to parse error	       ;AN000;

   CALL ShDispMsg			; display the parse error	       ;AN000;

   ret					; this should never be used

Parse_Ret_Code label byte

   db	0				; Ret Code 0 -
   db	9				; Ret Code 1 - Too many parameters
   db	9				; Ret Code 2 - Required parameter missing
   db	3				; Ret Code 3 - Invalid switch
   db	9				; Ret Code 4 - Invalid keyword
   db	9				; Ret Code 5 - (reserved)
   db	6				; Ret Code 6 - Parm val out of range
   db	9				; Ret Code 7 - Parameter val not allowed
   db	9				; Ret Code 8 - Parameter val not allowed
   db	9				; Ret Code 9 - Parm format not correct

   EndProc ShComndParse 		;				       ;AN000;

   %include "msgdcl.mac"

; (no prior section) ; SHARE ENDS

section STACK stack class=STACK
   DB	278  + 128 DUP (?)		; 278 == IBM's ROM requirements
; (no prior section) ; STACK ENDS

%ENDIF

   END	shareinit


