| COBOL インターフェイス環境 | H2cpy ユーティリティ |
この章では、複数の言語を使用したアプリケーション開発、つまり COBOL プログラムから C プログラムを呼び出したり、C の関数から COBOL のプログラムや入口点を呼び出す方法について説明します。C 言語に言及している箇所は、C の呼び出し規約に準拠する他の高水準言語にも該当する場合があります。次の構文に含まれる C++ のコードはその例です。
extern "C" { ... }.
ここでは、COBOL プログラムから C プログラムを呼び出す方法と、呼び出した C プログラムをキャンセルする方法について説明します。
COBOL プログラムでは、CALL 構文を使用して C の関数を呼び出すことができます。次に呼び出し構文の例を示します。
CALL "c_func" [USING ...] [RETURNING ...]
c_func に C の関数名を指定します。USING に指定した項目は、すべてパラメータとして C の関数に渡されます。RETURNING に項目を指定した場合には、呼び出した C の関数のリターンコードが格納されます。
詳細については、『COBOL インターフェイス環境』を参照してください。
呼び出した COBOL プログラムをキャンセルすると、そのプログラムは次回、初期状態で呼び出されます。非 COBOL プログラムをキャンセルした場合は、次回に必ずしも初期状態で呼び出されるとは限りません。非 COBOL プログラムが使用したメモリやファイルなどのリソースがクリアされるかどうかは、そのプログラムの開発に使用した言語の仕様と、コンパイラに関連付けられている各種のオプションによって左右されます。
動的に呼び出した COBOL プログラムや非 COBOL プログラムをキャンセルすると、そのプログラムが使用していたメモリ領域は解放されます。一方、呼び出し側の実行可能ファイルに静的にリンクされているプログラムの場合には、キャンセルしてもメモリ領域は解放されません。
Server Express には、COBOL プログラムから C と C++ の関数を呼び出すサンプルプログラム、cobmain1.cbl が付属しています。このサンプルプログラムは、Infomgr を使って容易に見つけることができます。cobmain1.cbl で実行される処理は次のとおりです。
C のコード内では、C の関数を呼び出す場合と同じ要領で COBOL の入口点を呼び出すことができます。次の例は、COBOL の入口点、 cobep
を 2 つの引数を指定して呼び出します。
cobep(arg1, arg2);
C のコード内で COBOL のプログラムや入口点を呼び出したり、呼び出しをキャンセルするには、cobcall()、cobcancel()、および cobfunc() ルーチンを使用します。具体的な方法については後述します。
COBOL プログラムを呼び出すには、その前に cobinit() を実行する必要があります。
C の main() を持つアプリケーションで、コマンド行に COBOL からアクセスする場合は、cobcommandline() を使用します。
COBOL プログラムを呼び出したスレッドが COBOL API で生成されたものではない場合には、そのスレッドを終了する前に cobthreadtidy() を実行する必要があります。『C から COBOL を呼び出すためのルーチン』も参照してください。
アプリケーションで COBOL プログラムを呼び出した場合は、そのアプリケーションを終了する前に必ず cobtidy() または cobexit() を呼び出してください。
COBOL プログラムとそれを呼び出す C の関数が同じ呼び出し可能共有オブジェクトに含まれておらず、しかもシステムの実行可能ファイル内で互いにリンクされていない場合には、次の処理が必要になります。
cob ... -d cobep
この結果、cobep に該当するディスク上のファイル、cobep.so、cobep.gnt、および cobep.int が検索されます。
Server Express には、C および C++ のプログラムから COBOL を呼び出す方法を示すサンプルプログラム、cmain1.c と cxxmain1.C が付属しています。これらのサンプルプログラムは、Infomgr を使って容易に見つけることができます。具体的な処理内容は次のとおりです。
Server Express は、C プログラムと COBOL プログラム間のインターフェイスとして機能する数多くのライブラリルーチンを備えています。
ここでは、名前が cob で始まる一連のルーチンについて詳しく説明します。これらのルーチンは C 専用であり、COBOL のコード内で使用できません。また、これらのルーチンは、特に明示している場合を除き、全プラットフォームで使用できます。
これらのルーチンは、後述のように C のヘッダーファイル内でプロトタイプが宣言されており、$COBDIR/include ディレクトリ内に格納されています。
各ルーチンは、全プラットフォームを通じて共通のデータ型を使用します。これらのデータ型はいずれも cobデータ型_t という名前を持ち、$COBDIR/include ディレクトリ内の cobtypes.h ヘッダーファイル内で定義されています。
ここでは、C の関数から COBOL プログラムを呼び出したり、呼び出した COBOL プログラムをキャンセルするためのルーチンについて説明します。『C からの COBOL の呼び出し』と『C の main() を呼び出すためのルーチン』も参照してください。
COBOL プログラムを呼び出すには、その前に必ず cobinit() を実行する必要があります。
COBOL プログラムを呼び出したスレッドは、COBOL のマルチスレッド化構文やライブラリルーチンで生成した場合を除き、終了前に必ず cobthreadtidy() を実行してください。
アプリケーションで COBOL プログラムを呼び出した場合には、そのアプリケーションを終了する前に必ず cobtidy() または cobexit() を呼び出してください。
C から COBOL の プログラム、副プログラム、または入口点を呼び出します。
#include "cobcall.h"
cobrtncode_t cobcall (const cobchar_t *name, int argc,
cobchar_t **argv);
name |
呼び出す COBOL プログラムの名前。末尾が NULL の文字列です。 |
argc |
argv に指定する引数の数 |
argv |
COBOL プログラムに渡す引数 |
指定された名前の COBOL プログラムを argv に指定された引数を使って呼び出します。各パラメータは参照渡しされます。
cobcall() の結果は、ANSI '74 規格に定義されているように COBOL 型の呼び出しになり、その動作も COBOL から COBOL 入口点を呼び出した場合と同様です。
この関数の呼び出しは、C のコードから C の構文を使って COBOL プログラムを直接呼び出す場合と変わりありません。
指定された名前がロード済みの COBOL プログラムの入口点であれば、そのプログラムが呼び出されます。指定された名前が C の関数の場合も、その関数を含むプログラムが呼び出されます。この 2 つに該当しない場合には、指定された名前の基本名を持つプログラムがディスク上で検索されます。検索の順序とパスは COBOL 標準です。プログラムの検索順序を変更する方法については、『Server Express ユーザガイド』の『実行時の構成』の章にある『program_search_order 実行時調整可能変数』の説明を参照してください。
指定された名前に合致するプログラムが見つからないとランタイムエラーが発生します。後述の cobgetfuncaddr() 関数も参照してください。
呼び出し対象の COBOL 入口点が引数を必要としない場合は、argc に 0、argv
に NULL を指定します。
CALL "name" [USING ...] [RETURNING ...]
C の関数から COBOL の入口点 cobep を 2 つの引数を指定して呼び出すには、次のいずれかのコードを使用します。
コード例 1
cobchar_t *argv[2]; argv[0] = arg1; argv[1] = arg2; cobcall("cobep", 2, argv);
コード例 2
cobep(arg1, arg2);
cobep がロードされていない場合は、該当するファイルがディスク上で検索されます。検索順序とパスは COBOL 標準 (cobep.so, cobep.gnt、cobep.int の順) です。
呼び出した COBOL プログラムをキャンセルします。
#include "cobcall.h" void cobcancel (const cobchar_t *name);
name |
キャンセルする COBOL プログラムの名前。末尾が NULL の文字列です。 |
name で指定された COBOL プログラムがキャンセルされ、そのプログラムで使用されていたデータは COBOL
CANCEL 文の定義どおり初期状態に戻ります (詳細については、『言語リファレンス』を参照してください)。
name に C の関数を指定した場合には、データは変更されません。『呼び出した C の関数のキャンセル』も参照してください。
name に該当するロード済みの COBOL 入口点や C の関数が存在しない場合は、このルーチンが処理を実行せずに正常終了します。
CANCEL "name"
呼び出し済みの COBOL 入口点 cobep をキャンセルするには、次のコードを使用します。
cobcancel("cobep");
COBOL のプログラム、副プログラム、または入口点を呼び出し、ただちにキャンセルします。
#include "cobcall.h"
cobrtncode_t cobfunc (const cobchar_t *name, int argc,
cobchar_t **argv);
name |
呼び出す COBOL プログラムの名前。末尾が NULL の文字列です。 |
argc |
argv に指定する引数の数 |
argv |
COBOL プログラムに渡す引数 |
このルーチンの効果は cobcall() と cobcancel() を続けて実行する場合と同じです。つまり、指定されたプログラムを呼び出した後、ただちにキャンセルします。プログラム内のデータは初期状態のまま変更されません。
詳細については、cobcall() と cobcancel() の説明を参照してください。
この関数で呼び出したプログラムは cobcall() の場合 (COBOL の規則に従う) とは異なり、あたかも C の関数のルールで呼び出されたかのように動作します。cobfunc() は戻り値として、呼び出したプログラムのリターンコードを返します。
CALL "name" [USING ...] [RETURNING ...] CANCEL "name"
引数を必要としない COBOL の入口点、 cobep を呼び出し、ただちにキャンセルするには、次のコードを使用します。
cobfunc("cobep", 0, NULL);
COBOL のプログラム、副プログラム、または入口点へのポインタを返します。
#include "cobcall.h" PFR cobgetfuncaddr(int type, const cobchar_t *name);
type |
指定されたプログラムが見つからない場合に返される値を指定します。
|
|||||||||
name |
検索するプログラムの名前を、末尾が NULL の文字列で指定する。 |
このルーチンは、指定された名前を持つ COBOL プログラムへのポインタを返します。
指定された名前がロード済みの COBOL プログラムの入口点であれば、そのプログラムが呼び出されます。指定された名前が C の関数の場合も、その関数を含むプログラムが呼び出されます。この 2 つに該当しない場合には、指定された名前の基本名を持つプログラムがディスク上で検索されます。検索の順序とパスは COBOL 標準です。プログラムの検索順序を変更する方法については、『Server Express ユーザガイド』の『実行時の構成』の章にある『program_search_order 実行時調整可能変数』の説明を参照してください。
ビット 0 が 1 に設定されたエラールーチンが返された場合は、そのルーチンを続いて呼び出すと、cobcall() の場合と同様にランタイムエラーが発生します。
このルーチンは指定された名前のプログラムをロードしますが、呼び出しは行いません。そのため、プログラムをプリロードする手段として使用できます。この機能は、プログラム内のメイン入口点以外の入口点を呼び出す必要がある場合に役立ちます。
SET procedure-pointer TO ENTRY "name"
次の C コードは COBOL プログラム cobprog を 2 つの引数を使って呼び出し、その際に発生する可能性があるエラーにも対処します。
PFR cobprog;
if ((cobprog = cobgetfuncaddr(0, "cobprog")) == NULL)
{
/* エラー処理 */
}
else
{
/* ロード成功 */
(*cobprog)(arg1, arg2); /* 呼び出し */
}
ここでは、アプリケーションのメイン入口点が COBOL で記述されていない場合、つまり C の main() やサードパーティのコードを使用する場合に必要になるルーチンについて説明します。
COBOL のグローバル実行環境を終了します。
#include "cobmain.h" void cobexit (cobrtncode_t exitstatus);
exitstatus |
呼び出し側のプロセス (シェルなど) に終了状態が返されます。 |
このルーチンは COBOL STOP RUN 文と同様、実行中のアプリケーションを終了します。アプリケーションの終了とともに、バッファをクリアしてファイルを閉じ、COBOL システムによって割り当てられていたすべてのデータ領域を開放します。
非 COBOL アプリケーションから終了するには、C の exit() ライブラリルーチンではなく、cobexit() 関数を使用する必要があります。
このルーチンは最終的に exit() を呼び出すため、制御が戻ることはありません。
呼び出し側のプロセス (シェルなど) には、指定された終了状態が返されます。UNIX では通常、正常終了を表す終了状態として 0 が使用されます。
COBOL ランタイムは、エラーやシグナルで終了すると、 -1 (255) および -2 (254) を返します。詳細については、『保護違反エラーへの対処』を参照してください。
STOP RUN
次の例は、COBOL を使用したアプリケーションを終了し、親プロセスに終了状態 123 を返します。
cobexit(123);
COBOL 環境を初期化し、非 COBOL プログラムからの COBOL プログラムの呼び出しを可能にします。
#include "cobmain.h" int cobinit (void);
なし。
アプリケーションが COBOL プログラムと非 COBOL プログラムから構成されており、メインプログラムが非 COBOL プログラムの場合は、COBOL プログラムを呼び出す前に cobinit() を実行する必要があります。cobinit() を実行しないと、COBOL プログラムに制御を移したときに初期化エラーが発生する可能性があります。
使用しているプラットフォームによっては、COBOL ランタイムシステムのロード時 (プロセスの開始時など) に COBOL 環境が初期化されることもあります。
COBOL 環境がすでに初期化されている場合は、cobinit() がそのまま正常終了します。
このルーチンは正常終了すると 0 を返します。
なし。
次のコード例は、C の main() から COBOL 環境を初期化し、COBOL プログラムを呼び出した後、COBOL 環境を終了します。
main(int argv, char *argv)
{
cobinit(); /* COBOL 環境の初期化 */
cobcall("cobep", 0, NULL); /* COBOL プログラムの呼び出し */
cobtidy(); /* COBOL 環境の終了 */
return(0);
}
COBOL のグローバル実行環境を終了します。
#include "cobmain.h" int cobtidy (void);
なし。
COBOL 環境を終了します。同時にバッファをクリアしてファイルを閉じ、COBOL システムによって割り当てられていたすべてのデータ領域を開放します。
このルーチンは、COBOL システムを終了し、プログラムの実行をそのまま継続する場合に使用します。正常に終了すると 0 を返します。
cobtidy() は、すべての COBOL モジュールを終了し、再実行しない場合のみ使用します。COBOL プログラムからは呼び出さないでください。
いったん cobtidy() を呼び出した後、COBOL の入口点やルーチン (cobinit() など) を呼び出すべきではありません。これらの呼び出しを行うと、アプリケーションが動作異常に陥る可能性があります。
なし。
次のコード例は、C の main() から COBOL 環境を初期化し、COBOL プログラムを呼び出した後、COBOL 環境を終了します。
main(int argv, char *argv)
{
cobinit(); /* COBOL 環境の初期化 */
cobcall("cobep", 0, NULL); /* COBOL プログラムの呼び出し */
cobtidy(); /* COBOL 環境の終了 */
return(0);
}
COBOL コマンド行をセットアップします。
#include "cobmain.h"
cobchar_t *cobcommandline(int flags, int *argcp,
cobchar_t ***argvp, cobchar_t ***envpp,
cobchar_t **namep);
flags |
予備。値は常に 0 |
argcp |
C の main() に渡す引数、 argc のアドレス。 |
argvp |
C の main() に渡す引数、 argv のアドレス。 |
envpp |
C の main() に渡す引数、 envp のアドレス。 |
namep |
予備。値は常に NULL |
このルーチンは、コマンド行を渡して COBOL 実行環境をセットアップします。
C の main() を持つアプリケーションで、コマンド行に COBOL からアクセスする場合は、このルーチンを使用する必要があります。
この形式で cobcommandline() を使用できるのは UNIX 上のみです。
なし。
cobcommandline() を使用するコードの例を次に示します。
main(int argc, char **argv, char **envp)
{
cobinit();
cobcommandline(0, &argc, &argv, &envp, NULL);
/* その他の処理 */
cobexit(0);
}
ここでは、非 COBOL API で生成されたスレッドから COBOL のコードを呼び出すために必要なルーチンについて説明します。非 COBOL API とは、C のライブラリや OS、またはサードパーティのスレッド化 API のことです。
COBOL プログラムを呼び出す前には、必ず cobinit() を実行します。マルチスレッド環境では、COBOL 環境が初期化されているかどうかを知ることができません。そのため、COBOL の各スレッドで cobinit() を実行する必要があります。
COBOL プログラムを呼び出したアプリケーション (カレントスレッドを含む) を終了する前には、必ず cobtidy() または cobexit() を呼び出してください。
カレントスレッドの COBOL 実行環境を終了します。
#include "cobmain.h" int cobthreadtidy(void);
なし。
カレントスレッドの COBOL 環境を終了し、スレッド状態情報をクリアして、カレントスレッドに割り当てられていたすべてのデータ領域を開放します。このルーチンは正常終了すると 0 を返します。
このルーチンを使用できるのは、カレントスレッドが非 COBOL API で生成された場合 (C のライブラリや OS、またはサードパーティのスレッド化 API で生成された場合) のみです。COBOL 構文や CBL_THREAD_CREATE ライブラリルーチンで生成したスレッドで使用すべきではありません。また、COBOL プログラムからは呼び出さないでください。
このルーチンは、カレントスレッドですべての COBOL モジュールを終了し、再実行しない場合のみ使用します。
いったん cobthreadtidy() を呼び出したスレッドでは、COBOL の入口点やルーチン (cobinit() など) の呼び出しを行うべきではありません。これらの呼び出しを行うと、アプリケーションが動作異常に陥る可能性があります。
なし。
C のスレッドの生成時には、そのスレッドの最初の入口点となる C の関数を指定する必要があります。次のコード例は、該当する入口点、c_thread() で次の処理を行う方法を示しています。
cobep を引数なしで呼び出す。c_thread(void)
{
cobinit(); /* 当該プロセスで呼び出し */
/* 済みであれば不要 */
cobcall("cobep", 0, NULL); /* COBOL を呼び出す */
cobthreadtidy(); /* このスレッドの COBOL */
/* を終了する */
return 0; /* スレッドの終了 */
}
ここでは、COBOL プログラム内で宣言されたデータに C プログラムから安全にアクセスするためのルーチンについて説明します。
COBOL のレコードの構造は、文字列配列を含む C の構造体に似ています。COBOL コンパイラがレコードを割り当てるときには、特定のハードウェアプラットフォーム向けに 2 進項目やポインタデータ項目が整列されたり、 2 進 (COMP-X) データ項目のバイトオーダーが調整されたりすることはありません。その結果、COBOL アプリケーションは、異なるプラットフォーム間で優れた互換性を発揮します。ただし、複数の言語を使用する開発環境では、この互換性が問題の原因になる可能性もあります。C の構造体は COBOL のレコードに比べ定義が緩やかであり、オペレーティングシステムによって構造が大きく異なります。たとえば、次のCOBOL レコードについて考えてみましょう。
01 mystruct is typedef.
05 mystruct-key-1 pic x occurs 2.
05 mystruct-bin-1 pic x(4) comp-5.
このレコードのサイズは、コンパイルするプラットフォームにかかわらず常に 6 バイトです。ところが、このレコードを単純に書き直しただけでも、C の構造体は異なる結果になります。次に例を示します。
struct mystruct
{
char mystruct_key_1[2];
long mystruct_bin_1;
};
この構造体のサイズは通常、32 ビットオペレーティングシステムで 8 バイト、64 ビットオペレーティングシステムでは 16 バイトになります。これは、整列によるパディングが施されるためです。そのため、互換性を重視する場合には、C の構造体でも次のようにメンバーのサイズまで固定すべきです。
struct mystruct
{
char mystruct_key_1[2];
char mystruct_bin_1[4];
};
Server Express には、COBOL データレコードを C のデータ型に正しく変換する手段として、COBOL アプリケーションや CBL_ ライブラリルーチンのインターフェイスで使用される基本的なデータ型のほとんどを定義した C のインクルードファイル ($COBDIR/include/cbltypes.h) が付属しています。このインクルードファイル内のデータ型は、$COBDIR/cpylib/cbltypes.cpy コピーファイル内のデータ型と直接に対応しています。
ここで、『11.3.4.1』で紹介した COBOL レコードをもう一度見てみましょう。
01 mystruct is typedef.
05 mystruct-key-1 cblt_x1 occurs 2. *> PIC X と同義
05 mystruct-bin-1 cblt_x4_comp5. *> PIC X(4) COMP-5
# と同義
このレコードは、$COBDIR/include/cbltypes.h の定義を使用すれば、次のような C 構造体で適切に表現されます。
#include "cbltypes.h"
struct mystruct
{
cbl_x1_t mystruct_key_1[2];
cbl_x4_comp5_t mystruct_bin_1;
};
cbltypes.h ファイルで定義されている各データ型には、対応する COBOL 型定義の PICTURE 文字列を反映した名前が付けられています。同ファイル内で定義されている基本的なデータ型を次に示します。
cbl_x1_t COBOL の文字型 − 最も基本的なデータ型 cbl_pointer_t USAGE POINTER に相当 cbl_ppointer_t USAGE PROCEDURE-POINTER に相当 cbl_sx1_comp5_t PIC s9(n) COMP-5 に相当 (n は 1 または 2) cbl_sx2_comp5_t PIC s9(n) COMP-5 に相当 (n は 3 または 4) cbl_sx4_comp5_t PIC s9(n) COMP-5 に相当 (n は 7 〜 9) cbl_sx8_comp5_t PIC s9(18) COMP-5 に相当 cbl_x1_comp5_t PIC X(1) COMP-5 に相当 cbl_x2_comp5_t PIC X(2) COMP-5 に相当 cbl_x4_comp5_t PIC X(4) COMP-5 に相当 cbl_x8_comp5_t PIC X(8) COMP-5 に相当 cbl_x1_compx_t PIC X(1) COMP-X に相当 cbl_x2_compx_t PIC X(2) COMP-X に相当 cbl_x4_compx_t PIC X(4) COMP-X に相当 cbl_x8_compx_t PIC X(8) COMP-X に相当
COBOL のデータ型に相当する型を使用すれば、COBOL 側とのデータの互換性は維持できます。ただし、これらのデータは C のコード内で処理する必要があります。Server Express は、COBOL に基づくさまざまなデータ型を C のアプリケーションコンポーネントに適した形式に変換する C ライブラリルーチンを備えています。
COBOL に基づくデータ型と C ネイティブのデータ型の相互変換を行います。
#include "cbltypes.h" /* cbltypes.h と cobgetput.h をインクルード */ cobuns8_t cobget_x1_compx(const cbl_x1_compx_t *cbldata); cobuns16_t cobget_x2_compx(const cbl_x2_compx_t *cbldata); cobuns32_t cobget_x4_compx(const cbl_x4_compx_t *cbldata); cobuns64_t cobget_x8_compx(const cbl_x8_compx_t *cbldata); cobuns8_t cobget_x1_comp5(const cbl_x1_comp5_t *cbldata); cobuns16_t cobget_x2_comp5(const cbl_x2_comp5_t *cbldata); cobuns32_t cobget_x4_comp5(const cbl_x4_comp5_t *cbldata); cobuns64_t cobget_x8_comp5(const cbl_x8_comp5_t *cbldata); cobs8_t cobget_sx1_comp5(const cbl_sx1_comp5_t *cbldata); cobs16_t cobget_sx2_comp5(const cbl_sx2_comp5_t *cbldata); cobs32_t cobget_sx4_comp5(const cbl_sx4_comp5_t *cbldata); cobs64_t cobget_sx8_comp5(const cbl_sx8_comp5_t *cbldata); cobuns64_t cobget_xn_comp5(const cbl_x1_t *cbldata, cobuns8_t n); cobuns64_t cobget_xn_compx(const cbl_x1_t *cbldata, cobuns8_t n); cobs64_t cobget_sxn_comp5(const cbl_x1_t *cbldata, cobuns8_t n); void *cobget_pointer(const cbl_pointer_t *cbldata); PFR cobget_ppointer(const cbl_ppointer_t *cbldata); void cobput_x1_compx(cbl_x1_compx_t *cbldata, cobuns8_t val); void cobput_x2_compx(cbl_x2_compx_t *cbldata, cobuns16_t val); void cobput_x4_compx(cbl_x4_compx_t *cbldata, cobuns32_t val); void cobput_x8_compx(cbl_x8_compx_t *cbldata, cobuns64_t val); void cobput_x1_comp5(cbl_x1_comp5_t *cbldata, cobuns8_t val); void cobput_x2_comp5(cbl_x2_comp5_t *cbldata, cobuns16_t val); void cobput_x4_comp5(cbl_x4_comp5_t *cbldata, cobuns32_t val); void cobput_x8_comp5(cbl_x8_comp5_t *cbldata, cobuns64_t val); void cobput_sx1_comp5(cbl_sx1_comp5_t *cbldata, cobs8_t val); void cobput_sx2_comp5(cbl_sx2_comp5_t *cbldata, cobs16_t val); void cobput_sx4_comp5(cbl_sx4_comp5_t *cbldata, cobs32_t val); void cobput_sx8_comp5(cbl_sx8_comp5_t *cbldata, cobs64_t val); void cobput_xn_comp5(cbl_x1_t *cbldata, cobuns8_t n, cobuns64_t val); void cobput_xn_compx(cbl_x1_t *cbldata, cobuns8_t n, cobuns64_t val); void cobput_sxn_comp5(cbl_x1_t *cbldata, cobuns8_t n, cobs64_t val); void cobput_pointer(cbl_pointer_t *cbldata, void *val); void cobput_ppointer(cbl_ppointer_t *cbldata, PFR val);
cbldata |
cobget では COBOL データ項目、cobput ではデータ定義へのポインタ |
n |
cobget_xn_ および cobput_xn_ ルーチン用。cbldata で指定されるデータ領域の文字数 |
val |
COBOL データ項目 cbldata に格納する値 |
cobget_xn_compx ルーチンと cobput_xn_compx ルーチンでは、PIC X(3) などのデータ定義からデータを取り込むことが可能です。
mycblprog.cbl:
program-id. mycblprog.
copy "cbltypes.cpy".
01 myrec.
05 myrec-key cblt-x4-compx value 10.
procedure division.
call 'mycprog' using myrec-key
if myrec-key = 20
display "First call to 'mycblprog'"
else
display "Subsequent call to 'mycblprog'"
end-if
exit program
end program mycblprog.
mycprog.cbl:
#include "cbltypes.h"
void
mycprog(cbl_x4_compx_t *cbldata)
{
cobput_x4_compx(cbldata, cobget_x4_compx(cbldata) + 10);
}ここでは、COBOL ランタイムルーチンに動作環境の変更を検出させるためのルーチンについて説明します。
C の getenv() ライブラリルーチンと同等です。
#include "cobenv.h" cobchar_t *cobgetenv(const cobchar_t *name);
name |
検索すべき環境変数の名前。末尾が NULL の文字列です。 |
指定された環境変数を検索し、その値を返します。該当する環境変数が見つからない場合は、NULL を返します。
DISPLAY name UPON ENVIRONMENT-NAME ACCEPT ... FROM ENVIRONMENT-VALUE
次のコードは $COBDIR の値を表示します。
cobchar_t *cobdir;
if ((cobdir = cobgetenv("COBDIR")) == NULL)
cobdir = "<unset>";
cobprinf("COBDIR=%s\n", cobdir);
環境変数の設定を実行時に動的に変更します。
#include cobenv.h" int cobputenv (const cobchar_t *envstr);
envstr |
環境に適用する設定。末尾が NULL の文字列です。 |
envstr に指定された環境文字列を適用します。環境文字列は通常、次の形式で指定します。
NAME=VALUE
ただし、envstr の形式はランタイムシステムではチェックされません。
envstr の値は C の putenv() ライブラリルーチンに渡され、同ルーチンによって OS 環境に適用されます。いったん適用された envstr の設定は、同じ環境変数を cobputenv() で再定義しない限り、変更または解放されることはありません。
cobputenv() は正常終了するとゼロを返します。その他の戻り値は、処理が正しく実行されなかったことを示します。無効な環境変数名を指定すると、結果が未定義 (undefined) となります。
DISPLAY name UPON ENVIRONMENT-NAME DISPLAY value UPON ENVIRONMENT-VALUE
次に環境変数、 $ABC を XYZ に設定する例を示します。
cobputenv("ABC=XYZ");
ランタイムシステムに環境設定を走査させ、COBOL 環境文字列を検索させます。
#include "cobenv.h" int cobrescanenv(void);
なし。
このルーチンを実行すると、ランタイムシステムが環境設定を走査して COBOL 環境文字列 (ファイルと名前のマッピングを示す dd_ エントリなど) を検索します。この機能は、実行環境を変更した場合に、ランタイムシステムに新しい環境を認識させる手段として活用できます。
正常終了時の戻り値はゼロです。その他の値は、呼び出しが失敗したことを示します。
なし。
次のコード例は、環境変数 dd_ の設定を変更しています。この変更によって、ファイル abc を開く処理を行うと、かわりに xyz が開くようになります。
cobputenv("dd_abc=xyz");
cobrescanenv();
dd_ エントリによるファイルマッピングの詳細については、『ファイル操作』の『ファイル名』の章に記載されている『ファイル名マッピング』を参照してください。
Server Express は、C プログラム内で使用できるいくつもの画面操作ルーチンを備えています。これらのルーチンを使用すれば、COBOL プログラムから呼び出された C プログラムによる画面操作の出力と、呼び出し側の COBOL プログラムで実行された ACCEPT/DISPLAY 処理の出力をランタイムシステムやランタイムサポートライブラリで処理することが可能になります。このルーチンを使用しない場合は、COBOL の制御範囲外 (C プログラム内など) で実行された画面操作の出力が COBOL 側に制御が戻ると未定義になり、それ以降の画面操作の結果が保証できなくなります。画面操作ルーチンを使用すれば、この問題を回避できます。
ここで説明する各ルーチンは、いずれも UNIX 以外の環境では使用できません。
画面操作ルーチンを使用するには、$COBDIR/include ディレクトリに格納されている cobscreen.h ヘッダーファイルをインクルードする必要があります。このファイルには、使用可能な属性とデータ型 (cobchtype) の定義、および画面操作ルーチンに必要なすべての外部関数の宣言が含まれています。cobchtype 型は 2 バイトから構成されるデータ型で、最初のバイトに文字、2 番めのバイトにその属性を格納します。
cobchtype の属性バイトは、一部の画面操作ルーチンではユーザ属性バイトと同じ値になります。UNIX のユーザ属性バイトは、標準では次に一覧するモノクロ属性を持ちます。これらの属性値は、(COBOL コード内で) 汎用属性を使用すると変更される可能性があります。属性設定に使用できる COBOL システムライブラリルーチンの詳細については、『Programmer's Guide to Creating User Interfaces』の『Low-level Routines for Character Interfaces』の章を参照してください。
標準のモノクロ属性はビット設定であり、論理和 (OR) によって組み合わせることができます。cobscreen.h ファイルでは、次の属性ビット値が定義されています。
| 属性
|
意味
|
|---|---|
| A_NORMAL A_BOLD A_UNDER |
標準 (属性なし) 太字またはハイライト表示 下線 |
| A_REVERSE A_BLINK A_DIM |
反転表示 点滅 淡色表示 |
画面操作ルーチンは「仮想カーソル」と呼ばれる位置基準を使用します。各スレッドがそれぞれ独自に仮想カーソルを持ち、その位置は cobmove() で設定されます。
仮想カーソルの現在位置に、指定された文字を表示します。
#include "cobscreen.h" void cobaddch (cobchtype ch);
ch |
表示する文字とその属性 |
ch で指定された文字と属性を仮想カーソルの現在位置に表示します。
表示する文字として、改行文字 (\n) を指定することもできます。この文字を指定すると、仮想カーソルが次の行の先頭に移動します。その他の制御文字は無効であり、使用すると結果は未定義です。
なし。
ただし、次の COBOL 構文に類似しています。
CALL "CBL_WRITE_SCR_CHATTRS" USING ...
次の例は、文字 X を反転表示します。
cobaddch('X' | A_REVERSE);
指定された文字列を、仮想カーソルの現在位置から表示します。
#include "cobscreen.h" int cobaddstr (const cobchtype *str);
str |
表示する文字列とその属性 |
str で指定された文字列と属性を仮想カーソルの現在位置から表示します。文字列が仮想カーソルから行末までの幅より長い場合には、画面の端に達した時点で折り返されます。
表示する文字列には改行文字 (\n) を含めることができます。改行文字を含めると、その文字で仮想カーソルが次の行の先頭に移動します。ヌル文字
(\0) は文字列の末尾を表します。その他の制御文字は無効であり、使用すると、未定義の結果となります。
str の最大長は 255 字です。
このルーチンは、正常終了すると 0 を返します。
CALL "CBL_WRITE_SCR_CHATTRS" USING ...
次の例は、文字列、 HUB を強調表示、下線、および点滅付きで表示します。
cobchtype s[4] = { 'H' | A_BOLD, 'U' | A_UNDER, 'B' |
A_BLINK, 0 };
cobaddstr(s);
指定された文字列を、仮想カーソルの現在位置から表示します。
#include "cobscreen.h" int cobaddstrc (const cobchar_t *cstr);
cstr |
表示する文字列 |
cstr で指定された文字列を、標準属性 (A_NORMAL) で仮想カーソルの現在位置から表示します。文字列が仮想カーソルから行末までの幅より長い場合には、画面の端に達した時点で折り返されます。
表示する文字列には改行文字 (\n) を含めることができます。改行文字を含めると、その文字で仮想カーソルが次の行の先頭に移動します。ヌル文字
(\0) は文字列の末尾を表します。その他の制御文字は無効であり、使用すると、未定義の結果となります。
cstr の最大長は 255 字です。
次の 2 種類の構文に該当します。
CALL "CBL_WRITE_SCR_CHARS" USING ...
DISPLAY cstr AT yyxx
次の例は、文字列、 Hello World を表示し、直後で改行します。
cobaddstrc("Hello World\n");
画面表示を消去し、仮想カーソルを開始位置 (0 行、0 列) に戻します。
#include "cobscreen.h" void cobclear (void);
なし。
このルーチンは画面表示を消去し、仮想カーソルを開始位置 (0 行、0 列) に戻します。
次の 2 種類の構文に該当します。
CALL "CBL_CLEAR_SCREEN" USING ...
DISPLAY SPACES UPON CRT
次の例は画面表示を消去し、左上の角からメッセージを表示します。
cobclear();
cobaddstrc("Cursor home occurs on cobclear\n");
画面の列の数を返します。
#include "cobscreen.h" int cobcols (void);
なし。
このルーチンは、画面の幅 (列の数) を返します。
CALL "CBL_GET_SCR_SIZE" USING ...
次の例は、画面の列の数を取得してメッセージとともに表示します。
int width = cobcols();
cobprintf("Width of screen = %d\n", width);
キーボード入力された文字を取得します。
#include "cobscreen.h" int cobgetch (void);
なし。
キーボード入力を 1 文字読み取って返します。このルーチンは、キーが押されるまで待機します。
EOF などのエラーを検出すると -1 を返します。
CALL "CBL_READ_KBD_CHAR" USING ...
次の例は、処理の続行を確認するメッセージを表示します。
int ch;
cobaddstrc("Continue [y/n]? ");
ch = cobgetch();
if (ch == 'Y' || ch == 'y')
{
/* y が入力された場合の処理 */
}
画面の行数を返します。
#include "cobscreen.h" int coblines (void);
なし。
このルーチンは、画面の高さ (行数) を返します。
CALL "CBL_GET_SCR_SIZE" USING ...
次の例は、画面の行数を取得してメッセージとともに表示します。
int depth = coblines();
cobprintf("Depth of screen = %d\n", depth);
画面上の指定された位置 (行および列) に仮想カーソルを移動します。
#include "cobscreen.h" void cobmove (int y, int x);
y |
仮想カーソルの移動先の行番号 |
x |
仮想カーソルの移動先の列番号 |
各ルーチンで使用される仮想カーソルが、y および x パラメータで指定される位置に移動します。
仮想カーソルの開始位置は画面の左上角、つまり y と x がともに 0 の位置です。各スレッドは、それぞれ独自に仮想カーソルを持ちます。
CALL "CBL_PUT_SCR_POS" USING ...
次の例は、仮想カーソルを 1 秒ごとに移動させてカウントダウンメッセージを表示します。
int secs = 10;
char *message = "Time Left: ";
cobmove(10, 10);
cobprintf("%s%d", message, secs);
while (secs--)
{
sleep(1);
cobmove(10, 10 + strlen(message));
cobprintf("%2d", secs);
}
指定フォーマットの文字列を、仮想カーソルの現在位置から表示します。
#include "cobscreen.h" int cobprintf (const cobchar_t *fmt, ...);
fmt |
フォーマット文字列 |
fmt に指定したフォーマット文字列を展開し、標準属性 (A_NORMAL) で仮想カーソルの現在位置から表示します。展開後の文字列が仮想カーソルから行末までの幅より長い場合には、画面の端に達した時点で折り返されます。
fmt パラメータは、C の printf() ライブラリルーチンの場合と同様のフォーマット文字列であり、展開して表示されます。たとえば、fmt に %s が含まれている場合には、cobprintf() で続けて指定した該当する文字列引数によって置き換えられます。
フォーマット文字列には改行文字 (\n) を含めることができます。改行文字を含めると、その文字で仮想カーソルが次の行の先頭に移動します。ヌル文字
(\0) は文字列の末尾を表します。その他の制御文字は無効であり、使用すると未定義の結果となります。
fmt に指定するフォーマット文字列は、展開後の長さが 255 字以下でなければなりません。
このルーチンは出力した引数の数を返し、エラーを検出すると -1 を返します。
なし。
次の例は、指定されたフォーマットのカウントダウンメッセージを表示します。
int secs = 10;
char *message = "Time Left: ";
cobmove(10, 10);
cobprintf("%s%d", message, secs);
while (secs--)
{
sleep(1);
cobmove(10, 10 + strlen(message));
cobprintf("%2d", secs);
}
画面表示をスクロールします。先頭と末尾の行を指定し、その領域内をスクロールします。
#include "cobscreen.h" void cobscroll (int top, int bot);
top |
スクロールする画面領域の先頭行。先頭行が画面から外れないように、画面の行数より 2 行以上少ない値を指定します。 |
bot |
スクロールする画面領域の末尾の行。 |
なし。
画面の先頭行は 0、末尾の行は coblines() で取得できます。そのため、画面全体をスクロールするには、このルーチンを次のように使用します。
cobscroll(0, coblines() - 1);
ここでは、端末専用の機能に対処するためのルーチンについて説明します。『Server Express ユーザガイド』の『Terminfo データベースおよび端末装置』の章も参照してください。
ここで説明する関数は、いずれも UNIX 環境のみで有効です。
端末キーボードのテンキーをローカルモードまたは送信モードに設定します。
#include "cobscreen.h" void cobkeypad(int mode);
mode |
テンキーのモード。0 または 1 に設定します。 |
端末キーボードのテンキーをローカルモードまたは送信モードに設定します。テンキーを持たない端末では無効です。また、使用している種類の端末用の terminfo データベースに、smkx エントリ (送信モード) と rmkx エントリ (ローカルモード) が含まれている必要があります。
mode パラメータには、テンキーをローカルモードに設定する場合は 0、送信モードに設定する場合は 1 を指定します。その他の値は無効です。
terminfo データベースに smkx エントリが含まれている端末については、ランタイムシステムはデフォルトで、そのテンキーを送信モードに設定します。
なし。
端末画面を標準モードと拡張モード間で切り替えます。
#include "cobscreen.h" int cobtermmode(const cobuns8_t *mode)
mode |
端末の画面モード。0 または 1 を指定します。 |
このルーチンは、端末画面を標準モードと拡張モード間で切り替えます。ただし、使用している種類の端末用の terminfo データベース内で、必要な機能が定義されていることが条件です。
具体的には、使用している端末の種類のエントリに加え、それに -w を付けたエントリが terminfo データベースに登録されており、エントリに適切な初期化機能とリセット機能の両方、または一方が定義されていることが条件になります。たとえば、vt100 端末を使用している場合には、terminfo データベース内に vt100-w のエントリも含まれており、vt100 と vt100-w の両方に is1、is2、is3、rs1、rs2、および rs3 の各エントリ (またはいずれかのエントリ) が定義されている必要があります。
mode パラメータには、端末画面を標準モード (デフォルト) に設定する場合は 0、拡張モードに設定する場合は 1 を指定します。その他の値は無効です。
端末画面のモードが切り替わると、画面処理システムが必要に応じて初期化され、画面表示が消去されます。その結果、カーソルは開始位置に配置されます。
このルーチンは COBOL コードからも問題なく呼び出すことができます。詳細については、『Server Express ユーザガイド』の『Terminfo データベースおよび端末装置』の章に記載されている『拡張端末モード』を参照してください。
このルーチンの使用にあたっては、次の点に留意してください。
この場合、標準モードでは COBTERMINFO で指定される terminfo データベースが使用され、拡張モードではシステム用の terminfo データベースが使用されます。その結果、この 2 つの terminfo データベース間に列の数以外の相違が存在すると、予期しない問題が発生する可能性があります。
terminfo データベースの検索順序の詳細については、『Server Express ユーザガイド』の『Terminfo データベースおよび端末装置』の章を参照してください。
このルーチンは正常終了すると 0 を返します。エラーが検出された場合は、次のいずれかの値が返されます。
| 1 | terminfo ファイルが見つからない (またはアクセスできない/破損している) かメモリが不足している。 |
| 2 | terminfo ファイル内に 1 つ以上の必要なエントリが存在しない。 |
| 3 | 必要なコードが使用環境でサポートされていない。 |
CALL "cobtermmode" using mode
次のコード例は、端末を拡張モードに切り替えて短いメッセージを表示し、キーボードから文字が入力されるまで待機します。いずれかのキーが押されると、ただちに端末を標準モードに戻します。
cobuns8_t mode_on=1, mode_off=0;
if (cobtermmode(&mode_on) == 0)
{
/* 80 列を超えた位置に文字列を表示し、 */
/* 拡張モードの機能を確認 */
cobmove(10, 100);
cobprintf("Line 10, Column 100");
cobgetch();
/* キーが押されるまで待機 */
cobtermmode(&mode_off);
/* 標準モードに戻る */
}
COBOL ランタイムシステムは端末を COBOLに適したモードに設定しますが、そのために、 C の system() ライブラリルーチンの呼び出しは失敗する可能性があります。同ルーチンの呼び出しでは端末設定がリセットされず、呼び出し後も端末設定は復元されません。
そのため、system() のかわりに SYSTEM() を使用すべきです。SYSTEM() の詳細については後述します。
ここで説明する関数は、UNIX 環境のみで有効です。
指定されたコマンド行をシステムのシェルに渡して実行させます。
#include <sys/wait.h> int SYSTEM (const unsigned char *cmd);
cmd |
シェルに実行させるコマンド行。末尾が NULL の文字列です。 |
C の system() ライブラリルーチンと同様、指定されたコマンド行をシェルに渡して実行させます。
COBOL 端末は呼び出し前にシェルモードにリセットされ、呼び出し後に復元されます。
COBOL 環境はシェルや SYSTEM() で実行されるコマンドなど、他のプロセスによる出力を関知しません。そのため、SYSTEM() では画面の更新を伴う処理を実行しないようにする、または呼び出し後に画面を更新する必要があります。
このルーチンの戻り値は、C の system() ライブラリルーチンと同じ形式です。C ライブラリのインクルードファイル <sys/wait.h> には、実行されたコマンドの戻り値を取得し、その値を操作するマクロが定義されています。詳細については、使用しているシステムのマニュアルを参照してください。
CALL "CBL_EXEC_RUN_UNIT" USING ...
次のコード例はファイルをコピーして標準出力と標準エラーをエラーファイルにリダイレクトし、コマンドのリターンコードを表示します (実行後にエラーファイルを開くと、コマンドで生成された出力が確認できます)。
int res;
res = SYSTEM("cp f1 f2 > errfile 2>&1");
if (WIFEXITED(res))
cobprintf("Exit status: %d\n", WEXITSTATUS(res));
ここでは、C プログラムと COBOL プログラムを併用する場合に必要になる 2 つのルーチン、cobsetjmp() と coblongjmp() について説明します。これらのルーチンは、C のライブラリルーチン、setjmp() および longjmp() に類似した機能を持っています。setjmp() と longjmp() の詳細については、使用している UNIX のマニュアルを参照してください。
cobsetjmp() と coblongjmp() は、複数のスレッドにわたる GO TO 機能を提供します。この機能は、エラーや例外の処理に利用できます。
エラーや例外処理向けに、複数のスレッドにわたる GO TO 機能を提供します。
#include "cobsetjmp.h" int cobsetjmp(struct cobjmp_buf *buf); void coblongjmp(struct cobjmp_buf *buf);
buf |
現在の実行環境が格納されるバッファ |
cobsetjmp() ルーチンは、現在の COBOL プログラムと C の関数の環境を、buf で指定されるバッファに格納します。このルーチンでは、C の setjmp() ライブラリルーチンが呼び出されます。setjmp() は呼び出し後、ただちに 0 を返します。
cobsetjmp() の呼び出しに続いて、同じ C 関数内や、そのサブプログラム内で coblongjmp() が呼び出されると、cobsetjmp() を呼び出した直後の位置から実行が再開されます。
coblongjmp() の呼び出し後、cobsetjmp() はゼロ以外の値を返します。この戻り値をチェックすれば、cobsetjmp() の結果を知ることができます。
これらの関数を使用する際には、次の点に留意してください。
なし。
これらのルーチンの使用例を次に示します。
void
some_c_func(void)
{
struct cobjmp_buf buf;
if (cobsetjmp(&buf) != 0)
{
cobprintf("Returned from coblongjmp\n");
return;
}
/* COBOL の呼び出しなど有効なコードをここに記述する */
coblongjmp(&cobjmp_buf);
}
ここでは、アプリケーションでシグナル処理を行うために必要なルーチンについて説明します。これらのルーチンは、COBOL と C を併用するアプリケーション開発で特に有効です。
ランタイムシステムは、プログラムの強制終了につながるオペレーティングシステムの各種シグナルに対して、デフォルトのシグナルハンドラを設定します。そのため、そのようなシグナルが発生した場合でも、COBOL システムでは終了前に適切なクリーンアップ処理が実行されます。たとえば、ファイルの破損を防止するために開いているファイルはすべて閉じられ、端末は常に同じ状態に戻され、開発者側で準備した終了プロシージャやエラー処理プロシージャが実行されます。
ランタイムシステムによるシグナル処理では、そのほかにも割り込みキーの検出や Animator Zoom 割り込みキー、ジョブ制御など、さまざまな処理が実行できます。
Server Express では、ランタイムシステムによるデフォルトのシグナル処理を補完したりオーバーライドするための手段として、次の 2 つのルーチンが使用できます。
cobpostsighandler()
cobremovesighandler()
これらのルーチンを使用すると、同じシグナルに複数のハンドラを関連付け、ハンドラ間に優先順位を設定できます。これらのルーチンを呼び出すことができるのは C プログラムのみです。シグナル値はプラットフォームに依存するため、正しく識別するには C のインクルードファイル <signal.h> が必要なためです。
これらのルーチンは、オペレーティングシステムによるシグナルルーチン (signal()、sigaction() など) のかわりに使用してください。オペレーティングシステムによるシグナルルーチンでは、1 つの呼び出しに設定できるシグナルハンドラは 1 つのみであるため、複数のアプリケーションを並行して実行する環境の安定性を損なう可能性があります。
COBOL プログラムの呼び出し前に、上記の C ライブラリのシグナルルーチンを使ってシグナルハンドラを設定すると、デフォルトのシグナルハンドラがオーバーライドされる可能性があります。また、同じ C ライブラリのシグナルルーチンを COBOL プログラムの呼び出し後に使用すると、ランタイムシステムのデフォルトハンドラ、COBOL の拡張機能、および開発者側で設定したハンドラがすべてオーバーライドされ、予期しない結果につながります。
指定された優先度付きのハンドラを、指定されたシグナル用のハンドラリストに追加します。
#include "cobsignal.h"
cobsigtype_t cobpostsighandler (int signal, int priority,
PFI_SIG handler);
signal |
<signal.h> インクルードファイルで定義されているシグナル番号 |
priority |
ハンドラの優先度。有効値は 1 〜 254 |
handler |
シグナルハンドラのアドレス |
このルーチンは、指定されたハンドラを、指定された優先度で、指定されたシグナル用のハンドラリストに追加します。後述する『シグナルハンドラの設定』も参照してください。
priority には、追加するハンドラの優先度を 1 〜 254 の範囲で指定します。値の大きいハンドラが優先されます。なお、127
と 129 〜 139 は予約されており、使用できません。
handler には、シグナルハンドラのアドレスを整数で返す Cの関数を指定します。
このルーチンは、追加したシグナルハンドラの削除に使用するポインタを返します。指定したシグナル用のハンドラ設定に失敗すると NULL が返されます。ハンドラの設定に失敗する原因としては、メモリが不足している場合や、signal_regime 調整可能変数によって当該シグナルが無効化されている場合などが考えられます。
なし。
指定されたシグナルハンドラを、ハンドラリストから削除します。
#include "cobsignal.h" void cobremovesighandler (cobsigtype_t sighandler);
sighandler |
cobpostsighandler() の呼び出しで返されたシグナルハンドラを示すポインタ |
このルーチンは、設定済みのシグナルハンドラを削除します。
sighandler には、cobpostsighandler() ルーチンでシグナルハンドラを設定したときに返されたポインタを指定します。次の『シグナルハンドラの設定』も参照してください。
このルーチンには戻り値はありません。
なし。
シグナルハンドラを設定するには、cobpostsighandler() ルーチンを使用します。
cobpostsighandler() ルーチンは、ランタイムシステムによるデフォルトのシグナルハンドラの設定にも使用されます。ランタイムシステムは、プログラムの強制終了につながるオペレーティングシステムの各種シグナルに対して、デフォルトのシグナルハンドラを設定します。そのため、そのようなシグナルが発生した場合でも、COBOL システムでは終了前に適切なクリーンアップ処理が実行されるか、またはシグナルが無視されます。これらのデフォルトハンドラは、重大なエラーによってオペレーティングシステムが生成したシグナルや、生成を予期しないシグナルを対象としており、通常はランタイムシステムメッセージ (114 、115 など) を生成します。
同じハンドラが 2 つ設定され、どちらも cobremovesighandler() ルーチンによって削除されない場合には、同じハンドラが二度実行されます (2 つのハンドラと、その間に実行される各ハンドラが、いずれもゼロを返さない場合のみ)。
複数のハンドラの優先度が同じである場合は、最後に設定されたハンドラが先に実行されます。
いったん設定したハンドラの再設定は不要です。対応するシグナルが受信されるたびに、そのハンドラが繰り返し実行されます (最初の受信でプログラムが終了した場合を除く)。シグナルハンドラを一度のみ実行するには、該当するハンドラ内で cobremovesighandler() ルーチンを呼び出し、そのハンドラを削除する必要があります。
シグナルハンドラの処理中には、該当するシグナルの受信はブロックされます。そのため、同じシグナルが連続的に生成されても、ハンドラが再帰的に処理されることはありません。
シグナルハンドラは正常終了させる必要があります。longjmp() や coblongjmp() など、シグナルハンドラを強制終了させる関数は使用しないでください。また、値を返すには常に、次に示す C の構文を使用してください。
return(num);
他の関数を使って値を返すとシグナルの受信ブロックが解除されないため、同じシグナルがそれ以降、処理できなくなります。
シグナルが受信されると、そのシグナル用に設定されている最も優先順位の高いハンドラが呼び出されます。呼び出されたハンドラは、後で処理するようなフラグを設定するなどの適切な処理を実行して制御を戻します。このハンドラの戻り値がゼロ以外であれば、同じシグナル用に設定されている次の優先順位のハンドラが呼び出されます。シグナルハンドラの戻り値がゼロの場合は、次のハンドラは呼び出されません。
ランタイムシステムによって設定されるデフォルトのシグナルハンドラの優先度は 127 です。そのため、デフォルトハンドラを独自ハンドラでオーバーライドしたり、独自ハンドラをデフォルトハンドラより前に実行するには、独自ハンドラに 128 以上の優先度を設定します。逆に、独自ハンドラをデフォルトハンドラに続いて実行するには、独自ハンドラの優先度を 126 以下に設定します (ただし、デフォルトハンドラでシグナル処理が終了しない場合に限る)。
ランタイムシステムは、デフォルトハンドラ以外にも、さまざまな機能を持つシグナルハンドラを備えています。これらのハンドラには 129 〜 139 の範囲で優先度が与えられています。
シグナルハンドラは、きわめてコンパクトであることが条件です。シグナルは決まったタイミングで生成されるわけではありません。そのため、シグナル生成時にはグローバル変数を変更しているルーチンなど、不安定なルーチンが存在している可能性があります。そのようなルーチンをシグナルハンドラ内で実行しようとすると問題が発生します。シグナルハンドラ内で実行すべきでないルーチンには、C の標準ライブラリルーチン (malloc() など) も含まれます。
C のライブラリルーチンには、シグナルハンドラから安全に呼び出すことができるサブセットが含まれており、このサブセット以外の C ライブラリルーチンや COBOL ルーチンはシグナルハンドラ内で使用すべきではありません。安全に呼び出すことができるライブラリルーチンの詳細については、使用しているシステムのマニュアルを参照してください。
一部のシグナルは、重大なエラーの発生時にオペレーティングシステムによって生成されます。これらのシグナルが検出されると、予期しない問題や重大な問題につながる可能性があるため、シグナル処理を試みるべきではありません。該当するシグナルは次のとおりです。
SIGBUS
SIGSEGV
SIGEMT
SIGILL
SIGIOT / SIGABRT
SIGSYS
SIGFPE
SIGTRAP
一部のシグナルは、ランタイムシステムのデフォルトハンドラで処理しないと、ランタイムシステムの機能を損なう可能性があります。該当するシグナルは次のとおりです。これらのシグナルを処理する優先度 140 以上のシグナルハンドラでは、引き続きデフォルトハンドラが呼び出されるように、ゼロ以外の値を返す必要があります。
SIGCHLD
SIGPIPE
SIGUSR1
SIGUSR2
SIGVTALRM
SIGWINCH
SIGCONT
SIGTSTP
SIGTTIN
SIGTTOU
注: 将来的には、デフォルトのシグナルハンドラに変更が加えられたり、シグナルが追加される可能性があります。
SIGALRM シグナル用のハンドラを設定するコード例を次に示します。
#include <stdio.h>
#include <signal.h>
#include "cobsignal.h"
#include "cobmain.h"
int
mysigcatch(int sig)
{
cobprintf("In new signal handler\n");
return(0); /* 以降のハンドラを呼び出さない */
}
void
mysigexit(cobsigtype_t sighand)
{
cobprintf("Cancelling handler\n");
cobremovesighandler(sighand); /* ハンドラを削除 */
}
main(int argc, char *argv[])
{
cobsigtype_t sighandle;
cobinit();
sighandle = cobpostsighandler(SIGALRM, 128, mysigcatch);
cobfunc("cobolprog", argc, argv); /* cobfunc 関数で COBOL
プログラムを呼び出す */
alarm(1); /* シグナルを生成 */
pause(); /* シグナル受信まで待機 */
sleep(4);
mysigexit(sighandle);
cobexit(0); /* COBOL ランタイム環境を終了 */
}
cobpostsighandler() ルーチンを使用すれば、1 つのシグナルに複数のハンドラを設定することが可能です。割り込みキーが押されたときに生成される SIGINT シグナルなど、各種のシグナルがオペレーティングシステムによって生成されると、該当するハンドラが適切な処理を実行し、ゼロ以外の値を返して次のハンドラを実行させます。戻り値としてゼロを返すと、以降のハンドラは実行されず、その時点でシグナル処理が終了します。ただし、優先度 140 以上のハンドラはゼロ以外の値を返し、他のハンドラを実行させるべきです。優先度 128 のハンドラでゼロを返せば、ランタイムシステムのデフォルトハンドラの実行を回避できます (デフォルトハンドラを実行しないと、プログラムが強制終了される可能性があります)。
シグナルを意図的に生成する場合 (alarm() による SIGALRM シグナルの生成や、他の実行単位やアプリケーション向けのシグナル生成など) は、独自に設定したハンドラのみでシグナルを処理し、その他のハンドラの呼び出しを回避する必要が生じることがあります。このような場合は、シグナルを生成する前にグローバルリソースを設定し、シグナル生成後にハンドラ内でリソースをチェックします。リソースが設定されている (シグナルを意図的に生成した) 場合は、独自ハンドラ内で適切な処理を行った後、ゼロを返して他のハンドラの呼び出しを回避します。リソースが設定されていない場合は、他のユーザやオペレーティングシステムによって生成されたシグナルであるため、独自ハンドラでは特に処理を行わず、ゼロ以外の値を返します。その結果、シグナルの処理は以降の各ハンドラに委ねられます。
グローバルリソースとして利用できるリソースには、グローバル変数や共有メモリ、パイプ、ファイルなどがあります (ただし、グローバル変数を使用できるのは、該当する変数が同じ実行単位内に存在する場合のみです)。たとえば、前のコード例では、alarm() でシグナルを生成する前にグローバル変数を設定できます。グローバル変数をチェックするコードは、mysigcatch() シグナルハンドラ内に実装します。チェックの結果、グローバル変数が設定されている場合は、mysigcatch() ハンドラ内で必要な処理を実行し、変数をリセットしてゼロを返します。一方、グローバル変数が設定されていない場合は、他のコードによってシグナルが生成されたものと判断できるため、mysigcatch() では特に処理を行わず、ただちにゼロ以外の値を返します。その結果、続いて呼び出されるハンドラによってシグナルが処理されます。
signal() や sigaction() などの C のライブラリルーチンを使用してシグナルハンドラを設定している箇所があれば、すべて cobpostsighandler() ルーチンを使用するように変更してください。独自のハンドラとランタイムシステムのデフォルトハンドラ、および COBOL の拡張機能を共存させるためにも、この変更は不可欠です。C のライブラリルーチンでシグナルハンドラを設定すると、デフォルトハンドラや拡張機能との間で、一方が他方をオーバーライドすることになり、結果的に予期しない問題が発生します。
cobpostsighandler() ルーチンのインターフェイスは、signal() のインターフェイスに類似しています。cobpostsighandler() には、signal() に渡す 2 つのパラメータに加え、優先度を渡します。
signal() と cobpostsighandler() の間には、次の相違点があります。
cobpostsighandler() は、設定したハンドラを示すポインタまたは NULL を返します。返されたポインタは、cobremovesighandler() ルーチンでハンドラを削除する場合に使用できます。NULL の戻り値は、指定されたハンドラの設定に失敗したことを示します。
cobpostsighandler() ルーチンで設定したシグナルハンドラは、呼び出し後もそのまま維持されます。ハンドラの実行を 1 回のみに限定する場合は、cobremovesighandler() ルーチンを使って該当ハンドラを削除します。
signal() や sigaction() などの C ライブラリルーチンでシグナルハンドラを設定するサードパーティ製ソフトウェアを Server Express で使用すると、予期しない問題が発生する可能性があります。たとえば、サードパーティ製ソフトウェアが COBOL の呼び出し前に設定したシグナルハンドラは、COBOL の呼び出し後にランタイムシステムのデフォルトハンドラでオーバーライドされます。その結果、サードパーティ製ソフトウェアが該当するシグナルを生成すると、エラー 115 が発生します。
この問題は、signal_regime 実行時調整可能変数を使用して回避することが可能です。この調整可能変数を使用すると、COBOL が最初に呼び出される前にハンドラが設定されているシグナルには、新しいハンドラは設定されなくなります。
この調整可能変数によってハンドラを設定できない場合は、cobpostsighandler() ルーチンが NULL を返します。該当するシグナルについては、ランタイムシステムのデフォルトハンドラや、その他の COBOL の拡張機能は適用されません。この場合は、cobpostsighandler() ルーチンの戻り値が NULL かどうかをチェックする処理も必要になります。
signal_regime
調整可能変数を利用するのは、上記のようなサードパーティ製ソフトウェアを使用する場合のみです。自身で作成するコードでは常に、 cobpostsighandler()
ルーチンを使用すべきです。signal_regime の設定方法については、『Server Express ユーザガイド』の『signal_regime』を参照してください。
一部のプラットフォームでは、C のライブラリルーチンに sigaction() が含まれています。このルーチンは signal() に比べ、シグナルが連続的に受信される状況や、マシンの処理負荷が大きい環境での処理効率に優れています。cobpostsighandler() は、標準で sigaction() を呼び出します。
ただし、signal_interface 調整可能変数を使用すれば、cobpostsighandler() ルーチンに sigaction() のかわりに signal() を使用させることも可能です。この調整可能変数の詳細については、『Server Express ユーザガイド』の『実行時の構成』の章を参照してください。
Copyright © 2002 Micro Focus International Limited. All rights reserved.
本書、ならびに使用されている固有の商標と商品名は国際法で保護されています。
| COBOL インターフェイス環境 | H2cpy ユーティリティ |