Google
 

Trailing-Edge - PDP-10 Archives - 704rmsf2 - 10,7/rms10/rmssrc/rmsupd.b36
There are 11 other files named rmsupd.b36 in the archive. Click here to see a list.
%TITLE 'U P D A T E  -- $UPDATE processor'
!<BLF/SYNONYM ISON = EQL -1>
!<BLF/SYNONYM ISNT = NEQ>
!<BLF/SYNONYM IS = EQL>
!<BLF/LOWERCASE_USER>
!<BLF/UPPERCASE_KEY>
MODULE update (IDENT = 'V1.0'
		) =
BEGIN

GLOBAL BIND
    updtv = 1^24 + 0^18 + 7;			!EDIT DATE: 26-JAN-82

!++
!	FUNCTION:	THIS MODULE CONTAINS ROUTINES WHICH PROCESS
!			THE $UPDATE MACRO IN RMS-20.
!
!	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, 1982 BY DIGITAL EQUIPMENT CORPORATION
!
!
!	AUTHOR:	S. BLOUNT/RL
!
!
!	**********	TABLE OF CONTENTS	**************
!
!
!
!
!		ROUTINE			FUNCTION
!		=======			========
!
!		$UPDATE			PROCESSOR FOR $UPDATE MACRO
!
!		UPDSQR			$UPDATE FOR SEQUENTIAL/RELATIVE FILES
!
!		UPD			$UPDATE FOR INDEXED FILES
!
!		DOUPD			PERFORM THE ACTUAL UPDATE FOR INDEXED FIES
!
!		UPDUDR			RE-WRITE THE DATA IN THE CURRENT RECORD FOR INDEXED FILES
!
!-
%SBTTL 'REVISION HISTORY'
!+
!	REVISION HISTORY:
!
!	EDIT	WHO	DATE		PURPOSE
!	====	===	====		=======
!
!	1	JK	21-JUL-76	REQUIRE LIMITS.REQ
!	2	SB	29-NOV-76	ADD UPDATE FOR INDEXED FILES
!	3	SB	23-DEC-76	CHANGE ER$RSD TO ER$RSZ
!	4	SB	8-MAR-77	MAKE FAILURE FOR DELSIDR BE OK
!	5	SB	14-MAR-77	SET UP RRV FOR PUTSIDR FOR LENGTH CHANGES
!
!	*************************************************
!	*						*
!	*		NEW REVISION HISTORY		*
!	*						*
!	*************************************************
!
!	****** Release of RMS 1.0 *****
!
!	PRODUCT	MODULE	 SPR
!	 EDIT	 EDIT	 QAR		DESCRIPTION
!	======	======	=====		===========
!	57	6	20-17231	Free bucket after FOLLOWPATH call
!					in DOUPDIDX. (RLUSK, 26-Jan-82)
!
!****************** Start RMS-10 V1.1 *********************
!********************* TOPS-10 ONLY ***********************
!
!PRODUCT	MODULE	 SPR
! EDIT	 EDIT	 QAR		DESCRIPTION
!======	======	=====		===========
!
! 100	  7	Dev		Make declarations for routine names
!				be EXTERNAL ROUTINE so RMS will compile 
!				under BLISS V4 (RMT, 10/22/86).
!
!		***** END OF REVISION HISTORY *****
!
!
!
!
!-
%SBTTL 'EXTERNAL DECLARATIONS'
!
!   EXTERNAL DECLARATIONS
!

EXTERNAL ROUTINE
    crash,
    checkrp,
    dump,
    numbertorfa,
    gtbyte,
    lockit,
    moverec,
    rsetup,
!
!  ERROR CODES USED WITHIN THIS MODULE
!
!    msgpgone,					! FILE PAGE GONE
    msgcantgethere,				! RMS HAS REALLY SCREWED UP
    msgflags,					! BAD FLAG VALUES
    msgfailure,					! A ROUTINE FAILED
    msgbkt;					! BAD BUCKET DESCRIPTOR

!    msgunlocked;				! RECORD UNLOCKED, BUT SHOULD BE LOCKED

REQUIRE 'rmsreq';

extdeclarations;
%SBTTL '$UPDATE -- processor for $UPDATE macro'
! $UPDATE
! =======
!
! PROCESSOR FOR $UPDATE MACRO
! 	THE $UPDATE MACRO ALWAYS OPERATES ON THE CURRENT RECORD.
!
! FORMAT OF $UPDATE MACRO:
!
!		$UPDATE		<RAB-ADDRESS> [,<ERROR-ADDRESS>]
!
! RAB FIELDS USED AS INPUT TO $UPDATE:
!
!	ISI		INTERNAL STREAM IDENTIFIER
!	RBF		ADDRESS OF USER RECORD BUFFER
!	ROP		RECORD OPTIONS
!		<NO OPTIONS CURRENTLY USED>
!	RSZ		SIZE OF UPDATED RECORD
!
! RAB FIELDS RETURNED TO USER BY $UPDATE:
!
!	STS		STATUS OF CURRENT OPERATION
!	STV		ADDITIONAL STATUS INFORMATION
!
! INPUT:
!	BLOCK		ADDRESS OF USER RECORD BLOCK
!	ERRORRETURN	ADDRESS OF USER ERROR ROUTINE
!
! OUTPUT:
!	<NO STATUS VALUE RETURNED>
!
! ROUTINES CALLED:
!	RSETUP
!	CHECKRP
!	UPDSQR
!	UPDIDX
!

GLOBAL ROUTINE $update (BLOCK, errorreturn) : NOVALUE =
    BEGIN
    argument (BLOCK, baseadd);
    argument (errorreturn, baseadd);

    EXTERNAL ROUTINE
		updsqr,					! $UPDATE FOR SEQUENTIAL AND RELATIVE FILES
		updidx;					! $UPDATE FOR INDEXED FILES
	
	    rmsentry ($update);
	    !
	    !   FETCH THE USER'S RAB AND HIS ERROR ROUTINE ADDRESS
	    !
	    rab = .BLOCK;				! FETCH RAB ADR
	    erradr = .errorreturn;			! AND USER ERROR ADDRESS
	    !
	    !   CHECK FOR STANDARD ERRORS AND $UPDATE ACCESS TO FILE
	    !
	    callrsetup (pci (axupd));			! SET UP THE WORLD
	    !
	    !   INSURE THAT FILE IS POSITIONED AND LOCKED
	    !
	    callcheckrp;
	    !
	    !   CHECK THAT THE USER'S BUFFER IS VALID, AND THAT
	    !   HE IS NOT TRYING TO CHANGE THE SIZE OF THE RECORD.
	    !
	
	    IF .rab [rabrbf] LEQ minuserbuff		! CHECK USER BUFFER
	    THEN
		usererror (er$rbf);
	
	    IF .rab [rabrsz] isnt .rst [rstrsz]		! CHECK RECORD SIZES
	    THEN
		usererror (er$rsz);
	
	    !
	    !   DISPATCH TO THE CORRECT ROUTINE FOR THIS ORGANIZATION
	    !
	
	    CASE fileorg FROM 0 TO 3 OF
		SET
	
		[0] : 					! ILLEGAL FOR ASCII FILES
		    usererror (er$iop);
	
		[1] : 					! SEQUENTIAL
		    callupdsqr;
	
		[2] : 					! RELATIVE
		    callupdsqr;
	
	%IF indx
	%THEN
	
		[3] : 					! INDEXED
		    callupdidx;
	%FI
	
		TES;					! END OF CASE FILEORG
	
	    !
	    !   RETURN HERE ONLY IF EVERYTHING WAS OK
	    !
	    setsuccess;
	    userexit
	    END;					! OF $UPDATE
	
	
%SBTTL 'UPDSQR -- sequential/relative $UPDATE'
	! UPDSQR
	! ======
	! ROUTINE TO PROCESS THE $UPDATE MACRO FOR SEQUENTIAL AND
	!	RELATIVE FILES. THIS ROUTINE MUST DO THE FOLLOWING:
	!
	!		1) DETERMINE THE BYTE ADDRESS OF THE CURRENT RECORD
	!		2) MAKE SURE THE CORRECT PAGE IS MAPPED IN THE BUFFER
	!		3) CHANGE THE DATA IN THE RECORD
	!
	! INPUT:
	!	<NONE>
	!
	! OUTPUT:
	!	<NO STATUS RETURNED>
	!
	! RST FIELDS WHICH ARE USED:
	!
	!	CBKD		CURRENT BUCKET DESCRIPTOR
	!	PAGPTR		ABSOLUTE POINTER TO CURRENT RECORD
	!	RSZ		SIZE IN BYTES OF CURRENT RECORD
	!	RSZW		SIZE IN WORDS OF CURRENT RECORD
	!
	! NOTES:
	!
	!	1.	IF THERE IS ANY ERROR DURING THIS ROUTINE, IT
	!		WILL EXIT DIRECTLY TO THE USER; IT WILL NOT
	!		RETURN TO THE $UPDATE DISPATCHER.
	!
	!	2.	CURRENTLY, THIS ROUTINE ASSUMES THAT THE RECORD
	!		LENGTH CANNOT BE CHANGED ON EITHER FILE ORGANIZATION.
	!		IF THIS RESTRICTION IS LIFTED FOR RELATIVE FILES,
	!		THEN THE CHECK MUST BE MADE ONLY FOR SEQUENTIAL FILES.
	
	GLOBAL ROUTINE updsqr : NOVALUE =
	    BEGIN
	
	    LOCAL
		temp,					! TEMPORARY LOCAL VARIABLE
		crp,					! CURRENT RECORD POINTER VALUE
		filepointer,				! POINTER TO CURRENT RECORD IN FILE BUFFER
		userpointer,				! POINTER TO USER'S DATA
		bytecount,				! SIZE OF THE RECORD IN BYTES
		bytesize,				! BYTE SIZE OF THE FILE
		bytenum;				! BYTE NUMBER OF THE START OF THE TARGET RECORD
	
	    TRACE ('UPDSQR');
	    !
	    !   DETERMINE BYTE-ADDRESS OF START OF RECORD
	    !
	    bytenum = (crp = .rst [rstdatarfa]);	! ASSUME SEQ FILE
	    !
	    !   FOR RELATIVE FILES, WE MUST CONVERT THE RFA (RECORD #)
	    !   INTO THE ACTUAL ADDRESS OF THE TARGET RECORD.
	    !
	
	    IF relfile
	    THEN
		BEGIN					!  TO CONVERT CRP TO A RECORD ADDRESS
		bytenum = callnumbertorfa (lci (crp));
	
		IF .bytenum is false THEN error (er$rfa)
	
		END;					!  OF IF RELFILE
	
	    !
	    !   "BYTENUM" NOW CONTAINS THE ADDRESS
	    !   OF THE TARGET RECORD.  WE MUST MAP
	    !   THE RECORD AND RE-WRITE THE DATA PORTION OF IT
	    !
	    bytenum = .bytenum + headersize;		! IGNORE HEADER
	
	%IF dbug
	%THEN
	    lookat ('	BYTE # TO UPDATE: ', bytenum);	! **DEBUG**
	%FI
	
	    !
	    !   MAP THE RECORD (IT MAY HAVE SPANNED PAGES )
	    !
	    callgtbyte (lci (bytenum), 			! BYTE NUMBER
		pci (false));				! DON'T CHECK
	    !
	    !   REWRITE THE RECORD'S DATA
	    !
	    bytecount = .rst [rstrsz];			! FETCH THE SIZE IN BYTES
	    bytesize = .fst [fstbsz];			! AND THE BYTE SIZE
	    filepointer = .rst [rstpagptr];		! WHERE RECORD IS TO GO
	    userpointer = .rab [rabrbf];		! WHERE RECORD IS TO COME FROM
	    !
	    !   WE ARE NOW READY TO MOVE THE UPDATED RECORD INTO THE FILE.
	    !   IF THE RECORD DOESN'T SPAN A FILE PAGE, WE CAN BLT IT
	    !   DIRECTLY. IF NOT, WE MUST CALL MOVEREC TO DO ALL PAGE
	    !   TURNING AND ASSORTED MACHINATIONS.
	    !
	
	    IF (.filepointer AND ofsetmask) + .rst [rstrszw] LSS pagesize
	    THEN 					! WE CAN MOVE THE RECORD DIRECTLY
		BEGIN
		setbfdupd (cbd [bkdbfdadr]);		!INDIC FILE PAGE UPD
		movewords (.userpointer, 		! FROM
		    .filepointer, 			! TO
		    .rst [rstrszw])			! SIZE
		END
	    ELSE
		callmoverec (lci (filepointer), 	! TO
		    lci (userpointer), 			! FROM
		    pci (true), 			! FLAG
		    lci (bytecount), 			! COUNT
		    lci (bytesize));			! SIZE
	
	    !
	    !   UNLOCK THE RECORD IF IT WAS LOCKED
	    !
	
	    IF datalocked THEN unlock (crp);		! UNLOCK THE RECORD
	
	    RETURN
	    END;					! OF UPDSQR
	
	
%SBTTL 'UPDIDX -- $UPDATE for indexed files'
	! UPDIDX
	! ======
	! ROUTINE TO PERFORM THE $UPDATE OPERATION FOR INDEXED FILES.
	!	IN ORDER TO PROCESS AN $UPDATE FOR INDEXED FILES, THE
	!	FOLLOWING LOGICAL OPERATIONS MUST BE DONE:
	!
	!		1 )	LOCK THE FILE (IF NECESSARY)
	!		2 )	DELETE THE RECORD POINTERS FOR ALL
	!			SECONDARY INDICES FOR WHICH THE KEY CHANGED
	!		3 )	UPDATE THE USER DATA RECORD
	!		4 )	INSERT NEW RECORD POINTERS IN THE INDEX
	!			FOR EACH NEW SECONDARY KEY VALUE.
	!
	! INPUT:
	!	<NONE>
	!
	! OUTPUT:
	!	<NO STATUS RETURNED>
	!
	! ROUTINES CALLED:
	!	DOUPDIDX
	!	PUTBKT
	!
	! NOTES:
	!
	!	1.	THIS ROUTINE WILL NOT RETURN TO THE CALLER (UPDIDX)
	!		IF AN ERROR WAS ENCOUNTERED; IT WILL EXIT DIRECTLY
	!		TO THE USER.
	!
	!	2.	THIS CODE CURRENTLY DOES NOT SUPPORT THE MODIFICATION
	!		OF THE RECORD LENGTH ON AN $UPDATE. FOR MORE INFO
	!		ON HOW THIS MIGHT BE DONE, SEE THE HEADING OF "UPDUDR".
	!
	!	3.	IF ANY ERROR OCCURS DURING THE PROCESSING OF AN $UPDATE,
	!		THE PROCESSING IS TERMINATED AND CONTROL IS RETURNED
	!		TO THE USER. IN SUCH A CASE, THE STATE OF THE USER'S
	!		DATA MAY BE UNDEFINED (PROPERLY STRUCTURED, BUT NOT
	!		LOGICALLY CONSISTENT). FOR EXAMPLE, ONE OF THE SIDR
	!		POINTERS MAY HAVE ALREADY BEEN DELETED; THUS, THE
	!		USER MAY HAVE LOST AN ACCESS PATH TO THE FILE.
	
	GLOBAL ROUTINE updidx : NOVALUE =
	    BEGIN
	
	%IF indx
	%THEN
	
	    LOCAL
		recdesc : formats [rdsize],		! USE LOCAL RECORD DESCRIPTOR
		databd : formats [bdsize],		! AND BUCKET DESCRIPTOR
		savedstatus;				! STATUS RETURNED TO US
	
	    EXTERNAL ROUTINE
	putbkt,					! RELEASE A BUCKET
	doupdidx;				! DO THE DIRTY WORK

    TRACE ('UPDIDX');

    !+
    !   FIRST, WE WILL SET UP THE RECORD DESCRIPTOR PACKET.
    !   THE FOLLOWING FIELDS MUST BE INITIALIZED:
    !
    !		FLAGS	= NULL
    !		STATUS	= NULL
    !		RECPTR	= ADDRESS OF CURRENT RECORD
    !-

    recdesc [rdflags] = zero;			! CLEAR FLAGS
    recdesc [rdstatus] = zero;			! CLEAR STATUS
    recdesc [rdrecptr] = .rst [rstpagptr];

    !+
    !   FETCH THE CURRENT BUCKET DESCRIPTOR FROM THE RST.
    !   NOTE THAT THERE MUST BE A CURRENT BUCKET OR SOMETHING
    !   HAS BEEN MESSED UP.
    !-

    fetchcurrentbkt (databd);

    IF nullbd (databd) THEN rmsbug (msgbkt);

    !
    !   PERFORM THE ACTUAL WORK OF THE $UPDATE
    !   AND  SAVE THE RESULTS THAT WE GOT
    !
    savedstatus = calldoupdidx (lct (recdesc), 	! RD
	lct (databd));				! BKT
    !
    !   RELEASE THE CURRENT BUCKET (NOTE THAT WE ARE
    !   RELEASING THE BUCKET AS IT IS STORED IN THE RST
    !   BECAUSE THIS WILL ALSO SET THE RST CURRENT BUCKET
    !   TO BE NULL)
    !
    releascurentbkt;
    !
    !   UNLOCK THE FILE IF ONE OF THE ROUTINES WE CALLED LOCKED IT
    !

    IF indexlocked THEN unlockindex;

    IF .savedstatus is false THEN usexiterr;	! HAD AN ERROR

    !
    !   EVERYTHING WENT OK...CHECK FOR INDEX ERROR OR DUPLICATES
    !

    IF samekeyflag (recdesc) ison THEN usrsts = su$dup;

    IF idxerrorflag (recdesc) ison THEN usrsts = su$idx;

    RETURN
%FI

    END;					! OF UPDIDX

%SBTTL 'DOUPIDX -- perform actual indexed update'
! DOUPDIDX
! ========
! ROUTINE TO ACTUALLY PERFORM THE $UPDATE MACRO PROCESSING FOR
!	AN INDEXED FILE. THIS ROUTINE MUST PERFORM THE FOLLOWING OPERATINS:
!
!		1 )	CHECK FOR CHANGES IN ALL KEYS
!		2 )	DELETE OLD KEY VALUES FROM SECONDARY INDEX
!		3 )	CAL UPDUDR TO UPDATE AND WRITE OUT THE
!			PRIMARY DATA RECORD
!		4 )	ADD NEW SIDR ENTRIES FOR THE NEW KEY VALUES
!
! INPUT:
!	RECDESC		RECORD DESCRIPTOR PACKET
!		RECPTR		ADDRESS OF CURRENT RECORD IN BUFFER
!		FLAGS		PROCESSING FLAGS
!			RDFLGHORIZOK
!
!	DATABD		BUCKET DESCRIPTOR OF CURRENT BUCKET
!		<THIS BUCKET DESCRIPTOR MUST NOT BE EMPTY>
!
!
! OUTPUT:
!	TRUE:	OK...$UPDATE WAS PERFORMED
!	FALSE:	ERROR
!		FILE IS FULL (FOR RECORD LENGTH MODIFICATION)
!
! NOTES:
!
!	1.	ON ERROR, THE ERROR CODE WILL BE PUT INTO USRSTS.
!
!	2.	THIS ROUTINE NEVER RELEASES THE BUCKET WHICH IS
!		PASSED TO IT. THIS MUST DO DONE BY "UPDIDX".
!
!	3.	ON ANY ERROR CONDITION, THE STATE OF THE USER'S FILE
!		IS INDETERMINATE. THIS DOES NOT MEAN THAT IT IS
!		"SCREWED UP" IN SOME PHYSICAL WAY, BUT SIMPLY THAT
!		SOME ACCESS PATHS (SIDR RECORDS) MAY HAVE BEEN LOST,ETC.
!
!	4.	THIS ROUTINE WILL NOT ALLOW THE SIZE OF THE RECORD TO
!		BE CHANGED ON AN $UPDATE. HOWEVER, COMMENTS ARE MADE
!		THRUOUT THIS ROUTINE WHICH EXPLAINS WHAT HAS TO BE
!		DONE IF THIS FACILITY IS EVER IMPLEMENTED. ALL
!		COMMENTS ENCLOSED IN ANGLE BRACKETS ( "<,>") REFER
!		TO THIS FACILITY.
!
!	5.	THIS ROUTINE USES THE "FLGDIDCHANGE" BIT IN THE
!		KDB TO KEEP TRACK OF WHETHER THE CORRESPONDING
!		SECONDARY KEY VALUE CHANGED. THIS IS NECESSARY
!		BECAUSE ONCE THE DATA RECORD IS WRITTEN OUT,
!		THE OLD VALUE OF THE KEY IS (OBVIOUSLY) NOT
!		AVAILABLE.

GLOBAL ROUTINE doupdidx (recdesc, databd) =
    BEGIN

%IF indx
%THEN
    argument (recdesc, baseadd);		! RECORD DESC PACKET
    argument (databd, baseadd);			! CURRENT BUCKET DESC

    MAP
	recdesc : pointer,
	databd : pointer;

    LOCAL
	tempbd : formats [bdsize],
	temprd : formats [rdsize],
	oldrecordptr : pointer,			! PTR TO CURRENT RECORD IN FILE
	newrecordptr : pointer,			! PTR TO USER'S NEW UPDATED RECORD
	keyofreference,				! CURRENT KEY NUMBER
	newrecordsize,				! SIZE OF NEW RECORD
	oldrecordsize,				! SIZE OF OLD RECORD
	maxrecordsize,				! MAX SIZE OF A NEW RECORD
	ptrtodata : pointer;			! PTR TO THE DATA PORTION OF CURRENT RECORD

    EXTERNAL ROUTINE
	delsidr,				! DELETE A SECONDARY KEY
	ckeyuu,					! COMPARE USER KEY TO USER KEY
	tbuffer,				! TEMP BUFFER FOR KEY STORAGE
	updudr,					! UPDATE PRIMARY DATA RECORD
	movekey,				! MOVE A KEY STRING
	putsidr,				! INSERT A NEW SECONDARY KEY
	lockit;					! LOCK THE FILE

    TRACE ('DOUPDIDX');
    !
    !   FIRST, SET UP THE PRIMARY KDB ADDRESS
    !
    kdb = .fst [fstkdb];
    !
    !   SET UP POINTERS TO BOTH THE OLD AND THE NEW RECORDS,
    !   AND <GET THE SIZE OF BOTH RECORDS>
    !
    oldrecordptr = .recdesc [rdrecptr];		! OLD RECORD IN FILE
    ptrtodata = .oldrecordptr + .kdb [kdbhsz];
    newrecordptr = .rab [rabrbf];		! USER'S NEW RECORD
    oldrecordsize = .rst [rstrsz];		! SIZE OF OLD RECORD
    newrecordsize = .rab [rabrsz];		! SIZE OF NEW RECORD
    !
    !   <NOW, LET'S PERFORM SOME CHECKS ON THE SIZE OF THE
    !   NEW RECORD>
    !
!  	MAXRECORDSIZE = .KDB [ KDBDBKZ ] ^ B2W;	! RECORD CANT BE BIGGER THAN THIS
!  	DEC ( MAXRECORDSIZE, BHHDRSIZE + .KDB [ KDBHSZ ] );
!  	IF	( .NEWRECORDSIZE LSS .KDB [ KDBMINRSZ ] )
!  			OR
!  		( SIZEINWORDS ( .NEWRECORDSIZE, .FST [ FSTBSZ ]) GTR .MAXRECORDSIZE)
!  	THEN	%THE RECORD IS EITHER TOO SHORT OR TOO LONG%
!
! 		RETURNSTATUS ( ER$RSZ );
    !
    !   GET THE RRV ADDRESS OF THE CURRENT RECORD
    !
    recdesc [rdrrv] = .oldrecordptr [drrrvaddress];

    !+
    !   WE MUST NOW CYCLE THRU EACH KEY TO SEE IF IT CHANGED.
    !   THE PRIMARY KEY CAN BE TREATED JUST LIKE A SECONDARY
    !   KEY SINCE IT HAD TO BE DEFINED WITH THE "NO CHANGE"
    !   ATTRIBUTE. IN ORDER TO SAVE TIME LATER (BECAUSE WE WON'T
    !   HAVE TO COMPARE THE KEY STRINGS TWICE), WE WILL SET A
    !   FLAG IN THE KDB WHICH WILL INDICATE WHETHER A PARTICULAR
    !   KEY VALUE CHANGED OR NOT.  THEN LATER, WE ONLY HAVE TO
    !   CHECK THE BIT INSTEAD OF COMPARING THE KEYS AGAIN.
    !-

    !
    !   SET UP THE ADDRESS OF THE DATA PORTION OF THE RECORD
    !
    recdesc [rduserptr] = .ptrtodata;
    !
    !   PROCESS THIS LOOP ONCE FOR EACH KEY
    !
    keyofreference = zero;

    UNTIL .kdb is zero DO 			! THIS LOOP
	BEGIN
	!
	!   ASSUME THE KEY DIDNT CHANGE
	!
	clrflag (kdb [kdbflags], flgdidchange);	! CLEAR THE FLAG

	!+
	!   CHECK IF THE KEY VALUE CHANGED. THIS CHECK IS
	!   DONE ONLY IF THE OLD RECORD WAS BIG ENOUGH TO
	!   HOLD THE ENTIRE KEY STRING, <AND THE NEW RECORD
	!   IS ALSO BIG ENOUGH>
	!-

	IF .oldrecordsize GEQ .kdb [kdbminrsz]	!
	    %(AND)%				!
	    %(.NEWRECORDSIZE GEQ .KDB [ KDBMINRSZ ])%	!
	THEN 					! WE CAN COMPARE THE KEYS
	    BEGIN
	    lookat ('	CHECKING KEY #: ', keyofreference);

	    IF callckeyuu (			!
		    bpt (recdesc), 		! OLD PTR
		    lpt (newrecordptr)		! NEW PTR
		) is false OR 			!
		(lssflag (recdesc) ison )
		!
		!   IF THIS KEY CHANGED, WE MUST SET THE CORRESPONDING
		!   FLAG BIT IN THE KDB. NOTE THAT THE KEY
		!   DIDNT CHANGE IF CKEYUU RETURNED TRUE WITH THE
		!   LSSFLAG NOT ON
		!
	    THEN 				! THE KEY CHANGED
		BEGIN
		rtrace ('	KEY HAS CHANGED...');
		!
		!   IS THIS KEY ALLOWED TO CHANGE?
		!

		IF chkflag (kdb [kdbflags], flgchange) is off
		THEN
		    BEGIN
		    usrstv = .keyofreference;	!INDIC WHICH KEY CHANGED
		    returnstatus (er$chg);
		    END;

		setflag (kdb [kdbflags], flgdidchange);

		!+
		!   *****
		!   IF DUPLICATES ARE NOT ALLOWED
		!   THEN CHECK FOR DUPLICATES
		!   ******
		!-

		IF chkflag (kdb [kdbflags], flgdup) is off
		THEN
		    BEGIN
		    !
		    !   MAKE A COPY OF BD
		    !
		    movebktdesc (bpt (databd), 	! FROM
			lct (tempbd));		! TO
		    !
		    !   MAKE A COPY OF RDP
		    !
		    movewords (bpt (recdesc), 	! FROM
			lct (temprd));		! TO
		    !
		    !   MOVE KEY TO TEMP BUFFER
		    !
		    callmovekey (lpt (newrecordptr), 	! FROM
			gct (tbuffer));		! TO
		    temprd [wholeword] = 0;
		    temprd [rduserptr] = tbuffer;
		    temprd [rdusersize] = .kdb [kdbksz];

		    IF callfollowpath (lct (temprd), lct (tempbd)) is false	!
		    THEN
			BEGIN
			!					       !A57
			!   Free the bucket without update	       !A57
			!					       !A57
			callputbkt (pci (false), lct (tempbd));	!A57
			RETURN false;
			END;

		    IF callchkdup (		! DUPLICATE?
			    lct (temprd), 	! RECDES
			    lct (tempbd)	! BKTD
			) is false
		    THEN
			BEGIN
			!					       !A57
			!   Free the bucket without update	       !A57
			!					       !A57
			callputbkt (pci (false), lct (tempbd));	!A57
			returnstatus (er$dup);
			END;

		    !                                                  !A57
		    !   Free the bucket without update                 !A57
		    !						       !A57
		    callputbkt (pci (false), lct (tempbd));	!A57
		    END;

		!
		!   *	END OF CHECK FOR DUPLICATES	**
		!
		END				! OF IF KEY CHANGED
	    END;				! OF IF RSZ GTR MINRSZ

	!
	!   WE HAVE FINISHED CHECKING THIS KEY..MOVE TO NEXT ONE
	!
	kdb = .kdb [kdbnxt];			! GET NEXT KDB
	inc (keyofreference, 1)			! BUMP KEY NUMBER
	END;					! OF UNTIL .KDB IS ZERO

    !+
    !   WE NOW KNOW THAT THE NEW RECORD IS OK. WE MUST
    !   REMOVE THE OLD SIDR ENTRIES FOR EACH KEY VALUE WHICH
    !   HAS CHANGED.
    !-

    kdb = .fst [fstkdb];			! RESET TO PRIMARY KEY
    kdb = .kdb [kdbnxt];			! MOVE TO FIRST SECONDARY
    !
    !   DELETE SIDR POINTERS
    !

    UNTIL .kdb is zero DO 			! THIS LOOP
	BEGIN
	keyofreference = .kdb [kdbref];

	!+
	!   NOW, IF THE KEY CHANGED, WE MUST DELETE THE OLD SIDR
	!   ENTRY. FIRST, WE WILL MOVE THE KEY VALUE OF THIS
	!   KEY INTO THE TEMPORARY KEY BUFFER (WE COULD USE THE
	!   BOTTOM HALF OF THE RST KEY BUFFER)
	!
	!   NOTE THAT IF RECORD-LENGTH MODIFICATION IS
	!   SUPPORTED, THERE MUST ALSO BE A CHECK HERE TO SEE
	!   IF THE OLD RECORD CONTAINED THE KEY VALUE, BUT THE
	!   NEW RECORD DIDNT (SINCE IN THIS CASE THE DIDCHANGE
	!   BIT WOULD NOT BE ON)
	!-

	IF (chkflag (kdb [kdbflags], flgdidchange) ison )
	THEN
	    BEGIN
	    lookat ('	DELETING KEY #', keyofreference);
	    callmovekey (lpt (ptrtodata), 	! FROM
		gct (tbuffer));			! TO
	    !
	    !   SET UP THE RECORD DESCRIPTOR PACKET
	    !
	    recdesc [rdusersize] = .kdb [kdbksz];	! USE FULL KEY SIZE
	    recdesc [rduserptr] = tbuffer;	! USE GLOBAL TBUFFER
	    !
	    !   IF THE FILE HASN'T BEEN LOCKED, WE MUST LOCK IT
	    !

	    IF locking
	    THEN

		IF NOT indexlocked
		THEN
		    BEGIN

		    IF lockindex (		!
			    enqblk, 		! WAIT
			    enqexc		! EXCLUSIVE
			) is false
		    THEN
			returnstatus (er$edq)

		    END;			! OF IF WE NEED TO LOCK THE FILE

	    !
	    !   AT THIS POINT, THE FILE IS LOCKED (IF NECESSARY)
	    !   SO WE CAN DELETE THE OLD SIDR ENTRY
	    !
	    calldelsidr (bpt (recdesc))
	    !
	    !   ***NOTE THAT IF DELSIDR FAILS, THERE IS NO
	    !   CHECK HERE FOR THAT CONDITION. THIS IS BECAUSE
	    !   AN EARLIER CRASH MAY HAVE CAUSED THE KEY TO
	    !   NOT BE CORRECTLY INSERTED INTO ALL SECONDARY
	    !   INDICES. ***
	    !
	    END;				! OF IF CHANGEFLAG ISNT FALSE

	kdb = .kdb [kdbnxt]			! GO TO NEXT KDB
	END;					! OF UNTIL .KDB IS ZERO

    !+
    !   AT THIS POINT, ALL OLD SIDR ENTRIES HAVE BEEN DELETED.
    !   ALSO, THE FILE MAY STILL BE LOCKED. WE MUST NOW PERFORM
    !   THE ACTUAL UPDATE OF THE USER DATA RECORD. WE WILL PASS
    !   A FLAG TO "UPDUDR" INDICATING WHETHER THE FILE IS CURRENTLY
    !   LOCKED OR NOT, AND THAT ROUTINE CAN THEN ALTER THE FLAG
    !   IF IT HAS TO LOCK THE FILE.
    !-

    recdesc [rdrecptr] = .oldrecordptr;		! PTR TO FILE RECORD
    kdb = .fst [fstkdb];			! START WITH PRIMARY

    IF callupdudr (				!
	    bpt (recdesc), 			! RD
	    bpt (databd)			! BKT
	) is false
	!
	!   IT IS CURRENTLY IMPOSSIBLE FOR UPDUDR TO FAIL...HERE
	!   IS THE CODE TO HANDLE AN ERROR WHEN RECORD-LENGTH
	!   MODIFICATION IS SUPPORTED:
	!
    THEN
	BEGIN
	rmsbug (msgcantgethere);		! **** REMOVE WHEN IMPLEMENTED
! 		USRSTV = ER$UDF;		! INDICATE UNDEFINED RECORD
!  		BADRETURN
	END;					! OF IF UPDUDR FAILED

    !
    !   NOW, PERFORM THIS LOOP ONCE FOR EACH SECONDARY KEY.
    !   WE MUST NOW INSERT THE NEW VALUES OF THE SECONDARY
    !   KEYS INTO THE CORRESPONDING INDEX.
    !
    kdb = .kdb [kdbnxt];			! MOVE TO FIRST SECONDARY

    UNTIL .kdb is zero DO 			! THIS LOOP
	BEGIN
	!
	!   DID THIS KEY VALUE CHANGE?
	!
	!   THE SIZE OF THE NEW RECORD MUST INCLUDE THIS KEY STRING
	!

	IF %((.NEWRECORDSIZE GEQ .KDB[ KDBMINRSZ] ))%	!
	%(AND)%					!
	    (chkflag (kdb [kdbflags], flgdidchange) ison )
	THEN 					! WE MUST INSERT THIS NEW KEY VALUE
	    BEGIN
	    !
	    !   <WE MAY NEED TO LOCK THE FILE AT THIS POINT.
	    !   THIS WOULD BE NECESSARY ONLY IF RECORD LENGTH
	    !   MODIFICATION IS SUPPORTED. THUS, IF THE OLD
	    !   RECORD LENGTH WAS NOT BIG ENOUGH FOR ANY KEYS,
	    !   AND THE PRIMARY DATA BUCKET DIDN'T SPLIT, AND
	    !   THE NEW RECORD SIZE IS BIG ENOUGH FOR A SECONDARY
	    !   KEY, THEN WE NEED TO LOCK THE FILE>
	    !

%IF dbug
%THEN

	    IF chkflag (kdb [kdbflags], flgchange) is off	!
	    THEN
		rmsbug (msgflags);

%FI

	    IF locking
	    THEN

		IF NOT indexlocked
		THEN
		    BEGIN

		    IF lockindex (		!
			    enqblk, 		! WAIT
			    enqexc		! EXCL
			) is false
		    THEN
			returnstatus (er$edq)

		    END;			! OF IF WE NEED TO LOCK THE FILE

	    !
	    !   INSERT THE NEW SECONDARY KEY VALUE
	    !
	    lookat ('	INSERTING SIDR FOR KEY: ', kdb [kdbref]);

	    IF callputsidr (bpt (recdesc)) is false
	    THEN
		BEGIN
		usrstv = er$udf;		! INDICATE UNDEFINED RECORD
		badreturn
		END;				! OF IF WE COULN'T INSERT NEW SIDR

	    END;				! OF IF .NEWRECORDSIZE GEQ MINRSZ

	!
	!   GO TO NEXT SECONDARY KEY
	!
	kdb = .kdb [kdbnxt]
	END;					! OF UNTIL .KDB IS ZERO

    !
    !   AT THIS POINT, THE UPDATE WAS SUCCESSFUL.
    !
    goodreturn
%FI

    END;					! OF DOUPDIDX

%SBTTL 'UPDUDR -- Update current record, indexed'
! UPDUDR
! ======
! ROUTINE TO UPDATE THE CONTENTS OF THE CURRENT RECORD FOR AN INDEXED FILE.
!	THIS ROUTINE CURRENTLY IS TRIVIAL BECAUSE IT IS NOT POSSIBLE
!	TO CHANGE THE LENGTH OF THE RECORD ON AN $UPDATE OPERATION.
!	HOWEVER, IF THIS CODE IS CHANGED TO ALLOW RECORD LENGTH
!	MODIFICATION, THEN THE FOLLOWING MUST BE DONE:
!
!		1 )	DETERMINE IF THE NEW RECORD WILL FIT IN THE
!			CURRENT BUCKET.
!
!		2 )	IF SO, SQUEEZE THE OLD ONE OUT (WITH SOME
!			OPTIMIZATION) AND INSERT THE NEW ONE WITHOUT
!			ASSIGNING A NEW ID FOR IT.
!
!		3 )	IF NOT, SPLIT THE BUCKET AND CONTINUE AS IF
!			THIS WERE A NORMAL $PUT OPERATION. HOWEVER,
!			NOTE THAT SINCE WE MAY NOT HAVE TRAVERSED THE
!			INDEX TO GET HERE, THE ENTIRE INDEX MAY HAVE TO
!			BE SEARCHED AGAIN SIMPLY TO SET UP THE "PATH"
!			ARRAY.  IF THE LAST OPERATION USED KEY ACCESS,
!			THEN "PATH" IS ALREADY SET UP AND DOES NOT HAVE
!			TO BE RE-INITIALIZED.
!
!
! INPUT:
!	RECDESC		RECORD DESCRIPTOR PACKET
!		RECPTR		ADDRESS OF OLD RECORD (IN CURRENT BUFFER)
!
!	DATABD		CURRENT BUCKET DESCRIPTOR (MUST NOT BE NULL)
!
!
! OUTPUT:
!	TRUE:	OK, RECORD HAS BEEN UPDATED
!	FALSE:	ERROR
!		ENQ/DEQ ERROR
!		COULD NOT INSERT NEW RECORD
!
! INPUT ARGUMENTS MODIFIED:
!	RECORD DESCRIPTOR
!		RECPTR		ADDRESS OF RECORD (MAY HAVE MOVED)
!
! ROUTINES CALLED:
!	<NONE CURRENTLY>
!
! NOTES:
!
!	1.	FILEISLOCKED IS RETURNED TO THE CALLER BECAUSE THIS
!		ROUTINE MAY HAVE TO LOCK THE FILE ON ITS OWN.

GLOBAL ROUTINE updudr (recdesc, databd) =
    BEGIN

%IF indx
%THEN
    argument (recdesc, baseadd);		! RECORD DESCRIPTOR
    argument (databd, baseadd);			! BUCKET DESCRIPTOR

    MAP
	recdesc : pointer,
	databd : pointer;

    LOCAL
	newrecordptr : pointer,			! PTR TO USER'S NEW UPDATED RECORD
	oldrecordptr : pointer;			! PTR TO OLD RECORD IN BUFFER

    TRACE ('UPDUDR');
    !
    !   FOR NOW, WE SIMPLY HAVE TO MOVE THE NEW RECORD (IN THE
    !   USER'S RECORD BUFFER) INTO THE OLD EXISTING RECORD
    !   (POINTED TO BY THE RECPTR FIELD). NOTE THAT THE SIZE OF
    !   OLD RECORD IS KEPT IN THE RST SO WE DON'T HAVE TO RE-COMPUTE IT.
    !
    newrecordptr = .rab [rabrbf];		! GET USER'S BUFFER ADDRES
    oldrecordptr = .recdesc [rdrecptr] + .kdb [kdbhsz];
    !
    !   MOVE THE RECORD DATA
    !
    movewords (.newrecordptr, 			! FROM
	.oldrecordptr, 				! TO
	.rst [rstrszw]);			! SIZE
    !
    !   WRITE OUT THE CURRENT BUCKET TO THE FILE
    !

    IF NOT writebehind				!
    THEN
	updatebucket (databd)			!
    ELSE
	setbfdupd (databd [bkdbfdadr]);		!SET WRITE FLAG

    goodreturn
%FI

    END;					! OF UPDUDR

END

ELUDOM