ユーザーが提供する OPEN プロシージャー: USEROPEN 指定子

OPEN 文で USEROPEN 指定子を使用して、ファイルを直接開くルーチンに制御を渡すことができます。呼び出されるルーチンでは、システムコールまたはライブラリー・ルーチンを使用することで、ファイルを開き、後続の I/O 文の効果を変更する特別なコンテキストを確立することができます。

インテル® Fortran ランタイム・ライブラリー (RTL) の I/O サポートルーチンでは、I/O のためにファイルが最初に開かれたときに通常使用されるシステムコールの代わりに、USEROPEN 関数を呼び出します。OPEN 文の USEROPEN 指定子では、制御を受け取る関数の名前を指定します。

呼び出される関数は、ファイル (またはパイプ) を開いて、RTL に制御を戻す際にそのファイル (またはパイプ) のファイル記述子を返す必要があります。ファイルを開く際に、呼び出される関数は、標準の OPEN 文で指定されるものとは異なるオプションを指定したり、異なるファイルを指定することがあります。

PXFFILENO ルーチンを使用して、インテル® Fortran RTL から特定のユニット番号のファイル記述子を取得することができます。

呼び出される関数は、C 以外の言語 (Fortran など) で記述することができますが、通常、open または create などのシステムコールの作成には、C 言語が最適です。

C 言語を使用してファイルの開閉、およびすべてのレコード操作を行う必要があるアプリケーションでは、Fortran の OPEN 文を使用せずに、インテル® Fortran プログラムから適切な C プロシージャーを呼び出します。

USEROPEN 指定子を含む OPEN 文でファイル名が指定されると、STATUS=DELETE (または DISPOSE=DELETE) を指定する後続の CLOSE 文は、OPEN 文で指定されたファイル名に対してのみ適用されます。USEROPEN 文の名前付き関数で異なるファイル名を指定した場合、CLOSE 文はそのファイル名に対して効果がありません。

USEROPEN 指定子の構文および動作

OPEN 文の USEROPEN 指定子の形式は次のとおりです。

USEROPEN = function-name

function-name は外部関数の名前を示します。外部関数は、Fortran、C、またはその他の言語で記述できます。

戻り値はファイル記述子です。関数でエラーが発生した場合は -1 を返します。

呼び出すプログラム側では、この関数は EXTERNAL 文で宣言する必要があります。例えば、次のインテル® Fortran コードを使用して、UOPEN という名前の USEROPEN プロシージャー (リンカーでは uopen_ として認識) を呼び出します。

EXTERNAL  UOPEN
INTEGER   UOPEN
.
.
.
OPEN (UNIT=10, FILE='/usr/test/data', STATUS='NEW', USEROPEN=UOPEN)

OPEN 文が実行されると、uopen_ という外部プロシージャーが制御を受け取ります。関数はファイルを開き、指定された操作を実行し、そして RTL に制御 (およびファイル記述子) を返します。USEROPEN 関数内でその他のシステムコールまたはライブラリー・ルーチンを使用することができます。

ほとんどの場合、USEROPEN 関数はインテル® Fortran RTL によって渡されるオープンフラグ引数を変更するか、open (または create) システムコールの前に新しい値を使用します。関数は、ファイルを開いた後で RTL に制御を返す必要があります。

Linux* および macOS* では、ファイル記述子は、32 ビットと 64 ビットの両方のシステムで 4 バイトの整数です。Windows* では、ファイル記述子は、32 ビットのシステムでは 4 バイト、64 ビットのシステムでは 8 バイトです。

呼び出された関数は、RTL にファイル記述子を返すか、関数でエラーが発生した場合は -1 を返す必要があります。

次に、Linux* および macOS*、Windows* で利用可能な引数と定義を示します。

Linux* および macOS* の引数と定義:

int   uopen_ (          (1)
char  *file_name,       (2)   
int   *open_flags,      (3)
int   *create_mode,     (4)
int   *lun,             (5)
int   file_length);     (6)

Linux* および macOS* において、インテル® Fortran RTL から渡される関数の定義と引数は次のとおりです。

  1. 関数は 4 バイト整数 (int) として宣言する必要があります。

  2. 開くパス名 (ファイル名を含む) を指定します。

  3. オープンフラグを指定します。オープンフラグについては、/usr/include/sys/file.h ヘッダーファイルまたは open(2) で説明されています。

  4. 作成モード (Linux* 形式のファイルの作成時に必要な保護) を指定します。作成モードについては、open(2) で説明されています。

  5. 論理ユニット番号を指定します。

  6. パス名の長さ (パス名の文字長の隠し引数) を指定します。

Linux* および macOS* の引数に関する注意事項:

open システムコール (open(2) を参照) では、渡されたパス名、オープンフラグ (必要なアクセスタイプやファイルが存在するかどうかなどを定義)、および作成モードが必要です。OPEN 文で指定された論理ユニット番号は、USEROPEN 関数で必要となる場合に備えて渡されます。また、パス名の文字長の隠し引数も渡されます。

Windows* の引数と定義:

int uopen_ (                              (1)
char   *filename,                         (2)
int    *desired_access,                   (3)
int    *share_mode,                       (4)
int     a_null,          /* 常に 0 */   (5)
int    *flags_attr,                       (6) 
int     b_null,          /* 常に 0 */   (7)
int    *unit,                             (8)
int    [*]flen);                          (9)

Windows* において、インテル® Fortran RTL から渡される関数の定義と引数は次のとおりです。

  1. 関数は、32 ビットのシステムでは 4 バイト整数 (int)、64 ビットのシステムでは 8 バイト整数 (long long int) として宣言する必要があります。

  2. 開くパス名 (ファイル名を含む) を指定します。

  3. アクセスモードを指定します。読み取り、書き込み、または読み取り/書き込みを指定できます。

  4. ファイルの保護モードを指定します。

  5. NULL の場合、文字ゼロとして値渡しされます。

  6. これは、ファイルモードといくつかのファイル機能 (シーケンシャル・アクセスかランダムアクセスか、閉じるときに削除するかどうかなど) を指定するフラグを設定します。

  7. NULL の場合、文字ゼロとして値渡しされます。

  8. 論理ユニット番号を指定します。

  9. パス名の長さ (パス名の文字長の隠し引数) を指定します。

Windows* の引数に関する注意事項:

Windows* では、USEROPEN ルーチンの引数リストは、Microsoft* Windows* の CreateFile 関数の引数リストとよく似ています。そのため、簡単に USEROPEN ルーチンを記述し、入力引数を CreateFile の呼び出しに渡すことができます。CreateFile システムコールには、filename、desired_access、shared_mode、flags_attr が必要です。これらの引数は、OPEN 文で必要なファイルのセマンティクスに対応しています。OPEN 文で指定された論理ユニット番号は、USEROPEN 関数で必要となる場合に備えて渡されます。また、パス名の文字長の隠し引数も渡されます。

32 ビットの Windows* では、Fortran の USEROPEN 関数はデフォルトの "C、REFERENCE" 呼び出し規約を使用しなければなりません。iface コンパイラー・オプションでデフォルトの呼び出し規約を stdcall または cvf に変更した場合は、関数のソースに !DIR$ ATTRIBUTES DEFAULT ディレクティブを追加して、正しい呼び出し規約が使用されるようにする必要があります。

呼び出された USEROPEN 関数の制限事項

インテル® Fortran RTL は、1 つの論理ユニットごとにファイル記述子を 1 つだけ使用します。ファイル記述子は、呼び出された関数によって返される必要があります。このため、ファイルを開く際に使用できるのは、特定のシステムコールまたはライブラリー・ルーチンだけです。

Linux* では、ファイル記述子を返さないシステムコールおよびライブラリー・ルーチンとして、mknod (mknod(2) を参照) および fopen (fopen(3) を参照) があります。例えば、fopen ルーチンはファイル記述子の代わりにファイルポインターを返します。

次のインテル® Fortran コードは、UOPEN という名前の USEROPEN 関数を呼び出します。

EXTERNAL  UOPEN 
INTEGER   UOPEN 
. 
. 
. 
OPEN (UNIT=1,FILE='ex1.dat',STATUS='NEW',USEROPEN=UOPEN,
ERR=9,IOSTAT=errnum)

UOPEN が Fortran 関数の場合は、Fortran の規則に基づいて名前が修飾されます。

UOPEN が C 関数の場合は、上記のコードに次の行が含まれている限り、C の規則に基づいて名前が修飾されます。

!DIR$ ATTRIBUTES C::UOPEN

以下に、Linux* および macOS* での使用例と Windows* での使用例を示します。

Linux* および macOS* の例:


      PROGRAM UserOpenMain
      IMPLICIT NONE

      EXTERNAL      UOPEN
      INTEGER(4)    UOPEN

      CHARACTER(10) :: FileName="UOPEN.DAT"
      INTEGER       :: IOS
      CHARACTER(255):: InqFullName
      CHARACTER(100):: InqFileName
      INTEGER       :: InqLun
      CHARACTER(30) :: WriteOutBuffer="Write_One_Record_to_the_File."
      CHARACTER(30) :: ReadInBuffer  ="??????????????????????????????"

110   FORMAT( X,"FortranMain: ",A," Created (iostat=",I0,")")
115   FORMAT( X,"FortranMain: ",A,": Creation Failed (iostat=",I0,")")
120   FORMAT( X,"FortranMain: ",A,": ERROR: INQUIRE Returned Wrong FileName")
130   FORMAT( X,"FortranMain: ",A,": ERROR: ReadIn and WriteOut Buffers Do Not Match")

      WRITE(*,'(X,"FortranMain: Test the USEROPEN Facility of Open")')

      OPEN(UNIT=10,FILE='UOPEN.DAT',STATUS='REPLACE',USEROPEN=UOPEN, &
           IOSTAT=ios, ACTION='READWRITE')
     
!     OPEN 文が実行されると、uopen_ 関数が制御を受け取ります。
!     uopen_ 関数は open() を呼び出してファイルを開き、その後
!     open() が返したハンドルとともに制御を返します。

      IF (IOS .EQ. 0)  THEN
         WRITE(*,110) TRIM(FileName), IOS
         INQUIRE(10, NAME=InqFullName)
         CALL ParseForFileName(InqFullName,InqFileName)
         IF (InqFileName .NE. FileName) THEN
             WRITE(*,120) TRIM(FileName)
         END IF
      ELSE
         WRITE(*,115) TRIM(FileName), IOS
         GOTO 9999
      END IF

      WRITE(10,*) WriteOutBuffer
      REWIND(10)
      READ(10,*) ReadInBuffer
      IF (ReadinBuffer .NE. WriteOutbuffer) THEN
         WRITE(*,130) TRIM(FileName)
      END IF

      CLOSE(10)
      WRITE(*,'(X,"FortranMain: Test of USEROPEN Completed")')

9999  CONTINUE
      END

!---------------------------------------------------------------
! SUBROUTINE: ParseForFileName
!             フルパス名を受け取り、拡張子付きのファイル名を
!             返します。
!---------------------------------------------------------------
      SUBROUTINE ParseForFileName(FullName,FileName)

      CHARACTER(255):: FullName
      CHARACTER(255):: FileName
      INTEGER       :: P

      P = INDEX(FullName,'/',.TRUE.)
      FileName = FullName(P+1:)

      END

//
// ファイル: UserOpen_Sub.c
//
// このルーチンは、インテル® Fortran OPEN 文から渡されたデータを使用してファイルを開きます。
//

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <errno.h>
int uopen_ ( char *file_name,   /* 読み取りアクセス: 開くファイル名 (終端は NULL) */
             int  *open_flags,  /* 読み取りアクセス: READ/WRITE、file.h または open(2) を参照 */
             int  *create_mode, /* 読み取りアクセス: ファイルを作成する場合は設定 */
             int  *unit_num,    /* 読み取りアクセス: 開く論理ユニット番号 */
             int  filenam_len ) /* 読み取りアクセス: ファイル名の文字数 */
{
    /*
    ** 戻り値は以下のとおり:
    **    value != -1 は有効なファイル記述子
    **    value == -1 はエラーが発生した場合に返される
    */
    int return_value;

    printf(" %s: Opening FILENAME = %s\n", __FILE__, file_name);
    printf(" %s: open_flags = 0x%8.8x\n", __FILE__, *open_flags);
    if ( *open_flags & O_CREAT ) {
        printf(" %s: the file is being created, create_mode = 0x%8.8x\n", __FILE__, *create_mode);
    }

    printf(" %s: open() ", __FILE__);
    return_value = open(file_name, *open_flags, *create_mode);
    if (return_value != 0) {
        printf("FAILED.\n");
        return_value = -1;
    } else {
        printf("SUCCEEDED.\n");
    }

    return (return_value);
} /* uopen_() 終了*/

Windows* の例:

呼び出し元の Fortran プログラムでは、USEROPEN で指定された関数をまず EXTERNAL 文で宣言する必要があります。例えば、次の Fortran コードを使用して、UOPEN という名前の USEROPEN プロシージャーを呼び出します。

  IMPLICIT INTEGER (A-Z)
  EXTERNAL UOPEN
  INTEGER(INT_PTR_KIND()) UOPEN
  ...
  OPEN(UNIT=10,FILE='UOPEN.DAT',STATUS='NEW',USEROPEN=UOPEN)

OPEN 文が実行されると、UOPEN 関数が制御を受け取ります。関数は CreateFile( ) を呼び出してファイルを開き、指定された操作を行ってから呼び出し元の Fortran プログラムに制御 (および CreateFile( ) によって返されたハンドル) を返します。

UOPEN 関数は、次のようになります。


        INTEGER(INT_PTR_KIND()) FUNCTION UOPEN( FILENAME,      &
                                DESIRED_ACCESS, &
                                SHARE_MODE,     &
                                A_NULL,         &
                                CREATE_DISP,    &
                                FLAGS_ATTR,     &
                                B_NULL,         &
                                UNIT,           &
                                FLEN )
        !DIR$ ATTRIBUTES C, ALIAS:'_UOPEN' :: UOPEN
        !DIR$ ATTRIBUTES REFERENCE :: FILENAME
        !DIR$ ATTRIBUTES REFERENCE :: DESIRED_ACCESS
        !DIR$ ATTRIBUTES REFERENCE :: SHARE_MODE
        !DIR$ ATTRIBUTES REFERENCE :: CREATE_DISP
        !DIR$ ATTRIBUTES REFERENCE :: FLAGS_ATTR
        !DIR$ ATTRIBUTES REFERENCE :: UNIT

        USE IFWIN
        IMPLICIT INTEGER (A-Z)
        CHARACTER*(FLEN) FILENAME
        TYPE(T_SECURITY_ATTRIBUTES), POINTER :: NULL_SEC_ATTR

! フラグ属性の FILE_FLAG_WRITE_THROUGH ビットを CreateFile( ) に設定
        FLAGS_ATTR = FLAGS_ATTR + FILE_FLAG_WRITE_THROUGH

! CreateFile( ) を呼び出してステータスを Fortran rtl に返す
        STS = CreateFile( FILENAME,             &
                          DESIRED_ACCESS,       &
                          SHARE_MODE,           &
                          NULL_SEC_ATTR,        &
                          CREATE_DISP,          &
                          FLAGS_ATTR,           &
                          0 )

        UOPEN = STS
        RETURN
        END

UOPEN 関数は cdecl 呼び出し規約を使用するように宣言されているため、Fortran ランタイム・ライブラリーの USEROPEN ルーチンの宣言と一致します。

次の関数定義と引数が Fortran ランタイム・ライブラリーから USEROPEN で指定された関数に渡されます。


        INTEGER(INT_PTR_KIND()) FUNCTION UOPEN( FILENAME,       &
                                DESIRED_ACCESS, &
                                SHARE_MODE,     &
                                A_NULL,         &
                                CREATE_DISP,    &
                                FLAGS_ATTR,     &
                                B_NULL,         &
                                UNIT,           &
                                FLEN )
        !DIR$ ATTRIBUTES C, ALIAS:'_UOPEN' :: UOPEN
        !DIR$ ATTRIBUTES REFERENCE :: DESIRED_ACCESS
        !DIR$ ATTRIBUTES REFERENCE :: SHARE_MODE
        !DIR$ ATTRIBUTES REFERENCE :: CREATE_DISP
        !DIR$ ATTRIBUTES REFERENCE :: FLAGS_ATTR
        !DIR$ ATTRIBUTES REFERENCE :: UNIT

最初の 7 つの引数は CreateFile( ) API 引数に対応しています。これらの引数の値は呼び出し元の OPEN( ) 引数に応じて設定されます。

FILENAME

ファイル名を示す NULL で終わる文字列のアドレスです。

DESIRED_ACCESS

参照で渡されるアクセス (読み取り/書き込み) モードです。

SHARE_MODE

参照で渡されるファイルの共有モードです。

A_NULL

常に NULL です。Fortran ランタイム・ライブラリーは CreateFile( ) 呼び出しの SECURITY_ATTRIBUTES 構造体へのポインターに対して常に NULL を渡します。

CREATE_DISP

ファイルがすでに存在する場合あるいは存在しない場合の動作を指定する作成時の処理です。値は参照で渡されます。

FLAGS_ATTR

ファイル属性とファイルに対するフラグを指定します。値は参照で渡されます。

B_NULL

常に NULL です。Fortran ランタイム・ライブラリーは CreateFile( ) 呼び出しのテンプレート・ファイルへのハンドルに対して常に NULL を渡します。

最後の 2 つの引数は、Fortran ユニット番号とファイル名の長さです。

UNIT

この OPEN が実行される Fortran ユニット番号です。値は参照で渡されます。

FLEN

終端の NULL を除くファイル名の長さです。値で渡されます。

関連情報