「OPTIONS(MAIN)」は単なる開始点ではない。メインフレームの入り口を正しく理解する
PL/Iの保守現場に配属されたばかりの諸君、あるいは長年この言語を扱っていながら「なんとなく書いていた」というベテランもいるかもしれない。今日は、プログラムの冒頭に必ず現れる `OPTIONS(MAIN)` という記述について、少しだけ深掘りしてみよう。
単に「プログラムの開始位置を指定する」だけだと思っているなら、それは少々もったいない。この属性が持つ本当の意味は、「OS(z/OS)という巨大な制御プログラムと、どう握手するか」を定義することにあるんだ。
OPTIONS(MAIN) がOSに対して行っている「儀式」
z/OSのジョブステップが開始されるとき、JCLの `EXEC PGM=…` で指定されたプログラムへ制御が渡される。このとき、コンパイラが生成したスタートアップ・ルーチン(PL/Iの実行時環境であるLE: Language Environment)は、何をしていると思う?
`OPTIONS(MAIN)` が指定されていると、LEは以下の「儀式」を自動的に執り行う。
1. 引数の受け渡し: JCLの `PARM` フィールドに記述された文字列を、PL/Iの `PROCEDURE` 文の引数として正しく解釈し、マッピングする。
2. ONユニットの初期化: プログラム全体を保護する標準的なエラーハンドリング(ON条件)の土台を築く。
3. 環境の確立: ファイル(DD文)との紐付けに必要な制御ブロックの構築を行う。
これが欠けていると、OSは「どこから処理を始めればいいのか」「パラメータをどう渡せばいいのか」が判別できず、あえなく `SOC4` や `ABEND` で散ることになる。
実践的なコード構造:バッチ処理のテンプレート
現場でよく見かける、実務に即した標準的な構造を見てみよう。VSAMファイルへのアクセスと、堅牢なエラーハンドリングを意識した構成だ。
1
/ プログラム名: BATCH001 /
/ 機能: VSAMマスタファイルからの読み込みと条件判定 /
BATCH001: PROCEDURE(PARM_STR) OPTIONS(MAIN);
/ — 宣言部 — /
DCL PARM_STR CHAR(100) VARYING; / JCLのPARM値を受け取る /
DCL VSAM_FILE FILE RECORD INPUT ENV(VSAM);
/ 入出力用レコード構造体 /
DCL 1 MASTER_REC,
5 KEY_ID CHAR(5),
5 DATA_VAL FIXED BIN(31),
5 FILLER CHAR(44);
/ ビルトイン関数を利用した初期化 /
DCL I FIXED BIN(15) INIT(0);
/ — エラーハンドリング (ONユニット) — /
/ 予期せぬIOエラーを補足し、ダンプを出力して終了させる /
ON ENDFILE(VSAM_FILE) BEGIN;
PUT SKIP LIST(‘ END OF FILE REACHED ‘);
GO TO FINISH;
END;
/ — メイン処理 — /
OPEN FILE(VSAM_FILE);
DO WHILE(‘1’B);
READ FILE(VSAM_FILE) INTO(MASTER_REC);
/ 処理ロジック /
IF MASTER_REC.DATA_VAL > 1000 THEN DO;
/ 現場でよくある特記事項:
特定の条件下で警告を出す /
PUT SKIP LIST(‘HIGH VALUE DETECTED: ‘ || MASTER_REC.KEY_ID);
END;
END;
FINISH:
CLOSE FILE(VSAM_FILE);
/ 正常終了の明示 /
PUT SKIP LIST(‘BATCH001 COMPLETED SUCCESSFULLY.’);
END BATCH001;
現場で「ハマる」ポイント:知っておくべきこと
1. OPTIONS(MAIN)は一つだけ:
1つのロードモジュールの中に `OPTIONS(MAIN)` は一つしか存在できない。もしサブプログラムを呼び出す構造にするなら、サブプログラム側には `OPTIONS(MAIN)` を書いてはいけない。「そんなの当たり前だ」と思うだろう? だが、古いプログラムをコピー&ペーストで改修していると、稀にこの「二重宣言」をやらかしてリンクエラーで頭を抱える若手がいるんだ。
2. LE(Language Environment)の挙動:
最近のマイグレーションプロジェクトでは、古いコンパイラからEnterprise PL/Iへ移行することが多い。この際、`OPTIONS(MAIN)` の挙動はLEのデフォルト設定に依存する。特に `PARM` の渡し方や、異常終了時のダンプ出力形式は、LEのランタイムオプションで制御されることを忘れないでくれ。プログラムのソースだけでなく、JCL側の `CEEOPTS` や `CEEDUMP` の定義もセットで確認するのが「デキる」エンジニアの作法だ。
3. ONユニットのスコープ:
`OPTIONS(MAIN)` 内で宣言された `ON` ユニットは、そのプログラム全体に有効だ。もしサブプロシージャ(`PROCEDURE` 内の `PROCEDURE`)で個別のエラー処理が必要なら、そのスコープ内で再度 `ON` ユニットを定義する必要がある。この「動的スコープ」の理解が、バグを未然に防ぐ鍵になる。
最後に:諸君へのアドバイス
メインフレームの世界は、一見すると堅苦しいルールだらけに見えるかもしれない。しかし、そのルール(仕様)の裏側には、何十年もの間、止まることを許されなかった基幹システムを守り抜いてきた先人たちの知恵と工夫が詰まっている。
`OPTIONS(MAIN)` をただの「おまじない」にするな。OSとプログラムを繋ぐ「入り口」として、その役割を深く理解し、自信を持ってコードを書いてほしい。何か行き詰まったら、いつでもマニュアルの「LEプログラミング・ガイド」を紐解いてみることだ。あそこには、現役バリバリの答えが書いてある。
さて、今日はここまで。次のバッチ改修も、美しいコードで乗り切ってくれ。

コメント