タブの処理、detab,entab2014年11月03日 17:29

これまでに説明していないプログラムについて、2,3回に分けて説明します。

まずは、detabとentab。

detabは、文字列中にタブが現れると空白に置き換えます。entabはこの逆で、 続く空白をタブで置き換えます。

detabは、下記の通り。

# detab.for -- convert tabs to equivalent of blanks
      character getc
      character c
      integer tabpos
      integer col, tabs(MAXLINE)

      call settab(tabs) # set initial tab stops
      col = 1
      while (getc(c) != EOF)
          if (c == TAB)
              repeat {
                   call putc(BLANK)
                   col = col + 1
                   } until (tabpos(col,tabs) == YES)
          else if (c == NEWLINE) {
              call putc(NEWLINE)
              col = 1
              }
          else {
              call pudc(c)
              col = col + 1
              }
      stop
      end

settab()は、tabs配列に、タブ位置情報を作り出します。tabpos()は、現在タブ位置にあるか否かを判定します。 いずれも簡単なルーチンですが、くくり出すことでプログラムの見通しを良くするとともに、タブ位置情報の構造を隠蔽することに 役立っています。

settab()は以下の通り。

# settab.r4 -- set initial tab stops
      subroutine settab(tabs)
      integer mod
      integer i, tabs(MAXLINE)

      for (i = 1; i <= MAXLINE; i = i + 1)
          if (mod(i,8) == 1)
              tabs(i) = YES
          else
              tabs(i) = NO
      return
      end

tabpos()は以下の通り。

# tabpos.r4 -- return YES(.true.) if col is a tab stop
      integer function tabpos(col,tabs)
      integer col, tabs(MAXLINE)

      if (col > MAXLINE)
          tabpos = YES
      else
          tabpos = tabs(col)
      return
      end

detab,settab(),tabpos()のWatcom Fortran77版は、下記の通り。

c detab.for -- convert tabs to equivalent of blanks
      program detab
      integer*1 getc,c
      integer col,tabpos,tabs(81)       ! MAXLINE(81)

      call settab(tabs) ! set initial tab stops
      col = 1
      while (getc(c) .ne. -1) do        ! EOF(-1)
          if (c .eq. 9) then            ! TAB(9)
              loop
                  call putc(32)         ! BLANK(32)
                  col = col + 1
              until (tabpos(col,tabs) .eq. 1) ! YES(1)
          else if (c .eq. 10) then      ! NEWLINE(10)
              call putc(10)             ! NEWLINE(10)
              col = 1
          else
              call putc(c)
              col = col + 1
          endif
      end while
      stop
      end
c settab.for -- set initial tab stops
      subroutine settab(tabs)
      integer i,mod,tabs(81)            ! MAXLINE(81)

      i = 1
      while (i .le. 81) do              ! MAXLINE(81)
          if (mod(i,8) .eq. 1) then
              tabs(i) = 1               ! YES(1)
          else
              tabs(i) = 0               ! NO(0)
          endif
          i = i + 1
      end while
      return
      end
c tabpos.for -- return YES(1) if col is a tab stop
      integer function tabpos(col,tabs)
      integer col,tabs(81)              ! MAXLINE(81)

      if (col .gt. 81) then             ! MAXLINE(81)
          tabpos = 1                    ! YES(1)
      else
          tabpos = tabs(col)
      endif
      return
      end

entabは、下記の通り。

# entab -- replace blanks by tab and blanks
      character getc,c
      integer col,newcol,tabpos,tabs(MAXLINE)

      call settab(tabs)
      cal = 1
      repeat {
          newcol = col
          while (getc(c) == BLANK) { # collect blanks
              newcol = newcol + 1
              if (tabpos(col,tabs) == YES) {
                  call putc(TAB)
                  cal = newcol
                  }
              }
          for ( ; col < newcol; col = col + 1)
              call putc(BLANK)       # output leftover blanks
          if (c == EOF)
              break
          call putc(c)
          if (c == NEWLINE)
              col = 1
          else
              col = col + 1
          }
      stop
      end

ここでも、settab()、tabpos()を使用して、タブ位置情報の構造をを隠蔽しています。

entabのWatcom Fortran77版は、下記の通り。

c entab -- replace blanks by tabs and blanks
      program entab
      integer*1 getc,c
      logical tabpos, tabs(81)          ! MAXLINE(81)
      integer col, newcol

      call settab(tabs)
      col = 1
      loop
          newcol = col
          while (getc(c) .eq. 32) do    ! collect blanks BLANK(32)
              newcol = newcol + 1
              if (tabpos(newcol,tabs)) then
                  call putc(9)          ! TAB(9)
                  col = newcol
              endif
          end while
          while (col .lt. newcol) do    ! output leftover blanks
              call putc(32)             ! BLANK(32)
              col = col + 1
          end while
          if (c .eq. -1) then           ! EOF(-1)
              exit
          endif
          call putc(c)
          if (c .eq. 10) then           ! NEWLINE(10)
              col = 1
          else
              col = col + 1
          endif
      end loop
      stop
      end

detab,entabの拡張2014年11月08日 21:45

detab,entabを拡張します。引数でタブ位置の情報を渡せるようにします。

     detab 1 5 10 15 20
     entab 2 6 11 18 30

さらに、

     detab 1 +5
     entab 5 +6

はじめのは、タブ位置をそのまま引数で渡します。次のは、最初の引数を元に、次の引数で、インクリメンタルにタブ位置を計算します。

最初の版settab2.forを示します。

c settab2.for -- set initial tab stops
      subroutine settab(tabs)
      integer tabs(81)                  ! MAXLINE(81)
      integer*1 argv(81)                ! MAXLINE(81)
      integer argc,junk,t,tpos
      integer ctoi,getarg,mod

      do 10 tpos=1,81
          tabs(tpos) = 0                ! NO(0)
   10     continue
   
      if (getarg(1,argv,81) .eq. -1) then ! MAXLINE(81) EOF(-1)
          tpos = 1
          while (tpos .le. 81) do       ! MAXLINE(81)
              if (mod(i,8) .eq. 1) then
                  tabs(i) = 1           ! YES(1)
              endif
              tpos = tpos + 1
          end while
      else
          junk = 1
          tpos = ctoi(argv,junk)
          if ((tpos .gt. 0) .and. (tpos .le. 81)) then
              tabs(tpos) = 1            ! YES(1)
          end if
          if (getarg(2,argv,81) .ne. -1) then ! MAXLINE(81)
              if (argv(1) .eq. 43) then ! PLUS(43)
                  junk = 1
                  t = ctoi(argv,junk)
                  while (tpos .le. 81) do
                      tabs(tpos) = 1    ! YES(1)
                      tpos = tpos + t
                  end while
              else
                  junk = 1
                  tpos = ctoi(argv,junk)
                  if ((tpos .gt. 0) .and. (tpos .le. 81)) then
                      tabs(tpos) = 1    ! YES(1)
                  end if
                  argc = 3
                  while (getarg(argc,argv,81) .ne. -1) do ! MAXLINE(81) EOF(-1)
                      junk = 1
                      tpos = ctoi(argv,junk)
                      if ((tpos .gt. 0) .and. (tpos .le. 81)) then ! MAXLINE(81)
                          tabs(tpos) = 1 ! YES(1)
                      end if
                      argc = argc + 1
                  end while
              end if
          else
              tabs(tpos) = 1            ! YES(1)
          endif
      end if
      return
      end

見通しが悪く、長ったらしくなってしまいました。

ここで、下請けルーチン、インクリメンタル型の引数を確認するtabincr(),タブ位置が範囲に入っているかを調べるtabbound()を導入します。

tabincr()は以下の通り。

c tabincr.for -- return YES if incrimentsl tab set
      integer function tabincr()
      integer*1 argv(81)                ! MAXLINE(81)
      integer getarg

      if (getarg(2,argv,81) .eq. -1) then ! MAXLINE(81) EOF(-1)
          tabincr = 0                   ! NO(0)
      else
          if (argv(1) .eq. 43) then     ! PLUS(43)
              tabincr = 1               ! YES(1)
          else
              tabincr = 0               ! NO(0)
          end if
      end if
      return
      end

tabbound()は以下の通り。

c tabbound -- check tab position boundary
      integer function tabbound(tab,tabmin,tabmax)
      integer tab,tabmin,tabmax

      if ((tabmin .ge. tab) .and. (tab .le. tabmax)) then
          tabbound = 1                  ! YES(1)
      else
          tabbound = 0                  ! NO(0)
      end if
      return
      end

これらを使ったsettab()は以下の通り。

c settab3.for -- set initial tab stops
      subroutine settab(tabs)
      integer tabs(81)                  ! MAXLINE(81)
      integer*1 argv(81)                ! MAXLINE(81)
      integer argc,junk,tincr,tpos
      integer ctoi,getarg,mod,tabincr

      do 10 tpos=1,81
          tabs(tpos) = 0                ! NO(0)
   10     continue
   
      if (getarg(1,argv,81) .eq. -1) then ! MAXLINE(81) EOF(-1)
          tpos = 1
          while (tpos .le. 81) do       ! MAXLINE(81)
              if (mod(tpos,8) .eq. 1) then
                  tabs(tpos) = 1        ! YES(1)
              endif
              tpos = tpos + 1
          end while
      else
          junk = 1
          tpos = ctoi(argv,junk)
          if (tabbound(tpos,1,81) .eq. 1) then ! YES(1)
              if (tabincr() .eq. 1) then ! YES(1)
                  tabs(tpos) = 1        ! YES(1)
                  junk = getarg(2,argv,81) ! MAXLINE(81)
                  junk = 1
                  tincr = ctoi(argv,junk)
                  tpos = tpos + tincr
                  while (tpos .le. 81) do ! MAXLINE(81)
                      tabs(tpos) = 1    ! YES(1)
                      tpos = tpos + tincr
                  end while
              else
                  argc = 2
                  while (getarg(argc,argv,81) .ne. -1) do ! MAXLINE(81) EOF(-1)
                      junk = 1
                      tpos = ctoi(argv,junk)
                      if (tabbound(tpos,1,81) .eq. 1) then ! MAXLINE(1) YES(1)
                          tabs(tpos) = 1 ! YES(1)
                      end if
                      argc = argc + 1
                  end while
              end if
          end if
      end if
      return
      end

多少は見通しが良くなったでしょうか。

文書情報の圧縮と復元2014年11月16日 09:47

簡単な、圧縮、復元ツール、compress, expandです。

compressは、読み込んだ文字が繰り返されていたら、反復符号、繰り返し回数、文字に置き換え出力します。

compressは、下記の通り。

# compress.r4 -- compress standard input
      character getc
      character buf(MAXCHUNK),c,lastc
      integer nrep,nsave
      # must have RCODE > MAXCHUNK or RCODE = 0

      nsave = 0
      for (lastc = getc(lastc); lastc != EOF; lastc = c) {
          for (nrep = 1; getc(c) == lastc; nrep = nrep + 1)
              if (nrep >= MAXCHUNK)     # xount repetitions
                  break
              if (nrep < THRESH)        # apppend short string
                  for ( ; nrep >0; nrep = nrep - 1) {
                      nsave = nsave + 1
                      buf(nsave) = lastc
                      if (nsave >= MAXCHANK)
                          call putbuf(buf,nsave)
                  }
               else {
                   call putbuf(buf,nsave)
                   call putc(RCODE)
                   call putc(lastc)
                   call putc(nrep)
                   }
          }
      call putbuf(buf,nsave) # put last chunk
      stop
      end

putbufはbufにたまったnsave個の文字を書き出します。

# putbuf.r4 -- output buf(1)...buf(nsave), clear nsave
      subroutine putbuf(buf,nsave)
      character buf(MAXCHUNK)
      integer nsave
      integer i

      if (nsave > 0) {
          call putc(nsave)
          for (i = 1; i <= nsave; i = i + 1)
              call putc(buf(i)
          }
      nsave = 0
      return
      end

expandは、下記の通り。

# expand.r4 -- uncompress standard input
      character getc
      character c,code
      
      while (getc(code) != EOF)
          if (code == RCODE) { # expand repetition
              if (getc(c) == EOF)
                  exit
              if (getc(code) == EOF)
                  exit
              while (code > 0)
                  call putc(c)
                  code = code - 1
              }
          else {
              for ( ; code > 0; code = code - 1) {
                  if (getc(c) == EOF)
                      break
                  call putc(c)
                  }
              if (c == EOF)
                  break
              ]
      stop
      end

compress,putbuf(),expandのWatcom Fortran77版は、下記の通り。

c compress -- compress standard input
      program compress
      integer*1 getc
      integer*1 buf(10),c,lastc         ! MAXCHANK(10)
      integer nrep,nsave
      ! must have RCODE > MAXCHUNK or RCODE = 0

      nsave = 0
      lastc = getc(lastc)
      while (lastc .ne. -1) do          ! EOF(-1)
          nrep = 1
          while (getc(c) .eq. lastc) do
              if (nrep .ge. 10) then    ! MAXCHUNK(10)
                  exit
              end if
              nrep = nrep + 1
          end while
          if (nrep .lt. 5) then         ! THRESH(5)
              while (nrep .gt. 0) do
                  nsave = nsave + 1
                  buf(nsave) = lastc
                  if (nsave .ge. 10) then ! MAXCHUNK(10)
                      call putbuf(buf,nsave)
                  endif
                  nrep = nrep - 1
              end while
          else
              call putbuf(buf,nsave)
              call putc(35)             ! #(35)
              call putc(lastc)
              call putc(nrep)
          endif
          lastc = c
      end while
      call putbuf(buf,nsave)            ! put last chunk
      stop
      end
c putbuf.for -- output buf(1)...buf(nsave), clear nsave
      subroutine putbuf(buf,nsave)
      integer*1 buf(10)                 ! MAXCHUNK(10)
      integer nsave
      integer i

      if (nsave .gt. 0) then
          call putc(nsave)
          i = 1
          while (i .le. nsave) do
              call putc(buf(i))
              i = i + 1
          end while
      end if
      nsave = 0
      return
      end
c expand.for -- uncompress standard input
      program expand
      integer*1 getc
      integer*1 c,code
      
      while (getc(code) .ne. -1) do     ! EOF(-1)
          if (code .eq. 35) then ! expand repetition RCODE(#(35))  
              if (getc(c) .eq. -1) then ! EOF(-1)
                  exit
              end if
              if (getc(code) .eq. -1) then ! EOF(-1)
                  exit
              end if
              while (code .gt. 0) do
                  call putc(c)
                  code = code - 1
              end while
          else
              while (code .gt. 0) do ! expand chunk
                  if (getc(c) .eq. -1) then ! EOF(-1)
                     exit
                  end if
                  call putc(c)
                  code = code - 1
              end while
              if (c .eq. -1) then       ! EOF(-1)
                  exit
              end if
          end if
      end while
      stop
      end

暗号化2014年11月22日 21:39

暗号化、複合化プログラムcryptです。

入力文字と暗号化キーのXORをとることで暗号化しています。複合も同じです。

XORをとるには、組み込みのieorがありますが、ここでは、自作しました。

cryptのratfor版は下記の通り。

# crypt.r4 -- encrypt and decrypt
      character getc,xor
      character c,key(MAXKEY)
      integer getarg
      integer mod,i,keylen

      keylen = getarg(1,key,MAXKEY)
      if (keylen == EOF)
          call error('usage: crypt key.')
      for (i = 1; getc(c) != EOF; i = mode(i,keylen) + 1)
          call putc(xor(c,key(i)))
      stop
      end

とても単純ですね。Watcom Fortran 77版は下記の通り。

c crypt.for -- encrypt and decrypt
      program crypt
      integer*1 getc
      integer*1 c,key(11)               ! MAXKEY(11)
      integer getarg,ieor
      integer i,keylen

      keylen = getarg(1,key,11)         ! MAXKEY(11)
      if (keylen .eq. -1) then          ! EOF(-1)
          call error('usage: crypt key.')
      end if
      i = 1
      while (getc(c) .ne. -1) do        ! EOF(-1)
          call putc(xor(c,key(i)))
          i = mod(i,keylen) + 1
      end while
      stop
      end

xor()は、以下の通り。下請けルーチンbin2bit()は、integer*1の正の数を8ビットに分割します。bit2bin()は、8ビットに分割された数をinteger*1の数に戻します。いずれのルーチンも、負の数には対応できません。

c xor.for -- bit width xor
      integer*1 function xor(bin1,bin2)
      integer*1 bin1, bin2
      integer*1 bit2bin
      integer bit1(8),bit2(8),bit3(8),i

      call bin2bit(bin1,bit1)
      call bin2bit(bin2,bit2)
      do 10 i=1,8
          if (((bit1(i) .eq. 1) .and. (bit2(i) .eq. 0)) .or.
     1        ((bit1(i) .eq. 0) .and. (bit2(i) .eq. 1))) then
              bit3(i) = 1
          else
              bit3(i) = 0
          endif
   10 continue
      xor = bit2bin(bit3)
      return
      end

bin2bit()は、以下の通り。

c bin2bit.for -- convert bin to bit(i)
      subroutine bin2bit(bin8,bit)
      integer*1 bin8
      integer bit(8)
      integer abs,mod
      integer i,b

      b = bin8
      do 10 i=1,8
          bit(i) = abs(mod(b,2))
          b = b / 2
   10 continue
      return
      end

bit2bin()は、以下の通り。

c bit2bin.for -- convert bit(8) to bin
      integer*1 function bit2bin(bit)
      integer bit(8)

      bit2bin = bit(8)*128 + bit(7)*64 + bit(6)*32 + bit(5)*16
     1     + bit(4)*8 + bit(3)*4 + bit(2)*2 + bit(1)
      return
      end

実は、大きな落とし穴がありました。暗号化した文字が26、すなわち、0x1aになることがあります。 0x1aは、Windowsでは、テキストファイルの終わりのマークです。暗号化したファイルを読み込むと、0x1aのところで、 切れてしまいます。仕様なので、これ以上の事は追求しないことにしました。