Google
 

Trailing-Edge - PDP-10 Archives - CFS_TSU04_19910205_1of1 - update/lngsrc/open.bli
There are 21 other files named open.bli in the archive. Click here to see a list.
MODULE OPEN (	! Open a file using RMS or DAP
		IDENT = '20'
                %BLISS36(,ENTRY(
                                r$open,       !open a file
                                r$create,     !create a new file
                                r$close,      !close a file
                                r$reset,      !abort a file
                                r$erase,      !delete a file
                                rl$close,     !close a local file
                                dap$openfile, !open a remote file (using dap)
                                dap$close,    !close a remote file (using dap)
                                dap$handle    !RMS DAP condition handler
                                ))
		) =
BEGIN
!  COPYRIGHT (C) DIGITAL EQUIPMENT CORPORATION 1981, 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: DAP/RMS
!
! ABSTRACT:
!     Open a file using DAP if the file is on another system,
!     or using RMS if it is on the same system.
!
! ENVIRONMENT: BLISSNET, RMS, XPORT, Transportable Code.
!
! AUTHOR:	Andrew Nourse, CREATION DATE: 2-Jan-82
!
! 20    - Poor-man's routing now invoked by setting PMRFLG to -1
! 17    - Undefined ASCII to non-stream sets CR only if no other RAT bits set
! 16    - R$OPEN should merge related filespec
!       - R$ERASE should call DAP$NEXTFILESPEC for additional filespecs
! 15    - R$OPEN & R$CREATE continue after signalling RMS$_SUP for remote-only
! 14    - Make R$RESET get rid of link.
! 13    - Have R$RESET check the NEW FILE bit in the FST
! 12    - Send ACCESS COMPLETE(PURGE) as interrupt message
! 11    - Work around IAS wierdnesses
! 10    - Tell RMS that file is stream if using block mode
! 07    - Don't set implied CRLF unless user does not know format
! 06    - Give DD's pointers to each other
! 05    - Make DAP$CLOSE eat entire DATA message also
! 04    - Make DAP$CLOSE eat entire ACCESS COMPLETE message
! 03    - Put in ENTRY points
! 02    - Eat messages in pipe on close
! 01	- The beginning
!--
!
! INCLUDE FILES:
!

LIBRARY 'RMS';
LIBRARY 'RMSBLK';
LIBRARY 'DAP';
LIBRARY 'BLISSNET';
LIBRARY 'CONDIT';

!
! TABLE OF CONTENTS:
!

FORWARD ROUTINE
	R$OPEN,           !Open a file
        R$CREATE,         !Create a new file
        R$CLOSE,          !Close a file
        R$RESET,          !Abort a file
        R$ERASE,          !Delete a file
        RL$CLOSE,         !Close a local file
        DAP$OPENFILE,     !Open a remote file (using DAP)
        DAP$CLOSE,        !Close a remote file (using DAP)
        DAP$HANDLE;       !Condition handler for DAP RMS routines
!
! MACROS:
!


!
! EQUATED SYMBOLS:
!

COMPILETIME MULTIPLE_FILESPECS=1;       ![16] On for mult-filespec support

LITERAL OBJ_FAL=17;                     !The FAL Object Type

%IF %BLISS(BLISS36)
    %THEN
    %IF %SWITCHES(TOPS20)
        %THEN
            LITERAL OUR_OSTYPE=DAP$K_TOPS20;
            LITERAL MA_RETURN = 1;

        %FI
    %FI

!
! OWN STORAGE:
!

GLOBAL
    PMRFLG: INITIAL(0);                 ! Set nonzero for poor-man's routing

!
! EXTERNAL REFERENCES:
!

EXTERNAL ROUTINE
                SETAI,
                DAP$PUT_MESSAGE,
                DAP$GET_MESSAGE,
                DAP$GET_CONFIG,
                DAP$PUT_CONFIG,
                DAP$PUT_ATTRIBUTES,
                DAP$PUT_BITVECTOR,
                DAP$PUT_ACCESS,
                DAP$PUT_BYTE,
                DAP$PUT_HEADER,
                DAP$GET_HEADER,
                DAP$GET_STATUS,
                DAP$GET_ATTRIBUTES,
                DAP$ERROR_DAP_RMS,
                DAP$MERGE,
                DAP$UNGET_HEADER,
                DAP$SIZE_BITVECTOR,
                DAP$GET_BYTE,
                DAP$GET_BITVECTOR,
                RMS$DIRECTORY,
                RMS$SEARCH,
                RMS$MERGE,
                RL$CLMACY11,
                RL$ERASE,
                R$NULL,
                XPN$SIGNAL;
%IF MULTIPLE_FILESPECS
%THEN
EXTERNAL ROUTINE DAP$NEXTFILESPEC;
%FI
EXTERNAL D_CFG;
EXTERNAL T20BUG: BITVECTOR,
         VMSBUG: BITVECTOR,
         RSXBUG: BITVECTOR,
         RSTBUG: BITVECTOR,
         IASBUG: BITVECTOR,
         T10BUG: BITVECTOR,
         RTBUG:  BITVECTOR;
EXTERNAL COPYRIGHT; ! Keep the legal office happy
GLOBAL ROUTINE R$OPEN (FAB,ERR)  =	! Open a (possibly remote) file

!++
! FUNCTIONAL DESCRIPTION:
!
!       Open a file.
!       If the filename contains a remote node-id,
!       communicate with FAL on the remote system to open the file,
!       otherwise, open it using RMS.
!
! FORMAL PARAMETERS:
!
!	FAB: An RMS FAB, ready to open a file.
!       ERR: Address of error routine
!    
!
! COMPLETION CODES:
!
!	RMS$_SUC or RMS error code
!
! SIDE EFFECTS:
!
!	Associated data structures (for RMS or DAP) allocated
!
!--

    BEGIN
    MAP FAB: REF $FAB_DECL;
    BIND ROUTINE $$ERRRTN=.ERR: RMS_ERCAL;      ! Error routine
    BIND TYP=.FAB[FAB$A_TYP]: $TYP_DECL;
    BIND NAM=.FAB[FAB$A_NAM]: $NAM_DECL;        ! Name block, if present
    LOCAL CLASS;

    IF TYP NEQ 0
    THEN CLASS=.TYP[TYP$H_CLASS]
    ELSE CLASS=TYP$K_CLASS_ASCII;

    IF .CLASS EQL 0 THEN CLASS=TYP$K_CLASS_ASCII;

    IF FIND_SUBSTRING(.FAB[FAB$A_FNA],PP('::')) NEQ 0
    THEN
        BEGIN
        FAB[FAB$V_REMOTE]=1;                    ! Remember

        IF NAM NEQ 0                            ![16] Merge related file
        THEN DAP$MERGE(FAB[$],MERGE$M_RLF,.ERR);! 

        DAP$OPENFILE(FAB[$],DAP$K_OPEN,0,.ERR)    ! File is remote.
        END
    ELSE
        BEGIN                                  ! File is Local
        LOCAL RFM, MRS;

        ! Caller can set this bit before the call to disable local files.
        IF .FAB[FAB$V_REMOTE]       ! Must it be remote?
        THEN                                   ! If caller sets the bit
            BEGIN                              ! (s)he does not support
            FAB[FAB$H_STS]=RMS$_SUP;           ! local RMS file access
            $$ERROR(OPEN,FAB[$]);              ! Which this is. complain
            RETURN .FAB[FAB$H_STS]             ![15] Do not continue
            END;

        IF (NAM NEQ 0)                         ! Fill in NAM block if any
        THEN                                   ! 
            BEGIN                              ! Name block exists
            IF .NAM[NAM$H_WCC_COUNT] EQL 0     ! If Search has not been done
            THEN                               !  then we should do one
                BEGIN                          !  so wildcarding will work
                RMS$DIRECTORY(FAB[$],.ERR);    ! Check for wildcards.
                RMS$SEARCH(FAB[$],R$NULL);     !  and fill in NAM block
                IF .FAB[FAB$V_FOP_CIF] EQL 0   ! Create-if nonexistant?
                AND (.FAB[FAB$H_STS] NEQ RMS$_SUC) ! successful?
                THEN $$ERROR(OPEN,FAB[$])      ! Give error if not
                END;                           ! with components of filespec
            END;                               ! NAM block processing

        IF (.CLASS EQL TYP$K_CLASS_MACY11)     ! For MACY11 files:
        OR .FAB[FAB$V_FAC_BIO]                 ![10] or if BLOCK I/O
        THEN                                   ! 
            BEGIN                              ! Pretend ASCII if MACY11
            RFM=.FAB[FAB$Z_RFM];               ! because RMS does not yet
            FAB[FAB$Z_RFM]=FAB$K_RFM_STM;      ! really know about MACY11 files
            MRS=.FAB[FAB$H_MRS];               ! Save the original MRS value
            END;                               ! 

        IF (.FAB[FAB$Z_RFM] EQL FAB$K_RFM_UDF) ! If record format not known
        THEN FAB[FAB$Z_RFM]=FAB$K_RFM_STM;     ! Default to /STREAM

        IF .FAB[FAB$V_FOP_CIF]                 ! If create-if, do a $CREATE
        THEN $CREATE(FAB=FAB[$],ERR=.ERR)      !  since RMS-20 ignores CIF
        ELSE $OPEN(FAB=FAB[$],ERR=.ERR);       !  on $OPEN

        IF (.CLASS EQL TYP$K_CLASS_MACY11)     ! For MACY11 files:
        OR .FAB[FAB$V_FAC_BIO]                 ![10] or if BLOCK I/O
        THEN                                   ! 
            BEGIN                              ! Restore what was
            FAB[FAB$Z_RFM]=.RFM;               ! changed to satisfy RMS
            FAB[FAB$H_MRS]=.MRS;               ! or changed by RMS
            END;                               ! 
        END;                                   ! Local File Open

    .FAB[FAB$H_STS]                     ! Returned status
    END;			!End of R$OPEN
GLOBAL ROUTINE R$CREATE (FAB,ERR)  =       ! Create a (possibly remote) file
!++
! FUNCTIONAL DESCRIPTION:
!
!       Open a new file.
!       If the filename contains a remote node-id,
!       communicate with FAL on the remote system to open the file,
!       otherwise, open it using RMS.
!
! FORMAL PARAMETERS:
!
!	FAB: An RMS FAB, ready to open a file.
!       ERR: Address of error routine
!
! COMPLETION CODES:
!
!	RMS codes
!
! SIDE EFFECTS:
!
!	A new file will be created
!
!--

    BEGIN
    MAP FAB: REF $FAB_DECL;
    BIND ROUTINE $$ERRRTN=.ERR: RMS_ERCAL;      ! Error routine

    BIND NAM=.FAB[FAB$A_NAM]: $NAM_DECL;        ! Name block if present
    BIND TYP=.FAB[FAB$A_TYP]: $TYP_DECL;
    LOCAL CLASS;
    LOCAL RFM, MRS;

    IF TYP NEQ 0
    THEN CLASS=.TYP[TYP$H_CLASS]
    ELSE CLASS=TYP$K_CLASS_ASCII;

    IF .CLASS EQL 0 THEN CLASS=TYP$K_CLASS_ASCII;

    IF FIND_SUBSTRING(.FAB[FAB$A_FNA],PP('::')) NEQ 0
    THEN
        BEGIN
        FAB[FAB$V_REMOTE]=1;                    ! Remember

        IF NAM NEQ 0
        THEN DAP$MERGE(FAB[$],MERGE$M_CREATE+MERGE$M_RLF,.ERR);

        DAP$OPENFILE(FAB[$],DAP$K_CREATE,0,.ERR)  ! File is remote.
        END
    ELSE
        BEGIN
        ! Caller can set this bit before the call to disable local files.
        IF .FAB[FAB$V_REMOTE]       ! Must it be remote?
        THEN
            BEGIN
            FAB[FAB$H_STS]=RMS$_SUP;
            $$ERROR(OPEN,FAB[$]);   ! Yes. complain
            RETURN .FAB[FAB$H_STS]      ![15] Do not continue
            END;

        IF NAM NEQ 0
        THEN
            BEGIN                       ! Merge related filespec, get resultant
            RMS$MERGE(FAB[$],MERGE$M_CREATE+MERGE$M_RLF,.ERR);
            END;

        IF .CLASS EQL TYP$K_CLASS_MACY11
        THEN
            BEGIN
            RFM=.FAB[FAB$Z_RFM];        ! Save real record format
            MRS=.FAB[FAB$H_MRS];
            FAB[FAB$Z_RFM]=FAB$K_RFM_STM;
            END;

        IF .FAB[FAB$Z_RFM] EQL FAB$K_RFM_UDF    ! If record format not known
        THEN FAB[FAB$Z_RFM]=FAB$K_RFM_STM;      ! Default to /STREAM

        $CREATE(FAB=FAB[$],ERR=.ERR);

        IF .CLASS EQL TYP$K_CLASS_MACY11        ! Restore real record format
        THEN
            BEGIN
            FAB[FAB$Z_RFM]=.RFM;
            FAB[FAB$H_MRS]=.MRS;
            END;
        END;
    .FAB[FAB$H_STS]                     ! Return status code
    END;			!End of R$CREATE
GLOBAL ROUTINE R$ERASE (FAB,ERR)  =       ! Delete a (possibly remote) file

!++
! FUNCTIONAL DESCRIPTION:
!
!       Delete a file.
!       If the filename contains a remote node-id,
!       communicate with FAL on the remote system to delete the file,
!       otherwise, delete it using RMS.
!
! FORMAL PARAMETERS:
!
!	FAB: An RMS FAB, ready to open a file.
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!	RMS$_SUC or RMS error code
!
! SIDE EFFECTS:
!
!	NONE
!
!--

    BEGIN
    MAP FAB: REF $FAB_DECL;

    IF FIND_SUBSTRING(.FAB[FAB$A_FNA],PP('::')) NEQ 0
    THEN
        BEGIN
        %IF MULTIPLE_FILESPECS          ![16] Try for multiple filespecs
        %THEN
        DO  BEGIN
            FAB[FAB$V_REMOTE]=1;                     ! Remember
            DAP$OPENFILE(FAB[$],DAP$K_ERASE,0,.ERR)  ! File is remote.
            END WHILE DAP$NEXTFILESPEC(FAB[$],.ERR)  ![16] try for more
        %ELSE
        FAB[FAB$V_REMOTE]=1;                     ! Remember
        DAP$OPENFILE(FAB[$],DAP$K_ERASE,0,.ERR)  ! File is remote.
        %FI
        END
    ELSE RL$ERASE(FAB[$],.ERR);
    .FAB[FAB$H_STS]                     ! Return status code
    END;			!End of ERASE
GLOBAL ROUTINE R$CLOSE (FAB,ERR)  =	! Close a (possibly remote) file

!++
! FUNCTIONAL DESCRIPTION:
!
!       Close a file
!
! FORMAL PARAMETERS:
!
!	FAB: An RMS FAB for open file.
!       ERR: Address of error routine
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!	RMS$_SUC or RMS error code
!
! SIDE EFFECTS:
!
!	Internal blocks deallocated
!
!--

    BEGIN
    MAP FAB: REF $FAB_DECL;

    IF .FAB[FAB$V_REMOTE]
    THEN
        BEGIN
        DAP$CLOSE(FAB[$],DAP$K_ACCOMP_COMMAND,.ERR)     ! File is remote.
        END
    ELSE
        BEGIN
        RL$CLOSE(FAB[$],.ERR);          ! Call local close routine
        END;
    .FAB[FAB$H_STS]                     ! Returned status code
    END;			!End of R$CLOSE
GLOBAL ROUTINE R$RESET (FAB,ERR)  =	! Reset a (possibly remote) file
!++
! FUNCTIONAL DESCRIPTION:
!
!       Reset (close/abort) a file
!
! FORMAL PARAMETERS:
!
!	FAB: An RMS FAB for open file.
!       ERR: Address of error routine
!
! COMPLETION CODES:
!
!	RMS$_SUC or RMS error code
!
! SIDE EFFECTS:
!
!	Internal blocks deallocated
!
!--

    BEGIN
    MAP FAB: REF $FAB_DECL;

    IF .FAB[FAB$V_REMOTE]
    THEN                                ! File is remote.
        BEGIN
        DAP$CLOSE(FAB[$],DAP$K_ACCOMP_PURGE,.ERR);      ! Close file

        IF (.FAB[FAB$H_FOP] NEQ RMS$_SUC)       ![14] If close failed
        AND (.FAB[FAB$V_FOP_DRJ] EQL 0) ![14] and link should close too
        THEN
            BEGIN
            BIND DIB=.FAB[FAB$A_IFI]: $DIB;
            DIB[DIB$V_ACCESS_ACTIVE]=DIB[DIB$V_FILE_OPEN]=0; ![14] just break
            DAP$CLOSE(FAB[$],DAP$K_ACCOMP_PURGE,.ERR)        ![14] link
            END;                        
        END
    ELSE
        BEGIN
        BIND FST=.FAB[FAB$A_IFI]: $RMS_FST;
        IF .FST[FST$V_NEW_FILE]              ![13] NEVER DELETE AN OLD FILE
        THEN $FAB_STORE(FAB=FAB[$],FOP=DLT); ! Set DELETE bit
        R$CLOSE(FAB[$],.ERR);           ! and close it
        END;
    .FAB[FAB$H_STS]                     ! Returned status code
    END;			!End of R$RESET
GLOBAL ROUTINE RL$CLOSE (FAB: REF $FAB_DECL, ERR)=
    BEGIN
    IF .FAB[FAB$A_IFI] NEQ 0        ! File really open?
    THEN
        BEGIN
        BIND TYP=.FAB[FAB$A_TYP]: $TYP_DECL;        ! Datatype block
        LOCAL DRJ;
        LOCAL CLASS;
        DRJ=.FAB[FAB$V_FOP_DRJ];        ! Save DRJ bit
        IF .FAB[FAB$V_FOP_DLT] THEN FAB[FAB$V_FOP_DRJ]=1; ! Keep JFN if delete
        CLASS=(IF TYP NEQ 0 THEN .TYP[TYP$H_CLASS] ELSE TYP$K_CLASS_ASCII);
        CASE .CLASS FROM 0 TO TYP$K_CLASS_MAX OF
             SET
             [TYP$K_CLASS_MACY11]: RL$CLMACY11(FAB[$],.ERR);
             [INRANGE]:  $CLOSE(FAB=FAB[$],ERR=.ERR);
             [OUTRANGE]: SIGNAL(FAB[FAB$H_STS]=RMS$_BUG,0,FAB[$]);
             TES;

        IF .FAB[FAB$V_FOP_DLT]              ! Should we delete it?
        THEN $ERASE(FAB=FAB[$]);            ! yes indeed

        FAB[FAB$V_FOP_DRJ]=.DRJ;        ! Restore DRJ
        END;

    %IF %SWITCHES(TOPS20)
        %THEN
            BEGIN
            LIBRARY 'TWENTY';
            REQUIRE 'JSYSDEF';
            IF (.FAB[FAB$H_JFN] NEQ 0)      ! Is the JFN still there?
            AND (.FAB[FAB$V_FOP_DRJ] EQL 0) !  and should go away
            THEN
                BEGIN
                JSYS_RLJFN(.FAB[FAB$H_JFN]); ! Try to release it
                FAB[FAB$H_JFN]=0;            !  and zero it
                END;
            END;
        %FI
    .FAB[FAB$H_STS]                     ! Return status
    END;                                ! RL$CLOSE
GLOBAL ROUTINE DAP$OPENFILE (FAB: REF $FAB_DECL,
                             FUNCTION,
                             NFAB: REF $FAB_DECL,
                             ERR): =
!++
! FUNCTIONAL DESCRIPTION:
!
!   OPEN/CREATE/... A REMOTE FILE
!
! FORMAL PARAMETERS:
!
!   FAB: An RMS FAB with a NODEID embedded in the filespec.
!   FUNCTION: ACCESS message function code (DAP) or 0 to exchange configs only
!   NFAB: New FAB for Rename
!   ERR: Address of error routine
!
! COMPLETION CODES:
!
!   RMS codes
!
! SIDE EFFECTS:
!
!  If .FAB[FAB$A_DIB] is zero:
!   A DIB is allocated.  FAB[FAB$A_DIB] will point to it
!   A NLB is allocated.  DIB[DIB$A_NLB] will point to it
!   2 $DAP_DESCRIPTORs are allocated, DIB[DIB$A_(I,O)_DD] point to them
!   A Configuration Block is allocated. DIB[DIB$A_CONFIG] will point to it
!
!   For ERASE and EXECUTE, the requested operation will have been completed
!   and the above data strucures de-allocated.
!--
    BEGIN
    MAP FAB: REF $FAB_DECL;
    MAP NFAB: REF $FAB_DECL;          ! New name for rename
    LOCAL FABSAV: VOLATILE;

    LOCAL NLB : REF $XPN_NLB();
    LOCAL DIB : REF $DIB;
    LOCAL ACCOPT: BITVECTOR[35] INITIAL(0),
          DISPLAY: BITVECTOR[28] INITIAL(0);
    LOCAL CFGBLK: REF $CONFIG;
    LOCAL OBUF,
          IDD: REF $DAP_DESCRIPTOR,
          ODD: REF $DAP_DESCRIPTOR;
    LOCAL CLASS;
    LOCAL ERRSAV: VOLATILE;

    ENABLE DAP$HANDLE(FABSAV,ERRSAV);
    ERRSAV=.ERR;
    FABSAV=.FAB;                        ! Handler will need this

    IF .FAB[FAB$A_DIB] EQL 0
    THEN
        BEGIN
        $XPO_GET_MEM(UNITS=DAP$K_BUFFER_SIZE %BLISS36(/4), RESULT=OBUF);
                                            ! Allocate output buffer

        $XPO_GET_MEM(UNITS=DAP$K_DESCRIPTOR_LEN, RESULT=ODD, FILL=0);
        $XPN_DESC_INIT(DESCRIPTOR=ODD[$],CLASS=DYNAMIC_BOUNDED,
                       BINARY_DATA=(DAP$K_BUFFER_SIZE,.OBUF,BYTES));

        $XPO_GET_MEM(UNITS=DAP$K_DESCRIPTOR_LEN, RESULT=IDD, FILL=0);
        $XPN_DESC_INIT(DESCRIPTOR=IDD[$],CLASS=BOUNDED);

        $XPO_GET_MEM(UNITS=NLB$K_LENGTH, RESULT=NLB, FILL=0); ! Allocate an NLB
        $XPN_NLB_INIT(NLB=NLB[$]);

        $XPO_GET_MEM(UNITS=DIB$K_LENGTH, RESULT=DIB, FILL=0); ! Allocate a DIB

        !Now set up the pointers:
        !
        !   +---------+   +---------+   +---------+
        !   |   FAB   |-->|   DIB   |-->| CONFIG  |
        !   +---------+   +----+----+   +---------+
        !            ,----'    V    `-.
        !   +---------+   +---------+ :  
        !   |   IDD   |   |   ODD   | :  
        !   +---------+   +--+------+ :
        !            `--.    V        ;
        !            +-----------+<--'
        !            |    NLB    |
        !            +-----------+
        ! 

        FAB[FAB$A_DIB]=DIB[$];              ! Point the Fab at the DIB
        DIB[DIB$A_NLB]=NLB[$];              ! Point the DIB at the NLB
        DIB[DIB$A_I_DD]=IDD[$];             !  ... and the input descriptor
        DIB[DIB$A_O_DD]=ODD[$];             !  ... and the output descriptor

        ODD[DAP$A_NLB]=NLB[$];              ! Output descriptor points to NLB
        IDD[DAP$A_NLB]=NLB[$];              ! Input descriptor ...

        IDD[DAP$A_OTHER_DD]=ODD[$];         ![6] DD's should point
        ODD[DAP$A_OTHER_DD]=IDD[$];         ![6]  to each other

        $XPO_GET_MEM(UNITS=CONFIG$K_LEN, RESULT=CFGBLK, FILL=0); ! Alloc config
        DIB[DIB$A_CONFIG]=CFGBLK[$];        ! Point the Dib at it

        SET_ACCESS_INFO (NLB[$],.FAB[FAB$A_FNA]);   ! Extract access info
                                                        ! from nodename

        !++
        ! Open the link
        !--

        IF .PMRFLG NEQ 0  ![10] PMRFLG is patched to enable poor-man's routing
        THEN
        $XPN_OPEN (NLB=NLB[$], OBJECT=OBJ_FAL, TYPE=ACTIVE,
                   OPTIONS=(WAIT,PMR),                  ! ask for PMR
                   BUFFER_SIZE=DAP$K_BUFFER_SIZE,       ! Tell PMR buffer size
                   TIMEOUT = 180, FAILURE=XPN$SIGNAL)
        ELSE
        $XPN_OPEN (NLB=NLB[$], OBJECT=OBJ_FAL, TYPE=ACTIVE,
                   OPTION=WAIT,
                   TIMEOUT = 180, FAILURE=XPN$SIGNAL);

        DAP$PUT_CONFIG(ODD[$],DAP$K_BUFFER_SIZE);
        DAP$PUT_MESSAGE(ODD[$]);                    ! Send configuration

        DAP$GET_CONFIG(IDD[$],CFGBLK[$]);           ! Get one back

        ! Workaround VMS BYTLM bug.
        ! If user BYTLM is less than our buffersize, VMS FAL hangs
        ! trying to send a message that is too large.
        ! Currently we set a limit of what the VAX sends us - 256
        IF .VMSBUG[VMS_BUG_BYTLM_HANG]  ! VMS does not check its own limit
        AND (.CFGBLK[CONFIG$B_OSTYPE] EQL DAP$K_VMS)
        THEN
            BEGIN                   ! Exchange new configurations
            LOCAL BUFSIZ;           ! Gets lower of our max or vms's
            OWN VMSSLP: INITIAL(256);  !! Patchable

            BUFSIZ=MIN(.CFGBLK[CONFIG$H_BUFSIZ]-.VMSSLP, DAP$K_BUFFER_SIZE);

            DAP$PUT_CONFIG(ODD[$],.BUFSIZ);
            DAP$PUT_MESSAGE(ODD[$]);
            DAP$GET_CONFIG(IDD[$],CFGBLK[$]);
            CFGBLK[CONFIG$H_BUFSIZ]=.BUFSIZ;        ! Don't forget the slop
            END;
        END
    ELSE
        BEGIN                           ! Set up local pointers
        DIB=.FAB[FAB$A_DIB];
        IDD=.DIB[DIB$A_I_DD];
        ODD=.DIB[DIB$A_O_DD];
        NLB=.DIB[DIB$A_NLB];
        CFGBLK=.DIB[DIB$A_CONFIG];
        END;

    ! Remember the function we were trying to do
    DIB[DIB$B_OPERATION]=.FUNCTION;
    DIB[DIB$A_NEW_FAB]=NFAB[$];         ! Remember new FAB (for RENAME)

    IF (.CFGBLK[CONFIG$H_BUFSIZ] GTR 0)
    THEN ODD[DAP$H_MESSAGE_LENGTH]=     ![14] Use remote buffer size if smaller
           MIN(.ODD[DAP$H_MESSAGE_LENGTH],.CFGBLK[CONFIG$H_BUFSIZ]);

    ! The link is now open, and CONFIG messages have been exchanged.
    ! Subsequent accesses over the same link can skip this block

    IF (.DIB[DIB$V_FILE_OPEN] AND .FAB[FAB$V_FOP_DRJ]) ! Already opened
    OR (.FUNCTION EQL 0)                       ! or didn't want to
    THEN RETURN (FAB[FAB$H_STS]=RMS$_SUC);     ! Return if that's all we wanted

    !
    ! Check for unsupported features
    !

    ! Block mode is only permissible between homogenous systems
    IF .FAB[FAB$V_FAC_BIO] AND (.CFGBLK[CONFIG$B_OSTYPE] NEQ OUR_OSTYPE)
    THEN SIGNAL(FAB[FAB$H_STS]=RMS$_SUP,
                FAB[FAB$H_STV]=DAP$K_MAC_UNSUPPORTED+DAP$K_MIC_ACCESS_FAC,
                FAB[$]);

    ! Default to ASCII if no datatype specified

    IF .FAB[FAB$A_TYP] NEQ 0
    THEN CLASS=.BLOCK[.FAB[FAB$A_TYP],TYP$H_CLASS]
    ELSE CLASS=0;

    IF .CLASS EQL 0 THEN CLASS=TYP$K_CLASS_ASCII;

    ! Default the RFM to something reasonable if this is an ASCII file

    IF (.CLASS EQL TYP$K_CLASS_ASCII) AND (.FAB[FAB$Z_RFM] EQL FAB$K_RFM_UDF)
    THEN
        BEGIN
        CASE .CFGBLK[CONFIG$B_OSTYPE]
        FROM 1 TO DAP$K_OSTYPE_MAX OF
        SET
        [DAP$K_RSTS,
         DAP$K_TOPS10,
         DAP$K_TOPS20,
         DAP$K_RT11,
         DAP$K_OS8,
         DAP$K_RTS8]:  FAB[FAB$Z_RFM]=FAB$K_RFM_STM;
        [INRANGE]:
             BEGIN
             FAB[FAB$Z_RFM]=FAB$K_RFM_VAR;      ![7] Variable
             IF .FAB[FAB$H_RAT] EQL 0           ![17] If no record attributes
             THEN FAB[FAB$V_RAT_CR]=1;          ![7] Set Implied CRLF
             END;
        [OUTRANGE]: SIGNAL(RMS$_DPE,
                           DAP$K_MAC_INVALID+DAP$K_MIC_CONFIG_OSTYPE,
                           FAB[$]);
        TES;
        END;     

    IF .FAB[FAB$V_FOP_CIF] THEN FAB[FAB$V_FOP_SUP]=0; ![11] CIF overrides SUP

    DAP$PUT_ATTRIBUTES(ODD[$],FAB[$]);          ! Send Attributes
    
    ! If FOP bit CIF (Create-if) is set, make CREATE into OPEN
    IF .FAB[FAB$V_FOP_CIF] AND (.FUNCTION EQL DAP$K_CREATE)
    THEN FUNCTION=DAP$K_OPEN;

    ! Now see what attributes we want back on the access.
    ! We will ask for whatever we have XAB's, etc for.
    IF .FAB[FAB$A_NAM] NEQ 0 THEN DISPLAY[DAP$V_DISPLAY_NAM]=1; ! File name

        BEGIN
        LOCAL XAB: REF $XABDAT_DECL;

        XAB=.FAB[FAB$A_XAB];                        ! Search the XAB chain
        WHILE .XAB NEQ 0
        DO  BEGIN
            CASE .XAB[XABDAT$Z_COD] FROM XABKEY$K_COD TO XABSUM$K_COD OF
                SET
    !            [XABKEY$K_COD]: DISPLAY[DAP$V_DISPLAY_KEY]=1;
    !            [XABALL$K_COD]: DISPLAY[DAP$V_DISPLAY_ALL]=1;
                [XABDAT$K_COD]: DISPLAY[DAP$V_DISPLAY_DTM]=1;
    !            [XABSUM$K_COD]: DISPLAY[DAP$V_DISPLAY_SUM]=1;
    !            [XABPRO$K_COD]: DISPLAY[DAP$V_DISPLAY_PRO]=1;
                [INRANGE]:;
                TES;
            XAB=.XAB[XABDAT$A_NXT];                 ! On to the next one
            END;
        END;
        DISPLAY[DAP$V_DISPLAY_ATT]=1;   ! Always get main attributes

    DAP$PUT_ACCESS(ODD[$],FAB[$],.FUNCTION,ACCOPT,DISPLAY,NFAB[$]);! And Access
    DAP$PUT_MESSAGE(ODD[$]);

    DIB[DIB$V_ACCESS_ACTIVE]=1;                 ! Access is now active

    SELECT .FUNCTION OF
    SET

    [DAP$K_DIRECTORY]:
        RETURN FAB[FAB$H_STS]= RMS$_SUC;        ! Get directory info on SEARCH

    [OTHERWISE]:
        DAP$GET_ATTRIBUTES(IDD[$],FAB[$]);      ! Get returned Attributes

    [DAP$K_OPEN, DAP$K_CREATE, DAP$K_SUBMIT]:
        DIB[DIB$V_FILE_OPEN]=.DIB[DIB$V_ACCESS_ACTIVE]; ! File is open, or
                                                        ! access is complete
    [DAP$K_EXECUTE, DAP$K_ERASE]:
        DIB[DIB$V_FILE_OPEN]=0;           ! These operations do not open files
    TES;

    IF (.FAB[FAB$V_FOP_DRJ] EQL 0)
    AND (.DIB[DIB$V_FILE_OPEN] EQL 0)
    THEN
        BEGIN                           ! Close the link & free data strs
        DAP$CLOSE(FAB[$],DAP$K_ACCOMP_COMMAND,.ERR);
        DIB[DIB$V_ACCESS_ACTIVE]=0;
        END;

    FAB[FAB$H_STS]=RMS$_SUC
    END;			!End of DAP$OPENFILE
GLOBAL ROUTINE DAP$CLOSE (FAB,FUNCTION,ERR): =
!++
! FUNCTIONAL DESCRIPTION:
!
!   Close a remote file
!
! FORMAL PARAMETERS:
!
!   FAB: An RMS FAB with a NODEID embedded in the filespec.
!   FUNCTION: ACCESS message function code (DAP)
!   ERR: Address of error routine
!
! SIDE EFFECTS:
!
!   If there are no more files coming:
!    The link is closed
!    The DIB attatched to the FAB, and its subsidiary data structures are freed
!   If there is another file (wildcarding), the attributes are read into FAB
!--
    BEGIN
    MAP FAB: REF $FAB_DECL;
    BIND DIB=.FAB[FAB$A_DIB]: $DIB;
    BIND ODD=.DIB[DIB$A_O_DD]: $DAP_DESCRIPTOR;
    BIND IDD=.DIB[DIB$A_I_DD]: $DAP_DESCRIPTOR;
    BIND NLB=.DIB[DIB$A_NLB]: $XPN_NLB();
    BIND C=.DIB[DIB$A_CONFIG]: $CONFIG;
    LOCAL V;                            ! Temp for returned value
    LOCAL FABSAV: VOLATILE;
    LOCAL ERRSAV: VOLATILE;
    LOCAL SEND_INTERRUPT: INITIAL(0);   ![12] Flag for interrupt message

    ENABLE DAP$HANDLE(FABSAV,ERRSAV);   ! Setup Condition handler
    ERRSAV=.ERR;
    FABSAV=.FAB;                        ! Handler will need this

    IF .FAB[FAB$A_IFI] EQL 0            ! Make sure it's open
    THEN
        BEGIN                           ! No DIB there
        FAB[FAB$H_STS]=RMS$_IFI;        ! Don't do it
        SIGNAL(RMS$_IFI,0,FAB[$]);
        RETURN .FAB[FAB$H_STS];         ! Even if handler doesn't care
        END;

    IF .FUNCTION EQL DAP$K_ACCOMP_PURGE ![12] Purge is sent as interrupt
    THEN
        BEGIN
        SEND_INTERRUPT=1;

        ! Forget what we were going to send
        ODD[DAP$H_BYTES_REMAINING]=16;   ! Max length for interrupt message
        ODD[DAP$A_DATA]=CH$PLUS(.ODD[DAP$A_DATA],-.ODD[DAP$H_BYTES_USED]);
        ODD[DAP$H_BYTES_USED]=0;                          !to data in message
        END;

    IF (.FAB[FAB$V_FAC_PUT] EQL 0) AND (.FUNCTION EQL DAP$K_ACCOMP_PURGE)
    THEN FUNCTION=DAP$K_ACCOMP_COMMAND; ! Some FAL's interpret purge as
                                        ! erase, even for input files!!

    IF .DIB[DIB$V_FILE_OPEN]            ! Send Access complete if file open,
    THEN                                ! otherwise don't bother
        BEGIN
        LOCAL FOP: BITVECTOR[42] INITIAL(REP ((%BPUNIT+41)/%BPUNIT) OF (0));
        LOCAL FOPLENGTH;

        $DAP_MOVE_BITS(FAB,FAB$V_FOP_,FOP,DAP$V_FOP_,
                       RWO,RWC,POS,DLK,LCK,
                       CTG,SUP,NEF,TMP,MKD,DMO,
                       WCK,RCK,CIF,LKO,SQO,MXV,SPL,
                       SCF,DLT,CBT,WAT,DFW,TEF,OPF,
                       DRJ,DFW);

        IF .IASBUG[IAS_BUG_ACM_FOP]     ![11] IAS does not like most fop bits
        AND (.C[CONFIG$B_OSTYPE] EQL DAP$K_IAS)
        THEN FOP=(.FOP AND DAP$V_FOP_SPL);

	FOPLENGTH=DAP$SIZE_BITVECTOR(FOP,6,0);

        INIT_MESSAGE(ODD[$]);
        ODD[DAP$B_OPERATOR]=DAP$K_ACCESS_COMPLETE;
        ODD[DAP$V_MFLAGS_LENGTH]=1;
        ODD[DAP$H_LENGTH]=.FOPLENGTH+1; ! Count length of FOP
        DAP$PUT_HEADER(ODD[$]);
        DAP$PUT_BYTE(ODD[$],.FUNCTION); ! COMMAND (normal) or PURGE (punt)

        IF .FOPLENGTH NEQ 0             ! Send FOP if needed
        THEN DAP$PUT_BITVECTOR(ODD[$],FOP,6);

        IF .SEND_INTERRUPT NEQ 0        ![12] Interrupt message?
        THEN ODD[DAP$V_INTERRUPT]=1;    ! 
        DAP$PUT_MESSAGE(ODD[$]);            ! Send it

        WHILE 1 DO                      ![2] Clean out pipeline
            BEGIN
            V=DAP$GET_HEADER(IDD[$]); ! Get response

            SELECT .V OF SET
            [DAP$K_ACCESS_COMPLETE]:
                BEGIN                               ! Yes,
                LOCAL CMPFUNC;
                LOCAL FOP: BITVECTOR[42];

                CMPFUNC=DAP$GET_BYTE(IDD[$]);         ![4] Eat rest of message
                IF .CMPFUNC NEQ DAP$K_ACCOMP_RESPONSE ![4] Is it a response
                THEN SIGNAL(RMS$_DPE,0,FAB[$]);       ![4] Error if not

                DIB[DIB$V_FILE_OPEN]=0;             ! File is not open any more
                DIB[DIB$V_ACCESS_ACTIVE]=0;         ! No access any more

                IF .IDD[DAP$H_BYTES_REMAINING] GTR 0
                THEN
                    BEGIN
                    DAP$GET_BITVECTOR(IDD[$],FOP,6);  ![4] Eat FOP field if any
                    $DAP_MOVE_BITS(FOP,DAP$K_FOP_,FAB,FAB$V_FOP_,
                                   RWO,RWC,POS,DLK,LCK,
                                   CTG,SUP,NEF,TMP,MKD,DMO,
                                   WCK,RCK,CIF,LKO,SQO,MXV,SPL,
                                   SCF,DLT,CBT,WAT,DFW,TEF,OPF,
                                   DRJ,DFW);
                    END;

                EXITLOOP FAB[FAB$H_STS]=RMS$_SUC;
                END;
            [DAP$K_NAME]:
                BEGIN
                EXITLOOP DAP$UNGET_HEADER(IDD[$]);  ! Back up for SEARCH
                END;                             
            [DAP$K_STATUS]:
                BEGIN
                LOCAL E;
                E=DAP$GET_STATUS(IDD[$]);   ! End of file or error
                FAB[FAB$H_STS]=DAP$ERROR_DAP_RMS(.E);
                FAB[FAB$H_STV]=.E<DAPCODE>;
                IF .FAB[FAB$H_STS] NEQ RMS$_EOF ! Break out for unusual errors
                THEN EXITLOOP SIGNAL(.FAB[FAB$H_STS],.FAB[FAB$H_STV],FAB[$]);
                END;                            ! error code

            [DAP$K_DATA]: WHILE .IDD[DAP$H_LENGTH] GTR 0 ![2] Eat data
                          DO DAP$GET_BYTE(IDD[$]);                ![2] 

            [OTHERWISE]: SIGNAL(RMS$_DPE,0,FAB[$]);
            TES;
            END; ! WHILE 1
        END;

    ! Note that DRJ must be set, if wildcarding,
    ! until all files have been read.

    IF (.FAB[FAB$V_FOP_DRJ] EQL 0)      ! Should we keep things open?
    THEN
        BEGIN
        $XPN_CLOSE(NLB=NLB[$], FAILURE=0);  ! Close the link

        $XPO_FREE_MEM(BINARY_DATA=(DAP$K_DESCRIPTOR_LEN,IDD[$]));
        $XPO_FREE_MEM(BINARY_DATA=(NLB$K_LENGTH,NLB[$]));    
        $XPO_FREE_MEM(BINARY_DATA=(DAP$K_BUFFER_SIZE %BLISS36(/4),
                                   %BLISS36( .(ODD[DAP$A_DATA])<0,18>)+1 )
                                  );       ! Deallocate output buffer
        $XPO_FREE_MEM(BINARY_DATA=(DAP$K_DESCRIPTOR_LEN,ODD[$]));
        $XPO_FREE_MEM(BINARY_DATA=(DIB$K_LENGTH,DIB[$]));
        FAB[FAB$A_DIB]=0;
            END;

    .V
    END;                ! DAP$CLOSE
GLOBAL ROUTINE DAP$HANDLE (SIGNAL_ARGS,MECH_ARGS,ENABLE_ARGS) =
!++
! FUNCTIONAL DESCRIPTION:
!
!       Condition handler for requests
!
! FORMAL PARAMETERS:
!
!	SIGNAL_ARGS: addr of vector of SIGNAL arguments,
!       MECH_ARGS: not used,
!       ENABLE_ARGS: args passed when this handler was established
!
! IMPLICIT INPUTS:
!
!	NONE
!
! IMPLICIT OUTPUTS:
!
!	NONE
!
! COMPLETION CODES:
!
!	0: Resignal, 1: Continue
!
! SIDE EFFECTS:
!
!	NONE
!
!--

    BEGIN
    MAP SIGNAL_ARGS: REF VECTOR,
        MECH_ARGS: REF VECTOR,
        ENABLE_ARGS: REF VECTOR;

    BIND BLK=..ENABLE_ARGS[1]: $FAB_DECL;          ! RMS block
    BIND ROUTINE $$ERRRTN=..ENABLE_ARGS[2]: RMS_ERCAL;       ! Error routine

    LOCAL SEVERITY;

    SEVERITY= .(SIGNAL_ARGS[1])<0,3>;

    SELECT .SIGNAL_ARGS[1] OF
           SET
           [SS$_UNWIND]:
                         BEGIN
                         RETURN STS$K_NORMAL;
                         END;
           [RMS$K_SUC_MIN TO RMS$K_SUC_MAX]: SEVERITY=SS$_NORMAL;

           [RMS$K_ERR_MIN TO RMS$K_ERR_MAX]:
                         SEVERITY=SS$_ERROR;
           [RMS$K_ERR_MIN TO RMS$K_ERR_MAX, RMS$K_SUC_MIN TO RMS$K_SUC_MAX]:
                         BEGIN
                         BLK[FAB$H_STS]=.SIGNAL_ARGS[1];
                         BLK[FAB$H_STV]=.SIGNAL_ARGS[2];
                         END;

           [DAP$K_FACILITY_CODE TO DAP$K_FACILITY_CODE+%O'7777777']:
                         BEGIN
                         BLK[FAB$H_STS]=DAP$ERROR_DAP_RMS(.SIGNAL_ARGS[1]);
                         BLK[FAB$H_STV]=.(SIGNAL_ARGS[1])<DAPCODE>;
                         END;

           [XPN$$SELECT_XPN_ERRORS]:
                         IF NOT .SEVERITY       ! If this is a connect error
                         THEN                   ! then change to RMS code
                             BEGIN
                             BLK[FAB$H_STS]=RMS$_CON;
                             BLK[FAB$H_STV]=.SIGNAL_ARGS[1]; ! XPN code
                             END;

           [XPN$_ABORTED, XPN$_DISCONN]:
                         BEGIN
                         MAP BLK: $RAB_DECL;
                         BIND FAB=(IF .BLK[RAB$H_BID] EQL RAB$K_BID
                                   THEN .BLK[RAB$A_FAB]
                                   ELSE BLK[$]): $FAB_DECL;

                         BIND DIB=.FAB[FAB$A_DIB]: $DIB;

                         FAB[FAB$V_FOP_DRJ]=0;  ![14] Link is no good any more
                         DIB[DIB$V_FILE_OPEN]=DIB[DIB$V_ACCESS_ACTIVE]=0;
                                      ![14] Abort means it is not open any more
                         SEVERITY=STS$K_ERROR;  ! Treat as error
                         BLK[RAB$H_STS]=RMS$_NLB;
                         END;       ! Network link broken (Abort or Disconnect)

           [XPN$_NO_OPEN, XPN$_REJECTED]:
                         BLK[FAB$H_STV]=.SIGNAL_ARGS[2];
                         ! DECnet reason code will be STV for
                         ! unspecified open error

           [OTHERWISE]:
                BEGIN
                BLK[FAB$H_STS]=RMS$_BUG;        ! Should not occur
                BLK[FAB$H_STV]=.SIGNAL_ARGS[1]; !
                SEVERITY=SS$_FATAL;             !
                END;
           TES;


    CASE .SEVERITY FROM 0 TO 7 OF
         SET
         [STS$K_ERROR]:
                BEGIN
                $$ERROR(OPEN,BLK);              !? Should get operation too
                SETUNWIND();
                MECH_ARGS[MA_RETURN]=.BLK[FAB$H_STS];   ! Return status code
                RETURN 0;
                END;
         [STS$K_WARNING]:  
                BEGIN
                $$ERROR(OPEN,BLK);              !? Should get operation too
                RETURN STS$K_NORMAL;
                END;
         [STS$K_NORMAL, STS$K_INFO]: RETURN STS$K_NORMAL;
         [STS$K_FATAL,INRANGE]: ;
         TES;

    SS$_RESIGNAL
    END;			!End of DAP$HANDLE
END            !End of module
ELUDOM