Google
 

Trailing-Edge - PDP-10 Archives - cuspbinsrc_2of2_bb-fp63b-sb - 10,7/rms10/rmssrc/rmsput.b36
There are 6 other files named rmsput.b36 in the archive. Click here to see a list.
MODULE  PUT =

BEGIN

GLOBAL BIND	PUTV = 1^24 + 0^18 + 11;		!EDIT DATE: 6-APR-77

%([

FUNCTION:	THIS MODULE CONTAINS ALL ROUTINES WHICH PROCESS
		THE $PUT MACRO FOR RMS-20.
AUTHOR:	S. BLOUNT

THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED
  OR COPIED IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE.

!COPYRIGHT (C) 1977, 1979 BY DIGITAL EQUIPMENT CORPORATION



**********	TABLE OF CONTENTS	**************




	ROUTINE			FUNCTION
	=======			========

	$PUT			PROCESSOR FOR $PUT MACRO

	PUTASC			"PUT"'S RECORD TO ASCII FILE

	PUTSQR			"PUT"'S RECORD TO A SEQUENTIAL OR RELATIVE FILE

	PUTREC			PERFORMS PHYSICAL TRANSFER OF RECORD

	PUT			"PUT"'S RECORD TO INDEXED FILE

	SETPUT			SET-UP FOR $PUT TO INDEXED FILE





REVISION HISTORY:

EDIT	DATE		WHO		PURPOSE
====	====		===		=======

1	7-OCT-76	SEB		MISC OPTIMIZATIONS
2	28-OCT-76	SEB		ADD RELEASECURRENTBUCKET MACRO
3	8-NOV-76	SEB		FIX BUG IN PUT TO REL FILE
4	23-NOV-76	SEB		FIX CHECK OF VALID BIT
5	7-FEB-77	SEB		FIX MAXRECORDSIZE COMP. IN SETPUT
					SO TOO BIG RECORDS ARE DETECTED
6	11-MAR-77	SEB		PUT CODE IN FOR CHECK BYTE
7	23-MAR-77	SEB		UNDO EDIT 6
8	5-APR-77	SEB		DON'T BLT FULL SIZE OF RELATIVE RECORD
9	6-APR-77	SEB		CHANGE HYBYTE TO RST FIELD

*************************************************
*						*
*		NEW REVISION HISTORY		*
*						*
*************************************************

****************** Start RMS-10 V1.1 *********************
********************* TOPS-10 ONLY ***********************

PRODUCT	MODULE	 SPR
 EDIT	 EDIT	 QAR		DESCRIPTION
======	======	=====		===========

 100	  10	Dev		Make declarations for routine names
				be EXTERNAL ROUTINE so RMS will compile 
				under BLISS V4 (RMT, 10/22/85).

 103	  11	Dev		Indicate that a page has been updated before
				starting the next file page so that RMS
				will only do deferred writing when that option
				is enabled. (RMT, 12/2/85)

	***** END OF REVISION HISTORY *****




])%


FORWARD ROUTINE		PUTSQR,
		PUTREC: novalue;			! FORWARD DECLARATONS

EXTERNAL ROUTINE
    CHKDUP,			! CHECK DUPLICATE RECORDS
!    CHKMAP,			
    CKEYKK,			! COMPARE KEY STRINGS
    CRASH,
    DUMP,
    CLEANUP,		! CLEAN UP AFTER A BAD PUT OPERATION
    FOLLOWPATH,		! FOLLOW THE INDEX PATH
    INSRTUDR,		! INSERT A USER DATA RECORD
    INSRTSIDR,		! INSERT A SEC INDEX DATA RECORD
    IDXUPDATE,		! UPDATE THE INDEX STRUCTURE
    MAKIDX,			! CREATE AN INDEX STRUCTURE
    MOVEKEY,		! MOVE A KEY STRING TO A BUFFER
    NUMBERTORFA,
    PUTBKT,			! PUT A BUCKET
    SETNRP,			! SET UP THE NEXT RECORD POINTER
    GTBYTE,
    LOCKIT,
    MOVEREC,
!    MOVESTRING,
    NOSPAN,
    PUTASCII,
    PUTLSN,
    REMOVRECORD,		! TAKE UDR OUT OF FILE ON ERROR
    RSETUP;

EXTERNAL
    TBUFFER;		! TEMP BUFFER FOR KEY STRINGS

%([ EXTERNAL ERROR MESSAGES INVOKED WITHIN THIS MODULE ])%

EXTERNAL
    MSGFAILURE,		! ROUTINE FAILURE
!    MSGUNLOCKED,		! RECORD IS UNLOCKED
    MSGCANTGETHERE;		! LOGIC BUG
!    MSGASCII;		! ASCII FILE NOT DETECTED




REQUIRE 'RMSREQ';
EXTDECLARATIONS;



! $PUT
! ====

! PROCESSOR FOR $PUT MACRO

! INPUT:
!	ADDRESS OF USER RECORD BLOCK (RAB)
!	ADDRESS OF USER ERROR ROUTINE

! OUTPUT:
!	<STATUS FIELD OF USER RAB>

! ROUTINES CALLED:
!	PUTASCII
!	PUTSQR
!	PUTIDX
!	RSETUP

! FORMAT OF THE $PUT MACRO:
!
!		$PUT	<RAB-ADDRESS> [,<ERROR-ADDRESS>]
!
! RAB FIELDS USED AS INPUT BY $PUT:
!
!	ISI		INTERNAL STREAM IDENTIFIER
!	KBF		KEY BUFFER ADDRESS (RELATIVE/INDEXED)
!	KSZ		SIZE OF KEY IN KEY BUFFER (INDEXED)
!	LSN		LINE-SEQUENCE NUMBER (LSA)
!	RAC		RECORD ACCESS
!	RBF		ADDRESS OF USER RECORD BUFFER
!	ROP		RECORD ACCESS OPTIONS
!	RSZ		SIZE OF RECORD
!	

! RAB FIELDS WHICH ARE SET BY $PUT:
!
!	BKT		RECORD NUMBER OF CURRENT RECORD (RELATIVE)
!	RBF		ADDRESS OF BUFFER FOR NEXT RECORD (-11 COMPATIBILITY)
!	RFA		RECORD'S FILE ADDRESS
!	STS		COMPLETION STATUS CODE
!	STV		ADDITIONAL STATUS INFORMATION

GLOBAL ROUTINE %NAME('$PUT') ( BLOCK, ERRORRETURN ) =
BEGIN
	ARGUMENT (BLOCK,BASEADD);
	ARGUMENT (ERRORRETURN,BASEADD);		! ADDRESS OF USER ERROR ROUTINE
REGISTER
    ERRORCODE;				! USED TO SAVE AN ERROR CODE
%IF INDX %THEN
 EXTERNAL ROUTINE
	PUTIDX;
%FI

	RMSENTRY ( $PUT );

	%([ FETCH INPUT ARGS ])%

	RAB = .BLOCK;					! GET ADDRESS OF RAB
	ERRADR = .ERRORRETURN;		! AND USER ERROR ADDRESS
	CALLRSETUP ( PCI ( AXPUT ));			! DO OTHER STUFF





	%(***	 ERROR PROCESSING FOR $PUT MACRO   ***
	*						*
	* THE FOLLOWING ERRORS ARE CHECKED:		*
	*	1. RFA ADDRESSING IS ILLEGAL		*
	*	2. RECORD-SIZE < = MAX-REC-SIZE		*
	*	3. RECORD BUFFER MUST BE PROPER		*
	*	4. RECORD-SIZE FOR FIXED-LENGTH RECORDS	*
	*						*
	*						*
	*************************************************
	])%


	ERRORCODE = ZERO;				! ASSUME NO ERROR
	IF RFAADR THEN ERRORCODE = ER$RAC;			! DONT ALLOW RFA ADDRESSING
	IF  ( .FST [ FSTMRS ] ISNT ZERO )			! IF THERE IS A MAX RECORD SIZE...
	THEN	IF ( .RAB [ RABRSZ ]  GTR .FST [ FSTMRS ] )
		THEN ERRORCODE = ER$RSZ;			! RECORD IF BIGGER THAN MAXIMUM

	IF .RAB [ RABRBF ] LEQ MINUSERBUFF THEN ERRORCODE = ER$RBF;	! CHECK BUFFER

	IF FIXEDLENGTH
	THEN
		BEGIN
		IF .RAB [ RABRSZ ] ISNT .FST [ FSTMRS ] THEN ERRORCODE = ER$RSZ
		END;

	%([ WAS THERE AN ERROR? ])%

	IF .ERRORCODE ISNT ZERO 
	THEN
		BEGIN
		USRSTS = .ERRORCODE;
		USEXITERR				! EXIT FROM RMS
	END;


	%([ *****  END OF ERROR PROCESSING FOR $PUT ****** ])%




	%([ DISPATCH TO A ROUTINE TO WRITE A RECORD FOR EACH FILE ORGANIZATION ])%

	CASE FILEORG FROM 0 TO 4 OF
		SET
		
		[0]:	%( ASCII )%	CALLPUTASCII;		! ASCII FILE
		[1]:	%( SEQ )%		CALLPUTSQR;		! SEQUENTIAL FILE
		[2]:	%( REL )%		CALLPUTSQR;		! RELATIVE FILE
		[3]:	%( INDX )%	%IF INDX %THEN
					CALLPUTIDX;		!INDEXED FILE
					%FI
		
		[4]:	
		TES;	%( END OF CASE FILEORG )%


	%([ SET THE "SUCCESS" BIT AND REMEMBER THAT THIS WAS A $PUT ])%

	SETSUCCESS;					! SET SUCCESS BIT AND LAST-OPER

	%([ RETURN THE RFA OF THIS RECORD TO THE USER ])%

	RAB [ RABRFA ] = .RST [ RSTDATARFA ];

	%([ EXIT TO THE USER ])%

	USEREXIT	%( EXIT TO USER )%


END; %( OF $PUT PROCESSOR )%




! PUTSQR
! ======

! THIS ROUTINE PROCESSES THE $PUT VERB TO A SEQUENTIAL OR RELATIVE
! FILE

! INPUT:
!	<NONE>

! OUTPUT:
!	<NONE>
!

GLOBAL ROUTINE PUTSQR  =
BEGIN


LOCAL
    TEMP1,
    BYTENUM,				! BYTE NUMBER OF TARGET RECORD
    HEADER,
    CRP;					! SAVE FOR ARA
REGISTER
    FULLRECORDSIZE;

	TRACE ( 'PUTSQR' );

	%([ FIND THE CRP AND BYTE-ADDRESS OF THE CURRENT RECORD FOR EACH FILE TYPE ])%

	%([ WE MUST PERFORM DIFFERENT OPERATIONS HERE DEPENDING
	   UPON THE FILE ORGANIZATION. HERE IS A SUMMARY OF
	   THE ACTIONS WHICH ARE DONE BELOW:


	   ASCII FILES:
		IF THIS IS AN ASCII FILE, THEN WE SHOULD NOT BE HERE, MUST BE A BUG.

	   SEQUENTIAL FILES:
		FOR SEQUENTIAL FILES, WE FIRST MUST COMPUTE THE SIZE
		OF THIS RECORD IN WORDS. THIS QUANTITY IS COMPUTED HERE
		BECAUSE IT IS USED HEAVILY LATER AND WE WOULD LIKE NOT
		TO HAVE TO RE-COMPUTE IT SEVERAL TIMES.  THEN, WE MUST
		DETERMINE IF THIS RECORD CAN FIT ON THE PAGE IF THE
		FB$BLK ATTRIBUTE IS DEFINED FOR THE FILE.

	   RELATIVE FILES:
		WE MUST FETCH EITHER THE NRP OR THE USER'S RECORD
		KEY, DEPENDING UPON HIS RECORD-ACCESS FIELD. THIS
		VALUE WILL THEN BE MAPPED INTO AN RFA WHICH GIVES
		THE STARTING ADDRESS OF THE TARGET RECORD. ])%

	%([ COMPUTE THE SIZE IN WORDS OF THIS RECORD ])%

	RST [ RSTRSZW ] = SIZEINWORDS ( .RAB [ RABRSZ ], .FST [ FSTBSZ ] );

	CASE FILEORG FROM 0 TO 3 OF

		SET
		[0]:	%(ASCII)%	0;	! SHOULD NOT GET HERE

		[1]:	%(SEQ)%		BEGIN

					CRP = .RST [ RSTNRP ] ;		! GET NRP

					%([ IF FILE IS BLOCKED, SEE IF RECORD
					    CAN FIT ON THIS PAGE ])%

					IF BLOCKED
					THEN				! CHECK TO SEE IF RECORD CAN FIT ON PAGE
						CRP =CALLNOSPAN ( LCI ( CRP ) );
					BYTENUM = .CRP			! GET FILE ADDRESS
					END; %( OF CASE SEQUENTIAL FILE )%

		[2]:	%(REL)%		BEGIN
					CRP = ( CASE RECORDACCESS FROM 0 TO 1 OF

						SET
						[0]:	%(SEQ)%	.RST [ RSTNRP ] ;		! FETCH NRP

						[1]:	%(KEY)%	BEGIN
								IF .RAB [ RABKBF ] LSS MINUSERBUFF THEN USERERROR ( ER$KBF );
								..RAB [ RABKBF ]
								END
						TES); %( END OF CASE ADRMODE )%

					IF ( BYTENUM = CALLNUMBERTORFA ( LCI ( CRP ) ) )	! COMPUTE BYTE-# OF RECORD
						IS FALSE THEN USERERROR ( ER$KEY )		! RECORD WAS .GTR. MRN

					END; %( OF RELATIVE FILE )%

		[3]:	
		TES; %( END OF CASE FILEORG OF SET )%

	%([ AT THIS POINT, WE HAVE THE FOLLOWING VALUES:
		CRP	=	RFA OF THE RECORD TO BE WRITTEN
		BYTENUM	=	BYTE ADDRESS OF RECORD ])%

	%([ SET UP SOME VALUES WHICH WE WILL USE LATER SO THEY
	   CAN BE IN REGISTERS TO SAVE TIME ])%

	FULLRECORDSIZE = .RST [ RSTRSZW ];			! SIZE OF ENTIRE RECORD

	%([ MAKE SURE THE RECORD CAN FIT ON ONE PAGE IF BLOCKED ])%

	IF BLOCKED
	THEN IF ( ( .FULLRECORDSIZE + HEADERSIZE ) GTR PAGESIZE ) THEN USERERROR ( ER$RSZ );

	%([ PRINT OUT THE RESULTS FOR DEBUGGING ])%

	LOOKAT ( '	BYTE NUMBER OF REC: ', BYTENUM );
	LOOKAT ( '	CURRENT REC PTR:: ', CRP );

	%([ UNLOCK CURRENT RECORD ])%

	IF DATALOCKED THEN UNLOCK ( RST [ RSTDATARFA ] );	! UNLOCK THE CURRENT RECORD

	%([ MAKE CRP EQUAL TO NRP ])%

	RST [ RSTDATARFA ] =  .CRP;				! CRP = NRP

	%([ LOCK THE RECORD TO BE WRITTEN ])%

	IF LOCKING THEN LOCKREC ( CRP );				! LOCK THE RECORD

	%([ THE RECORD IS NOW LOCKED ( IF FILE-SHARING )
	HOWEVER, IT MAY NOT BE IN OUR BUFFER SO
	WE MUST GET IT THERE AND SET UP THE
	POINTER TO IT ( PAGPTR ) WITHIN THE WINDOW PAGE ])%


	%([ POSITION THE FILE TO THE CORRECT RECORD ])%

	CALLGTBYTE ( LCI ( BYTENUM ), PCI ( FALSE ));		! FETCH FILE PAGE INTO USER WINDOW

	%([ THE PAGE IS NOW IN THE BUFFER AND PAGPTR POINTS TO IT ])%

	HEADER = .( .RST [ RSTPAGPTR ] );			! GET RECORD HEADER
	IF ( SEQFILE AND ( .HEADER  ISNT ZERO ) ) THEN USERERROR ( ER$NEF );	! ATTEMPT TO INSERT RECORD

	%([ FOR RELATIVE FILES, THE RECORD CAN BE WRITTEN EXCEPT
	   IN THE CASE WHERE THE RECORD IS VALID, BUT NOT DELETED.
	   THEREFORE, WE MUST CHECK BOTH THE VALID AND THE DELETED
	   BITS IN THE RECORD HEADER. ])%

	IF RELFILE
	THEN	%(CHECK FOR NON-DELETED RECORD)%
		BEGIN
		IF ( CHKFLAG ( HEADER, RHDRVALID+RHDRDELETE ) )
				IS
			( RHDRVALID )
		THEN
			USERERROR ( ER$REX );
		RAB [ RABBKT ] = .CRP			! RETURN RECORD NUMBER
		END; %(OF IF RELFILE)%


	%([ NOW, MOVE THE RECORD ONTO THE FILE PAGE ])%

	CALLPUTREC;						! MOVE RECORD

	%([ UPDATE THE HIGHEST BYTE IN THE FILE ])%

	TEMP1 = .BYTENUM + .FULLRECORDSIZE + HEADERSIZE;	! COMPUTE WHERE NEXT RECORD IS
	IF .TEMP1  GTR .RST [ RSTHYBYTE ]			! IF THIS RECORD IS HIGHER THAN ANY PREVIOUS ONE...
	THEN BEGIN
		RST [ RSTHYBYTE ] = .TEMP1;			! RESET EOF BYTE
		SIZEOFFILE = .TEMP1;				! CROCK
	END;


	%([ UPDATE THE NEXT RECORD POINTER ])%

	IF SEQADR THEN RST [ RSTNRP ] =
	( IF SEQFILE THEN .CRP + HEADERSIZE  + .FULLRECORDSIZE	! NRP = OLD NRP + SIZE OF HEADER + SIZE OF RECORD
		ELSE .CRP + 1 );				! BUMP RECORD NUMBER FOR RELATIVE FILES

	%( NOW RELEASE ANY LOCKED RECORD )%
	IF DATALOCKED THEN UNLOCK ( RST [ RSTDATARFA ] );	! UNLOCK THE RECORD

	GOODRETURN

END;	%( OF PUTSQR )%





! PUTREC
! ======

! THIS ROUTINE CREATES A RECORD IN A RMS-20 FILE. IT IS
!	ASSUMED ON INPUT THAT PAGPTR POINTS TO THE
!	CORRECT PLACE ON THE CURRENT WINDOW PAGE AT WHICH TO
!	WRITE THE RECORD. THERE MUST BE NO OVERLAP PAST
!	THE END OF THE WINDOW PAGE ON INPUT.
!	ON OUTPUT, PAGPTR WILL BE CLOBBERED.

! INPUT:
!	<NONE>

! OUTPUT:
!	<NO STATUS RETURNED>

! GLOBALS USED:
!	GTBYTE
!	MOVEREC

GLOBAL ROUTINE PUTREC: NOVALUE  =
BEGIN

LOCAL
    COUNT,
    BYTESIZE,
    DUMMYLOCAL,		! USED TO PASS SUBROUTINE ARGS
    NEWPAGE,
    USERBUFF;

REGISTER
    RECORDPTR,
    TEMP,
    BLTAC;

MAP
    RECORDPTR:	POINTER;


	TRACE ( 'PUTREC' );

	%([ CHECK VALIDITY OF A FEW THINGS ])%

	RECORDPTR = .RST [ RSTPAGPTR ];				! FETCH POINTER

	%([ WRITE HEADER ])%

	RECORDPTR [ WHOLEWORD ] = .RAB [ RABRSZ ] + RHDRVALID;	! RECORD-SIZE + VALID


	%([ WE HAVE NOW WRITTEN THE HEADER FOR THIS RECORD.
	   WE MUST BUMP THE FILE POINTER AND CHECK TO SEE
	   IF WE ARE PAST THE END OF THE FILE BUFFER. IF SO,
	   WE WILL MAP IN THE NEXT PAGE OF THE FILE AND WRITE
	   THE REST OF THE RECORD ])%

	RECORDPTR = .RECORDPTR + 1;				! BUMP FILE POINTER
	IF ( .RECORDPTR AND OFSETMASK ) IS ZERO
	THEN %( WE OVERLAPPED )%

		BEGIN	%( TO MAP NEXT PAGE )%
		SETBFDUPD (CBD [BKDBFDADR]);		! INDIC FILE PAGE UPD
		NEWPAGE = (( .CURRENTFILEPAGE +1 ) ^ P2W );	! GET START OF NEXT FILE PAGE


		%([ POSITION THE FILE ])%
		CALLGTBYTE ( LCI ( NEWPAGE ),	%( RFA OF NEXT PAGE )%
			       PCI ( FALSE ));		%( NO ABORT )%

		RECORDPTR = .RST [ RSTPAGPTR ]		! RESET POINTER
		END;	%( OF IF OFFSET IS ZERO )%




	%([ WE ARE NOW READY TO MOVE THE RECORD TO THE FILE.
	   HOWEVER, IN MOST CASES THE RECORD WILL FIT ENTIRELY
	   ON THE CURRENT FILE PAGE. IF THIS IS THE CASE, WE
	   DON'T NEED TO PERFORM ALL THE PAGE TURNING THAT
	   "MOVEREC" CAN DO; SO, A QUICK BLT IS ALL WE NEED.
	   THEREFORE, IF THIS RECORD DOES NOT SPAN FILE PAGES,
	   WE WILL DO THE MOVE HERE; OTHERWISE, WE WILL CALL
	   "MOVEREC" TO DO IT FOR US. ])%

	BYTESIZE = .FST [ FSTBSZ ];			! BYTE SIZE OF RECORD TO MOVE
	COUNT = .RAB [ RABRSZ ];			! SIZE OF RECORD
	USERBUFF = .RAB [ RABRBF ];			! GET USER ADDRESS
	TEMP = .RST [ RSTRSZW ];			! GET RSZW INTO AC
	IF ( ( .RECORDPTR AND OFSETMASK ) + .TEMP ) LSS PAGESIZE
	THEN %(THIS RECORD DOES NOT SPAN PAGE BOUNDARIES)%

		BEGIN
		SETBFDUPD(CBD[BKDBFDADR]);		!INDIC FILE PAGE UPD
		MOVEWORDS (	%(FROM)%	.USERBUFF,
				%(TO)%	.RECORDPTR,
				%(SIZE)%	.TEMP );

		RETURN
		END; %( OF IF THIS RECORD DOESNT SPAN PAGES )%

	%([ SET UP TO MOVE THE RECORD, IF IT SPANS PAGES ])%

	DUMMYLOCAL = .RECORDPTR;		! STORE IN LOCAL VARIABLE
	CALLMOVEREC (	LCI ( DUMMYLOCAL ),	%( WINDOW PTR )%
			LCI ( USERBUFF ),	%( USER BUFFER )%
			PCI ( TRUE ),		%( PUT-FLAG )%
			LCI ( COUNT ),		%( SIZE )%
			LCI ( BYTESIZE ) );	%( GUESS WHAT )%



	RETURN



END;	%( OF PUTREC )%


! PUTIDX
! ======

! ROUTINE TO INSERT A USER DATA RECORD INTO AN INDEXED FILE. ON
!	ENTRY TO THIS ROUTINE, NO PREPARATION OR RECORD UNLOCKING
!	HAS BEEN DONE. 

! INPUT:
!	<NONE>

! OUTPUT:
!	<NONE>

! ROUTINES CALLED:
!	FOLLOWPATH
!	INSRTUDR
!	IDXUPDATE
!	INSRTSIDR
!	REMOVRECORD

GLOBAL ROUTINE PUTIDX: NOVALUE =
 BEGIN

%IF INDX %THEN
 
 EXTERNAL ROUTINE
	PUTSIDR,				! PUT A SECONDARY RECORD
	SETPUT;					! SET UP ROUTINE FOR PUT
 LOCAL
	RESULT,
	RECDESC:	FORMATS[ RDSIZE ],	! RECORD DESCRIPTOR PACKET
	SECRECDESC:	FORMATS[ RDSIZE ],		!  RECORD DESCIPTOR PACKET FOR SEC INDEX
	BKTDESC:		FORMATS[ BDSIZE ],		! BUCKET DESCRIPTOR OF DATA
	SPLITBD1:	FORMATS[ BDSIZE ],		! BUCKET DESCRIPTOR FOR 1ST EXTRA BUCKET
	SPLITBD2:	FORMATS[ BDSIZE ],		! "         "      " 2ND EXTRA BUCKET
	ROOTBD:		FORMATS[ BDSIZE ],		! BUCKET DESC FOR INDEX ROOT
	TEMPPTR:	POINTER,			! PTR TO PROPER BUCKET DESCRIPTOR
	USERRECORDPTR:	POINTER,			! PTR TO THE USER'S RECORD
	ADDRESSOFRECORD	,			! SAVE LOCATION OF NEW RECORD HERE
	KEYOFREFERENCE	,				! CURRENT KEY VALUE
	DATAPAGE	;				! DATA PAGE NUMBER
 

%([ DONT COMPILE FOR VERSION 1 ])%

	TRACE ( 'PUTIDX');

	%([ SET UP THE ADDRESS OF THE USER'S DATA RECORD ])%

	USERRECORDPTR = .RAB [ RABRBF ];

	%([ SET UP THE ARGUMENTS FOR THE PUT INDEXED OPERATION ])%

	CALLSETPUT ( LCT ( RECDESC ) );

	%([ SET OUR BUCKET DESCRIPTOR TO BE NULL ])%

	SETNULLBD ( BKTDESC );

	%([***** LOCK THE ENTIRE INDEX DURING OUR OPERATION ***** ])%

	IF LOCKING
	THEN
		BEGIN
		IF LOCKINDEX (ENQBLK, ENQEXC) IS FALSE
		THEN  USERERROR ( ER$EDQ )
		END; %(OF IF LOCKING)%

	%([ BEFORE WE INSERT THE RECORD, WE MUST CHECK TO MAKE
	   SURE THAT THIS KEY HAS AN INDEX STRUCTURE ])%

	IF NOINDEXFLAG ISON
	THEN %(WE MUST CREATE AN INDEX ROOT AND A FIRST DATA BUCKET )%
		BEGIN
		CALLMAKIDX;			! MAKE THE INDEX

		%([ WE MUST NOW CHECK TO SEE IF THERE STILL ISN'T
		   AN INDEX FOR THIS KEY. THIS COULD BE TRUE IF
		   A FILE WITHOUT AN INDEX WAS OPENED, BUT SOMEONE
		   ELSE CREATED AN INDEX BEFORE WE DID. ])%

		IF NOINDEXFLAG ISON
		THEN %(THERE WAS AN ERROR)%
			CALLCLEANUP ( %(BKT)%	LCT ( BKTDESC ) )
		END; %(OF IF NOINDEX FLAG IS SET)%

	%([ INSERT THE DATA RECORD INTO THE PRIMARY INDEX ])%

	IF 	CALLFOLLOWPATH (	%(REC DESC)%	LCT ( RECDESC ),
				%(BUCKET)%	LCT ( BKTDESC )) IS FALSE

	%([ IF WE COULDN'T LOCATE THE DATA BUCKET, THEN WE MUST
	   EXIT WITH FAILURE. HOWEVER, IF THE SEARCH KEY IS .GTR.
	   THAN ALL KEYS IN THE BUCKET, THEN THAT IS A REASONABLE
	   SITUATION.  ])%

	THEN CALLCLEANUP ( %(BKT)%	LCT ( BKTDESC ) );


	%([ CHECK FOR DUPLICATE RECORD ])%

	RESULT =  CALLCHKDUP (	%(REC DESC)%	LCT ( RECDESC ),
			%(BUCKET)%	LCT ( BKTDESC ) );


	%([ WE NOW HAVE LOCATED THE POSITION WHERE THE NEW RECORD
	   IS TO BE INSERTED. HOWEVER, WE MUST DETERMINE IF
	   SOME OTHER PROCESS ALREADY HAS THE BUCKET LOCKED. ])%

	IF .RESULT ISNT FALSE	%(DID CHKDUP SUCCEED?)%
	THEN IF LOCKING
		THEN
		BEGIN	%(TO LOCK THE BUCKET)%
		RESULT = LOCKBD ( BKTDESC, ENQAA, ENQEXC )	! DON'T WAIT FOR BUCKET
		END;	%(OF IF LOCKING)%


	%([ THE RECPTR FIELD IN THE RECORD-DESCRIPTOR NOW CONTAINS
	   THE ADDRESS WHERE WE WANT TO WRITE OUR NEW RECORD. ])%

	IF .RESULT ISNT FALSE %(IF CHKDUP SAID WE CAN GO ON)%
	THEN
		RESULT = CALLINSRTUDR (	%(REC-DESC)%	LCT ( RECDESC ),
				%(REC)%		LPT ( USERRECORDPTR ),
				%(BUCKET)%	LCT ( BKTDESC ),
				%(EXTRA-1)%	LCT ( SPLITBD1 ),
				%(EXTRA-2)%	LCT ( SPLITBD2 ) );

	%([ IF EITHER CHKDUP, LOCKIT, OR INSRTUDR RETURNED AN ERROR, WE
	   MUST CLEAN UP OUR OPERATIONS. ])%

	IF .RESULT IS FALSE THEN
		CALLCLEANUP (	%(BUCKET)%	LCT ( BKTDESC ));

 	%([ WE MUST NOW SAVE THE ADDRESS WITHIN THE DATA BUCKET OF THE
 	   THE RECORD WE JUST INSERTED ])%

	ADDRESSOFRECORD = .RECDESC [ RDRECPTR ];



	%([** IF IT IS SEQ $PUT AND A BUCKET SPLIT(ONE WAY) OCCURRED **])%
	%([** WITH R-NEW GOING TO A NEW BUCKET, THEN THE ORIG BKT WAS **])%
	%([** FLUSHED IN INSRTUDR SO WE MUST MAKE THE SPLIT BUCKET   **])%
	%([** THE CURRENT BKT...				     **])%

	IF SEQADR		! SEQ ACCESS
	THEN
		IF FLUSHORIGBD ( RECDESC ) ISON
		THEN
			BEGIN
			MOVEBKTDESC ( %(FROM)% SPLITBD1,
				      %(TO)% BKTDESC );
			CLRFLAG ( RECDESC [ RDSTATUS ], RDFLGNEWINNEW );
			END;



	%([ AT THIS POINT, BOTH THE DATA BUCKET AND THE INDEX
	   ARE STILL LOCKED. WE MUST UPDATE THE INDEX STRUCTURE
	   (IF NECESSARY) AND THEN UNLOCK ALL THESE BUCKETS ])%

	IF IDXUPDATEFLAG ( RECDESC ) ISON
	THEN %(WE MUST UPDATE THE INDEX)%

		BEGIN
		IF 	CALLIDXUPDATE (	%(REC DESC)%	LCT ( RECDESC ),
				%(BUCKET)%	LCT ( BKTDESC ),
				%(EXTRA-1)%	LCT ( SPLITBD1 ),
				%(EXTRA-2)%	LCT ( SPLITBD2 ) )  IS FALSE
		THEN SETIDXERRORFLAG ( RECDESC )		! REMEMBER THE ERROR
		END; %(OF IF WE MUST UPDATE THE INDEX)%


	%([ MOVE THE RFA OF THIS RECORD INTO THE RRV ADDRESS 
	   IN THE RECORD DESCRIPTOR PACKET, AND INTO THE CURRENT
	   RP ADDRESS IN THE RST (SO IT CAN BE RETURNED TO THE
	   USER ON RETURN FROM PUTIDX) ])%

	RST [ RSTDATARFA ] = (SECRECDESC [ RDRRV ] = .RECDESC [ RDRFA ] );	! MOVE RFA TO RRV
	SECRECDESC [ RDSTATUS ] = .RECDESC [ RDSTATUS ];	! SAVE STATUS BITS

	%([ LOOP OVER ALL KEY DESCRIPTORS ])%

	KEYOFREFERENCE = REFPRIMARY;			! INITIALIZE TO 0
	WHILE ( KDB = .KDB [ KDBNXT ] ) ISNT ZERO
	DO %(THIS LOOP)%

		BEGIN

		%([ BUMP THE CURRENT KEY OF REFERENCE ])%

		INC ( KEYOFREFERENCE, 1 );

		%([ MAKE SURE RECORD IS LONG ENOUGH TO CONTAIN 
		   THIS SECONDARY KEY VALUE ])%

		IF .RAB [ RABRSZ ] GEQ .KDB [ KDBMINRSZ ]
				%(AND)%
		   %(THIS KEY IS NOT THE NULL KEY VALUE)%
		THEN
			BEGIN %(TO INSERT RECORD INTO THIS SEC INDEX)%

			RTRACE (%STRING('	INSERTING RECORD INTO SEC IDX',%CHAR(13),%CHAR(10)));

			%([ INSERT THIS RECORD IN THE SECONDARY INDEX ])%

			IF 	CALLPUTSIDR ( LCT ( SECRECDESC ) )  IS FALSE
			THEN
				CALLREMOVRECORD (	LCT ( RECDESC ),
							LCT ( BKTDESC ) );

			%([ WE MUST NOW MOVE THE FLAGS FROM THIS RECORD
			   DESCRIPTOR INTO THE PRIMARY RECORD DESCRIPTOR.
			   THUS, IF WE SAW EITHER AN INDEX ERROR OR A
			   DUPLICATE SECONDARY, WE MUST REMEMBER IT. ])%

			SETFLAG ( RECDESC [ RDSTATUS ], .SECRECDESC [ RDSTATUS ] )

			END %(OF RECORD IS TO BE INSERTED)%

		END; %(OF WHILE KDB ISNT ZERO)%


	%([ RECORD HAS BEEN INSERTED INTO ALL INDEXES ])%

	RTRACE (%STRING('	RECORD HAS BEEN INSERTED',%CHAR(13),%CHAR(10)));

	%([ UPDATE NRP IF THIS WAS A $PUT SEQUENTIAL ])%

	KDB = .FST [ FSTKDB ];				! SET UP FOR PRIM KEY
	RECDESC [ RDRECPTR ] = .ADDRESSOFRECORD;	! RESTORE LOC OF RECORD



%([**	UPDATE NRP IF THIS WAS A $PUT SEQ		**])%

	IF SEQADR 	! SEQUENTIAL ACCESS
	THEN
		CALLSETNRP ( LCT ( RECDESC ), LCT ( BKTDESC ) );



	%([ CHECK FOR DUPLICATE RECORDS ])%

	IF SAMEKEYFLAG ( RECDESC ) ISON THEN USRSTS = SU$DUP;

	%([ CHECK FOR INDEX UPDATE ERRORS ])%

	IF IDXERRORFLAG ( RECDESC ) ISON THEN USRSTS = SU$IDX;

	%([ GIVE THE DATA BUCKET BACK (IT HAS ALREADY BEEN WRITTEN TO THE FILE ])%

	CALLPUTBKT (	%(NO UPDATE)%	PCI ( FALSE ),
			%(BKT)%		LCT ( BKTDESC ) );


	%([ UNLOCK THE INDEX STRUCTURE OF THE FILE SO OTHER PROCESSES
	   CAN TRAVERSE THE INDEX. ])%

	IF LOCKING THEN UNLOCKINDEX;

	%([ RETURN TO THE $PUT DISPATCHER ])%

	RETURN
%FI
 END; %(OF PUTIDX)%



! SETPUT
! ======

! ROUTINE TO SET UP THE RECORD DESCRIPTOR PACKET FOR A
!	$PUT TO AN INDEXED FILE. THIS ROUTINE PERFORMS
!	THE FOLLOWING OPERATIONS:
!
!	1.	SET UP THE KEY DESCRIPTOR BLOCK POINTER
!	2.	CHECK THE LAST USER KEY IF THIS IS A $PUT SEQ
!	3.	CHECK THE USER RECORD SIZE
!	4.	COMPUTE THE WORD SIZE (RSZW) OF THE RECORD AND STORE IT
!	5.	MOVE THE KEY INTO A TEMPORARY BUFFER
!	6.	CHECK FOR OUT-OF-SEQUENCE KEY STRING

! INPUT:
!	RECDESC		=	RECORD DESCRIPTOR PACKET

! OUTPUT:
!	<NO STATUS RETURNED>

! FIELDS IN THE RECORD DESC. WHICH ARE MODIFIED:
!	USRSIZE		=	SIZE OF USER KEY STRING
!	USERPTR		=	ADDRESS OF PRIMARY KEY STRING

! ROUTINES CALLED:
!	CKEYUI

GLOBAL ROUTINE SETPUT ( RECDESC ) =
 BEGIN

%IF INDX %THEN
 	ARGUMENT	(RECDESC,BASEADD);
 
 MAP
	RECDESC:	POINTER;
 LOCAL
	DATARECORDPTR:	FORMAT,		! ADDR OF USER DATA RECORD
	CBKDPTR:	POINTER,	! PTR TO CURRENT BUCKET DESC
	LASTKEYBUFF;	
 
 REGISTER
	MAXRECORDSIZE,			! MAX SIZE OF USER DATA RECORD
	TEMPAC	,
	RECORDSIZE;				! CURRENT SIZE OF USER DATA RECORD
 

%([ ***DONT COMPILE FOR VERSION 1 ***])%

	TRACE ( 'SETPUT' );

	%([ SET UP THE KDB POINTER FOR PRIMARY KEY ])%

	KDB = .FST [ FSTKDB ];

	%([ RELEASE THE CURRENT BUCKET, IF ANY ])%

	RELEASCURENTBKT;

	%([ CHECK IF USER'S RECORD IS TOO BIG FOR A BUCKET ])%

	MAXRECORDSIZE = ( .KDB [ KDBDBKZ ] ^ B2W );	! SIZE OF BUCKET
	MAXRECORDSIZE = .MAXRECORDSIZE - BHHDRSIZE - .KDB [ KDBHSZ ];
	RECORDSIZE = ( RST [ RSTRSZ ] =  .RAB [ RABRSZ ] );	! GET RECORD SIZE

	IF ( RST [ RSTRSZW ] = ( RECDESC [ RDLENGTH ] =  SIZEINWORDS (  .RECORDSIZE, .FST [ FSTBSZ  ] ) ) )
		GTR .MAXRECORDSIZE THEN USERERROR ( ER$RSZ );

	%([ CHECK THAT THE RECORD CONTAINS THE PRIMARY KEY ])%

	IF .RECORDSIZE LSS .KDB [ KDBMINRSZ ] 
	THEN %(THE PRIMARY KEY IS NOT IN THE RECORD)%
		USERERROR ( ER$RSZ );

	%([ WE WILL NOW MOVE THE PRIMARY KEY TO A TEMPORARY
	   BUFFER TO SAVE TIME ])%

	DATARECORDPTR = .RAB [ RABRBF ];

	CALLMOVEKEY (	%(FROM)%	LPT ( DATARECORDPTR ),
			%(TO)%	GCT ( TBUFFER ) );

	%([ SET UP THE RECORD DESCRIPTOR TO INDICATE THAT THE
	   KEY STRING RESIDES IN OUR TEMPORARY BUFFER ])%

	%([ ***NEXT INSTR. ASSUMES THAT RDFLAGS AND RDSTATUS ARE
	   IN WORD 0 ********* ])%

	RECDESC [ WHOLEWORD ] = ZERO;
	RECDESC [ RDUSERPTR ] = TBUFFER;
	RECDESC [ RDUSERSIZE ] = .KDB [ KDBKSZ ];	! USE FULL KEY SIZE

	%([ IF THIS IS A SEQUENTIAL $PUT OPERATION, WE MUST CHECK
	   THAT THE CURRENT KEY IS HIGHER THAN THE LAST KEY USED ])%

	IF SEQADR
	THEN
		BEGIN

		%([ IF THE LAST OPERATION WAS ALSO A $PUT SEQUENTIAL,
		   THEN WE MUST COMPARE THE KEYS ])%

		IF .RST [ RSTLASTOPER ] IS C$PUT
		THEN IF ( CHKFLAG ( RST [ RSTFLAGS ], FLGLASTSEQ ) ISON )
		THEN
			BEGIN
			LASTKEYBUFF = .RST [ RSTKEYBUFF ];	! GET ADDR OF LAST KEY
			IF CALLCKEYKK (	%(REC DESC)%	BPT ( RECDESC ),
					%(LAST KEY)%	LPT ( LASTKEYBUFF ) ) IS TRUE

			%([ IS THIS KEY GTR THAN THE LAST ONE? ])%

			%([ NOTE THAT CKEYKK RETURNS TRUE ONLY IF THE
			   SEARCH KEY IS LEQ THE TARGET KEY, THUS WE
			   ACTUALLY WANT THIS ROUTINE TO FAIL FOR OUR
			   KEY COMPARISON ])%

			THEN USERERROR ( ER$SEQ )
			END;	%(OF IF LAST OPER WAS PUT AND LASTSEQ ISON)%

		%([ INDICATE THAT THIS OPERATION IS SEQUENTIAL ])%

		SETFLAG ( RST [ RSTFLAGS ], FLGLASTSEQ )

		END %(OF IF THIS WAS A PUT SEQUENTAIL)%

	ELSE	%(THIS IS A RANDOM PUT...CLEAR THE SEQ BIT)%

		CLRFLAG ( RST [ RSTFLAGS ], FLGLASTSEQ );

	GOODRETURN
%FI
 END; %(OF SETPUT)%

END
ELUDOM