変更の始まり

オープン・アクセス・ハンドラーの例

注: この例の説明では、オープン・アクセス・ファイルに関連した部分についてのみ詳しく説明されています。 IFS ファイルのオープン、書き込み、およびクローズを行うコードや、 例外を通知するコードなど、説明なしで示されているコードもあります。

この例では、ハンドラーを使用することによって、RPG プログラマーが統合ファイル・システムのストリーム・ファイル を読み取ることができます。

このハンドラーの作成者は、RPG プログラムとハンドラーとの間で通信域として 使用されるデータ構造のテンプレートが入っているコピー・ファイルも用意しています。 このデータ構造は、ファイルのパスと、ハンドラーがファイルを作成する方法を制御するための他のオプション を定義します。 コピー・ファイルには、ハンドラー・プログラムの名前が入っている 名前付き定数 IFSHDLRS_read_handler も含まれています。
 /IF DEFINED(IFSHDLRS_COPIED)
 /EOF
 /ENDIF
 /DEFINE IFSHDLRS_COPIED

 DCL-DS ifshdlrs_info_t QUALIFIED TEMPLATE;
    path VARCHAR(5000);
    createCcsid INT(10);
    append IND;
 END-DS;

 DCL-C ifshdlrs_write_handler 'IFSHDLRS/WRITEHDLR';

オープン・アクセス・ファイルを定義する RPG プログラムを以下に示します。

プログラムの以下の局面に注意してください。
  1. ハンドラー用に用意されたコピー・ファイル。
  2. ハンドラーと直接通信するために使用される通信域データ構造。 このデータ構造は、ハンドラーに渡されるパラメーター中の userArea サブフィールド によってポイントされます。
  3. オープン・アクセス・ファイル。この例のハンドラーは、 プログラム記述ファイルと外部記述ファイルの両方をサポートします。 このプログラム例では、次のソース・ファイルから定義された外部記述ファイル が使用されています。
    A          R STREAMFMT
    A            LINE       32740A         VARLEN
  4. HANDLER キーワード。
    1. HANDLER キーワードの最初のパラメーターは、 ハンドラー・プログラムまたはプロシージャーを定義する、コピー・ファイルからの 名前付き定数です。
    2. HANDLER キーワードの 2 番目のパラメーターは、 通信域データ構造です。
  5. このプログラムは、ハンドラーが必要とする追加情報で通信域を セットアップし、次に、ファイルをオープンします。
  6. 2 つのレコードをファイルに書き込みます。
 CTL-OPT DFTACTGRP(*NO) ACTGRP(*NEW);

 /copy IFSHDLRS/SRC,RPG  1 
 DCL-DS ifs_info LIKEDS(ifshdlrs_info_t);  2 
 DCL-F streamfile DISK(*EXT) USAGE(*OUTPUT)  3 
                  EXTDESC('MYLIB/MYSTMF')
                  HANDLER(ifshdlrs_write_handler  4a 
                        : ifs_info)               4b 
                  USROPN;

 ifs_info.path = '/home/mydir/myfile.txt';  5 
 ifs_info.createCcsid = 0;  // job CCSID
 ifs_info.append = *ON;
 OPEN streamfile;

 line = 'Hello';  6 
 WRITE streamFmt;
 line = 'world!';
 WRITE streamFmt;
 *inlr = '1';

以下の例はハンドラーを示しています。

制御ステートメント、コピー・ファイル、およびグローバル定義

  1. テンプレートのデータ構造 state_t は、 ハンドラーへのすべての呼び出しでハンドラーが必要とする情報を定義します。 この例では、ハンドラーはオープン・ファイルのディスクリプターを追跡する必要があります。
 CTL-OPT DFTACTGRP(*NO) ACTGRP(*CALLER)
         MAIN(writeHdlr);

 /COPY IFSHDLRS/SRC,RPG
 /COPY QOAR/QRPGLESRC,QRNOPENACC
 /COPY QSYSINC/QRPGLESRC,IFS

 DCL-S descriptor_t INT(10) TEMPLATE;
 DCL-DS state_t QUALIFIED template;  1 
    descriptor LIKE(descriptor_t);
 END-DS;

メイン・ハンドラー・プロシージャー

  1. プロシージャー・インターフェースは、オープン・アクセス・ハンドラーに毎回渡される パラメーターを定義しています。 QrnOpenAccess_T テンプレートは、ソース・ファイル QOAR/QRPGLESRC 内にある コピー・ファイル QRNOPENACC 内に定義されています。
  2. いくつかの基底データ構造が定義されています。 これらのデータ構造の基底ポインターは、ハンドラー・パラメーター中のポインターから設定されます。
    • データ構造 state は、どのハンドラー呼び出しでも ハンドラーが必ず必要とする情報を保持します。 基底ポインター pState は、ハンドラー・パラメーター 中の stateInfo サブフィールドから設定されます。
    • データ構造 ifsInfo は、通信域パラメーターです。 基底ポインター pIfsInfo を ハンドラー・パラメーター中の userArea サブフィールド から設定することによって、 ifsInfo データ構造 は、RPG プログラムで HANDLER キーワードの 2 番目のパラメーターとして指定 された ifs_info データ構造と同じ記憶域を参照するようになります。
    • データ構造 namesValues は、 ファイル内の外部記述フィールドを記述します。 基底ポインターは、ハンドラー・パラメーターの namesValues サブフィールド から設定されます。
  3. state および ifsInfo の基底ポインターが設定されます。
  4. OPEN 命令の場合、ハンドラーは以下を行います。
    • state データ構造用の記憶域を割り振り、 ポインターをハンドラー・パラメーターの stateInfo サブフィールド に割り当てます。 後続の命令のためにハンドラーが呼び出されるとき には、stateInfo サブフィールドに同じポインター値が 保持されているため、ハンドラーは状態情報にアクセスすることができます。
    • ファイルをオープンし、戻されたファイル記述子を状態情報データ構造に保存します。
    • ファイルが RPG プログラム内に外部記述されている場合、 ハンドラー・パラメーター内の useNamesValues 指標サブフィールドをオンに設定します。
      • そのサブフィールドがオンの場合、出力命令用のデータは 各フィールドについての情報を格納する配列に入って提供されます。
      • そのサブフィールドがオフの場合、出力命令用のデータは、 *OUTPUT 外部記述データ構造と同じレイアウトのデータ構造として提供されます。
  5. WRITE 命令の場合、ハンドラーは、 ハンドラー・パラメーターの useNamesValues サブフィールド に基づいて、プロシージャーのうちの 1 つを呼び出してファイルに書き込みます。
  6. CLOSE 命令の場合、ハンドラーはファイルをクローズします。
  7. 他の命令の場合、ハンドラーは例外を通知します。 この例のハンドラーは、OPEN 命令、WRITE 命令、および CLOSE 命令のみをサポートします。
 DCL-PROC writeHdlr;
    DCL-PI *N EXTPGM;   1 
       parm LIKEDS(QrnOpenAccess_T);
    END-PI;

    DCL-S stackOffsetToRpg INT(10) INZ(2);
    DCL-S errnoVal INT(10);

    DCL-DS state LIKEDS(state_t) BASED(pState);  2 
    DCL-DS ifsInfo LIKEDS(ifshdlrs_info_t) BASED(pIfsInfo);
    DCL-DS namesValues LIKEDS(QrnNamesValues_T)
                       BASED(parm.namesValues);

    pState = parm.stateInfo;  3 
    pIfsInfo = parm.userArea;

    SELECT;
    WHEN parm.RpgOperation = QrnOperation_OPEN;  4 
       pState = %ALLOC(%SIZE(state_t));
       parm.stateInfo = pState;

       state.descriptor = openFile (ifsInfo
                              : stackOffsetToRpg + 1);

       IF parm.externallyDescribed;
          parm.useNamesValues = '1';
       ENDIF;
    WHEN parm.RpgOperation = QrnOperation_WRITE;  5 
       IF parm.useNamesValues;
          writeFileNv (state.handle
                     : namesValues
                     : stackOffsetToRpg + 1);
       ELSE:
          writeFileBuf (state.handle
                      : parm.outputBuffer
                      : parm.outputBufferLen
                      : stackOffsetToRpg + 1);
       ENDIF;
    WHEN parm.RpgOperation = QrnOperation_CLOSE;  6 
       closeFile (state.handle
                : stackOffsetToRpg + 1);
       state.descriptor = -1;
       DEALLOC(N) pState;
    OTHER;  7 
       sendException ('Unexpected operation '
                      + %CHAR(parm.RpgOperation)
                    : stackOffsetToRpg + 1);
       // Control will not return here
    ENDSL;

 END-PROC writeHdlr;

ファイルをオープンするプロシージャー

  1. ファイルをオープンできなかった場合、プロシージャーは 例外メッセージを送信します。これによってハンドラーは失敗し、 それが原因で RPG プログラム内の OPEN 命令は失敗します。
 DCL-PROC openFile;
    DCL-PI *n LIKE(descriptor_t);
       ifsInfo LIKEDS(ifshdlrs_info_t) CONST;
       stackOffsetToRpg INT(10) VALUE;
    END-PI;
    DCL-C JOB_CCSID 0;
    DCL-S openFlags INT(10);
    DCL-S descriptor LIKE(descriptor_t);

    openFlags = O_WRONLY
              + O_CREAT + O_TEXT_CREAT + O_TEXTDATA
              + O_CCSID + O_INHERITMODE;
    IF ifsInfo.append;
       openFlags += O_APPEND;
    ELSE:
       openFlags += O_TRUNC;
    ENDIF;

    descriptor = open(ifsInfo.path
                    : openFlags
                    : 0
                    : ifsInfo.createCcsid
                    : JOB_CCSID);
    IF descriptor < 0;  1 
       errnoException ('Could not open ' + ifsInfo.path + '.'
                     : getErrno ()
                     : stackOffsetToRpg + 1);
       // Control will not return here
    ENDIF;

    return descriptor;
 END-PROC openFile;

ファイルをクローズするプロシージャー

 DCL-PROC closeFile;
    DCL-PI *n;
       descriptor LIKE(descriptor_t) VALUE;
       stackOffsetToRpg INT(10) VALUE;
    END-PI;
    DCL-S rc INT(10);

    rc = close (descriptor);
    IF rc < 0;
       errnoException ('Error closing file.'
                     : getErrno ()
                     : stackOffsetToRpg + 1);
       // Control will not return here
    ENDIF;
 END-PROC closeFile;

出力バッファーを使用してファイルに書き込むプロシージャー

 DCL-PROC writeFileBuf;
    DCL-PI *n;
       descriptor LIKE(descriptor_t) VALUE;
       pBuf pointer VALUE;
       bufLen INT(10) VALUE;
       stackOffsetToRpg INT(10) VALUE;
    END-PI;

    writeLine (descriptor : pBuf : bufLen
             : stackOffsetToRpg + 1);
 END-PROC writeFileBuf;

名前-値情報を使用してファイルに書き込むプロシージャー

  1. 名前-値情報には、外部記述形式の各フィールドについての情報の配列が含まれています。 この例のハンドラーでは、 外部記述形式のフィールドの数およびタイプに関して独自の制限があります。
    1. ハンドラーは、フィールドが 1 つのみであることを検証します。
    2. 次に、ハンドラーは、それが英数字フィールドであることを検証します。
  2. nv.field(1).value は、最初のフィールド内のデータを指すポインターです。 フィールドが可変長フィールドである場合、このポインターはフィールドのデータ部分を指します。 nv.field(1).valueLenBytes は、データの長さを保持します。
 DCL-PROC writeFileNv;
    DCL-PI *n;
       descriptor LIKE(descriptor_t) VALUE;
       nv LIKEDS(QrnNamesValues_T);
       stackOffsetToRpg INT(10) VALUE;
    END-PI;

    IF nv.num > 1;  1a 
       sendException ('Only one field supported.'
                    : stackOffsetToRpg + 1);
       // Control will not return here
    ELSE:
       IF nv.field(1).dataType <> QrnDatatype_Alpha  2b 
       AND nv.field(1).dataType <> QrnDatatype_AlphaVarying;
          sendException ('Field ' + nv.field(1).externalName
                         + 'must be Alpha or AlphaVarying type.'
                       : stackOffsetToRpg + 1);
          // Control will not return here
       ENDIF;
    ENDIF;

    writeLine (descriptor : nv.field(1).value : nv.field(1).valueLenBytes
             : stackOffsetToRpg + 1);
 END-PROC writeFileNv;

ファイルに 1 行を書き込むプロシージャー

 DCL-PROC writeLine;
    DCL-PI *n;
       descriptor LIKE(descriptor_t) VALUE;
       pBuf pointer VALUE;
       bufLen INT(10) VALUE;
       stackOffsetToRpg INT(10) VALUE;
    END-PI;
    DCL-S lineFeed CHAR(1) INZ(STREAM_LINE_FEED);
    DCL-S bytesWritten INT(10);

    bytesWritten = write (descriptor : pbuf : bufLen);
    IF bytesWritten < 0;
       errnoException ('Could not write data.'
                     : getErrno ()
                     : stackOffsetToRpg + 1);
       // Control will not return here
    ELSE:
       bytesWritten = write (descriptor : %ADDR(lineFeed) : 1);
       IF bytesWritten < 0;
          errnoException ('Could not write line-feed.'
                        : getErrno ()
                        : stackOffsetToRpg + 1);
          // Control will not return here
       ENDIF;
    ENDIF;
 END-PROC writeLine;

例外を送信するプロシージャー

 DCL-PROC sendException;
    DCL-PI *n;
       msg VARCHAR(2000) CONST;
       stackOffsetToRpg INT(10) VALUE;
    END-PI;
    DCL-DS msgFile qualified;
       *n CHAR(10) INZ('QCPFMSG');
       *n CHAR(10) INZ('*LIBL');
    END-DS;
    DCL-DS errorCode;
       bytesProvided INT(10) INZ(0);
       bytesAvailable INT(10);
       msgId CHAR(7);
       *n CHAR(1);
    END-DS;
    DCL-S key CHAR(4);
    DCL-PR QMHSNDPM EXTPGM;
       msgId CHAR(7) CONST;
       msgFile LIKEDS(msgFile) CONST;
       msgData CHAR(1000) CONST;
       dataLen INT(10) CONST;
       msgType CHAR(10) CONST;
       callStackEntry CHAR(10) CONST;
       callStackOffset INT(10) CONST;
       msgKey CHAR(4) CONST;
       errorCode LIKEDS(errorCode);
    END-PR;

    QMHSNDPM ('CPF9898' : msgFile : msg : %LEN(msg)
            : '*ESCAPE' : '*' : stackOffsetToRpg
            : key : errorCode);
 END-PROC sendException;

"errno" に関連する例外を送信するプロシージャー

 DCL-PROC errnoException;
    DCL-PI *n;
       msg VARCHAR(2000) CONST;
       errnoVal INT(10) VALUE;
       stackOffsetToRpg INT(10) VALUE;
    END-PI;
    DCL-S errnoMsg VARCHAR(200);
    DCL-S pErrnoMsg pointer;
    DCL-PR strerror pointer extproc(*dclcase);
       errnoVal INT(10) VALUE;
    END-PR;

    pErrnoMsg = strError (errnoVal);
    IF pErrnoMsg <> *null;
       errnoMsg = ' ' + %STR(pErrnoMsg);
    ENDIF;
    errnoMsg += ' (errno = ' + %CHAR(errnoVal) + ')';

    sendException (msg + errnoMsg
                 : stackOffsetToRpg + 1);
 END-PROC errnoException;

"errno" の値を取得するプロシージャー

 DCL-PROC getErrno;
    DCL-PI *n INT(10) END-PI;
    DCL-PR getErrnoPtr pointer extproc('__errno') END-PR;
    DCL-S pErrno pointer static INZ(*null);
    DCL-S errno INT(10) BASED(pErrno);

    IF pErrno = *null;
       pErrno = getErrnoPtr();
    ENDIF;

    return errno;
 END-PROC getErrno;

オープン・アクセス・ハンドラーの作成については、トピック 『Rational Open Access: RPG Edition』を参照してください。

変更の終わり