makecopy -- ファイルの動的作成2015年05月05日 08:42

ファイルを動的に作成できると、応用プログラムの幅が広がります。ここでファイルを動的に作成するfcreate()を紹介します。

fcreate()は、ファイルが存在しない時に作成し、作成できなかった場合はERRを返します。 WATCOM Fortran77版は下記の通り。この版は、ファイルを一度作成し閉じますので、作成したファイルを操作するには、再度オープンする必要があります。

C fcreate.for -- create file, file must be open once as write mode
      integer function fcreate(fn)
      integer*1 fn(81)                  ! NAMESIZE(81)
      integer uid
      integer fopen

      if (fopen(uid,fn,66) .ne. -1) then ! WRITE(LETW) ERR(-1)
          call fclose(uid)
          fcreate = 0
      else
          fcreate = -1                  ! ERR(-1)
      endif
      return
      end

これを利用したmakecopyを紹介します。これは、Windowsのcopyコマンドと似ています。

     makecopy oldfile newfile

oldfile,newfileの指定がなかったら、プログラムは打ち切られます。

RATFOR版は下記の通り。

# makecopy.r4 -- copy one file to another
      character iname(NAMESIZE),oname(NAMESIZE)
      integer fcreate,fopen,getarg
      integer fin,fout
      
      call initfile
      
      if((getarg(1,iname,NAMESIZE) == ERR)
          | (getarg(2,oname,CNAMESIZE) == ERR))
          call error('usage: makecopy input output.')

      if (fopen(fin,iname,READ) == ERR)
          call cant(iname)

      if (fcreate(oname) == ERR)
          call cant(oname)
      else if (fopen(fout,oname,WRITE) == ERR) then
          call cant(oname)
      endif

      call fcopy(fin,fout)
      call fclose(fin)
      call fclose(fout)
      stop
      end
WATCOM Fortran77版は下記の通り。
c makecopy -- copy one file to another
      integer*1 iname(81),oname(81) ! NAMESIZE(81)
      integer fcreate,fopen,getarg
      integer fin,fout
      
      call initfile
      
      if((getarg(1,iname,81) .eq. -1) ! NAMESIZE(81) ERR(-1)
     1    .or. (getarg(2,oname,81) .eq. -1)) then ! NAMESIZE(81) ERR(-1)
          call error('usage: makecopy input output.')
      end if

      if (fopen(fin,iname,82) .eq. -1) then ! ERR(-1) READ(LETR)
          call cant(iname)
      endif

      if (fcreate(oname) .eq. -1) then  ! ERR(-1)
          call cant(oname)
      else if (fopen(fout,oname,87) .eq. -1) then ! WRTIE(LETW) ERR(-1)
          call cant(oname)
      endif

      call fcopy(fin,fout)
      call fclose(fin)
      call fclose(fout)
      stop
      end

archive -- ファイル書庫2015年05月17日 13:58

簡単な、ファイル書庫archiveを紹介します。

archive [dptux] archive-file file ...
   d-delete fileを削除する
   p-print fileを標準出力に書き出す
   t-table archive-fileの一覧を書き出す
   u-update fileを更新または追加する
   x-extract fileを取り出す

ファイル書庫の形式は以下のとおり。

file1 header
file1
file2 header
file2
 .
 .
 .

file headerは、ファイル名、ファイル書庫内のファイル長さです(ファイルの実際の長さでないことに注意)。

archive本体から紹介します。

RATFOR版は下記の通り。

# archive.r4 -- file nameger
      character aname(NAMESIZE)
      integer getarg
      character comand(2)
      
      call initfile()

      if ((getarg(1,comand,2) == EOF)
     1    | (getarg(2,aname,NAMESIZE) == EOF))
         call help
      call getfns()
      
      if (comand(1) == UPD)
          call update(aname)
      else if (comand(1) == TBL)
          call table(aname)
      else if ((comand(1) == EXTR | (comand(1) == PRINT)
          call extrac(aname,comand(1))
      else if (comand(1) == DEL)
          call delete(aname)
      else
          call help()
      end if
      stop
      end

何か間違いがあれば、helpテキストを表示します。 WATCOM Fortran77版は、下記の通り。

c archive -- file nameger
      program archive
      integer*1 aname(81)               ! NAMESIZE(81)
      integer getarg
      integer*1 comand(2)
      
      call initfile()
      if ((getarg(1,comand,2) .eq. -1)  ! ERR(-1)
     1    .or. (getarg(2,aname,81) .eq. -1)) then ! NAMESIZE(81) ERR(-1)
         call help()
      end if
      call getfns()
      if (comand(1) .eq. 117) then      ! UPD(LETU)
          call update(aname)
      else if (comand(1) .eq. 116) then ! TBL(LETT)
          call table(aname)
      else if ((comand(1) .eq. 120)     ! EXTR(LETE)
     1  .or.   (comand(1) .eq. 112)) then ! PRINT(LETP)
          call extrac(aname,comand(1))
      else if (comand(1) .eq. 100) then ! DEL(LETD)
          call delete(aname)
      else
          call help()
      end if
      stop
      end

ヘルプを書き出すhelp()サブルーチンは下記の通り。

RATFOR版。

# help.r4 -- diagnostic printout
      subroutine help

      call error('usage: archive { dptux } archname [files].')
      return
      end

WATCOM Fortran77版。

c help.for -- diagnostic printout
      subroutine help

      call error('usage: archive { dptux } archname [files].')
      return
      end

コマンドラインの引数のうちfile名を処理するgetfns()サブルーチンを示します。このサブルーチンは、引数にあるファイル名を取り出し、重複がないか確認し、処理フラグをリセットします。

RATFOR版は次の通り。

# getfns.r -- get file names into fname, check for duplicates
      subroutine getfns
      integer equal,getarg
      integer i,j
      integer*1 junk(2)

      include carch.ri

      errcnt = 0
      for (i = 1; i <= MAXFILES; i = i + 1)
          if (getarg(i+2,fname(1,i),NAMESIZE) == EOF)
              break
      nfiles = i - 1

      if (i > MAXFILES)
          if (getarg(i+2,junk,2) != EOF)
              call error('too many files.')
      for (i = 1; i <= nfiles; i = i + 1)
          fstat(i) = NO
      for (i = 1, i < nfiles; i = i + 1)
          for (j = i+1; j <= nfiles; j = j + 1)
              if (equal(fname(1,i),fname(1,j)) == YES) {
                  call putlin(fname(1,i),ERROUT)
                  call error(': duplicate file name.')
                  }
      return
      end

WATCOMM Fortran77版は下記の通り。

c getfns.f -- get file names into fname, check for duplicates
      subroutine getfns
      integer equal,getarg
      integer i,j
      integer*1 junk(2)

      include carch.fi

      errcnt = 0
      i = 1
      while ( i .le. 20 ) do            ! MAXFILES(20)
          if (getarg(i+2,fname(1,i),81) .eq. -1) then ! NAMESIZE(81) ERR(-1)
              exit
          end if
          i = i + 1
      end while
      nfiles = i - 1
      if (i .gt. 20) then               ! MAXFILES(20)
          if (getarg(i+2,junk,2) .ne. -1) then ! ERR(-1)
              call error('too many files.')
          end if
      end if
      i = 1
      while (i .le. nfiles) do
          fstat(i) = 0                 ! NO(0)
          i = i + 1
      end while
      i = 1
      while (i .lt. nfiles) do
          j = i + 1
          while (j .le. nfiles) do
              if (equal(fname(1,i),fname(1,j)) .eq. 1) then ! YES(1)
                  call putlin(fname(1,i),6) ! ERROUT(6)
                  call error(': duplicate file name.')
              end if
              j = j + 1
          end while
          i = i + 1
      end while
      return
      end

いずれの版でも、commonファイルをインクルードします。ファイルのインクルードには、先に作ったincludeを使います。インクルードされるファイルは以下の通り。

RATFOR版。

# carch.ri -- common definiton of archive
      common /carch/fname(NAMESIZE,MAXFILES),fstat(MAXFILES),nfiles,errcnt
      character fname    ! file arguments
      integer fstat      ! YES if touched, NO otherwise; init = NO
      integer nfiles     ! number of file arguments
      integer errcnt     ! error count; init = 0

WATCOM Fortran77版。

c carch.fi -- common definiton of archive
      common /carch/fname(81,20),fstat(20),nfiles,errcnt
      integer*1 fname    ! file arguments
      integer fstat      ! YES if touched, NO otherwise; init = NO
      integer nfiles     ! number of file arguments
      integer errcnt     ! error count; init = 0

その他の下請けルーチンについては次回説明します。

update() -- ファイルの追加更新2015年05月24日 20:21

archiveのファイルの追加、更新を受け持つupdatre()を紹介します。

update()は、replac()で書庫の内容を一時ファイルに書き写し、その後、 指定されたファイルすべてについて、addfil()でファイルを追加します。 最後に、一連の処理でエラーがなければ、amove()で書庫を一時ファイルで上書きします。

update()のRATFOR版は以下の通り。

# update.f -- update existing files, add new ones at end
      subroutine update(aname)
      character aname(NAMESIZE)
      integer fcreate,fopen
      integer afd,i,tfd
      include carch.ri
      string tname "archtemp"

      if (fopen(afd,aname,READWRITE) == ERR)
          if (fcreate(aname) == ERR)
              call cant(aname)
          afd = fopen(afd,aname,READWRITE)
      if (fcreate(tname) == ERR)
          call cant(tname)
      if (fopen(tfd,tname,READWRITE) == ERR)
          call cant(tname)
      call replac(afd,tfd,UPD,errcnt)
      for (i = 1; i <= nfiles; i = i + 1)
          if (fstat(i) == NO) {
              call addfil(fname(1,i),tfd,errcnt)
              fstat(i) = YES
              }
      call fclose(afd)
      call fclose(tfd)
      if (errcnt == 0) then
          call amove(tname,aname)
      else
          call remark('fatal errors - archive not altered.')
      call fremove(tname)
      return
      end

WATCOM fortran77版は、以下の通り。

c update.f -- update existing files, add new ones at end
      subroutine update(aname)
      integer*1 aname(81)               ! NAMESIZE(81)
      integer fcreate,fopen
      integer afd,i,tfd
      
      include carch.fi

      integer*1 tname(9)
      data tname/'a','r','c','h','t','e','m','p',-2/

      if (fopen(afd,aname,66) .eq. -1) then ! READWRITE(LETB) ERR(-1)
                                        ! maybe it's new one
          if (fcreate(aname) .eq. -1) then ! ERR(-1)
              call cant(aname)
          endif
          afd = fopen(afd,aname,66)     ! READWRITE(LETB)
      endif
      if (fcreate(tname) .eq. -1) then  ! ERR(-1)
          call cant(tname)
      else if (fopen(tfd,tname,66) .eq. -1) then ! READWRITE(LETB) ERR(-1)
          call cant(tname)
      end if
      call replac(afd,tfd,117,errcnt)   ! UPD(LETU)
      i = 1
      while (i .le. nfiles) do
          if (fstat(i) .eq. 0) then     ! NO(0)
              call addfil(fname(1,i),tfd,errcnt)
              fstat(i) = 1              ! YES(1)
          end if
          i = i + 1
      end while
      call fclose(afd)
      call fclose(tfd)
      if (errcnt .eq. 0) then
          call amove(tname,aname)
      else
          call remark('fatal errors - archive not altered.')
      endif
      call fremove(tname)
      return
      end

update()の中で、fremove()はfcreate()の逆で、ファイルを削除します。 fremove()は下記の通り。

c fremove.for -- remove a file, file must be open once as read mode
      subroutine fremove(fn)
      integer*1 fn(81)                  ! NAMESIZE(81)
      integer uid
      integer fopen

      uid = fopen(uid,fn,82)            ! READ(LETR)
      close(unit=uid,status='delete')
      return
      end

amove()のRATFOR版は下記の通り。

# amove.r4 -- move name1 to name2
      subroutine amove(name1,name2)
      character name1(ARB),name2(ARB)
      integer fd1,fd2
      integer fopen,fcreate

      if (fopen(fd1,name1,READ) == ERR)
          call cant(name1)
      if (fcreate(name2) == ERR)
          call cant(name2)
      if (fopen(fd2,name2,WRITE) == ERR)
          call cant(name2)
      call fcopy(fd1,fd2)
      call fclose(fd1)
      call fclose(fd2)
      return
      end

WATCOM fortran77版は下記の通り。

c amove.for -- move name1 to name2
      subroutine amove(name1,name2)
      integer*1 name1(*),name2(*)       ! ARB(*)
      integer fd1,fd2
      integer fopen,fcreate

      if (fopen(fd1,name1,82) .eq. -1) then ! READ(LETR) ERR(-1)
          call cant(name1)
      end if
      if (fcreate(name2) .eq. -1) then  ! ERR(-1)
          call cant(name2)
      end if
      if (fopen(fd2,name2,87) .eq. -1) then ! WRITE(LETW) ERR(-1)
          call cant(name2)
      end if
      call fcopy(fd1,fd2)
      call fclose(fd1)
      call fclose(fd2)
      return
      end

addfil()は、指定されたファイルを開け、ヘッダーを作り、一時ファイルに 追加します。

RATFOR版は下記の通り。

# addfil.r4 -- add file name to erchive
      subroutine addfil(name,fd,errcnt)
      character name(ARB)
      integer fd,errcnt
      character head(MAXLINE)
      integer fopen,nfd

      if (fopen(nfd,name,READ) {
          call putlin(nale,ERROUT)
          call remark(': can not add.')
          errcnt = errcnt + 1
          }
      if (errcnt == 0) {
          call makhdr(name,head)
          call putlin(head,fd)
          call fcopy(nfd,fd)
          call fclose(nfd)
          }
      return
      end

WATCOM Fortran77版は下記の通り。

c addfil.for -- add file name to erchive
      subroutine addfil(name,fd,errcnt)
      integer*1 name(*)                 ! ARB(*)
      integer fd,errcnt
      integer*1 head(81)                ! MAXLINE(81)
      integer fopen,nfd

      if (fopen(nfd,name,82) .eq. -1) then ! READ(LETR) ERR(-1)
          call putlin(name,6)           ! ERROUT(6)
          call remark(': can not add.')
          errcnt = errcnt + 1
      end if
      if (errcnt .eq. 0) then
          call makhdr(name,head)
          call putlin(head,fd)
          call fcopy(nfd,fd)
          call fclose(nfd)
      end if
      return
      end

ヘッダーを作るのは、makhdr()が行います。

RATFOR版は下記の通り。

# makhdr.r4 -- make header line for archive member
      subroutine makhdr(name,head)
      character name(NAMESIZE),head(MAXLINE)
      integer fsize,itoc,length
      integer i
      string hdr "-h-"
      
      call scopy(hdr,1,head,1)
      i = length(hdr) + 1
      head(i) = BLANK
      call scopy(name,1,head,i+1)
      i = length(head) + 1
      head(i) = BLANK
      i = i + 1  + itoc(fsize(name),head(i+1),MAXCHARS)
      head(i) = NEWLINE
      head(i+1) = EOS
      return
      end

WATCOM Fortran77版は下記の通り。

c makhdr.for -- make header line for archive member
      subroutine makhdr(name,head)
      integer*1 name(81),head(81+1)     ! NAMESIZE(81) MAXLINE(81)
      integer fsize,itoc,length
      integer i
      integer*1 hdr(4)
      data hdr/'-','h','-',-2/
      
      call scopy(hdr,1,head,1)
      i = length(hdr) + 1
      head(i) = 32                      ! BLANK(32)
      call scopy(name,1,head,i+1)
      i = length(head) + 1
      head(i) = 32                      ! BLANK(32)
      i = i + 1  + itoc(fsize(name),head(i+1),81) ! MAXCHARS(81)
      head(i) = 10                      ! NEWLINE(10)
      head(i+1) = -2                    ! EOS(-2)
      return
      end

scopy()は、文字列をコピーします。

RATFOR版は、下記の通り。

# scopy.r4 -- copy string at from(i) to to(j)
      subroutine scopy(from,i,to,j)
      character from(ARB),to(ARB)
      integer i,j
      integer k1,k2

      k2 = j
      for (k1 = i; from(k1) != EOS; k1 = k1 +1) {
          to(k2) = from(k1)
          k2 = k2 + 1
          }
      to(k2) = EOS
      return
      end

WATCOM Fortran77版は、下記の通り。

c scopy.for -- copy string at from(i) to to(j)
      subroutine scopy(from,i,to,j)
      integer*1 from(*),to(*)           ! ARB(*)
      integer i,j
      integer k1,k2

      k2 = j
      k1 = i
      while (from(k1) .ne. -2) do       ! EOF(-2)
          to(k2) = from(k1)
          k2 = k2 + 1
          k1 = k1 + 1
      end while
      to(k2) = -2                       ! EOS(-2)
      return
      end

fsize()はファイルの文字数(ファイルのサイズではないことに注意)を 計算します。

RATFOR版は下記の通り。

# fsize.r4 -- size of file in characters
      integer function fsize(name)
      character name(ARB)
      character fgetc,c
      integer fd,fopen

      if (fopen(fd,name,READ) == ERR)
          fsize = ERR
      else {
          fsize = 0
          while (fgetc(fd,c) != EOF)
              fsize = fsize + 1
          call fclose(fd)
          }
      return
      end

WATCOM Fortran77版は下記の通り。

c fsize.for -- size of file in characters
      integer function fsize(name)
      integer*1 name(*)                 ! ARB(*)
      integer*1 fgetc,c
      integer fd,fopen

      if (fopen(fd,name,82) .eq. -1) then ! READ(LETR) ERR(-1)
          fsize = -1                    ! ERR(-1)
      else
          fsize = 0
          while (fgetc(fd,c) .ne. -1) do ! EOF(-1)
              fsize = fsize + 1
          end while
          call fclose(fd)
      end if
      return
      end

次回は、ほかの機能について説明します。

table() -- ファイル情報の書き出し2015年05月30日 15:19

ファイルの内容見出しの手続きtable()を紹介します。

table()は、書庫を開き、書庫内の各ファイルについて、引数のファイルと一致する ならば、ファイル情報を書き出します。

table()のRATFOR版は以下の通り。

# table.r4 -- print table of archive contents
      subroutine table(aname)
      integer*1 aname(NAMESIZE)
      integer*1 in(MAXLINE),lname(NAMESIZE)
      integer filarg,fopen,gethdr
      integer afd,size,i
      
      if (fopen(afd,aname,82) == ERR)
          call cant(aname)
      while (gethdr(afd,in,lname,size) != EOF) {
          if (filarg(lname) == YES)
              call tprint(lname)
          call fskip(afd,size)
          }
      call notfnd
      return
      end

WATCOM fortran77版は、以下の通り。

c table.for -- print table of archive contents
      subroutine table(aname)
      integer*1 aname(81)               ! NAMESIZE(81)
      integer*1 in(81),lname(81)       ! MAXLINE(81) NAMESIZE(81)
      integer filarg,fopen,gethdr
      integer afd,size

      if (fopen(afd,aname,82) .eq. -1) then  ! READ(LETR) ERR(-1)
          call cant(aname)
      end if
      while (gethdr(afd,in,lname,size) .ne. -1) do ! EOF(-1)
          if (filarg(lname) .eq. 1) then ! YES(1)
              call tprint(lname)
          endif
          call fskip(afd,size)
      end while
      call notfnd
      return
      end

tprint()は、ヘッダーから必要項目、ファイル名とサイズを書き出します。

RATFOR版は下記の通り。

# tprint.r4 -- print table entry for one member
      subroutine tprint(buf)
      character buf(ARB)

      call putlin(buf,STDOUT)
      call putc(NEWLINE)
      return
      end

WATCOM Fortran77版は下記の通り。

c tprint.for -- print table entry for one member
      subroutine tprint(buf)
      integer*1 buf(*)                  ! ARB(*)
      
      call putlin(buf,6)                ! STDOUT(6)
      call putc(10)                     ! NEWLINE(10)
      return
      end

ヘッダーを取り出すのは、gethdr()です。 書庫ファイルの終わりに達した時はEOFを返します。

RATFOR版は下記の通り。

# gethdr.r4 -- get header into from fd
      integer function gethdr(fd,buf,name,size)
      integer fd,i,len,size
      character buf(MAXLINE),name(NAMESIZE),temp(NAMESIZE)
      integer getlin,getwrd,ctoi,equal
      string hdr "-h-"

      if (getlin(buf,fd) == EOF) {
          gethdr = NO
          return
          }
      i = 1
      len = getwrd(buf,i,temp)
      if (equal(temp,hdr) == YES)
          call error('archive not in proper format.')
      gethdr = YES
      len = getwrd(buf,i,name)
      size = ctoi(buf,i)
      return
      end

WATCOM Fortran77版は下記の通り。

c gethdr.for -- get header into from fd
      integer function gethdr(fd,buf,name,size)
      integer fd,size
      integer*1 buf(81),name(81),temp(81) ! MAXLINE(81) NAMESIZE(81)
      integer getlin,getwrd,ctoi,equal
      integer*1 hdr(4)
      data hdr/'-','h','-',-2/

      if (getlin(buf,fd) .eq. -1) then  ! EOF(-1)
          gethdr = -1                   ! EOF(-1)
          return
      end if
      i = 1
      len = getwrd(buf,i,temp)
      if (equal(temp,hdr) .eq. 0) then  ! NO(0)
          call error('archive not in proper format.')
      end if
      gethdr = 1                        ! YES(1)
      len = getwrd(buf,i,name)
      size = ctoi(buf,i)
      return
      end

fskip()は、ファイルfdをn文字読みどばします。

RATFOR版は下記の通り。

# fskip.r4 -- skip n characters on the fd
      subroutine fskip(fd,n)
      integer fd,n
      
      character fgetc,c
      integer i
      
      for (i = 1;i <= n; i = i + 1)
          if (fgetc(fd,c) == EOF)
              break
      return
      end

WATCOM Fortran77版は下記の通り。

c fskip.for -- skip n characters on the fd
      subroutine fskip(fd,n)
      integer fd,n
      integer*1 fgetc,c
      integer i,junk

      i = 1
      while (i .le. n) do
          if (fgetc(fd,c) .eq. -1) then ! EOF(-1)
              exit
          end if
          i = i + 1
      end while
      return
      end

filarg()は書庫から取り出したファイル名が引数にあるかどうかを 判断します。ただし、引数がない場合は常にありと判断します。

RATFOR版は下記の通り。

# filarg.r -- check if name maches argument list
      integer function filarg(name)
      character name(ARB)
      integer equal,i

      include carch.fi

      if (nfiles <= 0) then
          filarg = YES
          return
      end if

      for (i = 1;i <= nfiles; i = i + 1)
          if (equal(name,fname(1,i)) == YES) {
              fstat(i) = YES
              filarg   = YES
              return
              }
      filarg = NO
      return
      end

WATCOM Fortran77版は下記の通り。

c filarg.f -- check if name maches argument list
      integer function filarg(name)
      integer*1 name(*)                 ! ARB(*)
      integer equal,i

      include carch.fi

      if (nfiles .le. 0) then
          filarg = 1                    ! YES(1)
          return
      end if

      i = 1
      while (i .le. nfiles) do
          if (equal(name,fname(1,i)) .eq. 1) then ! YES(1)
              fstat(i) = 1              ! YES(1)
              filarg   = 1              ! YES(1)
              return
          endif
          i = i + 1
      end while
      filarg = 0                        ! NO(0)
      return
      end

notfnd()は指定されたファイルが見つからなかったときに、メッセージを 書き出します。

RATFOR版は下記の通り。

#�notfnd.r -- print "not find" message
      subroutine notfnd
      integer i

      include carch.fi

      for (i = 1;i <= nfiles; i = i + 1)
          if (fstat(i) == NO) {
              call putlin(fname(1,i),ERROUT)
              call remark(':not in archive.')
              errcnt = errcnt + 1
              }
      return
      end

WATCOM Fortran77版は下記の通り。

c notfnd.f -- print "not find" message
      subroutine notfnd
      integer i

      include carch.fi

      i = 1
      while (i .le. nfiles) do
          if (fstat(i) .eq. 0) then     ! NO(0)
              call putlin(fname(1,i),6) ! ERROUT(6)
              call remark(':not in archive.')
              errcnt = errcnt + 1
          end if
          i = i + 1
      end while
      return
      end

次回は、残ったルーチンについて説明します。