edit -- 全体構造2015年11月21日 09:00

editは、非常に大きなプログラムで、なおかつ、問題が起きた時に黙って死んでしまうような、 弱いプログラムであってはならない。信頼性が必要なプログラムであることが大事である。

editへの入力は指令行である。指令行は、以下のようになっている。

     行1,行2 指令 追加情報
ここで、「行1」、「行2」、「追加情報」は省略可能である。したがって、editの主たるループは、 下記のようになる。
     while (getlin(lin,STDIN) != EOF {
         linから行番号を取り出す
         if ( 状況がOK )
             指令を実行
         }
指令を実行する部分では、指令別に多方向へ分岐して処理を進めることになる。これらの処理も 含めて、editの 各部分では、関数の値と、引数statusの両方に状況を返すようにしてある。うまくいったときは OK、まずいことがあればERRを下位のルーチンで入力を 読みつくしてしまった場合は、EOFをそれぞれ返すことになっている。

共通の制御情報は、clinesにある。

RATFOR版は、以下の通り。

# clines.ri
      common /clines/line1,line2,nlines,curln,lastln
      integer line1  # first line number
      integer line2  # second line number
      integer nlines # numberif line numbers specified
      integer curln  # current line: value of dot
      integer lastln # last line: value of $

WATCOM Fortran77版は以下の通り。

c clines.fi
      common /clines/line1,line2,nlines,curln,lastln
      integer line1  ! first line number
      integer line2  ! second line number
      integer nlines ! numberif line numbers specified
      integer curln  ! current line: value of dot
      integer lastln ! last line: value of $

指令行から、行番号を取り出すのは、getlst()が、getone()を呼び出しながら、 行う。行番号は、clinesのline1、line2に、入る。

RATFOR版のgetlst()は、次の通り。

# getlst.r -- collect line numbers (if any) at lin(i), increment i
      integer function getlst(lin,i,status)
      character lin(MAXLINE)
      integer i,status
      integer getone,min, num

      include clines.ri

      line2  = 0
      for (nlines = 0; getone(lin,i,num,status) == OK; ) {
          line1  = line2
          line2  = num
          nlines = nlines + 1
          if (lin(i) != COMMA & lin(i) != SEMICOL)
              break
          if (lin(i) == SEMICOL) then
              curln = num
          i = i + 1
          }
      nlines = min(nlines,2)
      if (nlines == 0)
          line2 = curln
      if (nlines <= 1)
          line1 = line2
      if (status != ERR)
          status = OK
      getlst = status
      return
      end

WATCOM Fortran77版は下記の通り。

c getlst.f -- collect line numbers (if any) at lin(i), increment i
      integer function getlst(lin,i,status)
      integer*1 lin(82)                 ! MAXLINE(82)
      integer i,status

      integer getone,min, num

      include clines.fi

      line2  = 0
      nlines = 0
      while (getone(lin,i,num,status) .eq. -2) do
          line1  = line2
          line2  = num
          nlines = nlines + 1
          if ((lin(i) .ne. '2c'x) .and. (lin(i) .ne. '3b'x)) then ! COMMA(2cx '.') SEMICIL(3bx ';')
              exit
          end if
          if (lin(i) .eq. '3b'x) then   ! SEMICOL(3bx ';')
              curln = num
          end if
          i = i + 1
      end while
      nlines = min(nlines,2)
      if (nlines .eq. 0) then
          line2 = curln
      end if
      if (nlines .le. 1) then
          line1 = line2
      end if
      if (status .ne. -3) then          ! ERR(-3)
          status = -2                   ! OK(-2)
      end if
      getlst = status
      return
      end

getone()では、行番号を一つ評価する。

RATFOR版は以下の通り。

# getone.r --  evaluate one line number expression
      integer function getone(lin,i,num,status)
      character lin(MAXLINE)
      integer i,num,status
      integer getnum
      integer istart,mul,pnum

      include clines.ri

      istart = i
      num    = 0
      call skipbl(lin,i)
      if (getnum(lin,i,num,status) == OK) # first term
          repeat {                        # + or - terms
              call skipbl(lin,i)
              if (lin(i) != PLUS & lin(i) != MINUS)
                  status = EOF
                  beak
              if (lin(i) .eq. PLUS)
                  mul = +1
              else 
                  mul = -1
              i = i + 1
              call skipbl(lin,i)
              if (getnum(lin,i,pnum,status) == OK)
                  num = num + mul * pnum
              if (status == EOF)
                  status = ERR
              } until (status != OK)
      if (num < 0 | num > lastln)
          status = ERR
      if (status == ERR)
          getone = ERR
      else if (i <= istart)
          getone = EOF
      else
          getone = OK
      status = getone
      return
      end

WATCOM Fortran77版は以下の通り。

c getone.f --  evaluate one line number expression
      integer function getone(lin,i,num,status)
      integer*1 lin(82)                 ! MAXLINE(82)
      integer i,num,status
      integer getnum
      integer istart,mul,pnum

      include clines.fi

      istart = i
      num    = 0
      call skipbl(lin,i)
      if (getnum(lin,i,num,status) .eq. -2) then ! OK(-2)
          loop
              call skipbl(lin,i)
              if ((lin(i) .ne. '2b'x) .and. (lin(i) .ne. '2d'x)) then ! PLUS(2bx '+') MINUS(2dx '-')
                  status = -1           ! EOF(-1)
                  exit
              end if
              if (lin(i) .eq. '2b'x) then
                  mul = +1
              else 
                  mul = -1
              end if
              i = i + 1
              call skipbl(lin,i)
              if (getnum(lin,i,pnum,status) .eq. -2) then ! OK(-2)
                  num = num + mul * pnum
              end if
              if (status .eq. -1) then  ! EOF(-1)
                  status = -3           ! ERR(-3)
              end if
          until (status .ne. -2)        ! OK(-2)
      end if

      if ((num .lt. 0) .or. (num .gt. lastln)) then
          status = -3                   ! ERR(-3)
      end if
      if (status .eq. -3) then
          getone = -3                   ! ERR(-3)
      else if (i .le. istart) then
          getone = -1                   ! EOF(-1)
      else
          getone = -2                   ! OK(-2)
      end if
      status = getone
      return
      end

下請けルーチンskipbl()は、空白文字をまたぎ越す。

RATFOR版は下記の通り。

# skipbl.r4 -- skip blank and tabs at lin(i)...
      subroutine skipbl(lin,i)
      character lin(ARB)
      integer i

      while (lin(i) == BLANK | lin(i) == TAB)
          i = i + 1
      return
      end

WATCOM Fortran77版は下記の通り。

c skipbl.for -- skip blank and tabs at lin(i)...
      subroutine skipbl(lin,i)
      integer*1 lin(*)                  ! ARB(*)
      integer i

      while ((lin(i) .eq. 32) .or. (lin(i) .eq. 9)) do ! BLANK(32) TAB(9)
          i = i + 1
      end while
      return
      end

getnum()は、項1つを行番号に変換する。

RATFOR版は以下の通り。

# getnum.r -- convert one term to line number
      integer function getnum(lin,i,pnum,status)
      character lin(ARB)
      integer i,pnum,status
      integer optpat
      integer ctoi,iindex,ptscan
      string digits='0123456789'

      include cpat.ri
      include clines.ri

      getnum = OK
      if (iindex(digits,lin(i)) > 0)
          pnum = ctoi(lin,i)
          i = i - 1
      else if (lin(i) == CURLINE)
          pnum = curln
      else if (lin(i) == LASTLINE)
          pnum = lastln
      else if (lin(i) == SCAN | lin(i) == BACKSCAN)
          if (optpat(lin,i) == ERR)
              getnum = ERR
          else if (lin(i) == SCAN)
              getnum = ptscan(FORWARD,pnum)
          else
              getnum = ptscan(BACKWARD,pnum)
      else
          getnum = EOF
      end if
      if (getnum == OK)
          i = i + 1                     # point at next character to be examined
      status = getnum
      return
      end

WATCOMM Fortran77版は以下の通り。

c getnum.f -- convert one term to line number
      integer function getnum(lin,i,pnum,status)
      integer*1 lin(*)                  ! ARB(*)
      integer i,pnum,status
      integer optpat
      integer ctoi,iindex,ptscan
      integer*1 digits(11)
      data digits/'0','1','2','3','4','5','6','7','8','9',-2/

      include cpat.fi
      include clines.fi

      getnum = -2                       ! OK(-2)
      if (iindex(digits,lin(i)) .gt. 0) then
          pnum = ctoi(lin,i)
          i = i - 1
      else if (lin(i) .eq. '2e'x) then  ! CURLINE(2ex '.')
          pnum = curln
      else if (lin(i) .eq. '24'x) then  ! LASTLINE(24x '$')
          pnum = lastln
      else if ((lin(i) .eq. '2f'x) .or. (lin(i) .eq. '5c'x)) then ! SCAN(2fx '/') BACKSCAN(5cx '\')
          if (.not. optpat(lin,i)) then ! error
              getnum = -3               ! ERR(-3)
          else if (lin(i) .eq. '2f'x) then ! SCAN(2fx '/')
              getnum = ptscan(1,pnum)   ! FORWARD(1)
          else
              getnum = ptscan(-1,pnum)  ! BACKWARD(-1)
          end if
      else
          getnum = -1                   ! EOF(-1)
      end if
      if (getnum .eq. -2) then
          i = i + 1
      end if
      status = getnum
      return
      end