タブの処理、detab,entab ― 2014年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のところで、 切れてしまいます。仕様なので、これ以上の事は追求しないことにしました。
最近のコメント