CL

104. 印刷スプールをデータ・ベースに変換する (2)

次に紹介する印刷スプールのデータ・ベースへの変換は
バッチ処理などにおける特殊事例に対する考慮とそのテクニックを
紹介するものである。

先の「印刷スプールをデータ・ベースに変換する (1)」で示したサンプルは
既に多く利用されている例であったが、実はすべての環境で正しく動作することまでは
考慮されていない。

先の説明では

4.CPYSLF で最後に出力された印刷スプールを QTEMP に
作成しておいたデータ・ベースに出力する。
CPYSPLF FILE(QPRTOBJD) TOFILE(QTEMP/QPRINT) JOB(*)
    SPLNBR(*LAST)

としていたが、これは対話式ジョブでの実行に限られており
印刷ジョブをバッチに投入した場合の印刷スプールのジョブ名は
QPRTJOB でありジョブ番号も i5/OS が割振るジョブ番号となるので
CPYSPLF に「JOB(*)」で指定することはできない。

となるとジョブ名の指定を

JOB(000123/MYUSR/QPRTJOB)

のようにジョブ番号 000123 のように明示的に指定しなければならない。
スプール・ファイル番号 (SPLNBR) には *LAST のような特殊指定は可能だが
ジョブ番号に *LAST として JOB(*LAST/MYUSR/QPRTJOB) のような
指定はできない。

そこで紹介するのが API : QSPRILSP : 最後に出力されたスプール識別の取得
( Retrieve Identity of Last Spooled File Created ) である。
API : QSPRILSP は i5/OS V5R2 で初めて公開された API であり
最後、つまり直前に出力された印刷スプールの情報を取得する API である。
最後のスプールの属性の中には、もちろんジョブ番号も属性の一部として
入っているのでこれを取得することによって QPRTJOB のジョブ番号も
知ることができる。

API: QSPRILSP の IBM による日本語解説はないので
下記に簡単に仕様を紹介する。

QSPRILSP : 最後に出力されたスプール識別の取得

パラメータ

1 受取り変数 出力 Char(*)
2 受取り変数の長さ 入力 BIN(4)
3 様式名 入力 Char(8)
4 エラー・コード 入出力 Char(*)

パブリック使用権限 : *USE

受取り変数

SPRL0100様式
Offset タイプ フィールド
Dec Hex
0 0 BIN(4) 戻りバイト数
4 4 BIN(4) 使用可能バイト数
8 8 CHAR(10) スプール・ファイル名
18 12 CHAR(10) ジョブ名
28 1C CHAR(10) ユーザー名
38 26 CHAR(6) ジョブ番号
44 2C BIN(4) スプール・ファイル番号
48 30 CHAR(8) ジョプ・システム名
56 38 CHAR(7) スプール作成日
63 3F CHAR(1) 予約済み
64 40 CHAR(6) スプール作成時刻

様式名 : SPRL0100 のみ指定可

【解説】

先ほどは「バッチ・ジョブに投入した場合は QPRTJOB」と説明したが
正確には IBM は

「現在のジョブのユーザー名が、現在実行されている
      ユーザー・プロファイルのものと 同じでない場合」

が QPRTJOB として実行されると説明しているがこの説明で理解できる人は
果たしてどれくらいいるのだろうか?

  • SNDNETSPLF で送信して受信されて作成されたスプールは QPRTJOB
  • RSTOBJ などで復元されて作成されるスプールは QPRTJOB
  • QBATCH に投入されて実行して作成されたスプールは QPRTJOB

であることがわかっている。
そこでバッチ・ジョブに投入されて印刷スプールが QPRTJOB となってしまったときも
考慮したプログラムに書き換えると下記のようになる。

【 サンプルCLP : TESTPRT3 】
0001.00              PGM
0002.00 /*-------------------------------------------------------------------*/
0003.00 /*   TESTPRT3  :   印刷スプールをデータ・ベースへコピーする          */
0004.00 /*                                                                   */
0005.00 /*   2017/08/19  作成                                                */
0006.00 /*-------------------------------------------------------------------*/
0007.00              DCL        VAR(&JOB)  TYPE(*CHAR) LEN(10)
0008.00              DCL        VAR(&USER) TYPE(*CHAR) LEN(10)
0009.00              DCL        VAR(&JOBNBR) TYPE(*CHAR) LEN(6)
0010.00              DCL        VAR(&TYPE) TYPE(*CHAR) LEN(1)
0011.00              DCL        VAR(&RCVDTA) TYPE(*CHAR) LEN(128)
0012.00              DCL        VAR(&RCVLEN) TYPE(*CHAR) LEN(4)
0013.00              DCL        VAR(&APIERR) TYPE(*CHAR) LEN(116) +
0014.00                           VALUE(X'000074') /* 2 進数  */
0015.00
0016.00 /*( 環境の取得 )*/
0017.00              RTVJOBA    JOB(&JOB) USER(&USER) NBR(&JOBNBR) TYPE(&TYPE)
0018.00
0019.00 /*( データ・ベースの作成 )*/
0020.00              CRTPF      FILE(QTEMP/QPRINT) RCDLEN(132) IGCDTA(*YES) +
0021.00                           LVLCHK(*NO) AUT(*ALL)
0022.00              MONMSG     CPF7300
0023.00
0024.00 /*( スプールを *HOLD にオーバーライド )*/
0025.00              OVRPRTF    FILE(QPRTOBJD) HOLD(*YES) LVLCHK(*NO) +
0026.00                           SECURE(*YES) OVRSCOPE(*JOB) OPNSCOPE(*JOB)
0027.00
0028.00 /*( 印刷ジョブの実行 )*/
0029.00              DSPOBJD    OBJ(SPOOLWTR) OBJTYPE(*LIB) OUTPUT(*PRINT)
0030.00              DLTOVR     FILE(QPRTOBJD) LVL(*JOB)
0031.00
0032.00 /*( QPRTJOB の JOB 番号を検索 )*/
0033.00              IF         COND(&TYPE *EQ '0') THEN(DO) /*  バッチ  */
0034.00              CHGVAR     VAR(%BIN(&RCVLEN)) VALUE(128)
0035.00              CALL       PGM(QSPRILSP) PARM(&RCVDTA &RCVLEN +
0036.00                           'SPRL0100' &APIERR)
0037.00              CHGVAR     VAR(&JOB) VALUE(%SST(&RCVDTA 19 10))
0038.00              CHGVAR     VAR(&USER) VALUE(%SST(&RCVDTA 29 10))
0039.00              CHGVAR     VAR(&JOBNBR) VALUE(%SST(&RCVDTA 39 6))
0040.00              ENDDO      /*  バッチ  */
0041.00
0042.00 /*( 印刷スプールをデータ・ベースにコピー )*/
0043.00              CPYSPLF    FILE(QPRTOBJD) TOFILE(QTEMP/QPRINT) +
0044.00                           JOB(&JOBNBR/&USER/&JOB) SPLNBR(*LAST)
0045.00
0046.00 /*( 不要になったスプールを削除する )*/
0047.00              DLTSPLF    FILE(QPRTOBJD) JOB(*) SPLNBR(*LAST)
0048.00              ENDPGM
【解説】

この CLP では対話式ジョブの場合は RTVJOBA コマンドによって
JOB, USER, JOBNBR を取得して
バッチ・ジョブの場合は QSPRILSP によって JOB, USER, JOBNBR を
取得しているのだが QSPRILSPは対話式で実行された場合も
JOB, USER, JOBNBR を取得できるのだから
一般的には対話式、バッチ・ジョブのどちらの場合であっても
印刷スプールの JOB, USER, JOBNBR を取得するには つねに QSPRILSP
使用しておけば対話式、バッチ・ジョブの判断は不要となる。
読者はそれをテストして確かめて欲しい。

このように条件によって分離して判断するのではなくできるだけ少ない条件で
一般的に恒等式のように成り立つ方法を生み出すことを「一般化」という。

この「一般化」という考え方は非常に重要である。
条件分岐をできるだけ少なくして
一般的なアルゴリズムで成立するようにしておくと
読みやすくデバッグも容易で品質に優れたプログラムを開発することができる。