- 「メインフレーム・コンピューター」で遊ぼう - http://www.arteceed.net -

QSAMでPDSメンバーにアクセスする

順次データセットは、QSAMを利用すれば論理レコード単位で簡単にアクセスできます。区分データセット用にはBPAMが提供されています。しかしながら、BPAMの場合はブロック(物理レコード)単位でないとアクセスできず、ブロックを論理レコードにバラしたり(あるいは論理レコードをまとめてブロックにまとめたり)バッファリングの制御なども自分でやらねばなりません。バッファリング制御はオプション処理的なものなので、やらなくてもアクセス自体はできます。しかし、ブロッキングとデブロッキングの処理は避けることができません。決して難しい処理ではありませんが、面倒であることは確かで、やらずに済むならそれに越したことはありません。
JCLでは、DSN=dsname(member)のようにデータセット名の後ろに()でメンバー名まで指定すれば、区分データセットのメンバーをQSAMでアクセスすることができます。これは、区分データセットのメンバー自体が、順次データセットと同じ構造を持っているからです。予めアクセスするメンバーが決まっているならJCL側の定義と組み合わせてQSAMでアクセスすることもできます。例えば、パラメーター・メンバーなどはその代表的なものの1つです。ところが、1つや2つのメンバーではなく、数十あるいは全部のメンバーに順番にアクセスしたい、となった場合はどうでしょうか?JCL側ではメンバー名まで定義できないことになります。そのような場合は、アクセスするメンバーを、JCLで定義する代わりに動的割り振り(DYNALLOC)でアロケーションしてQSAMでアクセス方法もあります。しかし、DYNALLOCは比較的オーバーヘッドが大きい処理なので、アクセスするメンバー数が多くなるとパフォーマンスの問題が出てきます。ISPFの起動プロシージャー内で、沢山のデータセットをALLOCコマンドで割り振っているとISPFが起動されるまでに時間が掛かることと同じです。
ここでは、メンバーをQSAMでアクセスでき、アロケーションのオーバーヘッドも掛からない方法として、JFCBを使用した区分データセットのオープン方法を紹介します。

    //STEP1    EXEC PGM=PDSPRINT
    //SYSPRINT DD SYSOUT=*
    //SYSUT1   DD DISP=SHR,DSN=USR1.JCLLIB
    //
    

このようなJCLで、SYSUT1 DDステートメントに定義された区分データセットの全メンバーを、SYSPRINT DDステートメントに定義されたデータセットへプリントするプログラムの例です。


DDステートメント定義情報の変更を行ってからデータセットをオープンする

JFCBを使用したデータセットのオープン処理を行うため、最初にJFCBを読み込みます。JFCBは、JCLのDDステートメントの定義情報だと考えればいいです。読み込んだJFCBには、JCLのDSNパラメーターで指定されたデータセット名は格納されていますが、メンバー名は当然ながら入っていません。メンバー名指定なしの区分データセットをQSAMでオープンすると、ディレクトリー部の先頭に位置付いてしまい、レコード形式などが不一致となるのでS013でABENDします。DSNパラメーターにメンバー名が入っていれば、オープンした時にそのメンバーの先頭に位置付けられてQSAMでアクセスができます。
従って、読み込んだJFCBのメンバー名フィールドにアクセスしたいメンバー名をセットすれば、JCL DDステートメントのDSNパラメーターに(メンバー名)まで指定したことと同じ状況を作り出せます。オープン前にJFCBを読み込み、その内容(JCL DDステートメント情報)を変更してからオープンすることもできます。

             USING INFMJFCB,JFCBAREA        ADDRESS TO JFCB READ AREA
             RDJFCB UT1DCB                  READ SYSUT1 DD STMT JFCB
    
             OI    JFCBIND1,JFCPDS         INDICATES DATASET IS PDS MEMBER
    
             MVC   JFCBELNM,MEMBER1        SET NEXT MEMBER NAME
             BAS   RA,MEMPROC               MEMBER PROCESSING FOR MEMBER1
    
             MVC   JFCBELNM,MEMBER2        SET NEXT MEMBER NAME
             BAS   RA,MEMPROC               MEMBER PROCESSING FOR MEMBER2
             :
             :
    MEMPROC  DS    0H
             OPEN  (UT1DCB,INPUT),TYPE=J    OPEN DATASET WITH MODIFIED JFCB+
                                            (OPEN THE SPECIFIED PDS MEMBER)
             :
             :
    UT1DCB   DCB   DDNAME=SYSUT1,           QSAM DCB                       +
                   DSORG=PS,MACRF=GL,                                      +
                   EXLST=UT1EXLST,EODAD=UT1EOD
    UT1EXLST DS    0F
             DC    XL1'87',AL3(JFCBAREA)
    JFCBAREA DS    0F
             DC    (JFCBLGTH)X'00'          JFCB READ AREA
             :
             :
    DJFCB    DSECT ,
             IEFJFCBN LIST=YES              JFCB DSECT
             :
    

ポイントになる箇所は太字で示してあります。メンバー名はJFCBELEMフィールドに設定します。DDステートメントにDSN=dsname(member)と定義されると、JFCBのJFCBDSNMにdsnameが、JFCBELEMにmemberが格納されます。従って、オープン前にJFCBELEMにメンバー名を設定すれば、DDステートメントでメンバー名まで指定したことと同じになります。なお、オープン時に区分データセットのメンバー部に位置付けさせるためには、メンバー名の設定に加えて、JFCBIND1フィールドのJFCPDSフラグをオン(1)にする必要があります。JFCPDSフラグは1度オンにすれば、MVS(DFSMSdfp)側で変更されることはありません。
JFCBの準備ができたら、データセットをQSAMでオープンします。RDJFCBマクロで使用したDCBを指定してOPENマクロを発行します。この時、OPENマクロにTYPE=Jパラメーターを追加します。TYPE=Jは、JFCBがユーザー側で提供されたことを示します。TYPE=Jパラメーターを忘れると変更後のJFCBは反映されません。メンバーの読み込みならGETマクロ、メンバーの書き込みならPUTマクロを使用します。QSAMなので論理レコード単位でのアクセスです。1つのメンバーの処理を終えたら、データセット(メンバー)をクローズします。別のメンバーへの処理を行うのであれば、JFCBELEMに次のメンバー名を設定してオープンから繰り返します。
なお、メンバー内容を書き込みする場合、クローズ時にメンバー・ディレクトリーのユーザー・データ部分は消えてしまいます。ISPFエディターで保管したメンバーを、アプリケーション・プログラムのQSAMアクセスで書き直すような場合、メンバーのISPF統計データは消えてしまうことは知っておく必要があります。必要であれば、BLDLマクロなどでメンバー・ディレクトリーの情報を読み取っておき、後でBPAMでオープンし直してSTOWマクロで必要なユーザー・データを書き込む、などの処理を追加します。
※JFCBの読み込み方とRDJFCBマクロの使い方は、「DD文定義情報を得る(DEVTYPEとRDJFCB)」 [1]に解説してあります。


サンプル・プログラム(PDSPRINT)ソース・コード

PDS内メンバーのQSAMアクセスをセットアップした後、PDSデータセットのディレクトリー部をやはりQSAMでオープンします。
GETマクロでPDSディレクトリーブロックを1ブロック読み込み、その中に登録されているメンバーエントリーを取り出します。メンバー名をJFCBにセットしたら、PDSメンバーをオープンしてGETマクロで1レコード読み込み出力側のデータセットに書き出します。メンバーがEODになるまで処理を繰り返し、PDSメンバーをクローズします。
読み込み済みディレクトリーブロック内の次のメンバーエントリーに位置付けて、メンバー名をJFCBにセットする処理から繰り返します。ディレクトリーブロック内の全てのメンバーを処理し終えたら、次のディレクトリーブロックの読み込みから繰り返します。次に処理すべきメンバーエントリーのメンバー名がxFFFFFFFFFFFFFFFFであれば(これ以上メンバーがないことを示す)、PDSデータセットのディレクトリー部と出力側のデータセットをクローズしてプログラム処理を終了します。
※PDSディレクトリーの読み込み方は、「アセンブラーで区分データセットのディレクトリー部を読み込む」 [2]に解説してあります。

    ----+----1----+----2----+----3----+----4----+----5----+----6----+----7--
    MAINENTR CSECT                          DEFINE CONTROL SECTION
             USING *,12                     DEFINE BASE REGISTER
             SAVE  (14,12),,                SAVE CALLER REGISTERS          +
                   'MAINENTR &SYSDATE &SYSTIME'
             LR    12,15                    GR12 --> OUR 1ST BASE ADDRESS
             LR    15,13                    SAVE CALLER SAVEAREA
             CNOP  0,4                      INSURE FULL WORD BOUNDARY
             BAS   13,*+4+72                AROUND OUR SAVEAREA
             DC    18F'-1'                  OUR GPR SAVEAREA
             ST    15,4(,13)                SAVE CALLER SAVEAREA POINTER
             ST    13,8(,15)                FORWARD CHAIN FOR LINK TRACE
             B     MAINPROC                 DO MAINLINE PROCESSING
    *                                  *----------------------------------*
    *                                  *  EXIT PROCESSING                 *
    *                                  *----------------------------------*
    EXIT8    DS    0H
             LA    15,8                     SET CC=8
             B     EXITPROC
    EXIT0    DS    0H
             SLR   15,15                    SET CC=0
    EXITPROC DS    0H
             L     13,4(,13)                RESTORE CALLER SAVEAREA
             ST    15,16(,13)               PASS RETURN CODE TO CALLER
             RETURN (14,12),T               RESTORE CALLER REGISTERS       +
                                                       AND RETURN TO CALLER
             EJECT ,
    ***********************************************************************
    *        SAMPLE CODE FOR PRINTING PDS/PDSE ALL MEMBERS                *
    *        =====================================================        *
    *        READ PDS/PDSE MEMBER BY QSAM.                                *
    ***********************************************************************
    MAINPROC DS    0H
    *                                  *----------------------------------*
    *                                  *  OPEN OUTPUT DATASET             *
    *                                  *----------------------------------*
             OPEN  (PRTDCB,OUTPUT)          OPEN THE PRINT DATASET
             LTR   R15,R15                  SUCCESSFUL ?
             BNZ   EXIT8                    NO, MAY BE DD STMT NOT DEFINED
    
    *                                  *----------------------------------*
    *                                  *  SETUP FOR PDS QSAM ACCESSING    *
    *                                  *----------------------------------*
             USING INFMJFCB,JFCBAREA        ADDRESS TO JFCB READ AREA
             RDJFCB UT1DCB                  READ SYSUT1 DD STMT JFCB
             LTR   R15,R15                  SUCCESSFUL ?
             BNZ   EXIT8                    NO, MAY BE DD STMT NOT DEFINED
             OI    JFCBIND1,JFCPDS          INDICATES DATASET IS PDS MEMBER
    
    *                                  *----------------------------------*
    *                                  *  OPEN PDS DIRECTORY BY QSAM      *
    *                                  *----------------------------------*
             OPEN  (DIRDCB,INPUT)           OPEN THE PDS DIRECTORY
    READDIR  DS    0H
             GET   DIRDCB                   READ NEXT DIR BLOCK RECORD
             LH    R7,0(,R1)                GR7 <--- REMAINING LENGTH
             AHI   R7,-2                    ADJUST IT(SUBTRACT COUNT FLD LN
             LA    R6,2(,R1)                GR6 <--- DIR BLOCK RECORD
             USING PDS2,R6                  ADDRESS IT
    
    *                                  *----------------------------------*
    *                                  *  PRINT NEXT PDS MEMBER           *
    *                                  *----------------------------------*
    MEMLOOP  DS    0H
             CLC   PDS2NAME,=8X'FF'         END OF DIRECTORY ?
             BE    CLOSDIR                  YES, END OF PROCESSING
    
             MVC   JFCBELNM,PDS2NAME        SET NEXT MEMBER NAME
             BAS   R10,PRTMEMBR             PRINT MEMBER RECORD
    
    *                                  *----------------------------------*
    *                                  *  LOCATE TO NEXT MEMBER ENTRY     *
    *                                  *----------------------------------*
             NI    PDS2INDC,PDS2LUSR        DROP OTHER BITS
             SLR   R15,R15                  CLEAR WORKREG
             IC    R15,PDS2INDC             LOAD USER DATA LENGTH INDICATOR
             SLL   R15,1                    GET USER DATA LENGTH
             LA    R6,PDS2USRD(R15)         LOCATE TO NEXT MEMBER
             SLR   R7,R15                   UPDATE REMAINING LENGTH
             AHI   R7,-(PDS2USRD-PDS2)      ADJUST IT
             BP    MEMLOOP                  LOOP FOR NEXT MEMBER
             B     READDIR                  LOOP FOR NEXT DIR BLOCK
             DROP  R6                       FORGET PDS DIRECTORY BLOCK
    
    CLOSDIR  DS    0H
             CLOSE (DIRDCB)                 CLOSE THE PDS DIRECTORY
    
    *                                  *----------------------------------*
    *                                  *  CLOSE OUTPUT DATASET            *
    *                                  *----------------------------------*
    DOCLOSE  DS    0H
             CLOSE (PRTDCB)                 CLOSE USED DATASET
             B     EXIT0                    ALL PROCESSING DONE
             EJECT ,
    ***********************************************************************
             EJECT ,
    ***********************************************************************
    *        INTERNAL SUB ROUTINES                                        *
    ***********************************************************************
    PRTMEMBR DS    0H
             MVC   MEMNAME,JFCBELNM         SET NEXT MEMBER NAME
             PUT   PRTDCB,TITLE1            PRINT TITLE-1 LINE
             PUT   PRTDCB,TITLE2            PRINT TITLE-2 LINE
    
             OPEN  (UT1DCB,INPUT),TYPE=J    OPEN DATASET WITH MODIFIED JFCB+
                                            (OPEN THE SPECIFIED PDS MEMBER)
    PRTLOOP  DS    0H
             GET   UT1DCB                   READ NEXT RECORD
             LR    R0,R1                    LOAD RECORD ADDRESS
             PUT   PRTDCB,(0)               PRINT IT TO SYSPRINT
             B     PRTLOOP                  LOOP UNTIL EOD
    
    UT1EOD   DS    0H
             CLOSE (UT1DCB)                 CLOSE INPUT DATASET
             B     0(,R10)                  RETURN TO MAINLINE
             EJECT ,
    ***********************************************************************
    *        DATA AREA                                                    *
    ***********************************************************************
    PRTDCB   DCB   DDNAME=SYSPRINT,         QSAM DCB FOR PRINT OUT         +
                   DSORG=PS,MACRF=PM,                                      +
                   RECFM=FB,LRECL=80
    DIRDCB   DCB   DDNAME=SYSUT1,           QSAM DCB FOR PDS DIRECTORY     +
                   DSORG=PS,MACRF=GL,                                      +
                   RECFM=F,LRECL=256,BLKSIZE=256
    UT1DCB   DCB   DDNAME=SYSUT1,           QSAM DCB FOR PDS MEMBER        +
                   DSORG=PS,MACRF=GL,                                      +
                   EXLST=UT1EXLST,EODAD=UT1EOD
    UT1EXLST DS    0F
             DC    XL1'87',AL3(JFCBAREA)
    JFCBAREA DS    0F
             DC    (JFCBLGTH)X'00'          JFCB READ AREA
    
    TITLE1   DC    CL80'MEMBER=XXXXXXXX'
    MEMNAME  EQU   TITLE1+7,8
    TITLE2   DC    CL80'----+----1----+----2----+----3----+----4----+----5-X
                   ---+----6----+----7----+----8'
    *=====================================================================*
             LTORG ,
             EJECT ,
    ***********************************************************************
    *        DSECT                                                        *
    ***********************************************************************
    DJFCB    DSECT ,
             IEFJFCBN LIST=YES              JFCB DSECT
             IHAPDS PDSBLDL=NO              PDS DIRECTORY ENTRY DSECT
             YREGS ,                        S/370 REGISTER EQUATES
             END