コマンドから呼び出されるプログラムのことをCPP(=Command Procee Program)
と呼ぶがここでは複合コマンドを処理するCPPの処理の方法について
説明する。
_
[コマンド: TESTPAGE ]
ソースはこちらから
0001.00 CMD PROMPT(' テスト・ページ ') 0002.00 PARM KWD(PAGEINFO) TYPE(PAGEINFO) + 0003.00 PROMPT(' 印刷の範囲 ') 0004.00 PAGEINFO: ELEM TYPE(*DEC) LEN(6 3) DFT(1) + 0005.00 PROMPT(' 開始ページ ') 0006.00 ELEM TYPE(*DEC) LEN(6 3) DFT(*ENDPAGE) SPCVAL((*ENDPAGE + 0007.00 -1)) PROMPT(' 終了ページ ')
[コンパイル]
CRTCMD OBJLIB/TESTPAGE PGM(OBJLIB/TESTPAGECL) SRCFILE(SRCLIB/QCMDSRC) AUT(*ALL)
開始ページと終了ページというふたつのパラメータがあるが
実行はPAGEINFOというひとつの複合パラメータである。
CPPが受取るのもひとつのパラメータとして受取るのでそれをどのように処理するか
後で解説する。
また「 終了ページ」というパラメータは*ENDPAGEという文字列が入力されるとそれを
-1という特殊な数値に置換えている。
これは「 終了ページ」が数字として定義されているので数値の入力しか許されないが
*ENDPAGEという意味のある文字列を表示するためである。
このテクニックも有用でありIBM提供のユーティリティ・コマンドではこの手法が
数多く使われている。
[実行画面]
テスト・ページ (TESTPAGE) 選択項目を入力して,実行キーを押してください。 印刷の範囲 : 開始ページ . . . . . . . . . 1 数値 終了ページ . . . . . . . . . *ENDPAGE 数値 , *ENDPAGE
[解説]
ご覧のような画面はIBMユーティリティで数多く見たことがあるはずである。
読者の会社では開始日~終了日のような入力の場合が多いかもしれない。
_
[CLP: TESTPAGECL ]
ソースはこちらから
0001.00 PGM PARM(+ 0002.00 &PAGEINFO + 0003.00 ) 0004.00 /*----------------------------------------------------------------------*/ 0005.00 /* TESTPAGECL: テスト・ページ */ 0006.00 /* */ 0007.00 /* 作成 : 2023/06/14 */ 0008.00 /*----------------------------------------------------------------------*/ 0009.00 DCL VAR(&PAGEINFO) TYPE(*CHAR) LEN(10) 0010.00 DCL VAR(&STRPAGEC) TYPE(*CHAR) STG(*DEFINED) + 0011.00 LEN(4) DEFVAR(&PAGEINFO 3) 0012.00 DCL VAR(&STRPAGEP) TYPE(*PTR) 0013.00 DCL VAR(&STRPAGE) TYPE(*DEC) STG(*BASED) LEN(6 + 0014.00 3) BASPTR(&STRPAGEP) 0015.00 DCL VAR(&ENDPAGEC) TYPE(*CHAR) STG(*DEFINED) + 0016.00 LEN(4) DEFVAR(&PAGEINFO 7) 0017.00 DCL VAR(&ENDPAGEP) TYPE(*PTR) 0018.00 DCL VAR(&ENDPAGE) TYPE(*DEC) STG(*BASED) LEN(6 + 0019.00 3) BASPTR(&ENDPAGEP) 0020.00 DCL VAR(&MSG) TYPE(*CHAR) LEN(132) 0021.00 DCL VAR(&STMMSG) TYPE(*CHAR) LEN(132) 0022.00 DCL VAR(&MSGID) TYPE(*CHAR) LEN(7) 0023.00 DCL VAR(&MSGF) TYPE(*CHAR) LEN(10) 0024.00 DCL VAR(&MSGFLIB) TYPE(*CHAR) LEN(10) 0025.00 DCL VAR(&MSGKEY) TYPE(*CHAR) LEN(4) 0026.00 DCL VAR(&MSGDTA) TYPE(*CHAR) LEN(132) 0027.00 DCL VAR(&ERRDTA) TYPE(*CHAR) LEN(132) 0028.00 DCL VAR(&TYPE) TYPE(*CHAR) LEN(1) 0029.00 DCL VAR(&TOPGMQ) TYPE(*CHAR) LEN(10) 0030.00 DCL VAR(&MSGTYPE) TYPE(*CHAR) LEN(10) + 0031.00 VALUE('*ESCAPE ') 0032.00 DCL VAR(&ERR) TYPE(*CHAR) LEN(1) 0033.00 DCL VAR(&NULL4) TYPE(*CHAR) LEN(4) + 0034.00 VALUE(X'00000000') 0035.00 DCL VAR(&APIERR) TYPE(*CHAR) LEN(116) + 0036.00 VALUE(X'0000007400000000') /* 2 進数 */ 0037.00 MONMSG MSGID(CPF9999) EXEC(GOTO CMDLBL(ERROR)) 0038.00 0039.00 /*( 環境の取得 )*/ 0040.00 RTVJOBA TYPE(&TYPE) 0041.00 IF COND(&TYPE *EQ '0') THEN(DO) /* バッチ */ 0042.00 CHGVAR VAR(&TOPGMQ) VALUE('*SYSOPR ') 0043.00 ENDDO /* バッチ */ 0044.00 ELSE CMD(DO) /* 対話式 */ 0045.00 CHGVAR VAR(&TOPGMQ) VALUE('*TOPGMQ ') 0046.00 ENDDO /* 対話式 */ 0047.00 0048.00 /*( 入力パラメータの検査 )*/ 0049.00 CHGVAR VAR(&STRPAGEP) VALUE(%ADDR(&STRPAGEC)) 0050.00 CHGVAR VAR(&ENDPAGEP) VALUE(%ADDR(&ENDPAGEC)) 0051.00 0052.00 RETURN 0053.00 0054.00 ERROR: RCVMSG MSGTYPE(*LAST) RMV(*NO) KEYVAR(&MSGKEY) + 0055.00 MSGDTA(&MSGDTA) MSGID(&MSGID) MSGF(&MSGF) + 0056.00 SNDMSGFLIB(&MSGFLIB) 0057.00 IF COND(&MSGID *EQ 'CPF9999') THEN(DO) 0058.00 CHGVAR VAR(&ERRDTA) VALUE(&MSGDTA) 0059.00 RCVMSG MSGTYPE(*PRV) MSGKEY(&MSGKEY) RMV(*NO) + 0060.00 MSG(&MSG) MSGDTA(&MSGDTA) MSGID(&MSGID) + 0061.00 MSGF(&MSGF) MSGFLIB(&MSGFLIB) 0062.00 CHGVAR VAR(&STMMSG) VALUE(' プログラム ' *CAT + 0063.00 %SST(&ERRDTA 8 10) *TCAT + 0064.00 ' のステートメント ' *CAT %SST(&ERRDTA + 0065.00 24 4) *CAT ' で次のエラーが発生しました。 ') 0066.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&STMMSG) + 0067.00 TOMSGQ(&TOPGMQ) MSGTYPE(*DIAG) 0068.00 ENDDO 0069.00 SNDMSG: IF COND(&MSGID *EQ ' ') THEN(DO) 0070.00 SNDPGMMSG MSGID(CPF9897) MSGF(QCPFMSG) MSGDTA(&MSG) + 0071.00 TOMSGQ(&TOPGMQ) MSGTYPE(&MSGTYPE) 0072.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0073.00 ENDDO 0074.00 ELSE CMD(DO) 0075.00 SNDPGMMSG MSGID(&MSGID) MSGF(&MSGFLIB/&MSGF) + 0076.00 MSGDTA(&MSGDTA) TOMSGQ(&TOPGMQ) + 0077.00 MSGTYPE(&MSGTYPE) 0078.00 MONMSG MSGID(CPF2400) EXEC(RETURN) 0079.00 ENDDO 0080.00 ENDPGM
[コンパイル]
CRTCLPGM PGM(OBJLIB/TESTPAGECL) SRCFILE(SRCLIB/QCLSRC) OPTION(*SRCDBG) AUT(*ALL)
[解説]
このCLPは先のコマンド: TESTPAGEからToolsで紹介されている「117.コマンドからCLPを生成するCRTCMDCPP」を
使って生成したものに手修正を加えたものである。
CRTCMDCPPは本当に便利なコマンドでコマンドからCLPを作成するときにいちいちコマンドのパラメータを
チェックしながらコーディングしなくてもCLPの生成が一瞬でできるのである。
しかも正確なのでぜひ利用して頂きたい。
さてここで問題の複合パラメータは
0001.00 PGM PARM(+ 0002.00 &PAGEINFO + 0003.00 )
のようにひとつのパラメータとしてコマンドより値を受取り
0009.00 DCL VAR(&PAGEINFO) TYPE(*CHAR) LEN(10)
として定義されている。
コマンドのパラメータは
0004.00 PAGEINFO: ELEM TYPE(*DEC) LEN(6 3) DFT(1) + 0005.00 PROMPT(' 開始ページ ') 0006.00 ELEM TYPE(*DEC) LEN(6 3) DFT(*ENDPAGE) SPCVAL((*ENDPAGE + 0007.00 -1)) PROMPT(' 終了ページ ')
として数字6.3のPACK形式で定義されているのでこれは各々4バイトである。
そこでCPPに渡される形式は
項目数2バイト(INT) + 開始ページ(4バイト) + 終了ページ(4バイト) = 10バイト
の受取りが必要である。
ここでもし開始ページや終了ページが文字列であれば
開始ページ = %SST(&PAGEINFO 3 4)
と実行するだけで簡単に値を取り出すことができるのだが
数字でしかもPACK形式(=CLPの数値: *DEC はすべてPACK形式として定義される。ZONE形式を
定義することはできない)
であるので容易ではない。
まず
0010.00 DCL VAR(&STRPAGEC) TYPE(*CHAR) STG(*DEFINED) + 0011.00 LEN(4) DEFVAR(&PAGEINFO 3)
として開始ページを&STRPAGECという4バイトの文字列で*DEFINEDを使って
DEFVAR(&PAGEINFO 3)として &PAGEINFOの3桁目から4バイトとして定義している。
これによって
CHGVAR VAR(&STRPAGEC) VALUE(%SST(&PAGEINFO 3 4))
という演算は必要ない。
*DEFINED のくわしい説明と使用方法はこちらで
次に
0012.00 DCL VAR(&STRPAGEP) TYPE(*PTR) 0013.00 DCL VAR(&STRPAGE) TYPE(*DEC) STG(*BASED) LEN(6 + 0014.00 3) BASPTR(&STRPAGEP)
として数字の開始ページ&STRPAGEはポインタ:&STRPAGEP を基底とするLEN(6 3)の
数字TYPE(*DEC)として定義している。
ポインタというと何やら難しく思えるかもしれないが
IBM iでもポインタの概念は導入されておりポインタとはひとことでいうと
「フィールドの開始位置」のことである。
よくポインタの説明に「番地」とかいう人がいるがあれは完全な間違いである。
「番地」ではボインタを理解できない。
ポインタは「変数の開始位置」であり
ここでは変数: &STRPAGEはポインタ&STRPAGEPを基底として定義されているが
基底となるポインタ:&STRPAGEPはまだ値が与えられていないので
変数: &STRPAGEもまだメモリのどの部分に配置するのか決められていないことになる。
これまで多くの変数はDCLなどで定義した途端にコンバイラーによってメモリの
どの位置からXXバイトというように静的に決定されている。
ところがポインタとは変数のメモリの位置であるので
ポインタが決まらないことには変数の位置も確保できない。
ボイタンが決まって初めてその変数も位置も値も確保することができる。
つまりポインタを使うということはメモリの動的な管理である。
そこで
0049.00 CHGVAR VAR(&STRPAGEP) VALUE(%ADDR(&STRPAGEC))
によってポインタ&STRPAGEPを %ADDR(&STRPAGEC)によって変数 &STRPAGECの
開始位置を設定すると変数 &STRPAGEの位置が決定されて値を取り出すことが
できるというわけである。
このようにポインタはCLPから学習するとわかりやすい。
_