次に紹介する印刷スプールのデータ・ベースへの変換は
バッチ処理などにおける特殊事例に対する考慮とそのテクニックを
紹介するものである。
先の「印刷スプールをデータ・ベースに変換する (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 による日本語解説はないので
下記に簡単に仕様を紹介する。
1 | 受取り変数 | 出力 | Char(*) |
---|---|---|---|
2 | 受取り変数の長さ | 入力 | BIN(4) |
3 | 様式名 | 入力 | Char(8) |
4 | エラー・コード | 入出力 | Char(*) |
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) | スプール作成時刻 |
先ほどは「バッチ・ジョブに投入した場合は QPRTJOB」と説明したが
正確には IBM は
「現在のジョブのユーザー名が、現在実行されている
ユーザー・プロファイルのものと 同じでない場合」
が QPRTJOB として実行されると説明しているがこの説明で理解できる人は
果たしてどれくらいいるのだろうか?
であることがわかっている。
そこでバッチ・ジョブに投入されて印刷スプールが QPRTJOB となってしまったときも
考慮したプログラムに書き換えると下記のようになる。
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
を
使用しておけば対話式、バッチ・ジョブの判断は不要となる。
読者はそれをテストして確かめて欲しい。
このように条件によって分離して判断するのではなくできるだけ少ない条件で
一般的に恒等式のように成り立つ方法を生み出すことを「一般化」という。
この「一般化」という考え方は非常に重要である。
条件分岐をできるだけ少なくして
一般的なアルゴリズムで成立するようにしておくと
読みやすくデバッグも容易で品質に優れたプログラムを開発することができる。