CL

126. CLPから英小文字APIを呼び出すには

IBM iには数多くのAPIが用意されていてCLPやRPG,COBOLから
呼び出して使うことができるが「英小文字」で表現されているAPIは
少し変わっている。
英小文字APIはプログラムではなくプロシージャーである。
従って CALL命令で呼び出すことができない。
このことがCLPやRPGで英小文字APIの利用の機会を少なくしているのでは
ないだろうか?

確かに多くの英小文字APIがAPIとして紹介されているが
使い方がわからない、という人が多いだろう。
それもそのはず英小文字APIはAPIとして紹介はされているが
CLPやRPGからの呼出し方法がどこにも書かれていないからである。
その理由は以前はCLPやRPGではポインターを使用することができなかった。
英小文字APIはポインター参照が多いのでIBMもCLPからの
呼出し方法についてはガイドしなかったのだろう。
しかし今ではCLPでもRPGでもポインタを使うことができるので
英小文字APIもCLP, RPGから呼び出すことができる。

そこでCLPでIBM iの英小文字APIを呼び出す方法を紹介しよう。
この方法はIBM iのマニュアルではガイドされていない。
IBM iのAPIは元々 C言語からの利用を対象にして書かれているので
C言語からの利用は簡単であるが CLPやRPGからでも呼び出すことができる。

ただし次のことに留意されたい。


・英小文字のAPI関数はプログラムではなくプロシージャーであるので
CALLPRC(プロシージャー呼出し)を使って呼び出す。

・英小文字APIに与えるAPIのパラメータや戻り値にはポインタ参照が多いので
ポインタに関する十分な理解が必要である。


簡単な英小文字API: getenv を例に学習してみよう。

API: getenv はAPIというよりもただのC言語の関数に近い環境変数の値を
読取るAPIである。
(環境変数とは QTEMPのようにJOBの中で一時的に定義される変数のことである)

getenv はIBMマニュアルの中では次のように定義されている。

#include
char *getenv(const char *varname);

これはIBMの表現が良くなくて正しくは

#include
char* getenv(const char *varname);

つまり varname というポインタによる変数値の環境変数を char*つまりポインタで戻すという意味です。

[C言語による例]

#include  
#include  
 
/* Where the environment variable 'PATH' is set to a value. */
 
int main(void)
{
   char *pathvar;
 
   pathvar = getenv("PATH");
   printf("pathvar=%s",pathvar);
}

[CLPによる例]

ソースはこちらから

0001.00              PGM                                                        
0002.00 /*-------------------------------------------------------------------*/ 
0003.00 /*   TESTENV    : GETENV の呼出しテスト                              */ 
0004.00 /*                                                                   */ 
0005.00 /*   2020/06/17  作成                                                */ 
0006.00 /*   CRTBNDCL TEST.COM/TESTENV SRCFILE(R610SRC/QCLSRC) AUT(*ALL)     */ 
0007.00 /*-------------------------------------------------------------------*/ 
0008.00              DCL        VAR(&QRYSTR) TYPE(*CHAR) LEN(128) VALUE('LANG') 
0009.00              DCL        VAR(&QRYSTR_P) TYPE(*PTR) ADDRESS(&QRYSTR)      
0010.00              DCL        VAR(&STRING_P) TYPE(*PTR)                       
0011.00              DCL        VAR(&STRING) TYPE(*CHAR) STG(*BASED) +          
0012.00                           LEN(132) BASPTR(&STRING_P)                    
0013.00              DCL        VAR(&NULL) TYPE(*CHAR) LEN(1) VALUE(X'00')      
0014.00                                                                         
0015.00 /*( getenv: 環境変数の取得 )*/                                          
0016.00              CHGVAR     VAR(&QRYSTR) VALUE('PATH' *TCAT &NULL)          
0017.00              CALLPRC    PRC('getenv') PARM((&QRYSTR)) RTNVAL(&STRING_P) 
0018.00              SNDPGMMSG  MSG(&STRING) MSGTYPE(*DIAG)                     
0019.00              RETURN                                                     
0020.00                                                                         
0021.00              ENDPGM  


                                                  

コンパイル

CRTCLPGM ではなく CRTBNDCL OBJLIB/TESTENV SRCFILE(MYSRCLIB/QCLSRC) AUT(*ALL) でコンパイルする。
ソース・タイプも CLPではなく CLLE でソース編集すること。

[解説]

注目は

0016.00              CHGVAR     VAR(&QRYSTR) VALUE('PATH' *TCAT &NULL)          
0017.00              CALLPRC    PRC('getenv') PARM((&QRYSTR)) RTNVAL(&STRING_P) 

の2行であるが パラメータは *BYREF で与えるので NULL-STOP を

0016.00              CHGVAR     VAR(&QRYSTR) VALUE('PATH' *TCAT &NULL)

で設定しておけば &QRYSTR はポインタとしてgetenvに渡したことになる。
重要なのは getenvからの戻り値である &STRIMG_P の定義の方法である。

0010.00              DCL        VAR(&STRING_P) TYPE(*PTR)                       
0011.00              DCL        VAR(&STRING) TYPE(*CHAR) STG(*BASED) +          
0012.00                           LEN(132) BASPTR(&STRING_P) 

のようにして定義しているが最初に

0010.00              DCL        VAR(&STRING_P) TYPE(*PTR)

としてポインタだけが先に定義されて、このポインタに対して
ポインタをベースにして

0011.00              DCL        VAR(&STRING) TYPE(*CHAR) STG(*BASED) +          
0012.00                           LEN(132) BASPTR(&STRING_P)

として &STRING が定義されていることに注目して欲しい。
getenv からはポインタが戻ってくるので戻ってきたポインタに対して &STRING を
定義しているのである。
これを逆に

0023.00              DCL        VAR(&STRING) TYPE(*CHAR) LEN(4096)        
0024.00              DCL        VAR(&STRING_P) TYPE(*PTR) ADDRESS(&STRING)

のように文字列 &STRING を定義してからその文字列に対するポインタを定義してはいけない。
このポインタを受取りパラメータとしても&STRINGには正しい値が入らない。
文字列に対して定義したポインとは別の異なったポインタが戻ってくるので
それでは &STRING には値が入らないからである。

ひのように戻り値がポインタである場合は先にポインタを定義してそのポインタをベースにする
変数を定義するように定義の順序を逆にしなければならない。

これがCLPから英小文字APIを利用する点で最大のミソである。
これが理解できれば英小文字APIだけでなくすべてのC言語の関数もCLPから呼び出して
使うことができるようになる。

ポインタの話は少し難しかったかもしれないがポインタとは変数の始まりの位置のことである。
ポインタの説明によく「番地」という言葉が使われるがそれではよけいにわからない。

多くの変数はメモリの中で

….[変数SHCODE]…………………..[変数: SHNAME]…………..

のように配置されているはずである。

  ....[変数SHCODE].......................[変数: SHNAME]..............
     |                   |
     |開始位置 ......................... |開始位置

このように変数はコンピュータの中でメモリされている場所が開始位置(ここから)と長さによって
配置されている。
このうちの開始位置のことをポインターと呼ぶ。
なんとわかりやすい説明ではないか。番地などと言われても想像がつかない。
要は記憶する開始位置のことをポインタと呼んでいるだけのことである。