オープン・アクセス・ハンドラーの例
注: この例の説明では、オープン・アクセス・ファイルに関連した部分についてのみ詳しく説明されています。
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 プログラムを以下に示します。
プログラムの以下の局面に注意してください。
- ハンドラー用に用意されたコピー・ファイル。
- ハンドラーと直接通信するために使用される通信域データ構造。 このデータ構造は、ハンドラーに渡されるパラメーター中の userArea サブフィールド によってポイントされます。
- オープン・アクセス・ファイル。この例のハンドラーは、
プログラム記述ファイルと外部記述ファイルの両方をサポートします。
このプログラム例では、次のソース・ファイルから定義された外部記述ファイル
が使用されています。
A R STREAMFMT A LINE 32740A VARLEN - HANDLER キーワード。
- HANDLER キーワードの最初のパラメーターは、 ハンドラー・プログラムまたはプロシージャーを定義する、コピー・ファイルからの 名前付き定数です。
- HANDLER キーワードの 2 番目のパラメーターは、 通信域データ構造です。
- このプログラムは、ハンドラーが必要とする追加情報で通信域を セットアップし、次に、ファイルをオープンします。
- 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 行を書き込むプロシージャー
- 例外を送信するプロシージャー
- "errno" に関連する例外を送信するプロシージャー
- "errno" の値を取得するプロシージャー
制御ステートメント、コピー・ファイル、およびグローバル定義
- テンプレートのデータ構造 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;
メイン・ハンドラー・プロシージャー
- プロシージャー・インターフェースは、オープン・アクセス・ハンドラーに毎回渡される パラメーターを定義しています。 QrnOpenAccess_T テンプレートは、ソース・ファイル QOAR/QRPGLESRC 内にある コピー・ファイル QRNOPENACC 内に定義されています。
- いくつかの基底データ構造が定義されています。
これらのデータ構造の基底ポインターは、ハンドラー・パラメーター中のポインターから設定されます。
- データ構造 state は、どのハンドラー呼び出しでも ハンドラーが必ず必要とする情報を保持します。 基底ポインター pState は、ハンドラー・パラメーター 中の stateInfo サブフィールドから設定されます。
- データ構造 ifsInfo は、通信域パラメーターです。 基底ポインター pIfsInfo を ハンドラー・パラメーター中の userArea サブフィールド から設定することによって、 ifsInfo データ構造 は、RPG プログラムで HANDLER キーワードの 2 番目のパラメーターとして指定 された ifs_info データ構造と同じ記憶域を参照するようになります。
- データ構造 namesValues は、 ファイル内の外部記述フィールドを記述します。 基底ポインターは、ハンドラー・パラメーターの namesValues サブフィールド から設定されます。
- state および ifsInfo の基底ポインターが設定されます。
- OPEN 命令の場合、ハンドラーは以下を行います。
- state データ構造用の記憶域を割り振り、 ポインターをハンドラー・パラメーターの stateInfo サブフィールド に割り当てます。 後続の命令のためにハンドラーが呼び出されるとき には、stateInfo サブフィールドに同じポインター値が 保持されているため、ハンドラーは状態情報にアクセスすることができます。
- ファイルをオープンし、戻されたファイル記述子を状態情報データ構造に保存します。
- ファイルが RPG プログラム内に外部記述されている場合、
ハンドラー・パラメーター内の useNamesValues 指標サブフィールドをオンに設定します。
- そのサブフィールドがオンの場合、出力命令用のデータは 各フィールドについての情報を格納する配列に入って提供されます。
- そのサブフィールドがオフの場合、出力命令用のデータは、 *OUTPUT 外部記述データ構造と同じレイアウトのデータ構造として提供されます。
- WRITE 命令の場合、ハンドラーは、 ハンドラー・パラメーターの useNamesValues サブフィールド に基づいて、プロシージャーのうちの 1 つを呼び出してファイルに書き込みます。
- CLOSE 命令の場合、ハンドラーはファイルをクローズします。
- 他の命令の場合、ハンドラーは例外を通知します。 この例のハンドラーは、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;
ファイルをオープンするプロシージャー
- ファイルをオープンできなかった場合、プロシージャーは 例外メッセージを送信します。これによってハンドラーは失敗し、 それが原因で 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 つのみであることを検証します。
- 次に、ハンドラーは、それが英数字フィールドであることを検証します。
- 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』を参照してください。
