外部ファイルの結びつけ、fopen(),fclose(),initfile()2015年01月11日 21:05

Watcom Fortran 77では、外部ファイルと措置番号を実行時に結びつけることができます。 しかし、あまり使い勝手がよいとは、限りません。 装置番号1とファイル"ABC.TXT"を結びつけるには、環境変数を設定する必要があります。

set 1=ABC.TXT
この方法だと、使っている装置番号を知っておく必要があります。あまり、賢くないやり方です。 別の方法を考えます。コマンドラインのパラメーターにファイル名を指定する方法を考えてみます。
program ABC.TXT
この方法は便利です。プログラム内部で使用されている装置番号を知る必要はありませんが、 外部ファイルと装置番号結びつける仕組みが必要です。その仕組みがWatcom Fortran 77にあり、それはopen文です。
open(unit=uid, file=fname, action=act, err=99)
    uid : 装置番号
    fname : ファイル名(character型の文字列)
    act : 'READ','WRITE'
    99 : ERRORが起きたときにジャンプする先
このままでは、使いにくいので一枚、皮をかぶせます。fopen()です。
c fopen.for -- connect intenal file descripter and external file
      integer function fopen(uid, fn, act)
      integer uid
      integer*1 fn(*), act
      integer i
      character*261 cfn                 ! MAXNAME(261)
      character*5 cact                  ! READ WRITE

      include 'files.fi'

      if (act .eq. 82) then             ! READ(LETR)
          cact = 'READ'
      else if (act .eq. 87) then        ! WRITE(LETW)
          cact = 'WRITE'
      else                              ! error
           uid = -1                     ! ERR(-1)
           fopen = -1                   ! ERR(-1)
           return
      end if

      call is2cs(fn,cfn,261)            ! MAXNAME(261) convert integer string to character string

      i = 1
      while (i .le. 20) do              ! MAXFIELS(20)
          if (finuse(i) .eq. 0) then    ! NOUSE(0)
              open(unit=i, file=cfn, action=cact, err=99)
              finuse(i) = 1             ! INUSE(1)
              uid = i
              fopen = i
              if (act .eq. 82) then     ! READ(LETR)
                  flastc(i) = 81        ! MAXCARD+1(81)
                  fbuf(i,81) = 10       ! NEWLINE(10)
                  fbuf(i,82) = -2       ! EOS(-2)
                  fmode(i) = 82         ! READ(LETR)
              else if (act .eq. 87) then ! WRITE(LETW)
                  flastc(i) = 0
                  fmode(i) =87          ! WRITE(LETW)
              end if
              return
          endif
          i = i + 1
      end while
      
   99 continue
      uid = -1                          ! ERR(-1)
      fopen = -1                        ! ERR(-1)
      return
      end

includeは、Watcom Fortran 77の機能で、'files.fi'をコンパイル時に、読み込みます。

この中で、is2cs()は、integer*1の文字列をcharacter型の 文字列に変換するものです。

c is2cs -- copy integer string to character string
      subroutine is2cs(is,cs,maxsiz)
      integer*1 is(*)
      character cs(maxsiz)
      integer maxsiz
      character char
      integer i

      i = 1                             ! clear character string
      while (i .le. maxsiz) do
           cs(i) = ' '
           i = i + 1
      end while

      i = 1
      while (is(i) .ne. -2) do          ! EOS(-2)
          if (i .ge. maxsiz) then       ! MAXNAME(261)
              exit
          end if
          cs(i) = char(is(i))
          i = i + 1
      end while
      return
      end

files.fiは下記の通りです。

c files.fi -- file interface common valiables
      common /files/finuse,fbuf,flastc,fmode
      integer finuse(20)                ! inuse flag               MAXFILES(20)
      integer*1 fbuf(20,82)             ! I/O buffer               MAXFILES(20) MAXLINE(81)+1
      integer flastc(20)                ! characters in I/O buffer MAXFILES(20)
      integer*1 fmode(20)               ! READ/WIRTE flag          MAXFILES(20)
  • finuse(i) : 装置番号iが使用中ならばINUSE、そうでなければNOUSE
  • fbuf(i,82) : i番目の装置の入出力バッファー
  • flastc(i) : i番目の装置の次の読み出し文字位置、または、次の書き出し位置
  • fmode(i) : i番目の装置が入出力モード

fopen()の逆で、ファイルを切り離すfclose()を示します。これは、Watcom Fortran 77のclose()に皮をかぶせたものです。 uid=5,6を除外しているのは、標準入力と標準出力だからです。

c fclose.for -- disconnect internal filedescripter and extenal file
      subroutine fclose(uid)
      integer uid

      include 'files.fi'

      if (.not. ((uid .eq. 5) .or. (uid .eq. 6))) then
          if (fmode(uid) .eq. 87) then  ! WRITE(LETW)
              call fputc(uid,-1)        ! flush buffer by put EOF
          end if
          close(unit=uid, status='keep')
          finuse(uid) = 0               ! NOUSE(0)
          uid = 0
      end if
      return
      end

'files.fi'にある変数を、fopen(),fclose()などを使う前に初期化する必要があります。初期化 モジュールinitfile()を作成します。標準入力と標準出力は事前にオープンする必要がないため、 読み出し位置、書き出し位置の初期値をここで設定します。

c initfile.for -- setup file manage array funit
      subroutine initfile()
      integer i

      include 'files.fi'

      i = 1
      while (i .le. 20) do              ! MAXFILES(20)
          finuse(i)  = 0                ! NOUSE(0)
          i = i + 1
      end while

      finuse(5) = 1                     ! INUSE(1) for STDIN
      flastc(5) = 81                    ! lastc of read buffer
      fbuf(5,81) = 10                   ! NEWLINE(10)
      fbuf(5,82) = -2                   ! EOS(-2)
      fmode(5) = 82                     ! READ(LETR)
      finuse(6) = 1                     ! INUSE(1) for STDOUT
      flastc(6) = 0                     ! lastc of write buffer
      fmode(6) = 87                     ! WRITE(LETW)
      return
      end

これらをコンパイルするのに、fc.batに修正が必要です。includeするファイルをサーチする場所を指定するオプションを 追加します。

@echo off
rem fc2.for
wfc386 ..\src\%1.for /INCPATH=..\src
move ..\bat\%1.obj ..\obj