Google
 

Trailing-Edge - PDP-10 Archives - tops20-v7-ft-dist1-clock - 7-sources/rmsdsi.b36
There are 6 other files named rmsdsi.b36 in the archive. Click here to see a list.
%TITLE 'DSI -- initialize data structures'
!<BLF/REQUIRE 'RMSBLF.REQ'>
MODULE dsi (IDENT = '2.0'
		) =
BEGIN
!
!	COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1984, 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:	RMS
!
! ABSTRACT:
!
!	DSI contains routines which initialize internal
!	data structures within RMS-20.
!
! ENVIRONMENT:	User mode, interrupts deferred until JSYS return.
!
! AUTHOR: Ron Lusk , CREATION DATE: 18-Mar-83
!
! REVISION HISTORY:
! RMS EDIT
!	Ron Lusk, 9-May-83 : VERSION 2
! 404	-   SETFST - No locking if FB$SMU is set (DSI edit 401)
!     
! 411	-   FENDOFKEY - Handle variable-byte-size records.
!
! 427   -   SETKDB - Do not touch KSD's
!
!	Andrew Nourse, 14-Jan-1985 : VERSION 3
! 504   -   Image mode
! 550	-   If an FFF file class if found (file class < 0), then
!	    use the non-RMS file organization FST$K_NON. (RL)
!--

!
! TABLE OF CONTENTS
!
!
!	SETFST	    -	Set up a File Status Table
!	SETRST	    -	Set up a Record Satus Table
!	READADB	    -	Read in the Area Descriptor Block
!	SETKDB	    -	Set up a Key Descriptor Block
!	FENDOFKEY   -	Compute the End-of-Key byte
!			number for SETKDB
!	UNDOKDBS    -	Flush a partially processed
!			KDB list
!
!
! INCLUDE FILES:
!

REQUIRE 'rmsreq';

!
! MACROS:
!
!   None.
!
! EQUATED SYMBOLS:
!

GLOBAL BIND
    dsiv = 2^24 + 0^18 + 401;			! Module version number

MAP
    dvflgs : monword;

!
! OWN STORAGE:
!
!   None.
!
! EXTERNAL REFERENCES:
!
!   None.
!
%SBTTL 'SETFST - initialize File Status Table'

GLOBAL ROUTINE setfst =

!++
! FUNCTIONAL DESCRIPTION:
!
!	SETFST initializes the contents of the File Status
!	Table.  It is called only from the $OPEN and $CREATE
!	processors when a file is initially opened for
!	processing.  It takes all relevant values from the
!	user's File Access Block (FAB) and moves them into
!	the FST.  Note that the FAB must contain all the
!	important information at this time...the File
!	Prologue is not read at all.
!
!	Note:  In order to make selection statements based
!	on file organization work more easily (as in
!	"CASE .fileorg ..."), this routine will set up a
!	special code if the file contains stream or LSA
!	format records.  If so, the file organization field
!	(FSTORG) will be set to ORGASC instead of the
!	sequential organization given by the user.
!	Therefore, after this routine exits, the original
!	file organization given by the user is lost.  A
!	quick check is made by this routine to make sure
!	that the user's file organization value is not
!	ORGASC already.
!
!	Note further that the block of storage which is
!	allocated for the FST is cleared by GMEM.  Thus,
!	there are several source statements in this routine
!	which clear certain fields and are commented out.
!	They are included only to make it clear which fields
!	are being manipulated.
!
! FORMAL PARAMETERS
!
!	NONE.
!
! IMPLICIT INPUTS
!
!	FAB	-   FAB must point to the user's File Access
!		    Block.  Data from the FAB is copied to
!		    the FST.
!	FST	-   assumes that the FST has been allocated
!		    and that the length of the FST is
!		    already in the BLOCKLENGTH field of the
!		    FST.
!	FPT	-   the File Prologue must be mapped and
!		    pointed to by FPT.
!
! COMPLETION CODES:
!
!	TRUE	-   FST initialized
!	FALSE	-   Error
!		    Invalid file organization specified
!
! SIDE EFFECTS:
!
!	If there are no errors, then the FST will be
!	    initialized.
!	If the file organization specified in the user's FAB
!	    is invalid, USRSTS is set to ER$ORG, and the FST
!	    is deallocated.
!
!--

    BEGIN

    REGISTER
	flags;

    TRACE ('SETFST');
!+
!   Check the file organization value to make
!   sure it is in the proper range.  If it is,
!   then set up the FST properly; if not, give
!   an error return.
!-

    CASE .Fab [Fab$v_Org] FROM Fab$k_Seq TO Fab$k_Idx OF		  !m504
	SET

	[OUTRANGE] : 				! Illegal organization
!+
!   The user gave us a bad file
!   organization, so give him an error.
!-
	    returnstatus( Rms$_Org );

	[Fab$k_Seq] :
!+
!   Non-RMS files have a
!   special internal "file organization",
!   ORGASC (0), so set it up that way.
!-
	    BEGIN

	    IF .fst [fst$h_file_class] lss 0	! FFF file?		!A550
	    then				! 			!A550
		fst [fst$h_org] = fst$k_non	! Non-RMS organization	!A550
	    else
		begin
		Fst [Fst$h_Org] =		!m504
		  ( CASE .fab [Fab$v_Rfm]	! 
		    FROM  Fab$k_Var TO Fab$k_Rfm_Max OF
		    SET
		    [Fab$k_Stm,     ! Stream format
		     Fab$k_Lsa,	! LSA format
		     Fab$k_Udf]:    !
			Fst$k_Non;     ! Non-RMS organization
		    [INRANGE]:
			Fst$k_Seq;	! Normal sequential org.
		    [OUTRANGE]:
			ReturnStatus( Rms$_Rfm );
		    TES );
		    end;
	    END;

	[INRANGE] :
	    !
	    !   No special handling for REL, IDX
	    !
	    Fst [Fst$h_Org] = .Fab [Fab$v_Org];
	TES;

!+
!   Fill in all fields in the FST.
!-
    fst [blocktype] = fstcode;			! Type of block
    fst [blocklength] = fstsize;		! And size [this is redundant;
    						!   the length is set in
    						!   RMSOPN]
    fst [blink] = .fst;				! Link this FST to itself
    fst [flink] = .fst;				!   and again...
    fst [fstjfn] = .userjfn;			! Save JFN
!+
!   We must now determine if we are going to lock this file
!   during its access.  This locking will be done only if there
!   is a writer of the file, and the file is not open for
!   exclusive access; locking is disabled when FB$SMU is set
!   in the FAC field, allowing LIBOL to handle locking itself.
!-
    flags = 0;					! Assume no locking

    IF rmsfile
    THEN

	IF ((.fab [fabfac, 0] AND fb$smu) EQL 0) AND 	! RMS locking	!M404
	    (((.fab [fabshr, 0] AND axwrt) NEQ 0) OR 	! If others will write
	    ((.fab [fabfac, 0] AND axwrt) NEQ 0))	! Or we will write
	THEN
	    flags = flglocking;			! We will lock each record

    						! Unless...

    IF .fab [fabshr, 0] NEQ axnil		! Nil sharing anyway?
    THEN
	fst [fstflags] = .flags;		! Sharing, so set locking

!+
!   We must now get some fields from the file prologue.
!   However, if the file is null (i.e., an empty ASCII file),
!   then we cannot even touch the page or we will get a read
!   trap.
!-

    IF rmsfile
    THEN
	BEGIN
!+
!   We must now set the LOBYTE field to be the start of the data
!   space of the file.  This information is necessary in order
!   to set the file EOF pointer on a $CLOSE, and to check a
!   user's RFA to make sure it is within the data space of the
!   file.
!-
	fst [fstlobyte] = 			! Start of records
	.fpt [blocklength] + endblocksize;
	END;

!   fst [fsthybyte] = 0;			! Clear these fields...
!   fst [fstflags] = 0;				! Store flags
    fst [fstfac] = .fab [fabfac, 0];
    fst [fstshr] = .fab [fabshr, 0];
!+
!   If the user specified NIL in the FAC field,
!   then we allow him to read and do no locking.
!-

    IF (.fab [fabfac, 0] EQL axnil) OR 		! NIL access	    !M404
	(.fab [fabfac, 0] EQL fb$smu)		! NIL with SMU set  !A404
    THEN
	fst [fstfac] = axget;			! Transparent read

    fst [fstrat] = .fab [fabrat, 0];
    fst [fstmrs] = .fab [fabmrs, 0];		! Maximum record size
    fst [fstmrn] = .fab [fabmrn, 0];		! Maximum record number
    fst [fstbsz] = .fab [fabbsz, 0];		! Byte size
    fst [fstrfm] = .fab [fabrfm, 0];		! Record format
    fst [fstfop] = .fab [fabfop, 0];		! File processing options
    fst [fstdevtype] = .dvflgs [dv_typ];	! Store device class
!+
!   We must now set up the KBFSIZE field, which represents the
!   size (in words) of a buffer which must be allocated on the
!   $CONNECT to hold the current key value (indexed files only).
!   However, the KDB information has not been set up yet, so we
!   will not fill in this field.  SETKDB will compute the value
!   and store it in the FST.
!-
!+
!   Fill in minimum number of buffers;
!   RMS requires at least this many.
!-
    fst [fstminbuf] = (				! Base count on organization
    CASE fileorg FROM orgasc TO orgidx OF
	SET
	[orgasc] : minbufasc;			! ASCII
	[orgseq] : minbufseq;			! RMS sequential
	[orgrel] : minbufrel;			! Relative
	[orgidx] : minbufidx			! Indexed
	TES);
!+
!   Fill in number of buffers to allocate.
!   (RMS should run "better" with more buffers.)
!   (Note that this field is the default number of
!   buffers.  The user may override by specifying
!   MBF in the RAB.)
!
!   Note that the number of buffers used for an indexed file is
!   a constant plus the maximum number of levels in any of the
!   indexes within the file.  This value is not known until we
!   process the File Prologue completely and set up the KDBs.
!   Therefore, we will not add in the MAXLEVEL value until later
!   when we set up all the KDBs.
!-
    fst [fstnumbuf] = (				!
    CASE fileorg FROM orgasc TO orgidx OF
	SET
	[orgasc] : numbufasc;			! ASCII
	[orgseq] : numbufseq;			! RMS Sequential
	[orgrel] : numbufrel;			! Relative
	[orgidx] : numbufidx;			! Indexed (increment later
						!   by number of levels in
						!   the file).
	TES);
!+
!   Fill in buffer size.
!
!   Note that this field will be filled with the
!   value 1 for now.  If this is an indexed file,
!   then the actual buffer size will be set later
!   when the Index Descriptors are read in.
!-
    fst [fstbufsiz] = defbufsiz;
    RETURN true
    END;					! End of SETFST
%SBTTL 'SETRST - initialize Record Status Table'

GLOBAL ROUTINE setrst =

!++
! FUNCTIONAL DESCRIPTION:
!
!	SETRST initializes the contents of the Record Status
!	Table.  This routine is called only from the
!	$CONNECT processor.  [It may, at some time in the
!	future, be called by a $REWIND processor.] It sets
!	up all the values in the Record Status Table,
!	including the Next-Record-Pointer.
!
!	Again, as in SETFST, the block allocated for the RST
!	is cleared by GMEM; some expressions which would
!	normally zero fields are therefore commented out,
!	although they are left for documentation.
!
!	Unlike SETFST (which expects to find an allocated
!	File Status Table waiting for it where FST is
!	pointing), SETRST itself will call GMEM to allocate
!	the core for the Record Status Table.  This is
!	because the actual amount allocated for the table
!	depends on the number of buffers allocated to the
!	record stream (see the opening code below).
!
! FORMAL PARAMETERS
!
!	None.
!
! IMPLICIT INPUTS
!
!	FST	-   the File Status Table is referenced to
!		    determine the number of buffers needed
!		    and the file organization.
!	RAB	-   the user's Record Access Block is read
!		    for buffer count and initial key of
!		    reference.
!	KDBs	-   The KDBs are checked to ensure that a
!		    valid key of reference has been chosen.
!
!
! COMPLETION CODES:
!
!	TRUE	-   Success
!	FALSE	-   Dynamic memory exhausted, or
!		    Bad key of reference
!		    (Error code in USRSTS.)
!
! SIDE EFFECTS:
!
!	FST [FSTSEQBKT] is set to -1 for ASCII files.
!
!	Core is allocated for the RST.
!
!	Core is allocated for the Key buffer.
!
!	Core is allocated for the file buffers.
!
!--

    BEGIN

    REGISTER
	tempac;					! Temporary AC

    LOCAL
	bytesword,
	keybuffsize,				! Size of the key buffer
	bufferpage,				! Page # of first buffer page
	totalpages,				! Number of buffer pages
	wordcount,
	bfdptr : REF BLOCK,			! Current buffer descriptor
	numbuf,					! Number of buffers to allocate
	enufbuf,				! Minimum buffers to allocate
	bufsiz,					! Pages per buffer
	eofbyte,				! Byte number of the EOF
	actrstsize,				! Actual size of this RST
	keyofref,				! Key of reference
	temp;

!
!   The meaning of the following comment is unclear, but
!   it is left for future generations to ponder.
!				- RL, March '83
!
!?! When not all buffers can be allocated
!
    TRACE ('SETRST');
    !
    !   Compute number buffers, size of buffer, etc.
    !
    enufbuf = .fst [fstminbuf];			! Minimum we must allocate
    bufsiz = .fst [fstbufsiz];			! Pages per buffer

    IF (numbuf = .rab [rabmbf, 0]) EQL 0	! Did user specify a count?
    THEN
	numbuf = .fst [fstnumbuf]		! No -- use default
    ELSE
	numbuf = MAX (.numbuf, .enufbuf);	! Yes -- but use

    						!   at least ENUFBUF
!+
!   The actual size of the RST is the size
!   of the basic structure itself plus
!   twice the number of buffers, for the
!   vector of buffer descriptors and the
!   vector of Least Recently Used values.
!-
    actrstsize = rstsize + 2*.numbuf;		! Compute actual RST size
!+
!   Allocate core for the RST.
!-

    IF (rst = gmem (.actrstsize)) EQL false	! Get core
    THEN
	returnstatus (er$dme);

!+
!   For indexed files, RABKRF must refer to an
!   existing key of reference.
!-

    IF idxfile
    THEN
	BEGIN
	rst [rstnrpref] = (keyofref = .rab [rabkrf, 0]);	! Fetch it
!+
!   Does a KDB exist for this key number?
!-

	IF getkdb (.keyofref) EQL false		! Locate KDB
	THEN
	    BEGIN
	    pmem (.actrstsize, rst);		! Free the memory
	    returnstatus (er$krf)		! Bad KRF
	    END;

	END;

    !
    !   Set up initial values.
    !
    rst [rstbfdcount] = .numbuf;		! Number of buffers
    rst [blocktype] = rstcode;			! Block-type code
    rst [blocklength] = .actrstsize;		! Length of an RST
    rst [rstfst] = .fst;			! Store address of FST
!	RST [ RSTRSZ ] = 0;			! These are commented out
!	RST [ RSTRSZW ] = 0;			!   as noted above
!	RST [ RSTDATARFA ] = 0;			!   in the routine
!	RST [ RSTFLAGS ] = 0;			!   description.
!	RST [ RSTLASTOPER ] = 0;		! Ditto
!	RST [ RSTBYTECOUNT ] = 0;		! Ditto
!+
!   For indexed files, we must now allocate a buffer to hold the
!   current key.  This buffer must be as large as the largest
!   key string defined for the file.  The size of this largest
!   key is kept in the FST.
!-
    keybuffsize = .fst [fstkbfsize];		! Get the size

    IF .keybuffsize NEQ 0
    THEN
	BEGIN
	lookat ('	KEY-BUFF-SIZE :', keybuffsize);
!+
!   If we can't get enough core for the keybuffer,
!   flush the RST and return ER$DME.
!-

	IF (rst [rstkeybuff] = gmem (.keybuffsize)) EQL false
	THEN
	    BEGIN
	    pmem (.actrstsize, 			! Size of RST
		rst);				! Location of RST
	    returnstatus (er$dme)
	    END

	END;

!++
!   Allocate buffers
!--

!+
!   Perform this loop until we either get a contiguous
!   buffer which is big enough, or we get below the
!   ENUFBUF value.
!-

    DO
	BEGIN
	!
	!   Try to get the buffer pages.
	!
	totalpages = .numbuf*.bufsiz;
!+
!   If we can't get enough, then release
!   the RST and the key buffer.
!-

	IF (bufferpage = gpage (.totalpages)) EQL false AND 	!
	    (numbuf = .numbuf - 1) LSS .enufbuf
	THEN
	    BEGIN
	    temp = .rst [rstkeybuff];		! Address of key buffer

	    IF .keybuffsize NEQ 0
	    THEN 				! Release key buffer
		pmem (.keybuffsize, 		! Size
		    temp);			! Location

	    pmem (.actrstsize, 			! RST size
		rst);				! Location of RST
	    returnstatus (er$dme)
	    END;

	tempac = .bufferpage
	END
    UNTIL .tempac NEQ false;			! END OF LOOP

!+
!   We now have allocated a big chunk of contiguous buffers.  We
!   must set up the buffer descriptors in the RST so we can use
!   the buffers in single units if we want to.
!-
    bfdptr = bfdoffset;
    rst [rstbfdcount] = .numbuf;		! Set buffer count

    INCR j FROM 1 TO .numbuf DO
	BEGIN
	bfdptr [bfdbpage] = .bufferpage;	! First page of buffer
	bfdptr = .bfdptr + 1;
	bufferpage = .bufferpage + .bufsiz;
	END;

!+
!   Setup buffer pointer for ASCII files.
!-

    IF asciifile                        ! ANY NON-RMS FILE
    THEN
	BEGIN
	!
	!   Form a pointer to the current
	!   bucket descriptor.
	!
	cbd = .rst + rstcbdoffset;		! Point to CBD in RST
!	bfdptr = bfdoffset;			! Form ptr to buf desc
	bfdptr = rst [rstbfd];			! Point to BFD vector
	fst [fstseqbkt] = -1;			! Force positioning on
						!   1st I/O operation
	currentfilepage = -1;			! Indicate no page mapped
	curentbufferadr = 			! Get pointer to buffer
	.bfdptr [bfdbpage]^p2w;
	cbd [bkdbfdadr] = .bfdptr;		! Set current BFD
	cbd [bkdbktsize] = asciibktsize;	! Set bucket size
	END;

!<BLF/PAGE>
!++
!	We are now ready to position the file.
!	There are several distinct cases which must be considered:
!
!	    Organization, access		Technique
!		=========			===
!
!	    Sequential, no appending		NRP = 1st byte after prologue
!
!	    Sequential, appending		NRP = EOF byte
!
!	    Relative, no appending		NRP = 1 (1st record)
!
!	    Relative, appending			<><> ILLEGAL <><>
!
!		Indexed				Undefined ( 0 )
!
!	    Stream, no appending		POSASCFILE (0)
!
!	    Stream, appending			POSASCFILE (EOF byte)
!
!--

!+
!   Determine the EOF byte number.
!-

    IF dasd AND (fileorg NEQ orgrel)		! Disk and not relative file?
    THEN
	eofbyte = sizefile (.fst [fstjfn])	! Yes -- return size of file
    ELSE
	eofbyte = -1;				! Not disk or relative file,

    						!   so no EOF
!+
!   If the file is zero-length, or we are
!   appending (and if this isn't a TTY:),
!   then we are at EOF.
!-

    IF (.eofbyte EQL 0 OR appendoption NEQ 0) AND 	!
	( NOT tty)				! Can't do it to TTY:
    THEN
	setflag (rst [rstflags], flgeof);	! Set EOF flag

    CASE fileorg FROM orgasc TO orgidx OF
	SET

	[orgasc] :
	    BEGIN				! ASCII

	    IF dasd
	    THEN
		BEGIN
!+
!   If RB$EOF is on, we will set the pointer to
!   the EOF.  Otherwise, we must set it to the
!   start of the file since another $CONNECT may
!   have been done to the file.
!-

		IF appendoption EQL 0 THEN eofbyte = 0;

		!
		!   Set pointer to start of file.
		!
		posascfil (.fst [fstjfn], 	! JFN
		    .eofbyte);			! Byte
		END;

	    END;

	[orgseq] :
	    BEGIN				! Sequential

	    IF (appendoption NEQ 0) AND 	! Appending and
		(.eofbyte NEQ 0)		!   non-zero file?
	    THEN
		rst [rstnrp] = .eofbyte		! Yes - point at EOF
	    ELSE
		rst [rstnrp] = .fst [fstlobyte];	! No - look at first

	    					!   byte after prologue
	    END;

	[orgrel] :
	    !
	    !	No choice for relative files
	    !
	    rst [rstnrp] = 1;			! Relative file

	[orgidx] :
	    !
	    !	Undefined for indexed files
	    !
	    rst [rstnrp] = 0;
	TES;

    !
    !	Finally, link the RST to the owning FST
    !
    link (rst, fst);
    RETURN true
    END;					! End of SETRST
%SBTTL 'READADB - read Area Descriptor Block'

GLOBAL ROUTINE readadb (prologptr : REF BLOCK) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	READADB reads in the Area Descriptor Block which is
!	stored in the initial prologue of an indexed file.
!	This routine must simply acquire core for the ADB
!	and transfer it into the new buffer.
!
!	This routine presupposes that all Area Descriptors
!	are contained within page 0 of the file.
!
! FORMAL PARAMETERS
!
!	PROLOGPTR   -	pointer to start of File Prologue
!
! IMPLICIT INPUTS
!
!	?
!
! COMPLETION CODES:
!
!	TRUE	-   OK
!	FALSE	-   Couldn't get core for the ADB
!		    File problem: No ADB
!
! SIDE EFFECTS:
!
!	On return, the location of the in-core
!	Area Descriptor Block will be stored in
!	the global variable ADB.
!
!--

    BEGIN

    REGISTER
	adbptr : REF BLOCK;			! Temporary ADB pointer

    LOCAL
	adblength;				! Length of the ADB

    TRACE ('READADB');
!+
!   First, we must find the location of the Area
!   Descriptors in the Prologue of the file.  This is
!   done by skipping over the initial file block because
!   the Area Descriptors are always the second block in
!   the File Prologue.
!-
    adbptr = 					! Skip over file block
    .prologptr [blocklength] + .prologptr;

    IF .adbptr [blocktype] NEQ adbcode
    THEN
	BEGIN					! We have a screwed-up
						!   File Prologue
	fileproblem (fe$noa);
	RETURN false
	END;

    adblength = .adbptr [blocklength];		! Fill in the length of
    						!   this block
!+
!   We must now allocate some free core for this block.
!-

    IF (adb = gmem (.adblength)) EQL false	! Get core
    THEN
	returnstatus (er$dme);

!+
!   Move the entire ADB into our free core chunk.
!-
    movewords (.adbptr, 			! From
	.adb, 					! To
	.adblength);				! Size
    RETURN true
    END;					! End of READADB
%SBTTL 'SETKDB - create KDB chain'

GLOBAL ROUTINE setkdb (prologptr : REF BLOCK) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	SETKDB reads in the chain of Index Descriptor Blocks
!	(IDBs) from the data file and creates the internal
!	chain of Key Descriptor Blocks (KDBs).  Each KDB is
!	composed of only a summary of the contents of the
!	IDB; only that information which will be needed
!	internally to process a key.  Each KDB is linked to
!	the next in a continuous linear series with a null
!	pointer in the last KDB.
!
!	There are also a few miscellaneous fields which must
!	be set up or altered in the FST by this routine.
!	For example, FSTNUMBUF is altered by adding the
!	number of levels in the deepest index in the file.
!	Also, the FSTKBFSIZE field is set up here to be able
!	to hold the largest key in the file.
!
! FORMAL PARAMETERS
!
!	PROLOGPTR   -	pointer to start of File Prologue
!
! IMPLICIT INPUTS
!
!	?
!
! COMPLETION CODES:
!
!	TRUE	-   OK
!	FALSE	-   Error (code in USRSTS)
!		    No more core
!		    Index bucket size not big enough
!		    No index descriptor in file (FE$NOI)
!
! SIDE EFFECTS:
!
!	FST fields are updated as noted above.
!
!--

    BEGIN

    LOCAL
	lastkdb : REF BLOCK,			! Last KDB we have created
	lastidb,				! Last IDB we have processed
	multipageflag,				! True if multi-page Prologue
	windowpage,				! Page into which we have
						!   mapped Prologue
	filepagenumber,				! Current page of PROLOGUE
	keybytesize,				! Bytesize for this key
	nextkdb,				! Next KDB in our chain
	maxlevels,				! Maximum number of levels
						!   for all indexes
	temp,					! Temporary local[??!!]
	totalsize,				! Total bytes in key string
	keybuffsize,				! Largest key buffer required
	biggestbkz,				! Biggest bucket size
	idbfileaddress,				! Address of IDB in the file
	endofkeybyte,				! Highest byte number
						!   of key string
	adbptr : REF BLOCK,			! Area descriptors
	areanumber,				! Area number for this index
	bucketsize,				! Bucket size for this index
	keysegptr : REF BLOCK,			! Key segment descriptors
	thiskdb : REF BLOCK,			! Current KDB
	idbptr : REF BLOCK,			! Current IDB
	xabptr : REF BLOCK;			! XAB portion of the IDB

    EXTERNAL
	dtptable : BLOCK;			! Key datatype table

    REGISTER
	bltac,
	tempac : BLOCK [1],
	tempac2;

    TRACE ('SETKDB');
    !
    !   We must now clear the pointer the list of KDBs.
    !
    fst [fstkdb] = 0;
    !
    !   Initialize some variables.
    !
    maxlevels = 1;				! Assume at least 1 level
    biggestbkz = 0;				! Largest bucket size
    filepagenumber = 0;
    windowpage = .prologptr^w2p;		! Find where our buffer is
    !
    !   Clear the largest key buffer value.
    !
    keybuffsize = 0;
    !
    !   We must find the first IDB in the File Prologue.
    !
    adbptr = .prologptr [fptadb] + .prologptr;	! Get Area Descriptor
    idbptr = .prologptr [fptidb] + .prologptr;	! ...and Index Descriptor
!+
!   Do a consistency check to see if this is an IDB.
!-

    IF .idbptr [blocktype] NEQ idbcode
    THEN
	BEGIN
	fileproblem (fe$noi);			! No index descriptor
	usrret ()
	END;

    idbfileaddress = 				! Form page 0 address
    (0^p2w) + (.idbptr AND ofsetmask);
    lastidb = (lastkdb = 0);			! Clear KDB address
!+
!   This is the main loop.  We will fetch each
!   IDB in the File Prologue area and create an
!   internal KDB for it.  If at any time our
!   free core runs out, we must unwind all this.
!-
    multipageflag = false;

    WHILE .idbfileaddress NEQ 0 DO
	BEGIN
!+
!   Get some core for a KDB.
!-

	IF (thiskdb = gmem (kdbsize)) EQL false
	THEN 					! We must unwind all KDBs
	    BEGIN
	    undokdbs ();

	    IF .multipageflag THEN ppage (.windowpage, 1, true);

	    returnstatus (er$dme)
	    END;

!+
!   We now are ready to set up the contents of
!   the KDB.  Note that this code could be
!   speeded up a little if certain fields which
!   are in both the IDB and KDB were BLTed
!   instead of moved one at a time.
!-
!+
!   Set up pointer to the XAB portion of the block.
!-
	xabptr = .idbptr + idbxaboffset;	! Make a pointer to
						!   the XAB portion
						!   of the IDB
!+
!   Set up header.
!-
	thiskdb [blockheader] = kdbcode^blktypelsh + kdbsize;
!+
!   Move common fields.
!-
	thiskdb [kdbroot] = .idbptr [idbroot];
	thiskdb [kdbnxt] = 0;			! Clear next pointer
!+
!   Update the maximum number of levels.
!-

	IF .idbptr [idblevels] GTR .maxlevels	!
	THEN
	    maxlevels = .idbptr [idblevels];	!

!+
!   Set up the pointer to the disk address of the IDB.
!-
	thiskdb [kdbidbaddr] = .idbfileaddress;
	!
	!   fill in the datatype of this key, and find
	!   the bytesize which is associated with
	!   that datatype.
	!
	thiskdb [kdbdtp] = .xabptr [xabdtp, 0];	! Get datatype
	keybytesize = .dtptable [.xabptr [xabdtp, 0], dtpbytesize];
	thiskdb [kdbkbsz] = .keybytesize;
	!
	!   Fetch the number of levels in the index.
	!
	thiskdb [kdblevels] = .idbptr [idblevels];
	!
	!   We will now fill in the fields from the
	!   XAB which is embedded within the IDB.
	!
	thiskdb [kdbref] = .xabptr [xabref, 0];
	!
	!   Move the XAB flags to the IDB, and
	!   clear the unused ones.
	!
	temp = .xabptr [xabflg, 0] AND allxabflags;

	IF .thiskdb [kdbroot] EQL 0		!
	THEN
	    setflag (temp, flgnoindex);

	thiskdb [kdbflags] = .temp;		! Store flags
	thiskdb [kdbdtp] = .xabptr [xabdtp, 0];
	!
	!   We will now set up the information about
	!   areas.  We will need to set up the area
	!   number and bucket size of both data and
	!   index buckets, and set up the word
	!   offset into a bucket which represents
	!   the IFL/DFL offset value.
	!
	tempac = .xabptr [xabian, 0];		! Get index area number
	thiskdb [kdbian] = .tempac;		! Store it
	bltac = (				! Set up AC for BLT
	thiskdb [kdbibkz] = 			!  and the bucketsize
	.adbptr [(.tempac*areadescsize) + 1, adbbkz]);	!
	!   If this bucket size is the largest one we have so far,
	!   remember how big it is (for future buffer allocation).
	!
	biggestbkz = MAX (.bltac, .biggestbkz);
	tempac2 = .bltac^b2w;			! Get maximum offset value
!+
!   Set up the index fill limit,
!   if user specified one.
!-

	IF .xabptr [xabifl, 0] GEQ (.tempac2/2)	! Must be 50 percent
	THEN
	    tempac2 = .xabptr [xabifl, 0];

	thiskdb [kdbifloffset] = .tempac2;
!+
!   Now do the data area.
!-
	tempac = .xabptr [xabdan, 0];		! Get data area number
	thiskdb [kdbdan] = .tempac;		! Store it
	bltac = (				!
	thiskdb [kdbdbkz] = 			!
	.adbptr [(.tempac*areadescsize) + 1, adbbkz]);
!+
!   Save the biggest bucketsize.
!-
	biggestbkz = MAX (.bltac, .biggestbkz);
	tempac2 = .bltac^b2w;			! Get maximum offset value

	IF .xabptr [xabdfl, 0] GEQ (.tempac2^divideby2lsh)	!
	THEN
	    tempac2 = .xabptr [xabdfl, 0];

	thiskdb [kdbdfloffset] = .tempac2;
!+
!   Now, set up key position and size.
!-
	movewords (.xabptr + xabksdoffset, 	! From
	    .thiskdb + kdbksdoffset, 		! To
	    maxkeysegs);			! Size
!+
!   We must now compute the total size of the key string (in
!   words).  This value is needed when we allocate key
!   buffers, and when we need to pass over an index record
!   which contains the key string.  We compute this value by
!   summing up the total size of the entire key (from each
!   key segment) and then using the bytesize of the key
!   data-type.
!   If the data is not byte-oriented (and therefore not segmented)
!   we can skip all that, since the size is fixed anyway.
!-

        keysegptr = .xabptr + xabksdoffset;     ! Point to first key segment


        CASE .xabptr [xabdtp,0] FROM 0 to maxdtp OF
             SET
             [dtpstg, dtpsix, dtpebc, dtpas8, dtppac]: ! Byte data
                 BEGIN
                 totalsize = 0;			     ! Initialize counter

                 INCR j FROM 0 TO maxkeysegs - 1     ! Loop over all segments
                 DO
                     BEGIN
                     totalsize = .totalsize + .keysegptr [.j, keysiz]
                     END;
                 END;
             [dtpin4, dtpun4, dtpfl1]:  ! Word data
                      totalsize = 1;                                    !M427
                      
             [dtpin8, dtpfl2, dtpgfl]:  ! Doubleword data
                      totalsize = 2;                                    !M427 
             TES;
!+
!   TOTALSIZE now contains the total number
!   of bytes in the key string.  We must now
!   compute the number of full words this
!   string will occupy.
!-
	lookat ('	TOTALSIZE: ', totalsize);
	thiskdb [kdbksz] = .totalsize;
	tempac = (thiskdb [kdbkszw] = sizeinwords (.totalsize, .keybytesize));

	IF .thiskdb [kdbkszw] GTR .keybuffsize
	THEN 					!
	!
	!	We need a bigger buffer to hold this key
	!
	    keybuffsize = .thiskdb [kdbkszw];

!+
!   We must now insure that the user's index
!   fill offset is large enough to allow a
!   minimum of three index records to be
!   manipulated in the same index bucket.
!   This restriction greatly simplifies the
!   algorithms for splitting the index
!   buckets, so the check should be done
!   here.
!-

	IF (temp = ((.tempac + irhdrsize)*3) + bhhdrsize) GTR 	!
	    .thiskdb [kdbifloffset]
	THEN 					!
	!
	!	Reset the IFL value to be higher
	!
	    thiskdb [kdbifloffset] = .temp;

!++
!		Now, compute the size of the record header.
!		There are three cases which must be considered:
!
!		   Type			Header size
!		   ====			===========
!
!		   Secondary			2
!
!		   Primary,variable		3
!
!		   Primary,fixed		2
!--

!+
!   Assume secondary keys.
!-
	tempac = sidrhdrsize;			! Size of a SIDR record

	IF .thiskdb [kdbref] EQL refprimary
	THEN 					! We have a primary data record
	    BEGIN

	    IF .fpt [fptrfm] EQL rfmfix
	    THEN 				! Fixed-length record
		tempac = fixhdrsize
	    ELSE 				! Variable-length record
		tempac = varhdrsize		! Use different header size
	    END;

	!
	!   Now, store this value in the KDB.
	!
	thiskdb [kdbhsz] = .tempac;
!+
!   We must now compute the minimum size
!   that the record must be in order to
!   fully include this key within the
!   record.  This computation is currently
!   trivial [!] due to the method of addressing
!   keys and the mapping of data-types onto
!   key byte sizes.  Once we have computed
!   this value, then later when a record is
!   inserted into the file and the record
!   size is less than this value, we won't
!   have to insert the record into the index
!   associated with this key string.
!-
	thiskdb [kdbminrsz] = fendofkey (.keybytesize, .keysegptr);
!+
!   We have now created the current KDB.  We must link
!   it into our chain of existing KDBs.
!-

	IF .lastkdb EQL 0			! Is there a "last KDB"?
	THEN
	    fst [fstkdb] = .thiskdb		! No - this is first
	ELSE
	    lastkdb [kdbnxt] = .thiskdb;	! Yes - add to chain

	lastkdb = .thiskdb;			! Update current pointer
	!
	!   Get the address of the next IDB in the file.
	!
	idbfileaddress = .idbptr [idbnxt];	! Bump next IDB pointer
!+
!   Does it span page boundaries?
!-

	IF (.idbfileaddress^w2p) NEQ .filepagenumber
	THEN
	    BEGIN

	    IF .multipageflag EQL false
	    THEN 				! Allocate a new page
		BEGIN
		multipageflag = true;

		IF (windowpage = gpage (1)) EQL false
		THEN
		    BEGIN
		    undokdbs ();
		    returnstatus (er$dme);
		    END

		END;

	    filepagenumber = .idbfileaddress^w2p;
!+
!   Map the File Prologue page in.
!-
	    pagin (.fst [fstjfn], 		! JFN
		.filepagenumber, 		! From
		.windowpage, 			! To
		axupd, 				! Access
		1);				! Count
	    END;

	idbptr = (.windowpage^p2w) OR 		! Use core page #
	(.idbfileaddress AND ofsetmask);	! And file offset
	lookat ('	NEXT IDB-PTR: ', idbptr);
	lookat ('	IDB-FILE-ADDRESS: ', idbfileaddress);
	!
	!   Print out what we've done.
	!

%IF dbug
%THEN
	begindebug (dbblocks)			!
	bugout(%STRING ('*Dump of KDB *: ', %CHAR (13), %CHAR (10)));
	dumpkdb (.thiskdb);
	enddebug;
%FI

	END;

!+
!   Store the key buffer size in the File Status Table.
!   Note that the value which is stored is twice that of
!   of the actual size of the key.  This is because the
!   top of the buffer is used for the actual current key
!   and the bottom half of the buffer is used for
!   temporary storage of the key in the last-record when a
!   bucket splits.
!-
    fst [fstkbfsize] = 2*.keybuffsize;
    !
    !   Set the number of pages in each
    !   file buffer into the FST.
    !
    fst [fstbufsiz] = .biggestbkz;
    !
    !   Add the # of levels into the number of buffers
    !   which this file needs to process correctly.
    !
    fst [fstnumbuf] = .fst [fstnumbuf] + .maxlevels;
!+
!   Release any extra pages we may have gotten.
!-

    IF .multipageflag				!
    THEN
	ppage (.windowpage, 1, true);

    RETURN true
    END;					! End of SETKDB
GLOBAL ROUTINE fendofkey (keybytesize, ksdptr : REF BLOCK) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	FENDOFKEY computes the byte number of the data
!	record which constitutes the last byte in the
!	specified key.  This value is necessary so we will
!	know whether we need to enter a particular record
!	into the index of the particular key.  This
!	computation is somewhat involved, so it is included
!	as a separate routine.
!
! FORMAL PARAMETERS
!
!	KEYBYTESIZE -	bytesize of this key
!	KSDPTR	    -	pointer to the first Key Segment
!			Descriptor in the XAB
!
! ROUTINE VALUE:
!
!	Byte number of last byte in key (expressed in
!	terms of the record's bytesize).
!
!--

    BEGIN                               !R411 Re-write this routine for
                                        !     mixed byte sizes

    LOCAL
	answer,					! Highest byte so far
        end_of_segment,                 	! Rec byte # for End of segment
	keybytesperword,			! Bytes of the key in each word
	recbytesperword,			! Bytes of record in each word
	recordbytesize,				! Byte size of the data record
        keyend,                         	! End of key in key-size bytes
	fullkeywords,				! Words occupied by key
	keybits;				! Bits occupied by key

    TRACE ('FENDOFKEY');
    !
    !   Initialize the final result to zero.
    !
    answer = 0;

    !+
    !   Do this loop, once for each key segment.
    !-
    INCR j FROM 0 TO maxkeysegs - 1 DO
	BEGIN
        !+
        !  Find the position of the end of the key
        !   (in key-byte-size bytes)
        !-
        keyend =  .ksdptr [.j, keypos] + .ksdptr [.j, keysiz];

        !+
        ! Find how many of each kind of bytes fit in a word
        !-
	recordbytesize = .fst [fstbsz];		! Get record byte size
	keybytesperword = %BPUNIT/.keybytesize;	! Key bytes in 1 word    !M411
	recbytesperword = %BPUNIT/.recordbytesize; ! Record bytes / word !M411

	!+
	!   Compute the number of full words to end of the key,
	!   and the bit offset in the word.
        !-
        fullkeywords = .keyend / .keybytesperword;                        !M411
        keybits = (.keyend MOD .keybytesperword) * .keybytesize;          !M411

	!
	!   Let's see some of this stuff.
	!
	lookat ('	KEYBYTESPERWORD:', keybytesperword);
	lookat ('	KEY ENDS AFTER WORD:', fullkeywords);
	lookat ('	KEY-BITS:', keybits);

	!+
	!   Compute minimum record size for this key.
        !   (in record-byte-size bytes)
	!-

	end_of_segment = (.fullkeywords*.recbytesperword)		  !M411
	                 +((.keybits+.recordbytesize-1)/.recordbytesize); !M411
        ! Convert number of full words into record bytes,
        ! and convert number of extra bits into record bytes
        ! The total is the number of record bytes we need to fit this segment

        !+
        !   Check if this key segment is "greater" than the highest one.
        !-
	IF .end_of_segment GTR .answer THEN answer = .end_of_segment

	END;

    lookat ('	Final result from FENDOFKEY: ', answer);
    RETURN .answer
    END;					! End of FENDOFKEY
GLOBAL ROUTINE undokdbs : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	UNDOKDBS gives back all KDB free memory if we
!	couldn't process the File Prologue correctly.
!
! FORMAL PARAMETERS
!
!	None.
!
! IMPLICIT INPUTS
!
!	FST [FSTKDB] - pointer to KDB chain.
!
! ROUTINE VALUE:
!
!	None.
!
! SIDE EFFECTS:
!
!	The free memory for the KDBs is freed.
!
!--

    BEGIN

    LOCAL
	thiskdb : REF BLOCK,
	nextkdb : REF BLOCK;

    TRACE ('UNDOKDBS');
    thiskdb = .fst [fstkdb];

    WHILE .thiskdb NEQ 0 DO
	BEGIN
	nextkdb = .thiskdb [kdbnxt];		! Save address of next KDB
	pmem (kdbsize, thiskdb);		! Release memory
	thiskdb = .nextkdb			! Advance our pointer
	END;

    RETURN
    END;					! End of UNDOKDBS

END						! End of Module DSI

ELUDOM