Google
 

Trailing-Edge - PDP-10 Archives - bb-h138f-bm - 7-sources/rmsfnd.b36
There are 6 other files named rmsfnd.b36 in the archive. Click here to see a list.
%title 'F I N D E R -- $FIND service routines'
!<BLF/REQUIRE 'RMSBLF.REQ'>
MODULE finder (IDENT = '3.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:
!
!	FINDER contains all routines which process
!	the $FIND service in RMS-20.
!
! ENVIRONMENT:	User mode, Top level
!
! AUTHOR: Ron Lusk , CREATION DATE:  1-Apr-83
!
! MODIFIED BY:
!
!	Ron Lusk, 7-Jul-83 : VERSION 2
! 01	-   Return relative record's page number in LSN if FB$SMU is set.
! 420	-   Use global addressing in fetching key buffer contents.
!       Andrew Nourse  Version 3
! 501   -   Implement remote FIND
! 504   -   Implement Image mode FIND
! 565	-   (RL) Return STV from FFF calls
!--

!
! TABLE OF CONTENTS
!
!
!	$FIND	    -	dispatcher for $FIND service
!	FINDASC	    -	$FIND for ASCII files
!       FINDIMA     -   $FIND for IMAGE files
!	FINDSEQ	    -	$FIND for Sequential files
!	FINDREL	    -	$FIND for Relative files
!	FINDIDX	    -	$FIND for Indexed files
!
!
! INCLUDE FILES:
!

REQUIRE 'rmsreq';

!
! MACROS:
!
!   None.
!
! EQUATED SYMBOLS:
!
!   None.
!
! OWN STORAGE:
!
!   None.
!
! EXTERNAL REFERENCES:
!
EXTERNAL ROUTINE
    F$Find;
%SBTTL '$FIND - $FIND dispatcher'

GLOBAL ROUTINE $find (rabblock, errorreturn) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	$FIND performs all processing of a $FIND macro.  It
!	is not called directly by any other routine within
!	RMS-20, but is called only from the primary RMS
!	dispatcher.
!
! FORMAL PARAMETERS
!
!	RABBLOCK	-   address of user Record Access Block
!		RABISI	-   Internal stream identifier
!		RABKBF	-   key buffer address (REL/IDX)
!		RABKRF	-   key of reference (IDX)
!		RABKSZ	-   size of key in buffer (IDX)
!		RABRAC	-   record access
!		RABRFA	-   record's file address (RFA access)
!		RABROP	-   record options
!		    RAH	-   Read-Ahead
!		    KGT	-   Key greater than (IDX)
!		    KGE	-   Key greater than or equal to (IDX)
!
!		    * RAB fields output *
!
!		RABBKT	-   relative record number of target (REL)
!		RABRFA	-   record's file address
!		RABSTS	-   status of operation
!		RABSTV	-   secondary status
!
!	ERRORRETURN	-   address of user error routine
!
! IMPLICIT INPUTS
!
!	?
!
! ROUTINE VALUE:
!
!	None, yet not novalue.
!
! SIDE EFFECTS:
!
!	?
!
!--

    BEGIN

    LOCAL
	!
	!	Data blocks for indexed files
	recdesc : BLOCK [rdsize],		! Record descriptor
	databd : BLOCK [bdsize];		! Bucket descriptor

    rmsentry ($find);
!+
!   Fetch the user's RAB and error address.
!-
    rab = .rabblock;				! Get RAB address
    erradr = .errorreturn;			!   and user error address
    rsetup (true);				! A $FIND is legal always


!+
!   Check for a remote file.  If it is use the DAP routine instead.
!-

    IF .Fst[Fst$v_Remote]
    THEN                                                                !m555
        Dap$Find( .Rab, .erradr )
    ELSE
!+
!   We now can dispatch to the correct routine to locate the
!   target record.  Notice that for all file organizations
!   except indexed, all parameters are stored in the Record
!   Status Table.  For indexed files, however, we must pass
!   a Record Descriptor and a Bucket Descriptor.  These
!   arguments are not used by this routine, but since they
!   are required for $GET, they will be passed fo FINDIDX.
!-

    CASE fileorg FROM 0 TO Fab$k_Idx OF
	SET

	[0] :                           ! Non-RMS file
            SELECT .Fst[Fst$h_File_Class] OF    		!a504 vv
                SET
                [0, Typ$k_Ascii]:
                    findasc ( true );                 ! ASCII           !m575
                [Typ$k_Image, Typ$k_Byte]:
                    findima ( true );                 ! IMAGE           !m575
                [OTHERWISE]:
                    BEGIN
                    F$Find( .Rab );                                     !m524 
		    usrstv = .rab [rab$h_stv];	! Return STV value	!A565

                    IF NOT $Rms_Status_Ok( .Rab )                       !a524
                    THEN Usererror( .Rab[Rab$h_Sts] );                  !a524
                    END;
                TES;                                             !a504 ^^

	[Fab$k_Seq] :
	    findseq ();				! Sequential

	[Fab$k_Rel] :
	    findrel ();				! Relative

	[Fab$k_Idx] :
	    findidx (recdesc, databd)		! Indexed
	TES;

    setsuccess;
    usrret ()					! Exit to user
    END;					! End of $FIND
%SBTTL 'FINDASC - $FIND for ASCII'

GLOBAL ROUTINE findasc ( eofcheck ) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Processes $FIND service for ASCII file.
!
! FORMAL PARAMETERS
!
!	EofCheck: Check for end-of-file if nonzero
!
! IMPLICIT INPUTS
!
!	RSTDATARFA  -	byte number of current record
!	RSTRSZ	    -	size in bytes of current record
!
! ROUTINE VALUE:
!
!	None.
!
! SIDE EFFECTS:
!
!	?
!
!--

    BEGIN

    LOCAL
	sizeoflastrecrd,
	temp;

    TRACE ('FINDASC');
!+
!   RFA access:
!-
![2] Using RFA from RAB, set up buffer, count, and pointers

    IF rfaadr
    THEN
	BEGIN

	LOCAL
	    pagenum,
	    rfanum,
	    pagebyte;

	! Clear End-Of-File, this is random access
	rst [rstflags] = .rst [rstflags] AND NOT flgeof;
	rst [rstdatarfa] = rfanum = .rab [rabrfa, 0];
	pagenum = .rfanum/ch_in_p;		! Get page number
	pagebyte = .rfanum MOD ch_in_p;		! and byte within page
	rst [rstnrp] = .pagenum;
	readbuffer ();
	! Read page containing start of record
	! Point page pointer to correct place in buffer
	rst [rstpagptr] = CH$PLUS (.rst [rstpagptr], .pagebyte);

	! Is RFA past end of file? If so, give EOF error

	IF .EofCheck AND (.rst [rstbytecount] LSS .pagebyte)           !m575
        THEN usererror (er$eof);	! Record not found

	! Update byte count
	rst [rstbytecount] = .rst [rstbytecount] - .pagebyte;
	RETURN true;
	END;

!+
!   SEQuential access:
!-
!+
!   If we are at EOF, then don't try finding a record.
!-

    IF .EofCheck AND endoffile THEN usererror (er$eof);                 !m575

!+
!   If the last RMS operation issued was a $FIND, or if the
!   last record fetched was too big for the user's buffer
!   (i.e., "partial" record), then we must skip a record and
!   position ourselves to the next one.  In all other cases
!   (i.e., the last JSYS was a $OPEN or $GET), we are
!   already positioned at the correct place.
!
!   This module also must insure that the current record's
!   starting address is maintained correctly.  This
!   procedure varies depending upon the current file
!   context.  For instance, if two successive $FINDs are
!   done, a record must be skipped and its length added to
!   the starting address of the last record.  If the last
!   record access was a "partial" record, the length of that
!   record must be added to its starting address, then the
!   rest of the record must be skipped and the amount that
!   was skipped also added into the current RFA address.
!
!   For normal conditions (i.e., a $FIND is done after a
!   successful $GET or $FIND), then the length of the last
!   record (i.e., current record to the user) must be added
!   to its starting byte number to produce the address of
!   the next record, which then immediately becomes the
!   current record.  This entire process must also be done
!   for sequenced files, except all bytes are in terms of
!   words and the line-number must be accounted for in
!   certain calculations.
!-

    IF (.rst [rstlastoper] EQL c$find) OR 	! If last RMS call was a $FIND
	(partialflag NEQ 0)			!   or last record was too big.
    THEN
	BEGIN					! To skip a record

	IF partialflag NEQ 0
	THEN 					! We must update current RFA
	    BEGIN
	    sizeoflastrecrd = .rst [rstrsz];	! Get last record's size
	    ! Compute start of this record
	    rst [rstdatarfa] = .rst [rstdatarfa] + .sizeoflastrecrd;
	    lookat ('	SIZEOFLAST: ', sizeoflastrecrd)
	    END;

	rst [rstrsz] = getascii (false);	! Skip a record
	clrflag (rst [rstflags], flgpartial);	! Clear the partial flag
	END;

!+
!   We must now update the RFA to point to the current
!   record's starting address.
!-
    rst [rstdatarfa] = 				! Compute start of next record
    .rst [rstdatarfa] + .rst [rstrsz];
!+
!   If this is a sequenced file, and if we need to update
!   the file pointer (such as on a $FIND-$FIND sequence),
!   then we must now move the file position to the next full
!   word in the file.
!-

    IF sequenced AND (chkflag (rst [rstflags], flgupdptr) NEQ 0)
    THEN
	BEGIN					! Move to next full word
	temp = (5 - .rst [rstdatarfa] MOD 5) MOD 5;
	rst [rstdatarfa] = .rst [rstdatarfa] + .temp;
	rst [rstbytecount] = .rst [rstbytecount] - .temp;
	temp = .rst [rstpagptr];		! Get sub-field

	IF .temp<30, 6> NEQ 36			! Word-aligned already?
	THEN
	    rst [rstpagptr] = POINT (.rst [rstpagptr] + 1, 36, 7);

!+
!   Clear the "update the pointer" flag.
!-
	clrflag (rst [rstflags], flgupdptr)
	END;

!+
!   Return RFA to user
!-
    rab [rabrfa, 0] = .rst [rstdatarfa];	![2] Return RFA to user
    RETURN true
    END;					! End of FINDASC
%SBTTL 'FINDIMA - $FIND for IMAGE'

GLOBAL ROUTINE findima ( EofCheck ) =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Processes $FIND service for IMAGE file.
!
! FORMAL PARAMETERS
!
!	EofCheck: Check for end-of-file if nonzero
!
! IMPLICIT INPUTS
!
!	RSTDATARFA  -	byte number of current record
!	RSTRSZ	    -	size in bytes of current record
!
! ROUTINE VALUE:
!
!	None.
!
! SIDE EFFECTS:
!
!	?
!
!--

    BEGIN

    LOCAL
	sizeoflastrecrd,
	temp;

    TRACE ('FINDIMA');
!+
!   RFA access:
!-
![2] Using RFA from RAB, set up buffer, count, and pointers

    IF rfaadr
    THEN
	BEGIN

	LOCAL
	    pagenum,
	    rfanum,
            bytesperpage,
	    pagebyte;

	! Check if last oper was sequential write
        ! If so flush buffer
        IF (.rst[rst$v_last_operation] EQL C$Put)
        AND .rst[rst$v_last_sequential]
        THEN WriteBuffer();

	rst[rst$v_eof] = 0;        ! this is random access
	rst [rstdatarfa] = rfanum = .rab [rabrfa, 0];
        bytesperpage = %BPVAL/.Fst[Fst$h_Bsz]*pagesize;
	pagenum = .rfanum/.bytesperpage;		! Get page number
	pagebyte = .rfanum MOD .bytesperpage;		! and byte within page
	rst [rstnrp] = .pagenum;
	readbuffer ();
	! Read page containing start of record
	! Point page pointer to correct place in buffer
	rst [rstpagptr] = CH$PLUS (.rst [rstpagptr], .pagebyte);
	! Is RFA past end of file? If so, give EOF error

	IF .EofCheck AND (.rst [rstbytecount] LSS .pagebyte)              !m575
        THEN usererror (er$eof);	! Record not found

	! Update byte count
	rst [rstbytecount] = .rst [rstbytecount] - .pagebyte;
        
        rst[rst$v_last_sequential] = 0; ! This is not sequential
	RETURN true;
	END;


!+
!   SEQuential access:
!-
    rst[rst$v_last_sequential] = 1;     ! This is sequential           !a577

!+
!   If we are at EOF, then don't try finding a record.
!-

    IF .EofCheck AND EndofFile THEN usererror (er$eof);                !m575

!+
!   If the last RMS operation issued was a $FIND, or if the
!   last record fetched was too big for the user's buffer
!   (i.e., "partial" record), then we must skip a record and
!   position ourselves to the next one.  In all other cases
!   (i.e., the last JSYS was a $OPEN or $GET), we are
!   already positioned at the correct place.
!
!   This module also must insure that the current record's
!   starting address is maintained correctly.  This
!   procedure varies depending upon the current file
!   context.  For instance, if two successive $FINDs are
!   done, a record must be skipped and its length added to
!   the starting address of the last record.  If the last
!   record access was a "partial" record, the length of that
!   record must be added to its starting address, then the
!   rest of the record must be skipped and the amount that
!   was skipped also added into the current RFA address.
!
!   For normal conditions (i.e., a $FIND is done after a
!   successful $GET or $FIND), then the length of the last
!   record (i.e., current record to the user) must be added
!   to its starting byte number to produce the address of
!   the next record, which then immediately becomes the
!   current record.  This entire process must also be done
!   for sequenced files, except all bytes are in terms of
!   words and the line-number must be accounted for in
!   certain calculations.
!-

    IF (.rst [rstlastoper] EQL c$find) OR 	! If last RMS call was a $FIND
	(partialflag NEQ 0)			!   or last record was too big.
    THEN
	BEGIN					! To skip a record

	IF partialflag NEQ 0
	THEN 					! We must update current RFA
	    BEGIN
	    sizeoflastrecrd = .rst [rstrsz];	! Get last record's size
	    ! Compute start of this record
	    rst [rstdatarfa] = .rst [rstdatarfa] + .sizeoflastrecrd;
	    lookat ('	SIZEOFLAST: ', sizeoflastrecrd)
	    END;

	rst [rstrsz] = getimage (false);	! Skip a record
	clrflag (rst [rstflags], flgpartial);	! Clear the partial flag
	END;

!+
!   We must now update the RFA to point to the current
!   record's starting address.
!-
    rst [rstdatarfa] = 				! Compute start of next record
    .rst [rstdatarfa] + .rst [rstrsz];

!+
!   Return RFA to user
!-
    rab [rabrfa, 0] = .rst [rstdatarfa];	![2] Return RFA to user
    RETURN true
    END;					! End of FINDIMA
%SBTTL 'FINDSEQ - $FIND for sequential file'

GLOBAL ROUTINE findseq =

!++
! FUNCTIONAL DESCRIPTION:
!
!	FINDSEQ processes the $FIND service for RMS
!	sequential files.
!
!	This routine exits directly to the user on an error.
!
! FORMAL PARAMETERS
!
!	None.
!
! IMPLICIT INPUTS
!
!	RSTDATARFA  -	byte number of current record
!	RSTRSZ	    -	size in bytes of current record
!
! ROUTINE VALUE:
!
!	None, yet not NOVALUE.
!
! SIDE EFFECTS:
!
!	?
!
!--

    BEGIN

    LOCAL
	temp1,
	recordlength,
	crp : BLOCK [1],			! Byte number of current record
	rfapagenum,				! Page number of current record
	nrp,					! Byte number of next record
	bytesword,				! Number of bytes in one word
	header : BLOCK [1],			! Storage for record header
	checkpageflag,				! On for RFA addressing,
						!   off for sequential
	valid;					! Flag for exit from loop

    REGISTER
	tempac;

    TRACE ('FINDSEQ');
!+
!   For sequential access, we can  assume the next page exists  unless
!   the current record address points to  the top of a page, in  which
!   case we must check  for that page.  However,  for RFA or  relative
!   addressing, we must check if the target page exists because if the
!   file is only being read, we  will get an illegal instruction  trap
!   if the page doesn't exist.  We can also clear the user's RFA field
!   unless he is using RFA addressing.
!-
    checkpageflag = false;

    IF seqadr                                                         !m567
    THEN
	BEGIN
	rab [rabrfa, 0] = 0;			! Clear the RFA field
	tempac = .rst [rstnrp];			! Next record pointer

	IF (.tempac^w2p)^p2w EQL .tempac	! Top of new page
	THEN
	    checkpageflag = true;		! Check for EOF

	END
    ELSE 					! Must be sequential addressing
	checkpageflag = true;			! Check for RFA addressing

!+
!   This is the main loop.  It continues until any of the
!   following are found:
!
!	1)  Record unavailable
!	2)  EOF
!	3)  Valid record found
!
!-
    valid = false;				! Set flag as invalid

    WHILE .valid EQL false DO
	BEGIN
!+
!   Fetch the user's record-access parameter (RAC) and use
!   it to determine the location of the next record.
!
!-
!+
!   Save the current record address.
!-

	CASE recordaccess FROM RAB$K_SEQ TO RAB$K_BFT OF            !m570
	    SET
	    [RAB$K_SEQ,
             RAB$K_TRA] : tempac =.rst [rstnrp];   ! Get NRP for sequential
	    [RAB$K_KEY] : tempac =.UAddr(rab [rabkbf, 0]);  ! User's key buffer
	    [RAB$K_RFA] : 
!+
!   If the current record address is less than the address
!   of the first data record, or is greater than the maximum
!   RFA, then there is an error.
!-
                        BEGIN
                 	tempac = .rab [rabrfa, 0];      ! Get user's RFA

                        IF (.tempac LSS .fst [fstlobyte])  ! Must be in
                        OR	(.tempac GEQ bitn (8))     ! file data space
                        THEN usererror (er$rfa);           ! Bad RFA
                        END;

            [RAB$K_BLK,
             RAB$K_BFT]: Usererror( er$bug );   ! Should not get here
	    TES;

	crp = .tempac;
!+
!   Dequeue the current record.  Note that if a record is
!   "found" twice in succession, RMS will still perform the
!   $FINDs as two separate operations:  the record will be
!   unlocked and then locked again.
!-

	IF datalocked
        THEN unlock (rst [rstdatarfa]);	! Unlock the current record

!+
!   Fetch the record pointer and make it current.
!-
	rst [rstdatarfa] = .crp;		! Save record pointer for user
!+
!   If the current page is in our window, then we don't have
!   to call an external routine to get it.
!-
	rfapagenum = .crp [rfapage];

	IF (.currentwindow EQL 0) OR 		! There is no current bucket
	    (.currentfilepage NEQ .rfapagenum)
	THEN
	    BEGIN				! Get the new page

	    IF getwindow (.rfapagenum, 		! Page number
		    .checkpageflag)		! Page must exist
		EQL false
	    THEN
		BEGIN				! Page does not exist

		IF rfaadr
		THEN 				! RFA error
		    usererror (er$rfa)
		ELSE 				! End of file
		    doeof ()

		END;

	    END;

!+
!   Now, the page we want is in our window.  We must set up
!   a pointer to it in the RST.
!-
	tempac = (rst [rstpagptr] = (.curentbufferadr) + .crp [ofset]);
!+
!   The record is now available to us.  We must pick up the
!   record header to see if it is a valid record.
!-
	header = ..tempac;
!+
!   If the header is an end-of-page marker, then we need to
!   bump our pointer to the start of the next file page.
!   This condition should occur only if the file has the
!   FB$BLK attribute.
!-

	IF .header EQL eopmarker		! Check for end-of-page
	THEN
	    BEGIN				! Found end-of-page marker
	    rtrace ('	End-of-page marker found');
!+
!   For RFA addressing, we can give an immediate error.
!-

	    IF rfaadr THEN usererror (er$rfa);

	    IF blocked
	    THEN 				! We can bump the pointer
	    					!   to the next file page
		nrp = (.crp OR ofsetmask) + %O'1'	! Bump to next page
	    ELSE
		rmsbug (msgeop);		! End-of-page marker found

	    END
	ELSE
	    BEGIN
	    lookat ('	Header read =  ', header);
	    !
	    !	Check that undefined bits in header are off.
	    !

	    IF .header [rhdrundef] NEQ 0	! Check unused part of header
	    THEN
		usererror (er$rfa);

!+
!   We will now compute the starting byte number of the next
!   record.  We will do this by computing the number of full
!   words this record occupies and adding the size of the
!   record header.
!-
	    recordlength = sizeinwords (.header [rhdrsize], .fst [fstbsz]);
	    nrp = .crp + .recordlength + headersize;
	    lookat ('	Updated NRP = ', nrp);

!++
!   We must now check to see if this record actually exists,
!   or if it has been deleted sometime in the past.  Before
!   we can check the "valid" bit for sure, we must lock the
!   record so than no one else can come in an delete it
!   after we have already examined the deleted bit.
!   However, the overhead for locking the record is extreme.
!   Therefore, we will check the bit to see if the record
!   exists or not.  If so, we will lock the record and check
!   again.  If either check fails, the record has been
!   deleted.  If it is deleted after we have checked the bit
!   but before we can lock it, that's OK because we will
!   continue and the record will be unlocked during the next
!   pass through this loop.
!--

!+
!   First, check that this record has been written; that is,
!   if the "valid" bit is on.
!-

	    IF (.header AND rhdrvalid) EQL 0
	    THEN 				! Is probably the EOF
		BEGIN

		IF rfaadr THEN usererror (er$rfa);	! Bad RFA

		doeof ()			! Sequential access means EOF
		END;

	    IF (.header AND rhdrdelete) EQL 0
	    THEN
		BEGIN

		IF locking THEN lockrec (crp);	! Lock record

		header = .(.rst [rstpagptr])	! Re-fetch header
		END;

!+
!   We now must check the header again to make sure it's
!   still good.
!-

	    IF (.header AND rhdrdelete) EQL 0
	    THEN
		valid = true			! Leave the loop
	    ELSE 				! Record has been deleted
		BEGIN

		IF rfaadr THEN usererror (er$del)

		END

	    END;

!+
!   Update all data pages.
!-
	rst [rstrszw] = .recordlength;		! Save record size in words
	rst [rstrsz] = .header [rhdrsize];	! And in bytes

	IF chkflag (rab [rabrop, 0], ropnrp) NEQ 0
	THEN
	    rst [rstnrp] = .crp			! Cause sequential GET to
	    					!   always get found record
	ELSE

	    IF (seqadr) OR (currentjsys EQL c$get)	!
	    THEN
		rst [rstnrp] = .nrp;		! Update the NRP if

						!   sequential access-mode
	END;

    !+
    !   Fall thru as soon as a valid record is found.
    !-

    lookat ('	Record found at: ', crp);
    lookat ('	NRP = ', nrp);
!+
!   Set up the user's RFA because the operation was OK.
!-
    rab [rabrfa, 0] = .rst [rstdatarfa];
    RETURN true
    END;					! End of FINDSEQ
%SBTTL 'FINDREL - $FIND for relative file'

GLOBAL ROUTINE findrel =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Processor for $FIND service for a relative file.
!
!	If FB$SMU is set in the FAC field, then return the
!	page number containing the record in the LSN field
!	of the user's RAB.  Zero LSN if a SMU find fails.
!
! FORMAL PARAMETERS
!
!	None.
!
! IMPLICIT INPUTS
!
!	?
!
! ROUTINE VALUE:
!
!	None.
!
! SIDE EFFECTS:
!
!	?
!
!--

    BEGIN

    LOCAL
	crp,
	tempnrp,
	header : BLOCK [1],
	temp,
	currentemtpage,				! Page number already checked
						!   to see if it exists
	valid,
	bytenum : BLOCK [1];

    REGISTER
	tempac;					! AC for temporary calculations

    TRACE ('FINDREL');
!+
!   Especially for FB$SMU, zero the LSN field of the RAB.
!-

    IF (.fab [fabfac, 0] AND fb$smu) NEQ 0	! SMU set?
    THEN
	rab [rablsn, 1] = 0;			! Zero the LSN

!+
!   Loop until a valid record is found.
!-
    valid = false;				! Assume record is not valid
    currentemtpage = 0;				! Clear this value
!+
!   In this loop, we will update the user's permanent NRP
!   only when a good record is found.  This is to avoid the
!   situation which would arise if the user tried to read a
!   record, when he was already at the last record in the
!   file.  In such a case, it is important that his NRP not
!   be changed from what it was before he attempted to read
!   the record.  There is a small drawback to this
!   technique, though.  If the user tries to read a record,
!   and the only one on thecurrent page is locked by
!   someone else, he will get an error message (as he
!   should), but his NRP will not have been changed from
!   what it was before he did the $GET.
!-
    tempnrp = .rst [rstnrp];			! Don't update real NRP until
    						!   operation succeeds

    WHILE .valid EQL false DO
	BEGIN
!+
!   Fetch the address of the target record, which depends on
!   the user's record-access (RAC) field value.
!-
	crp = (CASE recordaccess FROM racseq TO racrfa OF
	    SET
	    [racseq] : .tempnrp;		! Sequential (CRP = NRP)!M420
	    [rackey] : 				! Key retrieval		!M420
		BEGIN				! Fetch key value	!A420

		LOCAL 				!			!A420
		    kbuf;			! For building global	!A420

		kbuf = .rab [rabkbf, 0];	! User's key buffer	!A420

		IF .kbuf<lh> EQL 0		! Local address?	!A420
		THEN 				!			!A420
		    kbuf = .kbuf OR .blksec;	! Yes - build global	!A420

		..kbuf				! Return key value	!A420
		END;				!			!A420
	    [racrfa] : .rab [rabrfa, 0];	! RFA (CRP = RFA)	!M420
	    TES);
!+
!   Unlock the current record.
!-

	IF datalocked THEN unlock (rst [rstdatarfa]);

!+
!   Store the current record number in the RST.
!-
	rst [rstdatarfa] = .crp;		! Set CRP in user's RAB
!+
!   Find the starting byte address of this record.
!-

	IF (bytenum = numbertorfa (.crp)) EQL false
	THEN
	    BEGIN				! Record number was beyond MRN
!+
!   The record number of the target record was greater than
!   the maximum record number of the file.  If the user was
!   processing the file sequentially, then he should get an
!   EOF error; otherwise, he should get a record-not-found
!   error.
!-

	    IF seqadr THEN doeof ();

	    usrsts = er$key;			! Assume key error

	    IF rfaadr THEN usrsts = er$rfa;

	    usrerr ()				! Exit to user
	    END;

!+
!   We must now position to the target record.  However, we
!   must also check to see if the page exists.  If not, then
!   we may be through (if key access is being user).  If it
!   does exist, then we can go ahead and read it.
!-

	IF gtbyte (.bytenum, 			! RFA address
		true)				! Abort if non-existent
	    EQL false
	THEN
	    BEGIN				! Found a non-existent page

	    IF ( NOT seqadr)			! Record not found?
	    THEN
		usererror (er$rnf);

!+
!   This page is non-existent.  This could be caused either
!   by a sparse file, or by the true last page in the file.
!   If the file is sparse (there is more to come), then we
!   will continue to process it.  But if we have reached the
!   last page in the file, we must give an EOF error return.
!-

	    IF (tempac = .bytenum [rfapage]) NEQ (.currentemtpage)
	    THEN
		BEGIN				! We must check this page
		tempac = nextpage (.fst [fstjfn], .tempac);

		IF .tempac EQL false
		THEN
		    doeof ()			! If no more pages, it's EOF
		ELSE
		    currentemtpage = .tempac;	! Remember this page

		END

	    END
	ELSE
	    BEGIN
	    header = .(.rst [rstpagptr]);	! Fetch header

	    IF .header EQL eopmarker THEN rmsbug (msgptr);	! Bad header found

	    IF (.header [rhdrundef] NEQ 0) THEN usererror (er$rfa);	! Bad RFA

!+
!   Check to see if this record has been written.
!-

	    IF (.header AND rhdrvalid) EQL 0
	    THEN 				! The record was never created
		BEGIN
		rab [rabrfa, 0] = 0;		! Zap RFA

		IF NOT seqadr THEN usererror (er$rnf);

		END
	    ELSE 				! The record was written once
		BEGIN

		IF (.header AND rhdrdelete) EQL 0
		THEN
		    BEGIN

		    IF locking THEN lockrec (crp);

		    header = .(.rst [rstpagptr])
		    END;

!+
!   Recheck the record header.
!-

		IF (.header AND rhdrdelete) EQL 0	! Check delete bit
		THEN
		    valid = true		! Set flag so we'll fall thru
		ELSE 				! The record is deleted
		    BEGIN
!+
!   Decide what to do depending on RAC value.
!-

		    CASE recordaccess FROM 0 TO 2 OF
			SET

			[0] :
			    0;			! Sequential--do nothing

			[1] :
			    usererror (er$rnf);	! Key

			[2] :
			    usererror (er$del)	! RFA
			TES;

		    rab [rabrfa, 0] = 0
		    END

		END

	    END;

	lookat ('	Record header: ', header);
!+
!   Update user's parameter block.
!-

	IF (seqadr) OR (currentjsys EQL c$get)	!
	THEN
	    tempnrp = .crp + 1;			! Update loop variable to CRP+1

!+
!   Store the size in bytes and words of this record.
!-
	rst [rstrsz] = .header [rhdrsize];	! Save size of this record
	rst [rstrszw] = sizeinwords (.rst [rstrsz], .fst [fstbsz])
	END;

    IF (chkflag (rab [rabrop, 0], ropnrp) NEQ 0) AND 	! NRP?
	(currentjsys EQL c$find)		!   and $FIND?
    THEN
	rst [rstnrp] = .crp			! Make sequential GET to
						!   always be found record
    ELSE

	IF (seqadr) OR (currentjsys EQL c$get)	!
	THEN
	    rst [rstnrp] = .crp + 1;		! Update the NRP if sequential

    						!   access-mode
!+
!   Store the number of the current record in the RABBKT
!   field.  Note that if there is an error later during the
!   processing of this record, the BKT field will still
!   contain the number of the target record.  Thus, users
!   should not assume that a non-zero record number in BKT
!   reflects a successful operation.
!-
    rab [rabrfa, 0] = (rab [rabbkt, 0] = .crp);
!+
!   If we are doing a $FIND with FB$SMU set, return
!   the record's page number in the relative file
!   so LIBOL can do some locking.
!-

    IF (.fab [fabfac, 0] AND fb$smu) NEQ 0	! SMU in progress?
    THEN
	rab [rablsn, 1] = .bytenum [rfapage];	! Return the page number

    RETURN true
    END;					! End of FINDREL
%SBTTL 'FINDIDX - $FIND for ISAM file'

GLOBAL ROUTINE findidx (			! Find indexed record
    recdesc : REF BLOCK, 			! Record descriptor
    databd : REF BLOCK				! Bucket descriptor
    ) : NOVALUE =

!++
! FUNCTIONAL DESCRIPTION:
!
!	Routine to perform the $FIND service for an indexed
!	file.  This routine is called also by the $GET
!	processor in order to position to the correct
!	record.
!
!	If there is an error during this routine, it will
!	not return but will exit directly to the user.
!
! FORMAL PARAMETERS
!
!	RECDESC	    -	Record descriptor packet
!	    RFA	    -	RFA of target record (returned)
!	    RRV	    -	RRV address of target record (returned)
!
!	DATABD	    -	Data bucket descriptor, all fields
!			returned.
!
! IMPLICIT INPUTS
!
!	RAB	    -	User's Record Access Block
!	    RABRAC  -	Record access value
!	    RABKRF  -	Key of reference
!
! ROUTINE VALUE:
!
!	None.
!
! SIDE EFFECTS:
!
!	?
!
!--

    BEGIN

    LOCAL
	bdptr : REF BLOCK,			! Current bucket descriptor
	savedstatus,				! Store status here
	nextbucket,				! Next bucket for read-ahead
	bucketsize,				! Size of a bucket
	tptr : REF BLOCK,			! Temp pointer
	nextbd : BLOCK [bdsize],		! Descriptor of next bucket
	lockaccess,				! Shared/exclusive
	recordptr : REF BLOCK;			! Current record

    TRACE ('FINDIDX');
    kdb = .fst [fstkdb];			! Set up primary KDB
    !
    !   Clear some fields in the Record Descriptor which we will
    !   pass to the lower level routines.
    !
    recdesc [rdstatus] = 0;			! Clear status bits
    recdesc [rdflags] = rdflghorizok;		! Horizontal search OK
    fetchcurrentbkt (databd);			! Set current bucket to null
!+
!   Dispatch to the correct routine.
!-

    IF seqadr
    THEN
	BEGIN					! Sequential access
	setnullbd (cbd);			! Indicate no current bucket
	savedstatus = fidxseq (.recdesc, .databd);
	END
    ELSE
	BEGIN					! Random access
!+
!   We can release the current bucket because since this is
!   a random access, it is unlikely that both the target
!   record and the current record are in the same bucket.
!-
	releascurentbkt;
!+
!   We must now lock the entire index structure so that we
!   can move around the file at will until we reach the
!   target record.
!-

	IF locking
	THEN
	    BEGIN

	    IF lockindex (enqblk, enqshr) EQL false	! Wait, shared
	    THEN
		returnstatus (er$edq);		! Shouldn't fail

	    END;

!+
!   Dispatch to correct routine depending on record access.
!-

	IF keyadr THEN savedstatus = fbykey (.recdesc, .databd) ELSE savedstatus = frecrfa (.recdesc, .databd)

	;
	END;

!+
!   At this point, we have either succeeded or failed in
!   our efforts to locate the next record.  However, in
!   either case, we may have locked the index of the file
!   which now must be unlocked.  Also, if we located the
!   target record, we must lock its bucket if we have not
!   already done so.
!-

    IF locking AND .savedstatus NEQ false
    THEN
	BEGIN					! Do some unlocking & locking
!+
!   Lock the bucket if it is not locked.
!-

	IF ( NOT bktlocked (databd))
	THEN
	    BEGIN				! Lock the bucket descriptor
	    lockaccess = enqshr;		! Assume read-only

	    IF NOT inputmode THEN lockaccess = enqexc;

	    rtrace ('	Locking the bucket...');

	    IF lockbd (databd, enqaa, .lockaccess) EQL false
	    THEN
		BEGIN
		rtrace ('	***Lock already locked');
		savedstatus = false;		! Return failure
		putbkt (false, 			! No update
		    .databd);			! Bucket
		usrsts = er$rlk			! Set error code
		END

	    END;

!+
!   It is possible that another user could have deleted this
!   record between the time we found it and locked it.  If
!   this is the case, unlock the buffer and say the record
!   was never found.
!-
	recordptr = .recdesc [rdrecptr];

	IF deleteflag (recordptr) EQL 1
	THEN
	    BEGIN				! Record actually deleted
	    rtrace ('	Unlocking bucket - record deleted!');
	    unlockbucket (.databd [bkdbktno]);
	    savedstatus = false;		! Return failure
	    usrsts = er$rnf			! Set error code
	    END;

	END;

!+
!   Unlock the index structure, if necessary.
!-

    IF indexlocked THEN unlockindex;

!+
!   We have now done the $FIND.  Was it OK?
!-

    IF .savedstatus EQL false
    THEN
	BEGIN
	rtrace ('	Error in $FIND...');
	!
	!   Clear the user's RFA and our record pointer.
	!
	rst [rstdatarfa] = 0;

	IF NOT rfaadr THEN rab [rabrfa, 0] = 0;

	usrerr ()				! Exit to user
	END;

!+
!   The $FIND was successful.  We must now save the current
!   RFA and the current bucket.  However, if we are
!   currently processing a $GET macro, then we should return
!   without performing the normal clean-up operations, which
!   will be done in the $GET routine.
!-
    setcurrentbkt (databd);			! SAVE CURRENT BKT
!+
!   Save the pointer to the current record and set the
!   record size of this record (this is used only on $UPDATE
!   to check if the record size has been changed by the
!   user.  If record-length modification is supported, then
!   this operation is unnecessary.
!-
    rst [rstpagptr] = (recordptr = .recdesc [rdrecptr]);

    IF fixedlength				! What format?
    THEN
	rst [rstrsz] = .fst [fstmrs]		! Get record size from FST
    ELSE
	rst [rstrsz] = .recordptr [drrecsize];	! Get it from the record

    rst [rstrszw] = sizeinwords (.rst [rstrsz], .fst [fstbsz]);

    IF currentjsys EQL c$get THEN RETURN;	! Leave if we are $GETting

!+
!   Update the Next-Record-Pointer data in the RST.
!-
    setnrp (.recdesc, .databd);			! Update data base
!+
!   Set up the RFA in the user's RAB.
!-
    rab [rabrfa, 0] = .recdesc [rdrrv];
    RETURN;
    END;					! End of FINDIDX

END						! End of Module FINDER

ELUDOM