RPG

532. ライブラリー関数 _Rwriteのバグを回避する方法

API : ライブラリー関数とは ILE-RPGが実際に内部で行っている関数であり
ILE-RPGの演算命令を模擬実行することができる。
つまりILE-RPGの入出力演算命令は内部では「ライブラリー関数」と呼ばれるAPI
によって行われている。
  _

弊社が現在、開発中のPythonフレーム・ワーク(Python.400)では
このことを利用してPythonでILE=RPGがやっているのと同じ入出力命令を
実行している。
しかしその中で印刷ファイルに出力するAPIは120バイト以上の出力を
行おうとすると不明なエラー・メッセージが頻出することがわかった。
この記事はこの障害の回避の方法を紹介するものである。
一般的には使われない非常に特殊な処理なので
興味のない方は読み飛ばして頂いてもかまわない。
_

IBM ライブラリー関数の詳細はここから

現在、開発中のPython.400でもPythonで RPGのWRITE命令を
実行するために使われている。
ライブラリー関数を組み合わせることによってRPGがやっている演算命令と
同じものをそっくりPythonでも実行することができる。

_Rwrite関数のIBM 解説はここから
_

RPGの代わりに C言語やRPGからでも _Rwriteを使って
RPG の WRITE命令と同じことを実行することができる。
具体的にには


① _Ropen関数でファイルをオープンして
ファイルへのポインタを取得しておく。

② _Rformat関数で出力しようとするファイルのレコード名を
 宣言する。
つまり ILE-RPGの場合は WRITE 命令だけで
指定したレコードにデータを出力することができるが
ライブラリー関数を使う場合は _Rformat関数によって
レコード名を指定しなければならない。

③ _Rwrite関数でデータをファイル・ポインタをり指示して
 出力する。


という3段階を経てRPGのWRITE命令と同じことを
実現することができる。
つまりILE-RPGのひとつの入出力命令がライブラリー関数のひとつと
1対1に対応しているわけではなくて
ILE-RPGのWRITE命令= (_Rformat関数) + (_Rwrite関数) という
組み合わせでILE-RPG関数の動作を実現している。

この方法によって例えば印刷ファイルにも同様に_Rwriteで出力することができる。
_

[例] 印刷ファイル(PSHOHIN2)に出力するTESTPRT2

001.00 H DFTNAME(TESTPRT2) DATEDIT(*YMD/) BNDDIR('QC2LE')                     
002.00 F********** TRNPRT2 _RWrite 印刷テスト ******************************* 
003.00 F*                                                                     
004.00 F**********************************************************************
005.00                                                                        
006.00 D*( _Ropen()   レコード・ファイルをオープンして入出力操作を行う )      
007.00 D Ropen           PR              *          ExtProc('_Ropen')         
008.00 D  RFILE                          *   VALUE OPTIONS(*STRING:*TRIM)     
009.00 D  MODE                           *   Value OPTIONS(*STRING:*TRIM)     
010.00                                                                        
011.00 D*( _Rformat() -  レコード・フォーマット名の設定 )                     
012.00 D Rformat         PR              *          ExtProc('_Rformat')       
013.00 D  RFILE                          *   Value                            
014.00 D  FORMAT                         *   Value                            
015.00                                                                        
016.00 D*( _Rwrite() -  次のレコードの書き込み )  - WRITE                     
017.00 D Rwrite          PR              *          ExtProc('_Rwrite')        
018.00 D  RFILE                          *   Value                            
019.00 D  RECORD                         *   Value                            
020.00 D  RCD_LEN                      10I 0 Value                            
021.00                                                                        
022.00 D*( _Rclose() -  ファイルのクローズ ) - CLOSE                          
023.00 D Rclose          PR            10I 0        ExtProc('_Rclose')        
0024.00 D  RFILE                          *   Value                                                     
0025.00                                                                                                 
0026.00 D C2M_MSG         S             10I 0 IMPORT('_C2M_MSG')                                        
0027.00  *( 作業変数 )                                                                                  
0028.00 D RFILE_P         S               *                                         ファイル・ポインタ  
0029.00 D RECORD#         S             11A                                                             
0030.00 D NULL            S              1A   INZ(X'00')                                                
0031.00 D OUTBUF          S           1024A                                                             
0032.00 D OUTBUF_P        S               *   INZ(%ADDR(OUTBUF))                                        
0033.00 D OUTLEN          S             10I 0 INZ(168)                                                  
0034.00                                                                                                 
0035.00 C/FREE                                                                                          
0036.00    C2M_MSG = 0;                                                                                 
0037.00    RFILE_P = Ropen('PSHOHIN2': 'ar, indicators=y');                                             
0038.00    RECORD# = 'PRTHEAD   ' + NULL;                                                               
0039.00    Rformat(RFILE_P: %ADDR(RECORD#)); // レコード・フォーマット名の設定                          
0040.00    Rwrite(RFILE_P: OUTBUF_P: OUTLEN);                                                           
0041.00    Rclose(RFILE_P);                                                                             
0042.00    *INLR = *ON;                                                                                 
0043.00 C/END-FREE                                                                                      

[解説]

先に説明したように

0037.00    RFILE_P = Ropen('PSHOHIN2': 'ar, indicators=y');                                             
0038.00    RECORD# = 'PRTHEAD   ' + NULL;                                                               
0039.00    Rformat(RFILE_P: %ADDR(RECORD#)); // レコード・フォーマット名の設定                          
0040.00    Rwrite(RFILE_P: OUTBUF_P: OUTLEN);                                                           
0041.00    Rclose(RFILE_P)

というのが
①_Ropen関数でファイルをオープンして
ファイルへのポインタを取得しておく。

0037.00    RFILE_P = Ropen('PSHOHIN2': 'ar, indicators=y');

② _Rformat関数で出力しようとするファイルのレコード名を
 宣言する。

0038.00    RECORD# = 'PRTHEAD   ' + NULL;                                                               
0039.00    Rformat(RFILE_P: %ADDR(RECORD#)); // レコード・フォーマット名の設定 

③_Rwrite関数でデータをファイル・ポインタをり指示して
 出力する。

0040.00    Rwrite(RFILE_P: OUTBUF_P: OUTLEN);                                                           

である。
ところがこの_Rwrite の実行は「レコード長が長すぎる」という不明な
メッセージが出力されてエラーとなってしまう。
レコード・バッファーは出力されるのだが出力直後にエラーとなって
終了してしまう。
これは同じ _Rwrite命令を次のように C言語で実行してみると
起こらない。
_

_Rwrite命令を使ったC言語の例

0001.00 /********************************************************************/ 
0002.00 /*                                                                  */ 
0003.00 /*   TESTWRT2    :   印刷ファイル出力                               */ 
0004.00 /*                                                                  */ 
0005.00 /*          Office Quattro Co,.Ltd 2024/01/26  7:40:00 created      */ 
0006.00 /*                                                                  */ 
0007.00 /*                                                                  */ 
0008.00 /********************************************************************/ 
0009.00 #pragma comment(COPYRIGHT, "Visual PANEL-WORKER (C) CopyRight         
0010.00 Office Quattro.Corp. 2024- All right reserved. Users Restricted       
0011.00 Rights - Use, duplication or disclosure restricted by Office Quattro  
0012.00 Corp. Licenced Materials-Property of Office Quattro.")                 
0013.00 #include                                                      
0014.00 #include                                                     
0015.00 #include                                                     
0016.00 #include  /* triml */                                      
0017.00 #include                                                      
0018.00 #include                                                     
0019.00 #include                                                    
0020.00 #include              /* ropen(). rreadk()            */      
0021.00                                                                        
0022.00 #define TRUE         0                                                 
0023.00 #define FALSE       -1                                                 
0024.00 int    bLR = FALSE;                                                       
0025.00 typedef struct {                                                          
0026.00    int  BYTESPRO;                                                         
0027.00    int  BYTESAVL;                                                         
0028.00    char MSGID[7];                                                         
0029.00    char RESRVD;                                                           
0030.00    char EXCPDATA[100];                                                    
0031.00 } ERRSTRUCTURE;     /* Define the error return structure            */    
0032.00 ERRSTRUCTURE  errcode;/* Error Code Structure for RCVMSG      */          
0033.00 volatile _INTRPT_Hndlr_Parms_T ca;                                        
0034.00                                                                           
0035.00 /*************************************************************/           
0036.00 /*       内 部 使 用  関  数                          */           
0037.00 /*************************************************************/           
0038.00 void  GetParam(int argc, char *argv[]);                                   
0039.00 void  INZSR(void);                                                        
0040.00 void  ApiError(char* place, int stmno, ERRSTRUCTURE* errcode, char* pgm); 
0041.00 void  LRRTN(char *argv[]);                                                
0042.00                                                                           
0043.00 /*************************************************************/           
0044.00 /*       IMPORT  関  数                            */           
0045.00 /*************************************************************/           
0046.00 /*************************************************************/           
0047.00 /*       IMPORT  変  数                            */           
0048.00 /*************************************************************/          
0049.00 /*************************************************************/          
0050.00 /*       外 部 呼 出 し  関  数                      */          
0051.00 /*************************************************************/          
0052.00 void MonitorMSG(_INTRPT_Hndlr_Parms_T ca, char* ref);                    
0053.00 #pragma linkage(MonitorMSG, OS)                                          
0054.00 #pragma map(MonitorMSG, "ASNET.COM/MONMSG")                              
0055.00 /*************************************************************/          
0056.00 /*        グ ロ ー バ ル 変 数                         */          
0057.00 /*************************************************************/          
0058.00   /*------( 受取りパラメータ値 )----------*/                             
0059.00   char DATA[2049], TOFILLIB[22], ERR[1], MSG[133];                       
0060.00   /*------( 受取りパラメータ値 )----------*/                             
0061.00   char ref[133], MSG[133], ERR[1];                                       
0062.00 /********************************************************************/   
0063.00 /*            m  a  i  n --- main module of this pgm                */   
0064.00 /*                                                                  */   
0065.00 /*           1. DATA[2048]                               */   
0066.00 /*                      2. TOFILLIB[21]                             */   
0067.00 /*                      3. ERR[1]                                   */   
0068.00 /*                      4. MSG[132]                                 */   
0069.00 /*                                                                  */   
0070.00 /*------------------------------------------------------------------*/   
0071.00                                                                          
0072.00 int  main(int argc, char *argv[]){                                       
0073.00     _RFILE *fp = NULL;                                                   
0074.00    _XXOPFB_T   *opfb;                                                    
0075.00    int RECL;                                                             
0076.00                                                                          
0077.00    #pragma exception_handler(MONMSG, ca, 0, _C2_MH_ESCAPE, _CTLA_HANDLE) 
0078.00    /* GetParam(argc, argv);   *[ パラメータの取得 ]*/                    
0079.00    INZSR();               /*[ 初期設定 ]*/                               
0080.00                                                                          
0081.00    strcpy(TOFILLIB, "QTROBJ/PSHOHIN2");                                  
0082.00    fp = _Ropen(TOFILLIB, "ar+");                                         
0083.00    opfb = _Ropnfbk ( fp );                                               
0084.00    _Rformat ( fp,"PRTHEAD" );     /* Set the record format for the  */   
0085.00    //RECL = opfb->pgm_record_len + 12;                                   
0086.00    RECL = 168;                                                           
0087.00    _Rwrite(fp, DATA, RECL);                                              
0088.00    _Rclose(fp);                                                          
0089.00                                                                          
0090.00    LRRTN(argv);                                                          
0091.00    exit(0);                                                              
0092.00                                                                          
0093.00 MONMSG:                                                                  
0094.00    #pragma disable_handler                                               
0095.00    strcpy(ref, "CPYDTA");                                                
0096.00    MonitorMSG(ca, ref);                        
0097.00                                                
0098.00    exit(0);                                    
0099.00 }                                              
0100.00 /*************************************/        
0101.00 void  GetParam(int argc, char *argv[])         
0102.00 /*************************************/        
0103.00 {                                              
0104.00    int len;                                    
0105.00                                                
0106.00    memcpy(DATA, argv[1], 2048);                
0107.00    DATA[2048] = 0x00;                          
0108.00    memcpy(TOFILLIB, argv[2], 21);              
0109.00    TOFILLIB[21] = 0x00;                        
0110.00    len = triml(TOFILLIB, ' ');                 
0111.00    if(len > 0) TOFILLIB[len] = 0x00;           
0112.00    ERR[0] = ' ';                               
0113.00    memset(MSG, ' ', 132);                      
0114.00 }                                              
0115.00 /****************/                             
0116.00 void  INZSR(void)                              
0117.00 /****************/                             
0118.00 {                                              
0119.00    errcode.BYTESPRO = 160;                     
0120.00    errcode.BYTESAVL = 0;                                                    
0121.00    ERR[0] = ' ';                                                            
0122.00    memset(MSG, ' ', 132);                                                   
0123.00 }                                                                           
0124.00 /*********************************************************************/     
0125.00 void  ApiError(char* place, int stmno, ERRSTRUCTURE* errcode, char* pgm)    
0126.00 /*********************************************************************/     
0127.00 {                                                                           
0128.00    char msgid[8], msgdta[101], Message[512];                                
0129.00    int  msglen, msgdtalen, pos;                                             
0130.00    char* ptr;                                                               
0131.00    typedef struct {                                                         
0132.00      Qmh_Rtvm_RTVM0100_t  rtvm0100;                                         
0133.00      char msg[512];                                                         
0134.00    } ERRMSG;                                                                
0135.00    ERRMSG errmsg;                                                           
0136.00                                                                             
0137.00    memset(msgid, 0, sizeof(msgid));                                         
0138.00    memcpy(msgid, errcode->MSGID, 7);                                        
0139.00    msgid[7] = 0x00;                                                         
0140.00    memset(msgdta, 0, sizeof(msgdta));                                       
0141.00    memcpy(msgdta, errcode->EXCPDATA, 100);                                  
0142.00    msgdta[100] = 0x00;                                                      
0144.00    msgdtalen = strlen(msgdta);                                            
0145.00    memset(&errmsg, 0, sizeof(ERRMSG));                                    
0146.00    QMHRTVM(&errmsg,  msglen, "RTVM0100", msgid, "QCPFMSG   *LIBL     ",   
0147.00           msgdta, msgdtalen, "*YES      ", "*YES      ", errcode);        
0148.00    memset(Message, 0, sizeof(Message));                                   
0149.00    memcpy(Message, errmsg.msg, 512);                                      
0150.00    ptr = strstr(Message, "&N");                                           
0151.00    if(ptr != NULL){                                                       
0152.00      pos = (int)(ptr - Message);                                          
0153.00      Message[pos] = 0x00;                                                 
0154.00    }                                                                      
0155.00    printf("(%s) [ERR AT = %d] %s-%sn", place, stmno,  msgid, Message);   
0156.00    getchar();                                                             
0157.00    exit(-1);                                                              
0158.00 }                                                                         
0159.00 /************************/                                                
0160.00 void  LRRTN(char *argv[])                                                 
0161.00 /************************/                                                
0162.00 {                                                                         
0163.00    if(bLR == TRUE) return;                                                
0164.00    bLR = TRUE;                                                            
0165.00    memcpy(argv[3], ERR, 1);                                               
0166.00    memcpy(argv[4], MSG, 132);                                             
0167.00    exit(0);                                                               
0168.00 }

[解説]

これは社内用のプログラムなので冗長にはなっているが
本質の部分は

0081.00    strcpy(TOFILLIB, "QTROBJ/PSHOHIN2");                                  
0082.00    fp = _Ropen(TOFILLIB, "ar+");                                         
0083.00    opfb = _Ropnfbk ( fp );                                               
0084.00    _Rformat ( fp,"PRTHEAD" );     /* Set the record format for the  */   
0085.00    //RECL = opfb->pgm_record_len + 12;                                   
0086.00    RECL = 168;                                                           
0087.00    _Rwrite(fp, DATA, RECL);                                              
0088.00    _Rclose(fp);  

であり問題の部分は

0087.00    _Rwrite(fp, DATA, RECL);

であるがエラーなく実行される。

0086.00    RECL = 168; 

としているが RPGの場合も

0033.00 D OUTLEN          S             10I 0 INZ(168)

としているが実験してみると OUTLEN(=出力するレコード長)が
80バイト以下なら正常に動作するが 80バイトを超えると
「レコード長が長すぎる」という申告なエラーが発生してしまう。

しかし先のTESTPRT2は正常に動作できている。
理由は

0026.00 D C2M_MSG         S             10I 0 IMPORT('_C2M_MSG')                                        
 :
0036.00    C2M_MSG = 0;

によって特殊なフィールド値: _C2M_MSG を 0 にセットしておくと
このエラー・メッセージを抑制することができると
次の説明に書かれている。

レコード入出力中に C2M メッセージをオフにする

これによって入出力エラーを抑制することができたのだが
このエラーはどう見ても不正なエラーであって IBM のバグとしか思えない。
_Rwrite関数にこの説明があってよさそうなものであるが
どこにもそのような記述はない。
今後、_Rwrite関数を試してみようという人は少ないかも知れないが
弊社の備忘録とするとともに問題でもあるので解決方法として公開した。
_