IBM i / AS400 解析・リバース — 最良の手順と手法(匿名化公開用)
CHAINRECFMT01 / WRITESCR02 のように命令と対象がくっついて見えるのは、
「予約語が優先されて動く」のではなく、固定形式の列位置で別欄に置かれているだけ。
現行資産は RPG III 列(Opcode 28–32)が多い。RPG IV 列だけ見ると 0件になる。
実装の主経路は tools/rpg_io_parse.py。
| 欄 | RPG III(1始まり) | RPG IV |
|---|---|---|
| Form Type | 6 | 6 |
| Factor1 | 18–27 | 12–25 |
| Opcode | 28–32 | 26–35 |
| Factor2 | 33–42 | 36–49 |
| 命令 | Factor2に来やすいもの | CRUD寄せ先 |
|---|---|---|
| CHAIN/READ/SETLL… | ファイル名 or 様式 | ファイル(DDS突合) |
| WRITE/UPDAT/DELET | レコード様式 | 所属ファイルへ正規化 |
| EXFMT | DSPF様式 | 画面(CRUD図では除外) |
- 社内キュレーション IBM_i_AS400_詳細解析_情報収集手順書_v1.1.md(§6.1.1 ほか)
- IBM Documentation: DSPPGMREF / QWHDRPPR(WHFUSG)
- IBM ILE RPG Reference(C-spec 列位置)
- Midware RPG IV C-specs / MC Press(WRITE/UPDATE はレコード様式)
- midrange.com RPG400-L(FILE vs FORMAT)
- 現行 SRC 実例(SRC\**\*.RPG)
表示上は空白が無く見える。
C SSCODE CHAINRECFMT01 11
C EXFMTSCR01
C WRITESCR02
C SETLLFILE01 99
固定形式(RPG IV)では 列位置 が構文である。
| 欄 | RPG IV 列(1始まり) | 0始まり index |
|---|---|---|
| Form Type | 6 | 5 |
| Factor 1 | 12–25 | 11:25 |
| Opcode(+ext) | 26–35 | 25:35 |
| Factor 2 | 36–49 | 35:49 |
| Result | 50–63 | 49:63 |
| Resulting indicators | 71–76 | 70:76 |
CHAINRECFMT01 は「CHAIN という予約語が隣接識別子を食った」のではなく、
CHAINRECFMT01が隣接して 表示上つながって見える ことが多い。
C の行は列スライスで Opcode / Factor2 を取る(空白正規表現に依存しない)。OP_GLUED(CHAIN+識別子)はフォールバック。誤爆しやすい(CHAIN を含む長い名前等)。white-space: pre / コードフェンスで原文保持。chain CustomerNo CustomerF; // OK
chainCustomerNo CustomerF; // 通常は1トークン扱いかエラー
chain(e)CustomerNo CustomerF; // 括弧で区切れる場合あり(可読性は悪い)
/FREE 行では列パーサを使わず、字句解析する。
| 命令 | Factor2 / オペランドに来やすいもの |
|---|---|
| CHAIN / READ / READE / SETLL / SETGT | ファイル名 または レコード様式 |
| WRITE / UPDATE(UPDAT) / DELETE(DELET) | 原則 レコード様式名(ファイル名ではない) |
| EXFMT | レコード様式(DSPF) |
| EXCEPT | O-spec の例外名(帳票・部分出力)。DB 直書きと誤認しやすい |
FFA011L01IF ... DISKFA011R / LF メンバ FA011L01WRITE FA011R または WRITEFA011R(見た目連結)→ 証跡の「対象」が FA011L01IF(F仕様のファイル名)なのに、命令側は FA011R だけ、という 名前不一致 が PARTIAL の主因。
load_lf_dds を強化)。WRITE/UPDAT/DELET は Factor2 がレコード様式でも、所属ファイルへ CRUD を寄せる。EXCEPT は PRTF/O-spec か DB かを分け、DB の C と決め打ちしない。WHRFNM(レコード様式)と WHFNAM/WHSNAM を突合する。| F仕様の見え方 | やりがち | 正しい扱い |
|---|---|---|
... IP ... DISK | CONFIRMED READ | 宣言のみ → PARTIAL(操作命令待ち) |
... UF ... DISK | CONFIRMED UPDATE | 同上。更新可能宣言であり UPDATE 行が必要 |
... O ... DISK | CONFIRMED CREATE | WRITE/EXCEPT 行が必要 |
| WORKSTN / DSPF | DISK と同じ CRUD | 画面。EXFMT/WRITE 様式単位 |
RENAME(rec:alias) | 様式名不一致で見逃し | 別名表を作る |
PREFIX / EXTFILE / EXTMBR | 静的名で確定 | 実行時名は UNRESOLVED/推定 |
現行方針「F仕様だけでは CONFIRMED にしない」は正しい。維持する。
| ソースに出やすい | 正規化先 | CRUD めやす |
|---|---|---|
| UPDAT | UPDATE | U |
| DELET | DELETE | D |
| BITOF / LOKUP(RPG III 残滓・変換途中) | BITOFF / LOOKUP | I/O ではないことが多い |
| READE / READP / READPE | READ 系 | R |
| WRITE + EXFMT | 画面系 | R/C(DSPF) |
\bUPDATE\b だけだと UPDAT を落とす。現行 OP_PAT は両方あるが、列パーサ側の Opcode 正規化表も同じ集合にする。
C N60 WRITESCR02 61
C SSCODE CHAINRECFMT01 11
* の行だけスキップ。解析時に「標識付き行を雑に除外」すると CRUD が薄くなる。
OVRDBF FILE(ORDERPF) TOFILE(LIB/REALPF) MBR(MBR01)
CALL PGM(ORD001)
DLTOVR FILE(ORDERPF)
| 静的に見える名 | 実行時 |
|---|---|
| ORDERPF | REALPF / 別メンバの可能性 |
現行 OBJ情報/プログラム参照情報(DSPPGMREF).csv に既にある。
| 値 | 意味 | CRUD への寄せ方(めやす) |
|---|---|---|
| 1 | Input | R |
| 2 | Output | C |
| 3 | I/O | R+C |
| 4 | Update | U |
| 5 | I+U | R+U |
| 6 | O+U | C+U |
| 7 | I/O/U | R+C+U |
| 8 | Unspecified | 確定に使わない(CREATE/DROP 等も) |
| 0 | N/A | 使わない |
| 9–15 | 上記+Unspecified | 下位ビットだけ採用し 8 は無視 |
主要列: WHPNAM(PGM), WHFNAM/WHSNAM(参照名), WHLNAM, WHRFNM(様式), WHFUSG, WHOBJT。
- 昇格はしない(Bob ルール: SRC 行番号必須のまま)
- ただしメモ/含意に「DSPPGMREF: WHFUSG=5 → コンパイル時は I+U」を追記し、確認先を明示。
WRITE 様式 を検出したら、DSPPGMREF の WHRFNM でファイルへ正規化して CONFIRMED。| 記法 | 注意 |
|---|---|
READ ZS-STM INVALID ... | ファイル名にハイフン。RPG 用正規表現だと落ちる |
OPEN INPUT ZS / SELECT 名 | 内部名と SELECT 名の対応が必要 |
embedded SQL INSERT INTO | DSPPGMREF に出ても WHFUSG=8 になりうる。SQL ソース精読が必要 |
| QRYDFN / RUNQRY | CALL ツリーに出ない。CL 逆引き必須(現行ツールあり) |
CALL 変数 / CALLP 式 | 静的閉包に出ない → UNRESOLVED |
OPS = {
"CHAIN": "R", "READ": "R", "READE": "R", "READP": "R", "READPE": "R",
"SETLL": "R", "SETGT": "R", "EXFMT": "R",
"WRITE": "C", "EXCEPT": "C",
"UPDAT": "U", "UPDATE": "U",
"DELET": "D", "DELETE": "D",
}
def parse_fixed_c_spec(line: str) -> dict | None:
# 注意: タブ展開禁止。短行は右側を空白パディングしてからスライス
if len(line) < 6 or line[5].upper() != "C":
return None
if len(line) > 6 and line[6] == "*":
return None
padded = line.ljust(80)
op = padded[25:35].strip().upper()
# opcode extender: CHAIN(E) 等 → 括弧除去
op = re.sub(r"\(.*\)$", "", op)
if op not in OPS:
return None
return {
"factor1": padded[11:25].strip().upper(),
"op": op,
"factor2": padded[35:49].strip().upper(),
"result": padded[49:63].strip().upper(),
"crud": OPS[op],
}
自由形式・CL・COBOL は別パーサ。固定形式に \bCHAIN\s+(\w+) だけ頼らない。
| 項目 | 現状 | 推奨 |
|---|---|---|
OP_GLUED | あり(有効) | 列パーサを主、GLUED は副 |
| F仕様のみ PARTIAL | 正しい | 維持 |
| DDS 突合 | 一部 | WRITE 様式→ファイル必須化 |
| DSPPGMREF | 取得済・レポートに薄い | WHFUSG/WHRFNM を昇格判定の補助+メモに常時 |
| 業務意味 | 機能一覧・メニュー・画面ワイヤー | データ系(PFILE)単位の業務ラベルを用語集と接続 |
固定形式RPGでは、CHAIN、READ、WRITE、UPDATE、EXSR等が「予約語として周囲の文字より優先的に認識される」ため動作するわけではない。
コンパイラーは、行内の列位置を基準として、各部分を別の欄として読み取る。
概念的には次のように分かれている。
仕様タイプ | 条件標識 | 演算項目1 | 命令コード | 演算項目2 | 結果フィールド | 結果標識
例えば、等幅フォント上では次のように見えるコードがある。
C CUSTOMERNO CHAIN CUSTOMERF
これは空白文字を単なる区切りとして解釈しているのではなく、CUSTOMERNO、CHAIN、CUSTOMERFが、それぞれ所定の列範囲に置かれているため認識される。
したがって、画面表示、HTML変換、PDF変換、OCR、CSV化等によって空白が詰まり、次のように見えたとしても、
CUSTOMERNOCHAINCUSTOMERF
元のソース上で各文字が正しい列に存在していれば、コンパイル時には別々の欄として解釈される。
この場合の正しい理解は次のとおりである。
> 「書き方として不適切だが、予約語が優先されて動く」のではない。
> 「固定形式の列定義に従った正常な記述であり、表示上は空白が少なく見えることがある」。
自由形式RPGでは、列位置ではなく、空白、括弧、演算子、セミコロン等による字句の区切りが重要になる。
chain CustomerNo CustomerF;
次のように命令コードと識別子を連結すると、通常はCHAIN命令とCustomerNoには分解されない。
chainCustomerNo CustomerF;
これはchainCustomerNoという一つの識別子として扱われるか、構文エラーになる可能性がある。
括弧が字句の境界になる次の記述は解析できる場合がある。
chain(e)CustomerNo CustomerF;
ただし、可読性と保守性のため、次のように空白を入れる。
chain(e) CustomerNo CustomerF;
| 判定項目 | 固定形式 | 自由形式 |
|---|---|---|
| 仕様タイプ | 行頭付近のH、F、D、I、C、O、P | ctl-opt、dcl-f、dcl-s等 |
| 列位置 | 構文上の意味を持つ | 原則として意味を持たない |
| 文末 | セミコロンを使用しない旧形式が中心 | セミコロンが必要 |
| 命令位置 | 命令コード欄に配置 | 行内の任意位置 |
| 空白圧縮 | ソース破壊につながる | トークン境界を保てば影響が小さい |
| 自動整形 | 原則禁止、専用パーサーが必要 | RPG対応フォーマッターなら可能 |
固定形式RPGを解析、変換、検索、AI投入する場合は、次を守る。
white-space: preまたはpre-wrapを使用する。{
"sourceLineNumber": 245,
"rawLine": "C CUSTOMERNO CHAIN CUSTOMERF",
"specificationType": "C",
"factor1": "CUSTOMERNO",
"operationCode": "CHAIN",
"factor2": "CUSTOMERF",
"resultField": "",
"resultIndicators": [],
"parserMode": "FIXED_FORMAT",
"columnLayoutVersion": "RPG_IV"
}
rawLineは整形せず、抽出した原文をそのまま保存する。
次の場合は、ソースの破損または変換ミスを疑う。
0とO、1とI、標識欄が誤認されている。列ずれが疑われる場合は、ソース物理ファイルの元メンバー、コンパイルリスト、SEUまたはRDi上の列ルーラーを基準に再確認する。
抽出対象:
CHAIN
SETLL
SETGT
READE
READPE
READ
READP
WRITE
UPDATE
DELETE
EXCEPT
OPEN
CLOSE
UNLOCK
FEOD
各命令について記録する。
対象ファイル
対象レコード様式
キー値
キー項目順
部分キーか完全キーか
昇順/降順
検索開始位置
ループ条件
%FOUND
%EOF
%EQUAL
%ERROR
レコードロック
例外拡張子(E)
結果インジケーター
更新対象項目
コミットメント制御
setll (CustomerNo) CustL1;
reade (CustomerNo) CustL1;
dow not %eof(CustL1);
// 処理
reade (CustomerNo) CustL1;
enddo;
このコードを解析するときは、次が必要になる。
CustL1の実体。