「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 によって
手軽に扱うことができるようになる。