「17. IFSのファイルの存在チェックを行うCHKIFS」 では C/400 ソース・コードによる例を紹介したが、
弊社のユーザーから C/400 の環境のためにコンパイルがうまく行かないとの問い合わせがあった。
そこで作成したのがここに紹介する CLP による CHKIFS である。
CHKIFS の動作原理はシンプルであり、C 関数 (正確には API であるが ) であるopen 関数を使って
IFS のファイルのオープンを試行するだけである。
実は C 関数も ILE環境のごく一部であって、OS 提供のサービス・プログラムに含まれる単なる
プロシージャーのひとつにしか過ぎない。
open 関数 は QSYS/QP0LLIB1 という名前のサービス・プログラムの中に含まれている。
これは API のマニュアルを参照すれば、どのサービス・プログラムであるかを知ることができる。
たとえ API のマニュアルに書かれていなくても、小さな C プログラムを作成してそこにバインドされている
OS の サービス・プログラムの EXPORT プロシージャーを調べればすぐにもわかる。
ただし C 関数の多くのパラメータはポインターによるものが多い。
幸い、最近の OS リリースでは CLP の中でもポインターを使えるようになっているのでCLP によって
C 関数を扱うことができる。
もちろんここに紹介する手法は IBM マニュアルのどこを探しても紹介されていない裏ワザであり、
この方法を理解すれば、 C 関数や API を気軽に扱うことができるようになる。
---------------------------------------------------------------------------------
0001.00 CMD PROMPT('IFS 検査 ')
0002.00 PARM KWD(DIR) TYPE(*CHAR) LEN(256) CASE(*MIXED) +
0003.00 PROMPT('IFS ストリーム・ファイル ')
---------------------------------------------------------------------------------
----------------------------------------------------------------------------------
0001.00 PGM PARM(&DIR)
0002.00 /*----------------------------------------------------------------*/
0003.00 /* CHKIFS : IFS ストリーム・ファイルの存在検査 */
0004.00 /* */
0005.00 /* SRCTYPE : CLLE */
0006.00 /* */
0007.00 /* CRTCLMOD QTEMP/CHKIFSCL SRCFILE(MYSRCLIB/QCLSRC) */
0008.00 /* AUT(*ALL) */
0009.00 /* CRTPGM MYLIB/CHKIFSCL MODULE(QTEMP/CHKIFSCL) */
0010.00 /* BNDSRVPGM(QSYS/QP0LLIB1) ACTGRP(*NEW) */
0011.00 /* AUT(*ALL) */
0012.00 /*----------------------------------------------------------------*/
0013.00 DCL VAR(&MSG) TYPE(*CHAR) LEN(132)
0014.00 DCL VAR(&DIR) TYPE(*CHAR) LEN(256)
0015.00 DCL VAR(&PATH) TYPE(*CHAR) LEN(256)
0016.00 DCL VAR(&PATH_PTR) TYPE(*PTR) ADDRESS(&PATH 0)
0017.00 DCL VAR(&RES) TYPE(*INT) LEN(4)
0018.00 DCL VAR(&RES_PTR) TYPE(*PTR) ADDRESS(&RES 0)
0019.00 DCL VAR(&TRUE) TYPE(*INT) VALUE(0)
0020.00 DCL VAR(&FALSE) TYPE(*INT) VALUE(-1)
0021.00 DCL VAR(&O_RDONLY) TYPE(*INT) LEN(4) VALUE(1)
0022.00 DCL VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00')
0023.00 DCL VAR(&TYPE) TYPE(*CHAR) LEN(1)
0024.00 MONMSG MSGID(CPF0000) EXEC(GOTO CMDLBL(ERROR))
0025.00
0026.00 RTVJOBA TYPE(&TYPE)
0027.00 CHGVAR VAR(&PATH) VALUE(&DIR *TCAT &NULL)
0028.00 CALLPRC PRC('open') PARM((&PATH_PTR *BYVAL) +
0029.00 (&O_RDONLY *BYVAL) (*OMIT)) RTNVAL(&RES)
0030.00 IF COND(&RES *EQ &FALSE) THEN(DO)
0031.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) +
0032.00 MSGDTA(' ファイル ' *CAT &DIR *TCAT +
0033.00 ' が見つかりません。 ') MSGTYPE(*ESCAPE)
0034.00 ENDDO
0035.00 RETURN
0036.00
0037.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) MSG(&MSG)
0038.00 SNDMSG:
0039.00 IF COND(&TYPE *EQ '0') THEN(DO)
0040.00 SNDPGMMSG MSG(&MSG) TOMSGQ(*SYSOPR) MSGTYPE(*COMP)
0041.00 ENDDO
0042.00 ELSE CMD(DO)
0043.00 SNDPGMMSG MSG(&MSG) TOMSGQ(*TOPGMQ) MSGTYPE(*DIAG)
0044.00 ENDDO
0045.00 ENDPGM
----------------------------------------------------------------------------------
最初に open 関数 について紹介しよう。
open 関数は API 解説書の中では
------- 構文 --------------------------------------------------------------- #include <fcntl.h> int open(const char* path, int oflag, ...);
として紹介されている。
#include <fcntl.h> とは open 関数のプロトタイプが定義されているヘッダー・ファイルをインクルードするよう
指示している。#include <fcntl.h> の実体は QSYSINC/H(FCNTL) であり、SEU や PDM によって内容を
確認することができる。( 後でこのことは重要になる。)
int という型は 4バイトの整数を意味している。( integer )char* という型は文字列のポインターを意味している。
int oflag, ... の ... は複数個のフラグを定義することができるが、省略可能であることを意味している。
例えば使用例は次のとおりである。
#include <fcntl.h>
:
int fildes;
char file[256];
fildes = open(file, O_RDONLY);
ここで O_RDONLY とは読み取り専用として file をオープンすることを意味している。
O_RDONLY は SEU や PDM で QSYSINC/H(FCNTL) を調べれば
#define O_RDONLY 00001 /* Open for reading only */
として定義されているので O_RDONLY の値は 1 であることがわかる。
open 関数は戻り値として int型の値を戻し、正常にオープンすることができればファイル記述子としての
正の値を戻すが、オープンに失敗すれば -1 を戻す。ファイル記述子は正の値であるが OS によって
自動発生するので正というしかわからない。
上記の open 関数は CLP では
0028.00 CALLPRC PRC('open') PARM((&PATH_PTR *BYVAL) +
0029.00 (&O_RDONLY *BYVAL) (*OMIT)) RTNVAL(&RES)
として記述されている。
英小文字を指定するためにプロシージャー名を引用符で囲んで 'open' と記述する。
パス名を示すパラメータに関しては
0015.00 DCL VAR(&PATH) TYPE(*CHAR) LEN(256) 0016.00 DCL VAR(&PATH_PTR) TYPE(*PTR) ADDRESS(&PATH 0)
として定義されている。
これは 256バイトの文字変数 &PATH に対して &PATH_PTR は TYPE(*PTR) として定義されている
ポインターであり、ADDRESS(&PATH 0) によって &PATH_PTR は変数 &PATH の開始バイト( 0 )を
示すポインターである。RPG で記述する EVAL &PATH_PTR = %ADDR(&PATH) と同じである。
次に open 関数にパラメータとして渡す前に
0022.00 DCL VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00') : 0027.00 CHGVAR VAR(&PATH) VALUE(&DIR *TCAT &NULL)
として &PATH の後ろに NULL ( X'00') を付加しているのは char* という型が最後に NULL で終わる
( NULL-STOP) 型として定義されているからである。またパラメータ : PARM((&PATH_PTR *BYVAL) の
定義は char* として &PATH_PTR を *BYVAL として値を渡すことを意味している。
IBM の解説ではポインターを渡すときは *BYREF であると解説されているがこれは誤りである。
CALLPRC コマンドのパラメータ・タイプの省略値は *BYREF であるので *BYVAL に明示的に
変更することを忘れてはならない。( *BYREF はめったに使用しない。)
0021.00 DCL VAR(&O_RDONLY) TYPE(*INT) LEN(4) VALUE(1)
:
0028.00 CALLPRC PRC('open') PARM((&PATH_PTR *BYVAL) +
0029.00 (&O_RDONLY *BYVAL) (*OMIT)) RTNVAL(&RES)
によって TYPE(*INT) の値 1 の変数 : &O_RDONLY を (&O_RDONLY *BYVAL) としてパラメータとして
渡しているのがわかる。open 関数の戻り値は
0017.00 DCL VAR(&RES) TYPE(*INT) LEN(4)
:
0028.00 CALLPRC PRC('open') PARM((&PATH_PTR *BYVAL) +
0029.00 (&O_RDONLY *BYVAL) (*OMIT)) RTNVAL(&RES)
のようにして TYPE(*INT) の int型の変数 &RES として定義されている。
パラメータの場合は int でなくても 4バイトの CHAR に int型のバイナリー・コードを入れても動作するが
戻り値が int型である場合は TYPE(*INT) を戻り値として割り振らないと値が戻ってこないので注意が
必要である。
0020.00 DCL VAR(&FALSE) TYPE(*INT) VALUE(-1)
:
0030.00 IF COND(&RES *EQ &FALSE) THEN(DO)
0031.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) +
0032.00 MSGDTA(' ファイル ' *CAT &DIR *TCAT +
0033.00 ' が見つかりません。 ') MSGTYPE(*ESCAPE)
0034.00 ENDDO
の記述によって戻り値 &RES が -1 であればファイルが見つからないというエラーを MSGTYPE(*ESCAPE) で
戻すようにしている。それ以外の戻り値であればファイルは見つかったことになる。
MSGTYPE(*ESCAPE) で戻しているのは このプログラム CHKIFSCL を呼び出すコマンド:CHKIFS で
MONMSG を使用することができるようにするためである。例えば、
CHKIFS DIR('/AS400-NET.USR/MYFILE.HTM')
MONMSG CPF9800 DO
GOTO ERROR
ENDDO
のような処理を行うことができるようにするためである。
CRTCLMOD コマンドによってモジュールを作成してから CRTPGM コマンドによって
サービス・プログラム : QSYS/QP0LLIB1 をバインドしているがAPI の解説書(OS:V5R4M0〜) を参照すれば、
その API が含まれているサービス・プログラムがどれであるかが IBM によって公開されている。
DSPSRVPGM QSYS/QP0LLIB1
によって確かに open 関数が EXPORT されていることを確認されたい。
上記に紹介した CHKIFSCL は
について学習することができたはずである。
CHKIFSCL は、ひとつのサンプルにしか過ぎないが他の多くの C 関数や API をCLP によって
手軽に扱うことができるようになる。