Google
 

Trailing-Edge - PDP-10 Archives - bb-h138f-bm - 7-sources/utlio.b36
There are 6 other files named utlio.b36 in the archive. Click here to see a list.
%TITLE 'U T L I O -- RMSUTL bucket I/O'
!<BLF/REQUIRE 'RMSBLF.REQ'>
MODULE utlio (					! Module to do Bucket I/O
		IDENT = '2.0'
		) =
BEGIN
!
!	COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1980, 1986.
!	ALL RIGHTS RESERVED.
!
!	THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY  BE  USED  AND
!	COPIED ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH
!	THE INCLUSION OF THE ABOVE COPYRIGHT NOTICE.   THIS  SOFTWARE  OR
!	ANY  OTHER  COPIES  THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE
!	AVAILABLE TO ANY OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE
!	SOFTWARE IS HEREBY TRANSFERRED.
!
!	THE INFORMATION IN THIS SOFTWARE IS  SUBJECT  TO  CHANGE  WITHOUT
!	NOTICE  AND  SHOULD  NOT  BE CONSTRUED AS A COMMITMENT BY DIGITAL
!	EQUIPMENT CORPORATION.
!
!	DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR  RELIABILITY  OF
!	ITS SOFTWARE ON EQUIPMENT THAT IS NOT SUPPLIED BY DIGITAL.
!
!

!++
! FACILITY:	RMSUTL
!
! ABSTRACT:
!
!	THIS MODULE DOES ALL THE BKT I/O FOR RMSUTL.
!	IT ALSO DOES INTRA-BKT PROCESSING UPON ID'S & ENTRY NUMBERS.
!	THE BK$ ROUTINES INSURE THE TOP-LEVEL CODE DOESNT KNOW ABOUT BD'S.
!	ALL THAT IS REQUIRED IS CALLING BK$PUT DURING C.CLOSE.
!	OF COURSE, YOU MAY CALL BK$PUT IF YOU WANT TO WRITE A BKT.
!
! AUTHOR:	A. UDDIN		CREATION DATE: 08-14-80
!
! MODIFIED BY:
!
!	Ron Lusk, 3-Feb-84 : VERSION 2.0
! 423	-   Fix up for version 2: reformat, cleanup.
! 430	-   Change BK$PROL to get the prologue properly.
! 455	-   Make CURRBD a GLOBAL so that US.IDX (in UTLUSE)
!	    can get to it for a bucket check.
!
! COMMON RETURNS:
!
!	-1 IF OPERATION ABORTED (MSG ALWAYS OUTPUT)
!	0 IF OPERATION FAILED (EG. ENTRY # TO BK$ENT TOO LARGE)
!	ON SUCCESS, BKT NUMBER OR BKT ADDRESS USUALLY
! TABLE OF CONTENTS:
!

%IF 0
%THEN

FORWARD ROUTINE
    bk$adb,
    bk$data,
    bk$down,
    bk$ent,
    bk$get,
    bk$id,
    bk$idb,
    bk$next,
    bk$put : NOVALUE,
    bk$prol,
    bk$root,
    bk$up,
    bk$dent,
    bd$get,
    bd$put : NOVALUE;

%FI

!
! REQUIRE FILES:
!

REQUIRE 'sys:rmslus';

LIBRARY 'rmslib';

LIBRARY 'utlext';

!
! MODULE-LEVEL DATA
!

GLOBAL 						! So UTLUSE can get it	!A455
    currbd : BLOCK [bdsize];			! Master bucket desc'r	!M455

OWN
    arglst : BLOCK [5],
    p_in_file,					! Number of page in file for
    						!   BD$GET consistency check
    rddesc : BLOCK [rdsize],			! Record descriptor
    						!   for $UTLINT calls
    tempbd : BLOCK [bdsize];			! Use this bucket descriptor

						!   if we don't want to
						!   clobber CURRBD
!
! MACROS:
!
!	None.
!
! EQUATED SYMBOLS:
!

LITERAL
    rfmfix = fb$fix,				!SO SIZEOF--- RMS MACROS WORK
    sidhsz = sidrhdrsize,			!JUST FOR CONVEN
    one = 1;

GLOBAL LITERAL
    bbm_err = 0,
    bbm_info = 1,
    bbm_none = 2;
%SBTTL 'BK$ADB - Get area descriptor'

GLOBAL ROUTINE bk$adb (area_no) = 		! Get area descriptor

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns address of the area descriptor corresponding
!	to the given area number.
! FORMAL PARAMETERS:
!
!	The area number
!
! IMPLICIT INPUTS:
!
!	<list of inputs from global or own storage>
!
! IMPLICIT OUTPUTS:
!
!	None
! RETURNS:
!
!	Addr. of ADB
! SIDE EFFECTS:
!
!	The Prolog page will be read in.
!--

    BEGIN

    LOCAL
	prolog : REF BLOCK;

    prolog = bk$prol ();			! Get pointer to prologue

    IF .prolog LEQ 0 THEN RETURN -1;		! Transit return failure

    IF .area_no GEQ .prolog [fptareas] THEN RETURN 0;	! Area # out of range

    prolog = .prolog + fptsize + 1;		! Pointer to first ADB
    prolog = .prolog + (.area_no*areadescsize);	! Calc addr of desired ADB
    RETURN .prolog;
    END;					! End of routine BK$ADB
%SBTTL 'BK$CHK - check bucket validity'

GLOBAL ROUTINE bk$chk (bkt_no) =
! BK$CHK - CHK VALIDITY OF BKT BUT DO NOT OVWRITE CURR BKT
! ARGUMENTS:
!	BKT_NO = P# OF BKT TO CHK
! RETURNS:
!	-1 IF UNEXP ERR OR BKT CLOBBED
!	1 IF P# OF BKT OK
    BEGIN
    clear (tempbd, bdsize);			!INSURE CLEAN SLATE

    IF bd$get (.bkt_no, 1, 0, tempbd, bbm_err) GEQ 0
    THEN
	BEGIN					!SUCCESS
	bd$put (tempbd, 0);
	RETURN 1;				!TELL USER AFTER CLEANING UP
	END
    ELSE
	RETURN -1;				!FAILURE

    END;
%SBTTL 'BK$DATA - Get leftmost data bucket'

GLOBAL ROUTINE bk$data (bktno) = 		! Get the leftmost data bkt.

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns the bucket address and the bucket
!	number of the 'leftmost' data bucket under the current
!	bucket.
! FORMAL PARAMETERS:
!
!	BKTNO		PAGE OF BKT TO BEGIN SCAN AT
! RETURNS:
!
!	BUCKET # FND
    BEGIN

    LOCAL
	p_in_bkt,
	nextbd : BLOCK [bdsize],
	bktptr : REF BLOCK;

    p_in_bkt = .kdb [kdbibkz];			! Bucket size of index
    						! page for current KRF

    !+
    !    Get the bucket. There shouldn't be any errors. If there
    !    are,  they should be reported back as system errors.
    !-

    IF bd$get (.bktno, .p_in_bkt, 0, currbd, bbm_err) LEQ 0 THEN RETURN -1;

    bktptr = .currbd [bkdbktadr];		! GET ADR OF BKT IN CORE

    IF .bktptr [bhbtype] NEQ btypeindex
    THEN
	BEGIN
	txtout (utldbc);			!DATA BKT ALREADY CURRENT
	RETURN .bktno;				!JUST SHIP BACK ORIG VALUE
	END;

    !+
    !    We now go down the tree till we locate
    !    the Data bucket.
    !-

    DO
	BEGIN
	rddesc [rdrecptr] = .bktptr + bhhdrsize;	!ADDR OF FIRST REC.
	bld_arg_lst (arglst, u$gtbktptr, rddesc, currbd, nextbd);
	$utlint (arglst, bugerr);

	IF .arglst [0, wrd] EQL false
	THEN
	    BEGIN				!TROUBLE GOING DOWN A LEVEL
	    m$erms (arglst, UPLIT (%ASCIZ'?UTLUIO unable to do I/O'));
	    RETURN -1;
	    END;

	bktptr = .nextbd [bkdbktadr];		! GET ADR OF BKT
	bd$put (currbd, false);			! RELEASE BKT WE ARE DONE WITH
	movebktdesc (nextbd, currbd);		! MAKE THE NEW BKD CURRENT
	END
    UNTIL .bktptr [bhlevel] EQL datalevel;

    RETURN .currbd [bkdbktno];			! RETURN BKT NO. VIA REF. ARG
    END;					! end of routine BK$DATA
%SBTTL 'BK$DENT - expunge entry from bucket'

GLOBAL ROUTINE bk$dent (bkpage, bktadr, recptr) =
! BK$DENT - EXPUNGES ANY TYPE OF ENTRY FROM A BKT
! ARGUMENTS:
!	BKPAGE = FILE PAGE OF ENTRY'S BKT
!	BKTADR = ADDR OF BUFFER THAT CONTAINS THE BKT
!	RECPTR = ADDR IN BUFFER OF THE ENTRY TO BE EXPUNGED
! NOTES:
!	IF ENTRY IS UDR, THEN ITS RRV (IF ONE) IS EXPUNGED, & WRITTEN TOO
!	HOWEVER IT IS CALLER'S RESPONS TO WRITE OUT PAGE OF DEL ENT
! CONSIS CHKS MADE:
!	EXPUNGE PRIM DATA ENTRY ONLY IF DEL OR RRV BIT ON
!	EXP INDEX ENTRY ONLY IF IT DOESNT PT AT VALID BKT
!	EXP SIDR ONLY IF RFA ARRAY EMPTY
! IMPLICIT INPUTS:
!	FAB
!	CURRENT KDB
    BEGIN

    LABEL
	idxlen;

    LOCAL
	i,
	inuse,
	ptr : REF BLOCK,
	temp,
	rsizw;

    MAP
	recptr : REF BLOCK,
	bktadr : REF BLOCK;

    IF .bktadr [bhbtype] EQL btypeindex		!IS IT INDEX ENTRY?
    THEN
	BEGIN					!YES, SET SIZ & CHK IF OK TO DEL
idxlen :
	BEGIN
	rsizw = .kdb [kdbkszw] + 1;		!SIZE OF INDEX ENTRY

	IF .recptr [irbucket] GTR .p_in_file THEN LEAVE idxlen;	!OK TO DEL

	clear (tempbd, bdsize);			!START WITH NULL BKT DESC
	temp = bd$get (.recptr [irbucket], 1, 0, tempbd, bbm_none);	! GET THIS BKT

	IF .temp LSS 0				!DID CALL AT LEAST SUCC?
	THEN
	    RETURN -1;				!NO, DONT PLAY WITH FIRE

	ptr = .tempbd [bkdbktadr];		!GET PTR TO IT
	inuse = .ptr [bhnextbyte];		!GET WDS IN USE
	bd$put (tempbd, 0);			!FREE IT

	IF .temp EQL 1 AND .inuse NEQ bhhdrsize
	THEN
	    BEGIN				!OOPS, VALID NON-EMPTY PAGE
	    txtout (utlvex);			!VALID ENTRY MAY NOT BE EXPUNGED
	    RETURN -1;
	    END;

	END;					!END IDXLEN BLOCK
	END
    ELSE
	BEGIN					!DATA ENTRY
	rsizw = sizeofanyrecord (recptr);	!SIZE OF ARB DATA ENTRY

	IF .kdb [kdbref] EQL 0			!IS IT PRIM-DATA BKT?
	THEN
	    BEGIN				!YES

	    IF rrvflag (recptr) EQL 0 AND deleteflag (recptr) EQL 0
	    THEN
		BEGIN				!UNDELETED REC ENTRY
		txtout (utlvex);		!VALID ENTRY MAY NOT BE EXPUNGED
		RETURN -1;
		END;

	    IF rrvflag (recptr) EQL 0		!IS IT REC ENTRY?
	    THEN

		IF makerfa (.bkpage, .recptr [drrecordid]) NEQ .recptr [drrrvaddress]
		THEN
		    BEGIN			!YES, & THERE IS AN RRV FOR IT
		    rddesc [rdrecptr] = 0;	!START AT TOP OF PAGE
		    rddesc [rdrfa] = .recptr [drrrvaddress];	! AND FIND THIS REC
		    bld_arg_lst (arglst, u$fbyrfa, rddesc, tempbd);
		    $utlint (arglst, bugerr);	!FIND RRV

		    IF .arglst NEQ 0		!DID IT SUCCEED?
		    THEN
			BEGIN			!YES
			bk$dent (.tempbd [bkdbktno], .tempbd [bkdbktadr], .rddesc [rdrecptr]);
						!DELETE THE RRV
			setupd (tempbd);	!INDIC RRV PAGE MODIF
			bd$put (tempbd, 0);	!RELEASE RRV'S BKT
			END;			!END, DEL RRV

		    END;			!END, REC ENTRY WITH RRV

	    END					!END, PRIM-DATA PAGE
	ELSE

	    INCR i				!
		FROM sidhsz + .kdb [kdbkszw]	!
		TO .recptr [sidrrecsize] + sidhsz - 1 DO
		BEGIN				!SIDR PAGE

		IF .recptr [.i, wrd] NEQ 0	!NON-NULL SIDR
		THEN
		    BEGIN			!YES, CANT DEL IT
		    txtout (utlvex);		!VALID ENTRY MAY NOT BE EXPUNGED
		    RETURN -1;
		    END;

		END;				!IF EXIT LOOP, RFA ARRAY EMPTY

	END;					!END, IS DATA PAGE

    temp = (.bktadr + .bktadr [bhnextbyte]) - (.recptr + .rsizw);	!# OF WRDS TO MOVE
    movewords (.recptr + .rsizw, .recptr, .temp);	!MOVE REST OF BKT UP
    bktadr [bhnextbyte] = .bktadr [bhnextbyte] - .rsizw;	! Fix header
    RETURN 1;
    END;					! End BK$DENT
%SBTTL 'BK$DOWN - Get specified bucket'

GLOBAL ROUTINE bk$down (bktno, entry_no) = 	!Get the bkt pointed to by ENTRY_NO

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns the address and entry number of the bucket down
!	the tree which is pointed to by ENTRY_NO in the current bkt.
! FORMAL PARAMETERS:
!
!	ENTRY_NO	Entry no in current bkt which points to the
!			required bkt.
! RETURNS:
!
!	BKT # LOCATED
    BEGIN

    LOCAL
	p_in_bkt,
	movingptr : REF BLOCK,
	bktptr : REF BLOCK;

    p_in_bkt = .kdb [kdbibkz];			!GET INDEX BKT SIZE FOR CURR KRF
    ! BRING STARTING BKT IN

    IF bd$get (.bktno, .p_in_bkt, 0, currbd, bbm_err) LEQ 0	!
    THEN
	RETURN -1;				! INDICATE OPR ABORTED

    bktptr = .currbd [bkdbktadr];		! ADDR IN CORE OF CURR BKT

    IF .bktptr [bhbtype] NEQ btypeindex		! START BKT INDEX BKT?
    THEN
	BEGIN
	txtout (utldbc);
	RETURN .bktno;				!JUST SHIP ORIG VAL BACK
	END;

    movingptr = .bktptr + bhhdrsize;		! ADDR OF FIRST RECORD

    !+
    !    POSITION TO ENTRY_NO IN BUCKET
    !    CHECK IF IT IS INSIDE THE BKT.
    !-

    movingptr = .movingptr + (.entry_no - 1)*(.kdb [kdbkszw] + 1);

    IF .movingptr GEQ .bktptr [bhnextbyte] + .bktptr
    THEN
	BEGIN
	txtout (utlsen);			!ENT FOR DOWN NON-EX
	RETURN -1;				! CURRENT BKT NOT INDEX
	END;

    RETURN .movingptr [irbucket];		!PICK UP BKT NO FROM ENTRY
    END;					! end of routine BK$DOWN
%SBTTL 'BK$ENT - fetch entry address'

GLOBAL ROUTINE bk$ent (entry_no) = 		! Return addr of ENTRY_NO in current bkt
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns the addr of ENTRY_NO in the
!	current bkt. The current bkt can be either an index
!	bucket or a data bkt.
! FORMAL PARAMETERS:
!
!	ENTRY_NO
! IMPLICIT INPUTS:
!
!	CURRBD, KDB
! RETURNS:
!
!	False IF BAD ENTRY NUMBER
!	Addr. of Entry in Bucket.
    BEGIN

    LOCAL
	bkttype,
	movingptr : REF BLOCK,
	bktptr : REF BLOCK;

    IF .currbd [bkdbktsize] EQL 0
    THEN
	BEGIN
	txtout (utliue);			!NO CURR BKT
	RETURN -1;
	END;

    bktptr = .currbd [bkdbktadr];		! ADDR OF BKT IN CORE
    bkttype = .bktptr [bhbtype];		! GET BKT TYPE
    movingptr = .bktptr + bhhdrsize;		! POS TO 1ST ENTRY

    !+
    !    Scan the bucket until the requested entry is reached.
    !-

    INCR j FROM 1 TO .entry_no - 1 DO
	BEGIN

	IF .movingptr GEQ .bktptr [bhnextbyte] + .bktptr	!
	THEN
	    RETURN false;			! FAIL IF END OF DATA

	movingptr = .movingptr + 		!
	(IF .bkttype EQL btypeindex		!
	THEN .kdb [kdbkszw] + 1			!
	ELSE sizeofanyrecord (movingptr));	!
	END;

    cu$ent = .entry_no;				!RET ENTRY # FOUND

    IF .movingptr GEQ .bktptr [bhnextbyte] + .bktptr
    THEN
	RETURN false				!FAIL IF END OF DATA
    ELSE
	RETURN .movingptr;			! RETURN ENTRY ADDR.

    END;					! end of routine BK$ENT
%SBTTL 'BK$GET'

GLOBAL ROUTINE bk$get (bkt_no) =
    RETURN bk$gc (.bkt_no, bbm_info);
%SBTTL 'BK$GOK'

GLOBAL ROUTINE bk$gok (bkt_no) =
    RETURN bk$gc (.bkt_no, bbm_err);
%SBTTL 'BK$GQI'

GLOBAL ROUTINE bk$gqi (bkt_no) =
    RETURN bk$gc (.bkt_no, bbm_none);
%SBTTL 'BK$GC - map bucket and return address'

GLOBAL ROUTINE bk$gc (bkt_no, retopt) =
! FUNCTIONAL DESCRIPTION:
!
!	This routine maps in the requested bkt and returns
!	its address.
! FORMAL PARAMETERS:
!
!	BKT_NO		BUCKET NUMBER TO MAP
!	RETOPT		IF BBM_INFO, THEN RETS SUCC FOR CLOB BKT
!			IF BBM_ERR, THEN RETS FAILURE FOR CLOB BKT
! IMPLICIT OUTPUTS:
!
!	CU$TYPE IS SET
! RETURNS:
!
!	ADDRESS OF BKT LOCATED
    BEGIN

    LOCAL
	getcase,
	bktptr : REF BLOCK;

    IF chkflag (utlflg, ut_dbad) NEQ 0
    THEN
	BEGIN
	txtout (utlepc);			!DATA ENVIR NOT ESTAB
	RETURN -1;
	END;

!+
!    GET REQUESTED BUCKET. ASSUME ITS SIZE TO BE 1.
!    IF BKT SIZE GTR 1, BD$GET REREADS CORRECT SIZE AUTOMAT
!-
    getcase = bd$get (.bkt_no, 1, 0, currbd, .retopt);

    IF .getcase LSS 0 THEN RETURN -1;		!COULDNT GET IT, MSG ALR OUTPUT

    bktptr = .currbd [bkdbktadr];		!MAKE BKT ACCESSIBLE
    cu$type = bk$type (.bktptr, .getcase);	!DET TYPE OF BKT
    RETURN .bktptr;				!RET BKT'S ADDR
    END;					! end of routine BK$GET
%SBTTL 'BK$ID - return entry given ID'

GLOBAL ROUTINE bk$id (id) =
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns the addr. of bucket entry whose ID
!	matches the given ID.
! FORMAL PARAMETERS:
!	ID	Record ID
! IMPLICIT INPUTS:
!	CURRENT BKT DESC
!
! IMPLICIT OUTPUTS:
!	CU$ENT SET TO ENTRY # OF FND ID
!
! RETURNS:
!	0 IF LOOP TERMINATED WITHOUT SUCCESS
!	Address of FOUND Entry
    BEGIN

    LOCAL
	bkttype,
	movingptr : REF BLOCK,
	bktptr : REF BLOCK;

    IF .currbd [bkdbktsize] EQL 0
    THEN
	BEGIN
	txtout (utliue);			!NO CURR BKT
	RETURN -1;
	END;

    bktptr = .currbd [bkdbktadr];		! ADDR OF BKT IN CORE

    IF .bktptr [bhlevel] GTR datalevel
    THEN 					! BKT IS NOT DATA BKT
	BEGIN
	txtout (utlbnd);
	RETURN -1;
	END;

    movingptr = .bktptr + bhhdrsize;		! POSN PAST HEADER
    cu$ent = 1;					!START WITH 1ST ENTRY

    !+
    !    SCAN THE BKT. TIL THE ENTRY WITH REQUESTED ID IS REACHED.
    !-

    WHILE .movingptr LSS .bktptr [bhnextbyte] + .bktptr DO
	BEGIN

	IF .movingptr [drrecordid] EQL .id THEN RETURN .movingptr;	!SUCCESS

	movingptr = .movingptr + sizeofanyrecord (movingptr);
	cu$ent = .cu$ent + 1;			!SET CTR TO NEXT 1
	END;

    RETURN false;
    END;					! end of routine BK$ID
%SBTTL 'BK$IDB - return IDB address'

GLOBAL ROUTINE bk$idb (krf) =
! FUNCTIONAL DESCRIPTION:
!
!	Returns ADDR OF Index Descriptor block for SPECIFIED KRF
! FORMAL PARAMETERS:
!
!	KRF	Target index no. (key of ref.)
! IMPLICIT OUTPUTS:
!
!	CURRBD SETUP
! RETURNS:
!
!	0 (BAD KRF)
!	Addr. of DESIRED IDB
    BEGIN

    LOCAL
	idbadr;

    LOCAL
	t1;					!SAVE CURR KREF

    t1 = .kdb [kdbref];				!RESET AT END OF CALL

    IF NOT m$kdb (.krf)				!SET ENVIR
    THEN
	RETURN false;				!UNLESS BAD KRF

    bd$put (currbd, 0);				!CLEAR OLD STUFF
    bld_arg_lst (arglst, u$getidb, currbd);
    $utlint (arglst, bugerr);

    IF .arglst [0, wrd] EQL false
    THEN
	BEGIN
	m$erms (arglst, UPLIT (%ASCIZ'?UTLUIO unable to do I/O'));
	RETURN -1;
	END;

    idbadr = .arglst [0, wrd];			! RETURN IDB ADDR.
    m$kdb (.t1);				!RESTORE ORIG KDB INFO
    RETURN .idbadr;				!DONE
    END;					! end of routine BK$IDB
%SBTTL 'BK$NEXT - get next bucket'

GLOBAL ROUTINE bk$next (bktno) = 		!Get the Next bkt at the same lvl.
! FUNCTIONAL DESCRIPTION:
!
!	This routine traverses the index structure in the horizontal
!	direction. It gets the bkt at the same level of the tree with
!	the next higher group of keys.
! FORMAL PARAMETERS:
!
!	BKTNO		bucket no. whose NEXT bkt is desired
! RETURNS:
!
!	BKT # FND
! SIDE EFFECTS:
!
!	DISPLAYS INFO MSG IF STARTING BKT IS RIGHTMOST
    BEGIN

    LOCAL
	bktptr : REF BLOCK;

    ! ONLY NEED BKT HDR, SO CAN READ 1 PAGE REGARDLESS OF BKT SIZE

    IF bd$get (.bktno, 1, 0, currbd, bbm_info) LSS 0 THEN RETURN -1;

    bktptr = .currbd [bkdbktadr];		!GET PTR TO IT

    IF chkflag (bktptr [bhflags], bhflgend) NEQ 0	!
    THEN
	txtout (utlnbl);			! THERE IS NO NEXT BKT

    bktptr = .currbd [bkdbktadr];		! GET PTR TO IT (rpt cause bliss bug)
    RETURN .bktptr [bhnextbkt];			!RET NEXT PTR
    END;					! end of routine BK$NEXT
%SBTTL 'BK$PUT - release a bucket'

GLOBAL ROUTINE bk$put (flag) : NOVALUE = 	!Routine to release a bkt.
! FUNCTIONAL DESCRIPTION:
!
!	This routine always releases the bucket that is
!	described by CURRBD. MAY BE CALLED WHEN NO BKT CURRENT.
! FORMAL PARAMETERS:
!
!	FLAG	Update flag
! IMPLICIT INPUTS:
!
!	CURRBD
!
    BEGIN
    bd$put (currbd, .flag);
    RETURN;
    END;					! end of routine bk$put
%SBTTL 'BK$PROL - return prologue bucket'

GLOBAL ROUTINE bk$prol = 			!Returns the address of Prolog bucket
! FUNCTIONAL DESCRIPTION:
!
!	This routine returns the address of the bucket contaning
!	the file prolog. It does it in an inelegant manner:
!	It calls on RMS to read the IDB for the key of ref. in
!	the current KDB. The bucket descriptor that is returned
!	is the bkt desc of the prolog page.
! IMPLICIT INPUTS:
!
!	KDB
! IMPLICIT OUTPUTS:
!
!	CURRBD is set up.
! RETURNS:
!
!	ADDR OF PROLOG IF IT COULD BE OBTAINED
!
    BEGIN

    LOCAL
	prologue_table : REF BLOCK;

%IF 0
%THEN

    LOCAL
	t1;

    !+
    !    Read the IDB.
    !-

    t1 = bk$idb (0);				! Take advantage of fact that
    						!   1st IDB is on 1st file page

    IF .t1 LEQ 0 THEN RETURN .t1;		! Transmit return failure

%FI

    bd$put (currbd, 0);				! Clear old stuff	!A430
    bld_arg_lst (arglst, 			! Argument list		!A430
	u$getbkt, 				! GETBKT call		!A430
	0, 					! Page 0		!A430
	1, 					! Only 1 page needed	!A430
	0, 					! No locking		!A430
	currbd);				! Return bucket desc.	!A430
    $utlint (arglst, bugerr);			! Make the call		!A430
    prologue_table = .currbd [bkdbktadr];	! Address of prologue
    p_in_file = .prologue_table [fptnxtbkt];	! Set to current value,
    						!   keep it up to date
    RETURN .prologue_table;			! Return address of prologue
    END;					! End of routine BK$PROL
%SBTTL 'BK$ROOT - map root bucket'

GLOBAL ROUTINE bk$root (krf) = 			!Maps the Root bkt and returns its no.

!++
! FUNCTIONAL DESCRIPTION:
!
!	This routine maps the root bkt for the given key-of-ref.
! FORMAL PARAMETERS:
!
!	KRF	Key of reference
! RETURNS:
!
!	0, IMPLYING EMPTY INDEX
!	Bucket number OF DESIRED ROOT BKT
    BEGIN

    LOCAL
	t1;

    IF NOT m$kdb (.krf)				!SET ENVIR FOR SPEC KEY
    THEN
	BEGIN
	txtout (utlfni);			!BAD KRF
	RETURN -1;
	END;

    bd$put (currbd, 0);				!CLEAR OLD STUFF
    bld_arg_lst (arglst, u$getroot, rddesc, currbd);
    $utlint (arglst, bugerr);			!HAVE RMS MAP ROOT

    IF .arglst [0, wrd] EQL false
    THEN
	BEGIN
	t1 = .$field (sts, arglst);		!GET STATUS RET BY GETROOT

	IF .t1 EQL er$rnf OR .t1 EQL er$eof THEN RETURN false;	! INDEX EMPTY

	m$erms (arglst, UPLIT (%ASCIZ'?UTLUIO unable to do I/O'));
	RETURN -1;
	END;

    RETURN .currbd [bkdbktno];
    END;					! end of routine BK$ROOT
%SBTTL 'BK$TYPE - return bucket type'

GLOBAL ROUTINE bk$type (bktptr, getcase) =
! FUNCTIONAL DESCRIPTION:
!	CALCULATES BKT TYPE OF GIVEN BKT
! ARGUMENTS:
!	BKTPTR = PTR TO BKT TO BE TYPED
!	GETCASE = 1 UNLESS BKT CLOBBED
! RETURNS:
!	BKT TYPE
    BEGIN

    MAP
	bktptr : REF BLOCK;

    IF .getcase NEQ 1				!CLOBBERED BKT?
    THEN
	RETURN bty_clob;			!YES, CALL WAS _NONE OR _INFO

    IF .bktptr [bhlevel] NEQ 0			!INDEX BKT?
    THEN
	RETURN bty_idx;				!YES

    IF .kdb [kdbref] EQL 0			!NO, IS IT UDR?
    THEN
	RETURN bty_prim				!YES
    ELSE
	RETURN bty_sec;				!NO, 2NDARY DATA BKT

    END;					! OF BK$TYPE
%SBTTL 'BK$UP - return previous bucket number'

GLOBAL ROUTINE bk$up (bktno) =
! FUNCTIONAL DESCRIPTION:
!	THIS ROUTINE RETURNS THE BUCKET NUMBER OF THE PREVIOUS BUCKET,
!	I.E., THE BUCKET WHICH POINTS TO THE CURRENT BUCKET.
! FORMAL PARAMETERS:
!	BKTNO.	BUCKET NO. WHOSE PREVIOUS BKT IS DESIRED
! RETURNS:
!	FND BKT #
!	0 IF ALREADY AT ROOT
    BEGIN

    LOCAL
	level,
	bktptr : REF BLOCK;

    ! GET BKT, ABORT IF ERRS OR IF CLOB

    IF bd$get (.bktno, 1, 0, tempbd, bbm_err) LEQ 0 THEN RETURN -1;	! COULDNT GET BKT

    bktptr = .tempbd [bkdbktadr];		! ADDR OF BKT
    level = .bktptr [bhlevel];			! SAVE CURRENT LEVEL

    IF chkflag (bktptr [bhflags], bhflgroot) NEQ 0 THEN RETURN 0;	!ALREADY AT ROOT

    rddesc [rduserptr] = 			!
    m$kloc (buf$k1, .bktptr + bhhdrsize, bk$type (.bktptr, 1));	!GET (COMBINING IF NECES) 1ST KEY IN BKT

    !+
    !    Set up to actually call the FOLLOWPATH fucntion in RMS
    !-

    rddesc [rdusersize] = .kdb [kdbksz];	!LEN OF KEY TO FIND
    bd$put (tempbd, 0);				!CLEAR OLD STUFF
    bld_arg_lst (arglst, u$folopath, rddesc, tempbd);
    $utlint (arglst, bugerr);

    IF .arglst [0, wrd] EQL false
    THEN
	BEGIN					!KEY FROM FILE, SO MUST FIND IT
	txtout (utlaff);			!COULDNT FIND 1ST KEY ON PAGE
	RETURN -1;
	END;

    bd$put (tempbd, 0);				!CLEAR THIS TOO

    !+
    !    MAKE SURE MASTER BKT NO. AND THE ONE IN THE PATH ARRAY,
    !    CORRESPONDING TO THE CURRENT LEVEL,  MATCH
    !-

    IF .path [.level, pathbkt] EQL .bktno
    THEN
	RETURN .path [.level + 1, pathbkt]	! GET PREV BKT NUMBER
    ELSE
	BEGIN
	txtout (utlaff);			!KEY NOT FND
	RETURN -1;
	END;

    END;					! end of routine BK$UP
%SBTTL 'BD$GET - call RMS GETBKT'

GLOBAL ROUTINE bd$get (bktno, pagcnt, lockflg, bd, retopt) = 	!CALL ON THE RMS 'GETBKT' FUNC
! FUNCTIONAL DESCRIPTION:
!	SETS UP BKT DESC TO DESIRED BKT, READING IT IN IF NECESSARY
!
! FORMAL PARAMETERS:
!	BKTNO = 1ST PAGE IN FILE OF DESIRED BKT
!	PAGCNT = # OF P TO READ
!	LOCKFLG (ALWAYS 0)
!	BD = PTR TO BKT DESC THAT RMS FILLS IN
!	RETOPT = VALUE TO CTL BAD BKT HDR RETURN ACTION
!
! RETURNS NORMALLY:
!	1 WITH BD SETUP & BKT READ IN
!	-1 IF ANY ERROR MSG OUTPUT
! RETURNS FOR CLOBBERED BKT:
!	-1 & MSG FOR BBM_ERR
!	0 FOR BBM_INFO
!	MSG-PTR FOR BBM_NONE
!
! NOTES:
!	BD$GET HAS A PROBLEM IN THAT RMS BUCKETS CAN BE DIFFERENT SIZE
!	FOR DIFFERENT AREAS. TO LOCALIZE THE IMPACT OF THIS, IT PUTS
!	AND REGETS THE BKT IF THE BKT SIZE PASSED BY CALLER IS WRONG.
!	IT USES THE CURR KDB PLUS BKT TYPE TO DETERMINE ACTU BKT SIZE.
!	IF THE PARTIAL-GET IS THE ONLY CURRENT ACCESSOR OF THE BKT,
!	TWIDDLE RMS'S BUFFER DESC FOR THE BKT TO INSURE IT MAPS WHOLE BKT.
    BEGIN

    LOCAL
	bktsiz;					!ACTU # OF P IN BKT

    LOCAL
	bktptr : REF BLOCK;

    LOCAL
	bbm;

    MAP
	bd : REF BLOCK;

    bd$put (.bd, 0);				!CLEAR OLD STUFF

    IF .p_in_file LEQ .bktno			!REF PAST EOF?
    THEN
	BEGIN					!MAYBE, CHK IF FILE EXTENDED

	IF bk$prol () EQL -1			!GET PROLOG TO RESET P_IN_FILE
	THEN
	    RETURN -1;				!OOPS (MSG ALR OUTPUT)

	IF .p_in_file LEQ .bktno
	THEN
	    BEGIN				!"STILL" PAST EOF, GIVE ERROR
	    txtout (utlppe, .bktno);		!PAGE PAST EOF
	    RETURN -1;
	    END;

	END;

    bld_arg_lst (arglst, u$getbkt, .bktno, .pagcnt, .lockflg, .bd);
    $utlint (arglst, bugerr);

    IF .arglst [0, wrd] EQL false
    THEN
	BEGIN					!OOPS
	m$erms (arglst, UPLIT (%ASCIZ'?UTLUIO unable to do I/O'));
	RETURN -1;
	END;

    bktptr = .bd [bkdbktadr];			! ADDR OF BKT IN CORE
    !MAKE CONSIS CHKS:
    ! INDEX MUST HAVE NON-0 LEVEL
    ! DATA MUST HAVE 0 LEVEL
    ! NEXTBYTE MUST BE GTR 2 AND LE BKT SIZE
    ! AREA # MUST AGREE WITH KDB
    bbm = 0;					!SET TO DEFINED VAL

    IF .bktptr [bhbtype] EQL btypedata
    THEN

	IF .bktptr [bhlevel] EQL 0		!DOES LEVEL AGREE WITH TYPE?
	THEN
	    BEGIN				!YES, SET DATA BKT SIZE
	    bktsiz = .kdb [kdbdbkz];		! FROM KDB

	    IF .kdb [kdbdan] NEQ .bktptr [bhthisarea]	!
	    THEN
		bbm = UPLIT (%ASCIZ'area number');

	    END
	ELSE
	    bbm = UPLIT (%ASCIZ'type/level');	!BAD INFO IN BKT HDR

    IF .bktptr [bhbtype] EQL btypeindex
    THEN

	IF .bktptr [bhlevel] NEQ 0		!DOES LEV AGREE FOR IDX BKT?
	THEN
	    BEGIN				!YES, SET DATA BKT SIZE
	    bktsiz = .kdb [kdbibkz];		! FROM KDB

	    IF .kdb [kdbian] NEQ .bktptr [bhthisarea] THEN bbm = UPLIT (%ASCIZ'area number');

	    END
	ELSE
	    bbm = UPLIT (%ASCIZ'type/level');

    IF .bktptr [bhbtype] GTR 1			!TYPE OUT OF RANGE?
    THEN
	bbm = UPLIT (%ASCIZ'type');		!YES

    IF .bktptr [bhnextbyte] LSS bhhdrsize	!1ST FREE TOO SMALL?
	OR .bktptr [bhnextbyte] GTR (.bktsiz^p2w)	! OR TOO LARGE?
    THEN
	bbm = UPLIT (%ASCIZ'words-in-use');	!YES TO EITHER

    IF .bbm NEQ 0				!BAD BKT MSG SET UP?
    THEN
	BEGIN

	IF .retopt EQL bbm_none			!LET CALLER PUT MSG?
	THEN
	    RETURN .bbm;			!YES, RET PTR TO BAD INFO

	IF .retopt EQL bbm_err			!TREAT AS BARF CONDIT?
	THEN
	    BEGIN				!YES, PUT ERR MSG
	    bd$put (.bd, 0);			!CLEAN UP AFT ABORT
	    txtout (utlpne, .bktno, .bbm, .kdb [kdbref]);
	    RETURN -1;
	    END;

	IF .retopt EQL bbm_info			!TREAT AS INFO COND?
	THEN
	    BEGIN				!YES, DISP/CH B H
	    txtout (utlpni, .bktno, .bbm, .kdb [kdbref]);
	    RETURN 0;				!DONT TRUST REST OF BKT
	    END;

	END;

    ! GET WHOLE BKT NOW IF PARTIAL BKT SIZE WAS SPEC

    IF .pagcnt NEQ .bktsiz			!DID THE KLUDGE LUCK OUT?
    THEN
	BEGIN					!NO, GET RIGHT SIZE

	LOCAL
	    bfd : REF BLOCK;			!PTR TO BKT'S BUFF DESC

	bd$put (.bd, 0);			!RELEASE PARTIAL BKT
	bfd = .bd [bkdbfdadr];			!GET PTR TO BUFF DESC

	IF .bfd [bfdusecount] EQL 0		!WAS EARLIER GETBKT ONLY USER?
	THEN
	    bfd [bfdbktsiz] = 0;		!YES, MAKE RMS THINK BUFF EMPTY

	bld_arg_lst (arglst, u$getbkt, .bktno, .bktsiz, .lockflg, .bd);
	$utlint (arglst, bugerr);

	IF .arglst [0, wrd] EQL false
	THEN
	    BEGIN				!OOPS
	    m$erms (arglst, UPLIT (%ASCIZ'?UTLUIO unable to do I/O'));
	    RETURN -1;
	    END;

	END;

    RETURN 1;					! SUCCESS
    END;					! end of routine BD$GET
%SBTTL 'BD$PUT - Call RMS PUTBKT'

GLOBAL ROUTINE bd$put (bd, updateflag) : NOVALUE = 	!CALL THE RMS 'PUTBKT' FUNC
! FUNCTIONAL DESCRIPTION:
!
!	RELEASE THE SPEC BKT, OUTPUTTING IF UPDATEFLAG SET
!
! FORMAL PARAMETERS:
!
!	BD = PTR TO BKT DESC TO RELEASE
!	UPDFLAG = TRUE IF BKT SHOULD BE WRITTEN
!
    BEGIN

    MAP
	bd : REF BLOCK;

    IF nullbd (bd) THEN RETURN;			!BKT DESC EMPTY

    bld_arg_lst (arglst, u$putbkt, .updateflag, .bd);
    $utlint (arglst, interr);			! NO ERRORS EXPECTED
    setnullbd (bd);				!INSURE NOT "PUT" TILL AGAIN OCC
    RETURN;
    END;					! end of routine PUTBKT

END

ELUDOM