COBOL 処理パターン　C言語で作成した外部サブプログラムを呼ぶ


[「COBOL 処理パターン　外部サブプログラム呼ぶ」](https://qiita.com/mikomokaru-jpn/items/967036b9bd3a11af601f) のサブプログラムをC言語で作成してみる。

### 入出力構成図

<img src="http://mikomokaru.sakura.ne.jp/cobol/6/cob06_01.png" alt="[cob06_01.png]" width="300">

### ソースコード
### メインプログラム  
顧客マスタテーブルの有効件数を数値項目（COMP-X）として定義する。これを引数にして Cプログラムに渡すと、数値項目の桁数によりC側での整数の大きさ（1バイト、2バイト、4バイト、8バイト）が自動的に決まる。

```
 IDENTIFICATION              DIVISION.
 PROGRAM-ID.                 MAIN01.

 ENVIRONMENT                 DIVISION.
 INPUT-OUTPUT                SECTION.
 FILE-CONTROL.
*入力ファイル 行順編成ファイル(テキストファイル)  
 SELECT  F1  ASSIGN  TO  "./R002.txt"
 ORGANIZATION IS LINE SEQUENTIAL
 ACCESS  MODE  IS  SEQUENTIAL.  
*出力ファイル 行順編成ファイル(テキストファイル)  
 SELECT  F2  ASSIGN  TO  "./R003.txt"
 ORGANIZATION IS LINE SEQUENTIAL
 ACCESS  MODE  IS  SEQUENTIAL.  

 DATA                        DIVISION.
 FILE                        SECTION.
* 入力ファイル
 FD  F1.
 01  F1R.
     05  CUST-CODE           PIC 9(04). 
     05  SALES               PIC 9(10).
* 出力ファイル
 FD  F2.
 01  F2R.
     05  CUST-CODE           PIC 9(04). 
     05  SALES               PIC 9(10).
     05  CUST-NAME           PIC X(20).

 WORKING-STORAGE             SECTION.
 01  END-FLG                 PIC 9(01).
 01  I                       PIC 9(02).
 01  RETURN-VALUE            PIC 9(03).
* 顧客マスターテーブル有効件数（8バイト）
 01 CUST-COUNT               PIC 9(18) COMP-X VALUE 0. 
* 顧客マスターテーブル
 01 CUST-TABLE.
     05 CUSTOMER OCCURS 10 TIMES.
         10 CUST-CODE        PIC 9(04).
         10 CUST-NAME        PIC X(20).

 PROCEDURE                   DIVISION.
******************************************************************
* 主処理 
****************************************************************** 
 PERFORM INIT-RTN
 PERFORM LOOP-RTN UNTIL END-FLG NOT = 0
 PERFORM END-RTN
 STOP RUN.
******************************************************************
* 前処理 
******************************************************************
 INIT-RTN.
*    変数の初期化
     INITIALIZE END-FLG
*    ファイルオープン
     OPEN INPUT F1
     OPEN OUTPUT F2
*    サブルーチンCALL
     CALL "read_table" USING BY REFERENCE CUST-COUNT
                             BY REFERENCE CUST-TABLE
                       GIVING RETURN-VALUE

     DISPLAY CUST-COUNT;

     PERFORM VARYING I FROM 1 BY 1 UNTIL I > CUST-COUNT
         DISPLAY CUST-CODE OF CUSTOMER(I) " "
                 CUST-NAME OF CUSTOMER(I)
     END-PERFORM
*    1件目のレコードの入力
     PERFORM READ-RTN.
 INIT-RTN-EX. 
     EXIT.
******************************************************************
* 繰り返し処理 
******************************************************************
 LOOP-RTN.
*    出力レコードの編集 
     INITIALIZE F2R
     MOVE CUST-CODE OF F1R TO CUST-CODE OF F2R 
     MOVE SALES OF F1R TO SALES OF F2R 
     MOVE SALES OF F1R TO SALES OF F2R 
*    顧客マスターの検索 
     PERFORM VARYING I FROM 1 BY 1 UNTIL I > CUST-COUNT
         IF CUST-CODE OF F1R = CUST-CODE OF CUSTOMER(I)
             MOVE CUST-NAME OF CUSTOMER(I) TO CUST-NAME OF F2R     
             MOVE 99 TO I
         END-IF
     END-PERFORM           
*    レコードの出力     
     WRITE F2R
*    次のレコードの読み込み 
     PERFORM READ-RTN.
 LOOP-RTN-EX. 
     EXIT.
******************************************************************
* 後処理 
******************************************************************
 END-RTN.
     CLOSE  F1 F2.
 END-RTN-EX.
     EXIT.
******************************************************************
* 入力処理  
******************************************************************
 READ-RTN.
     READ  F1 
         AT END
             ADD 1 TO END-FLG
     END-READ.
 READ-RTN-EX.
     EXIT.
```

### サブプログラム

**エンディアンの変換が必要**
COBOL側はBig Endianだが、C言語側はLittle Endianになっている。引数の数値項目を整数値（COMP-X）とした場合、受け渡しのつどエンディアンの変換を行う必要がある。

エンディアンの変更は、単純なバイトスワップで良い。GNU Cライブラリに byteswap.hにマクロが定義されているのだが、OSXのライブラリにはなぜかないので、自身でマクロを実装している。

```
//------------------------------------------------------------------------------
//  sublib.c
//------------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <stdint.h>

#define BUFFER_LEN 64
#define CODE_LEN 4
#define NAME_LEN 20

//バイトスワップ（As Mac OS X does not have byteswap.h）
//2バイト
#define bswap_16(value) \
((((value) & 0xff) << 8) | ((value) >> 8))
//4バイト
#define bswap_32(value) \
(((uint32_t)bswap_16((uint16_t)((value) & 0xffff)) << 16) | \
(uint32_t)bswap_16((uint16_t)((value) >> 16)))
//8バイト
#define bswap_64(value) \
(((uint64_t)bswap_32((uint32_t)((value) & 0xffffffff)) << 32) | \
(uint64_t)bswap_32((uint32_t)((value) >> 32)))

//顧客マスター構造体
struct _customer{
    char code[CODE_LEN];
    char name[NAME_LEN];
};
typedef struct _customer customer_t;

//COBOLインタフェース関数
int read_table(uint64_t *cust_count, char *cust_table){
/* endianの判定
    int x=1; // 0x00000001
    if (*(char*)&x) {
        printf("little endian\n");
        // little endian. memory image 01 00 00 00 
    }else{
        printf("big endian\n");
        // big endian. memory image 00 00 00 01 
    }
*/
    //顧客マスターテーブル
    customer_t custTable[10];
    //ゼロクリア
    for(int i=0; i<10; i++){
        custTable[i] = (customer_t){0};
    }
    //顧客マスターファイルの読み込み
    const char *file = "./M001.txt";
    FILE *fp;
    fp = fopen(file, "r");
    char line[BUFFER_LEN];
    unsigned short count = 0;
    while(fgets(line, BUFFER_LEN, fp) != NULL){
        //改行コードの削除
        char *p = strchr(line, '\n');
        if (p != NULL){
            *p = '\0';
        }
        //顧客コード
        strncpy(custTable[count].code, line, CODE_LEN);        
        //顧客名
        int nlength = strlen(line)-CODE_LEN;
        strncpy(custTable[count].name, line+CODE_LEN, nlength);

        for (int i = nlength; i < NAME_LEN; i++){
            custTable[count].name[i] = '\0';
        }
        count++;
    }
    *cust_count = count; //顧客マスターテーブル有効件数（8バイト）
    *cust_count = bswap_64(*cust_count); //little endian -> big endian
    printf("%d\n", count);

    memcpy(cust_table, (char*)custTable, sizeof custTable);
    return 0;
}
```

関連記事
[COBOL 処理パターン　多段階集計](https://qiita.com/mikomokaru-jpn/items/09f6b0f056e329550854)
[COBOL 処理パターン　マッチング](https://qiita.com/mikomokaru-jpn/items/ec75da8319cf642c33f5)
[COBOL 処理パターン　テーブル展開](https://qiita.com/mikomokaru-jpn/items/ffb340cb0d647fc56597)
[COBOL 処理パターン　外部サブプログラム呼ぶ](https://qiita.com/mikomokaru-jpn/items/967036b9bd3a11af601f)