メニューボタン
IBMi海外記事2024.03.13

混合リストを使用してCLコマンドに「データ構造」を追加する

Ted Holt 著

データ構造がないRPGプログラムで最後に作業したのがいつだったか思い出せませんが、たぶん、System/34上だったような気がします。データ構造は誰もが使用しています。それにはもっともな理由があります。ポケットと同じくらい、手近にあってすぐに使えるからです。CLコマンドを書くときに、データ構造としてフォーマットされたパラメーターを組み込むことができます。IBMでは、それらを混合リストと呼んでいます。この記事では、そのやり方について説明します。

お気付きでないかもしれませんが、混合リスト パラメーターを持つIBM提供のコマンドは、皆さんも使用しています。たとえば、 ファイル・コピー(CPYF) コマンドには、そのようなパラメーターがいくつかあります(FROMKEY、TOKEY、INCCHAR、INCREL、およびSRCSEQ)。以下は、INCCHARパラメーターのプロンプトです。

シンプルな混合リスト

混合リスト パラメーターを定義するには、以下のように、ELEM(要素)キーワードが必要です。

             CMD        PROMPT('Do It')
             PARM       KWD(INCCHAR) TYPE(INCCHAR_T) +
                          PROMPT('Include records by char test')
 INCCHAR_T:  ELEM       TYPE(*CHAR) LEN(10) RSTD(*NO) DFT(*NONE) +
                          SPCVAL((*RCD) (*FLD)) EXPR(*YES) +
                          PROMPT('Field')
             ELEM       TYPE(*INT4) RSTD(*NO) EXPR(*YES) +
                          PROMPT('Character position')
             ELEM       TYPE(*CHAR) LEN(3) RSTD(*YES) DFT(*EQ) +
                          VALUES(*EQ *NE *GT *GE *LT *LE *NG *NL *CT) +
                          PROMPT('Relational operator')
             ELEM       TYPE(*CHAR) LEN(256) EXPR(*YES) +
                          PROMPT('Value')

要素が4つあり、それぞれが独自のデータ タイプ、プロンプト ストリングなどで定義されています。IBMのソース コードは手元にないので、これは、IBMがこのようにしてそのパラメーターを定義したのかもしれない、という1つの例に過ぎません。

この記事で使用されているコードは、 こちらからダウンロードできます。

コマンド処理プログラム(CPP)は、5つの値が入ったメモリーのブロックを受け取ります。1つ目の値は、混合リスト内に要素がいくつあるかを知らせる2バイトの整数です。この例では、その値は4となります。このフィールドは、私にとっては全く不要なので、いつも無視しています。その後に、4つの入力フィールド値が続きます。

INCCHARパラメーターは、CL CPPで以下のように処理されます。

pgm parm(&IncChar)                                                       
                                                                         
dcl var(&IncChar)  type(*char)               len(275)                    
dcl var(&Field)    type(*char) stg(*defined) len( 10) DefVar(&IncChar  3)
dcl var(&Position) type(*int)  stg(*defined) len(  4) DefVar(&IncChar 13)
dcl var(&RelOper)  type(*char) stg(*defined) len(  3) DefVar(&IncChar 17)
dcl var(&Value)    type(*char) stg(*defined) len(256) DefVar(&IncChar 20)

プログラムは、そのパラメーターを単一の文字ストリングとして受け取ります。ここでは、定義済み記憶域を使用して、パラメーターを4つの要素の記述でオーバーレイしました。定義済み記憶域は、RPGのデータ構造とまったく同じというわけではありませんが、同じ目的を果たします。

混合リストの配列

お望みなら、混合リストの配列をCPPに渡すこともできます。それがどのようなものなのかを、以下に示します。以下は、CPYFのINCRELパラメーターのプロンプトです。

混合リストの配列を定義した場合、コマンド プロセッサーは異なるパラメーター構造を使用します。このプロセスを説明するには、例を示すのが1番だと思われるので、私が先日関わったプロジェクトについてお話しすることとします。

私は、適切に書かれていない40年前のRPGプログラムの「ロジック」を理解しようとしていました(GOTOや標識を思い浮かべてみてください)。プログラムにプリンター ファイルを追加して、変数の値を記録しました。以下のようのものです。

 1 A Y 12.50 Y RA Y 12345
 2 A Y 12.50 Y RA Y 12345
 3 A Y  8.00 Y RA Y 12345
 4 C Y  6.00 Y DF Y 12345
 5 A Y       N DF Y 44444
 6 A Y 12.50 Y DF N 44444
 7 A Y 12.50 Y DF N 44444
 8 A Y 12.50 Y DF N 44444
 9 B Y       N RA N 44444
10 C N  8.00 Y RD N 12345
11 B Y  8.00 Y RD N 44444
12 A Y 12.50 Y RD N 44444
13 A N 12.50 Y DF N 12345
14 A N  6.00 N TS Y 12345
15 A N  6.00 Y DF Y 44444
16 A N 12.50 Y RD Y 44444
17 B N  8.00 Y TS Y 44444
18 C Y 12.50 Y TS Y 12345
19 C Y  8.00 Y TS N 44444
20 B Y       N RA N 12345
21 C Y  6.00 Y RD Y 44444
22 C Y       Y RA N 77777
23 A Y 12.50 N TS Y 12345
24 A Y  6.00 Y RA N 33333

スプール ファイルは、様々な条件と、それらがつながる出力について教えてくれました(おかげで、スパゲッティ コードをすべて解きほどく必要はなくなりました)。

スプール・ファイル表示(DSPSPLF)コマンドによって、レポートは表示されましたが、関連のある情報に絞る方法は示されませんでした。本当に必要だったのは、一定の基準を満たした行のみを表示することでした。これを実現するために、1回使うだけのユーティリティを書きました。この例では、SSF(Search Spooled File: スプール ファイル検索)ユーティリティと呼びます。SSFは、2つのオブジェクト(コマンドとCLプログラム)から成ります。

私のコマンドでは、1つから3つの検索基準を入力することができます。たとえば、

SSF FILE(QSYSPRT) JOB(*) SPLNBR(*LAST) +
       POS1(6)  VAL1(N) +
       POS2(16) VAL2(TS) +
       POS3(19) VAL3(Y)            

これは、「6桁目にN、16および17桁目にTS、19桁目にYがあるレポート行のみを表示する」ということを意味します。

*...+....1....+....2....+
14 A N  6.00 N TS Y 12345
17 B N  8.00 Y TS Y 44444

決して洗練されたものではありませんが、私の目的を満たすのには十分であり、急ごしらえで、掛かった時間と労力はわずかでした。このツール作成のための短期投資は十分に成果を上げ、私の多くの時間とクライアントの多くの金銭の節約になりました。

その後、時間があったときに、スプール ファイルの行を選択的に表示するためのきちんとしたユーティリティを書くこととしました。SSFの新バージョンでは、以下の点を改善しました。

  • 比較演算子一式をサポート(*EQ *NE *GT *GE *LT *LE *NG *NL *CT)。
  • and/or/not論理が可能になりました。
  • 条件を括弧でグループ化できるようになりました。
  • 混合リストの配列を使用して検索基準を指定。

新バージョンのSSFでは、前述の例は以下のようになります。

SSF FILE(QSYSPRT) JOB(*) SPLNBR(*LAST) +
       SELECT(( *N   *N *N  6 *EQ N  ) +
              ( *AND *N *N 16 *EQ TS ) +
              ( *AND *N *N 19 *EQ Y  ))

*Nトークンは、混合リストのその要素に対する値を提供しないことを意味します。確かに、この構文は少し暗号のようですが、それがCLであり、コマンドをプロンプトするときに、これらのパラメーターを適切にキー化することは難しくありません。

これで、ありとあらゆる検索を行えるようになりました。もうひとつ、例を示します。

SSF FILE(QSYSPRT) JOB(*) SPLNBR(*LAST) +
      SELECT((*N   *N *N   4 *EQ A) +
             (*AND *N '(' 16 *EQ DF) (*OR *N *N 16 *EQ TS ')'))        

これは、「カラム4にA、16および17にDFまたはTSのいずれかがあるレポート行を表示する」ということを意味します。

では、この魔法を起こしているコードを見てみましょう。まず、混合リストがどのように定義されるかを以下に示します。

  CMD        PROMPT('Search Spoole File')
  PARM  SELECT TYPE(SELECTION) MAX(12) PROMPT('Selection criteria')

 SELECTION: +
   elem TYPE(*CHAR) LEN(4) RSTD(*YES) VALUES(' ' *AND *OR) +
           EXPR(*YES) PROMPT('And/Or')
   elem TYPE(*CHAR) LEN(4) RSTD(*YES) VALUES(' ' *NOT) +
           EXPR(*YES) PROMPT('Not')
   elem TYPE(*CHAR) LEN(1) RSTD(*YES) VALUES(' ' '(') +
           EXPR(*YES) PROMPT('Open parenthesis')
   elem TYPE(*INT4) RSTD(*NO) EXPR(*YES) PROMPT('Position')
   elem TYPE(*CHAR) LEN(3) RSTD(*YES) DFT(*EQ) +
           VALUES(*EQ *NE *GT *GE *LT *LE *NG *NL *CT) +
           PROMPT('Test')
   elem TYPE(*CHAR) LEN(40) EXPR(*YES) PROMPT('Search value')
   elem TYPE(*CHAR) LEN(1) RSTD(*YES) VALUES(' ' ')') +
           EXPR(*YES) PROMPT('Close parenthesis')

混合リストには、要素が7つあります。これらでは、適切な場所に*AND、*OR、*NOT、および括弧を入力することができます。SELECTパラメーターは、MAX(12)を指定しています。これにより、混合リストの配列が作成されます。

私のCLコマンド処理プログラムは、データをレコード選択式に変換します。これは、「QUERYファイルのオープン(OPNQRYF)」に渡します。

%SST(SPLFDATA 4 1) *EQ 'A' *AND (%SST(SPLFDATA 16 2) *EQ 'DF' *OR 
%SST(SPLFDATA 16 2) *EQ 'TS')

このCLプログラムは、スプール ファイルをスクラッチ プログラム記述データベース ファイルにコピーし、「OPNQRYF」および「QUERYファイルからのコピー(CPYFRMQRYF)」を実行して、選択されたレポート行を2つ目のスクラッチ ファイルにコピーしてから、その2つ目のスクラッチ ファイルで「物理ファイル・メンバー表示(DSPPFM)」を使用して、私が表示したい行のみを表示してくれます。

私のCLプログラムは、SELECTパラメーターを1つの長いストリングとして受け取ります。そのストリングから適切にデータ値を抽出できるかどうかは私次第です。そのやり方を以下に示します。

最初の2バイトは、配列内の混合リストの数を知らせてくれる整数です。ここでは、%BIN関数を使用して、この値を取得しています。

pgm  ( . . . &inSlt)
dcl  &inSlt          *char    1024
dcl  &ListSize       *uint       2
ChgVar  &ListSize    %bin(&inSlt 1 2)

この後に続くのは、それぞれのデータ構造がパラメーター ストリング内のどこから始まるかを知らせる一連の2バイトの整数です。渡された混合リストごとに2バイトの整数が1つずつあります。以下は、そのデータを16進数で表したものです。

007E00430008

以下は、その情報を表にしたものです。

これらの値は、パラメーター ストリングの先頭からのオフセットであるので、たとえば、67という値はパラメーター ストリングの先頭から67バイト目を意味します。つまり、そのストリングの68桁目です。

それぞれの混合リストはそれらのオフセットの後に続きます(最後から最初へ)。それぞれの混合リストには、8つの値が格納されています。リスト内の値の数(これは常に7であり、無視することができる)と、その後に、コマンド定義でELEMによって定義された7つのフィールドが続きます。

混合リストの値をストリングから抽出するのは、煩雑ではありますが、難しいことではありません。

dcl &inSlt         *char  1024
dcl &ListSize      *uint     2
dcl &Lx            *uint     4
dcl &Ox            *uint     4
dcl &Quote         *char     1 value('''')
dcl &ValueLen      *uint     4
dcl &SplfData      *char    10 value(SPLFDATA)
dcl &Offset        *uint     4
dcl &Element       *char    59
dcl    &AndOr      *char     4 stg(*defined) defvar(&Element  3)
dcl    &Not        *char     4 stg(*defined) defvar(&Element  7)
dcl    &OpenParen  *char     1 stg(*defined) defvar(&Element 11)
dcl    &Position   *int      4 stg(*defined) defvar(&Element 12)
dcl    &Test       *char     3 stg(*defined) defvar(&Element 16)
dcl    &Value      *char    40 stg(*defined) defvar(&Element 19)
dcl    &CloseParen *char     1 stg(*defined) defvar(&Element 59)
dcl &QrySlt        *char  1024                                    
/* Build the query select expression */
chgvar  &ListSize    %bin(&inSlt 1 2)
dofor &Lx from(1) to(&ListSize)
   chgvar  &Ox       (&Lx * 2 + 1)
   chgvar  &Offset   (%bin(&inSlt &Ox 2) + 1)
   chgvar  &Element   %sst(&inSlt &Offset 59)
   chgvar  &ValueLen (%checkr(' ' &Value))
   if (&Position *ne 0) do
      ChgVar &QrySlt +
          ( &QrySlt *bcat +
                &AndOr *bcat &Not *bcat &OpenParen *cat +
               '%SST(' *cat &SplfData *bcat +
                %char(&Position) *bcat +
                %char(&ValueLen) *tcat ')' *bcat +
                &Test *bcat +
                &Quote *cat %trimr(&Value) *cat &Quote *cat +
                &CloseParen)
   enddo

照会選択ストリングが構築されたら、後は、スクラッチ ファイルを作成して、それを表示するだけです。

OpnQryF   file((&TempLib/&SplfData)) +
             QrySlt(&QrySlt) OpnID(&SplfData)
CpyFrmQryF  FromOpnID(&SplfData) +
              ToFile(&TempLib/&FileToShow) +      
              MbrOpt(*REPLACE) CrtFile(*YES)                        
DspPFM &tempLib/&FileToShow         

以下のコマンドを実行すると、

SSF FILE(QSYSPRT) JOB(*) SPLNBR(*LAST) +
      SELECT((*N   *N *N   4 *EQ A) +
             (*AND *N '(' 16 *EQ DF) (*OR *N *N 16 *EQ TS ')'))       

以下の結果が得られます。

*...+....1....+....2....+
 5 A Y       N DF Y 44444
 6 A Y 12.50 Y DF N 44444
 7 A Y 12.50 Y DF N 44444
 8 A Y 12.50 Y DF N 44444
13 A N 12.50 Y DF N 12345
14 A N  6.00 N TS Y 12345
15 A N  6.00 Y DF Y 44444
23 A Y 12.50 N TS Y 12345

このコードをダウンロードして、いろいろ試してみてください。

RPGのデータ構造が大好きという方なら(そうでない人はいるのでしょうか)、混合リストをCLコマンドに組み込みたいと思うのではないでしょうか。私はそれで構いません。

あわせて読みたい記事

PAGE TOP