zlib
何時も連絡を取り合ってる人から、連絡が無いんで、干からびていないか心配になって連絡してみた。朝の9時ごろ。
小声で、折り返すからって言われた。朝のお勤めでもしてたんかな?
暫くして、元気な声が聞こえてきた。図書館に居るとの事。みんな避難してるんで、早く行かないと駐車場が埋まっちゃうと言ってた。パチンコ屋に避難するよりは、よっぽど健康的かつ文化的かつ経済的。
飛んでる図書館で、視聴覚室はあるは、漫画はあるわとの事。人口が多い経済が豊な所は違うな。こちらは、そんな豪華なものは無いぞ。
ああ、隣町の図書館は、遊戯室が有って、寝転んでも良さそう。但し、回りのお母さんから顰蹙を買うだろね、場合によっては変質者と認定されて、通報されちゃったりして。
そんな事が無いように、お盆で帰省してくる、姪っ子の娘をだしにすればいいか。
gauche + rlwrap
前回調べておいた、gaucheとrlwrapを組み合わせるやつ、もう少し調べてみた。
補完用のデータは、
gosh -E"use gauche.interactive" -E"apropos #/.*/" -Eexit |tr "(" "\n" |tr -d " (" |sort |uniq >gosh_symbols
のようにして作っている。後は出来上がったものを加工(しなくてもいいけど)して、.gosh_completionsとして置いておく。
sakae@usvr:~$ cat /usr/local/bin/goshr #!/bin/sh rlwrap -c -q '"' -b "'"'(){}[].,#@;|`"' -m gosh $@
後は、こういうので起動すると、補完他が使えるようになって幸せ。aliasに登録しようとしたら、微妙な問題で登録出来なかった。しょうがないから、goshrって名前のスクリプトにして登録しといた。この時、引数を受け付けるよう、goshの後ろに $@ を付けるのが味噌。
それはいいんだけど、シンボルを集めてきてうんぬんするのに、1行野郎が使われている。展開してみるかな。
gosh> (use gauche.interactive) gosh> (apropos #/.*/) : x->string (gauche) zero? (scheme) |+.| (gauche) |-.| (gauche) |setter of object-apply| (gauche) |setter of ref| (gauche) ~ (gauche)
なる程、カッコ内は、出身地になるのかな? ならば、出身地別にカウントしてみよう。
sakae@usvr:~$ gosh -E"use gauche.interactive" -E"apropos #/.*/" -Eexit | awk '{print($2)}' | sort | uniq -c 1566 (gauche) 30 (gauche.interactive) 25 (null) 2 of 194 (scheme) 4 (user)
出身地がnullってどういう事? 何か手がかりが無いか、流行りのBigData分析(そんなにないけど)してみる。
sakae@usvr:~$ gosh -E"use gauche.interactive" -E"apropos #/.*/" -Eexit | fgrep '(null)' ... (null) => (null) and (null) begin (null) case (null) cond (null) define (null) define-syntax (null) delay (null) do (null) else (null) if (null) lambda (null) let (null) let* (null) let-syntax (null) letrec (null) letrec-syntax (null) or (null) quasiquote (null) quote (null) set! (null) syntax-rules (null) unquote (null) unquote-splicing (null)
schemeの根幹を成すものが集まってた。こういうのscheme用語で、何と言ったでしょうか? 昔の本を漁ってみるかな。そして、(scheme)族は、R5RSに定義される奴かな?
それはそうと、もう一つの補完方法を見つけた。
sakae@usvr:~/Gauche-readline-20100625$ sudo make install mkdir -p term ln -sf ../readline.scm term/readline.scm /usr/local/bin/gauche-install -m 644 -T /usr/local/share/gauche-0.9/site/lib/term readline.scm /usr/local/bin/gauche-install -m 755 -T /usr/local/lib/gauche-0.9/site/x86_64-pc-linux-gnu term--readline.so /usr/local/bin/gauche-install -m 644 -T /usr/local/share/gauche-0.9/site/lib/.packages Gauche-readline.gpd /usr/local/bin/gauche-install -m 755 --shebang=/usr/local/bin/gosh gosh-rl.scm /usr/local/bin/gosh-rl
useしたモジュールが 補完候補に追加されるんで、便利。更にカッコの対応を表示するように、.inputrcに
set blink-matching-paren on
と、書いておくと良い。
このreadlineモジュールの作成方法が、Gaucheの良いunix側のモジュール使用の実例になっているので、じっくり見ておくと吉。
gauche on FreeBSD and OpenBSD
今の所、gaucheはウブとNetBSDで動いている。FreeBSDはどうか? clangの新版で問題ないか確認の意味も込めてやってみた。全く問題無し。開発チームの人にFreeBSD使いの人がいたのだろうか? それとも、Mac系で出た問題を潰していたら、自然とFreeBSDでも無問題になって行ったのかな?
余勢を駆って、readlineのモジュール作成も試してみる。 盛大にコンパイルエラーが出て来た。どうも、readline.hあたりが見当たらない疑惑。
FreeBSDは、readline系がOS備え付けではなく、pkg扱い。その関係で/usr/localの下に入ってる。しょうがないので、configure時に、includedirとかを設定したけど、反映されず。 今後の事も考えて、/usr/include/readline/にリンクを張っておいた。
そして、lreadlineも反映されていなかったので、Makefileに書き足した。
readline_SRCS = readline.c readlinelib.stub readline_LIBS = -L/usr/local/lib -lreadline
使ってみる。callまで入力してTABキーを押すと、候補が出てくる。有り難い事です。
$ gosh-rl WARNING: Quote character setting is not supported by the library. gosh> (call call-formatter call-with-iterators call-macro-expander call-with-output-file call-syntax-handler call-with-output-string call-with-builder call-with-port call-with-current-continuation call-with-string-io call-with-input-file call-with-values call-with-input-string call/cc call-with-iterator call/pc
emacsから、M-x run-scheme して、出て来るセッション窓に、このgosh-rlが使えないか試してみたけど、補完は効かなかった。emacsが提供する窓は、馬鹿窓で、画面制御が出来ないんで、自粛しちゃってるんだな。残念。
更に余勢を駆って、OpenBSDでgosh出来るか試してみる。過去には、OpenBSDの特殊性を回避する為のパッチが存在してたけど、それが反映されてるかも知れないからね。
コンパイルは無事に通った。testで、srfiの所でtrapするのと、controlのテストの所で永久ループに入っちゃうという不都合が発生。コンパイルが通るようになったのは、きっとgcの版が上がったからに違いない。
ob6$ gosh srfi.scm : <srfi-155>--------------------------------------------------------------------- testing bindings in #<module srfi-155> ... ok <SRFI 155: Promises>----------------------------------------------------------- test (force (delay (+ 1 2))), expects 3 ==> Abort trap (core dumped)
頓死検案書
ob6$ gdb -q gosh gosh.core Reading symbols from gosh...done. [New process 245297] Core was generated by `gosh'. Program terminated with signal SIGABRT, Aborted. #0 thrkill () at -:3 3 -: No such file or directory. (gdb) bt #0 thrkill () at -:3 #1 0x00000bf0a29a00de in _libc_abort () at /usr/src/lib/libc/stdlib/abort.c:51 #2 0x00000bf0a29ad3d6 in _libc_pthread_mutex_unlock (mutexp=<optimized out>) at /usr/src/lib/libc/thread/rthread_mutex.c:266 #3 0x00000bf0ce08bb45 in force_cc (result=<optimized out>, data=<optimized out>) at lazy.c:166 #4 0x00000bf0ce037610 in run_loop () at ./vmcall.c:196 #5 0x00000bf0ce0379b6 in user_eval_inner ( program=0xbf0ce577a20 <internal_apply_compiled_code>, codevec=0x7f7ffffbd470) at vm.c:1489 #6 0x00000bf0ce03890c in apply_rec (vm=<optimized out>, proc=<optimized out>, nargs=<optimized out>) at vm.c:1582 #7 0x00000bf0ce08a79c in Scm_Load (cpath=<optimized out>, flags=<optimized out>, packet=0x7f7ffffbd650) at load.c:223 #8 0x00000bee5af00ad1 in execute_script (scriptfile=0x0, args=0xbee5b36d4d0) at main.c:550 #9 0x00000bee5af019e9 in main (ac=1, av=0x7f7ffffbd890) at main.c:751
もう一つの不具合
ob6$ gosh control.scm : test shutdown - raising <thread-pool-shutting-down>, expects #<<thread-pool-shut-down>> ==> ok test shutdown - killing a job in the queue, expects killed ==> ok test shutdown check, expects finished ==> ok ここで、黙りこんでしまう。別端末から、kill -9 gosh-pid killed
テストの不都合報告が無いのは、OpenBSDが辺境のOS。そしてgaucheを使ってみようと言う人は 更にいないからだろうね。ああ、あの人なら使いそうかな。
copipeP.scm
前々回にやった、『cut比べ』に出て来た、ファイルが似てる度を数値で表すshell語を、scheme語に変換してみたい。scheme語ってのは曖昧過ぎるな。正しくはgauche語だ。上でも見たけど、schemeの仕様に準拠してるのはほんのわずか。後は、現場で鍛えられ、使い易い手続きがわんさかと実装されてるからだ。
だから、gauche語で書かれたコードを他のschemeを標榜してるシステム(有名な所では Racketあたり)に持って行っても、動かすには大変な苦労を伴うだろう。ようするに、gaucheを使うことは、ロックインされちゃうって事。それを心得て使えば、気持ちよくコードが書けるだろう。
そうは言っても、すっかりgaucheとはご無沙汰になってるので、少しgauche語で書かれた資料(俗に言うCookbook)を探してみた。
以前に書いたshell語のコードを再掲。これをgauche語に変換する。
#!/bin/sh # Usage: copipeP.sh ref-file cmp-file a=$1 b=$2 saa=`cat $a $a | wc -c` zaa=`cat $a $a | gzip -c | wc -c` faa=`echo "$zaa * 10000 / $saa" | bc` sab=`cat $a $b | wc -c` zab=`cat $a $b | gzip -c | wc -c` fab=`echo "$zab * 10000 / $sab" | bc` echo "Same file : 100, Differ file : > 100" echo "$fab * 100 / $faa" | bc
オイラー的に欲しいのは、ファイル名を指定したら、それを読んで文字列にしてくれる手続きと、指定した文字列をgzipで圧縮して文字列を返してくれる手続きだな。
さっそくCOOKPADを探してみる。
(call-with-input-file "a.scm" port->string)
素晴らしいのが見つかった。
Scheme の入出力 (その1) と、 Scheme の入出力 (その2) にあるように、gaucheは、portと言うのが、過剰に発達してて便利に使える。上記もその例。
次は、gzipの扱い。unix側にあるgzipに一度文字列を投げて、圧縮されたデータを受け取るってのを考えた。けど、きっとgauche内からそんな事が出来る仕掛けが有るに違いない、と思って探してみたよ。
最初の所は、眠くなるような説明が続く。deflating/inflating 圧縮/展開ですって。これって、デフレとかインフレの経済用語に通じるのかしらん。
じっと我慢の子で流し読みしていくと、
gosh> (use rfc.zlib) gosh> (define z (gzip-encode-string "hello hwllo fufu")) #*"\x1f;\x8b;....;\0\0\0" gosh> (gzip-decode-string z) "hello hwllo fufu"
使えそうなのが出て来た。さあ、材料は揃ったぞ。
sakae@fb:~ % cat copipeP.scm ; Usage: gosh copipeP.scm ref-file cmp-file (use rfc.zlib) (define (calc a b) (let* ([ss (string-append a b)] [sz (string-length ss)] [zz (string-length (gzip-encode-string ss))]) (/ zz sz))) (define (main argv) (let ([a (call-with-input-file (list-ref argv 1) port->string)] [b (call-with-input-file (list-ref argv 2) port->string)]) (print (div (* (calc a b) 100) (calc a a))) 0))
オリジナルは、同じような事を繰り返していたので、手続きにまとめてみた。圧縮比の計算で、オリジナルはbcでの整数演算だったので、桁落ちを防ぐため、分子を10000倍してた。schemeは、整数の割り算は有理数になるんで、気にする事は無い。 その代わり、ユーザーに見せる結果の割り算の所では、divを使って整数に丸めた。
sakae@fb:~ % gosh copipeP.scm li.c li.c 100 sakae@fb:~ % gosh copipeP.scm li.c fb.c 197
以前やった、linuxのcut.cとFreeBSDのcut.cの似てる度。当然、同じ結果になった。
sakae@fb:~ % gosh build-standalone copipeP.scm cc -fPIC -I/usr/local/lib/gauche-0.9/0.9.6/include -o copipeP copipePWFW9aH.c -L/usr/local/lib/gauche-0.9/0.9.6/x86_64-unknown-freebsd11.2 -lgauche-static-0.9 -lz -lcrypt -lutil -lrt -lm -pthread sakae@fb:~ % ./copipeP li.c fb.c 197
そしてコンパイルしてみた。15Mのサイズになったぞ。
gzip-encode-string
今回お世話になった、gzip-encode-stringが、どのように実装されてるか? 答えはtar玉を展開したext/zlib/zlib.scmに有った。
;; utility procedures (define (deflate-string str . args) (call-with-output-string (^p (let1 p2 (apply open-deflating-port p args) (display str p2) (close-output-port p2))))) (define (inflate-string str . args) (port->string (apply open-inflating-port (open-input-string str) args))) (define (gzip-encode-string str . args) (call-with-output-string (^p (let1 p2 (apply open-deflating-port p :window-bits (+ 15 16) args) (display str p2) (close-output-port p2))))) (define (gzip-decode-string str . args) (port->string (apply open-inflating-port (open-input-string str) :window-bits (+ 15 16) args)))
見慣れない、^p って何? すかさずinfoを引く。
-- Macro: ^c body ... `(lambda (C) BODY ...)'の短縮表記です。 `c'には`#[_a-z]'に含まれる 任意の一文字が使えます。 (map (^x (* x x)) '(1 2 3 4 5)) ⇒ (1 4 9 16 25)
lambdaは、空気のようにあちこちに出て来るので、便利に書けるように。 なんでもλ
zlib
今回出てきた圧縮・伸長の大本、libzは、 圧縮アルゴリズム と、 SICPの例(2.3.4)に出て来るハフマン符号を組み合わせたものだという。
詳しくは、zlib.hを見てねって、shiroさんも言ってる。manでも言ってる。zlib(3)を見ると、
All functions of the compression library are documented in the file zlib.h. The distribution source includes examples of use of the library in the files test/example.c and test/minigzip.c, as well as other examples in the examples/ directory.
論よりsourceって事で、サンプルが提供されてる。FreeBSDだと寄贈ソフトの所に置いてある。 minizpi.cを試してみるか。
sakae@fb:/tmp % cc -g -O0 minigzip.c /tmp/minigzip-1ed379.o: In function `gz_compress': /tmp/minigzip.c:384: undefined reference to `gzwrite' /tmp/minigzip.c:384: undefined reference to `gzerror' /tmp/minigzip.c:387: undefined reference to `gzclose' : cc: error: linker command failed with exit code 1 (use -v to see invocation) sakae@fb:/tmp % cc -g -O0 minigzip.c -lz
リンクを忘れると盛大にエラーが出て来る。今回はclangさんから、あれリンクするといいよってのが出てこなかった。気まぐれなんか。
NetBSDのzlib(3)には
Basic functions int deflateInit(z_streamp strm, int level); int deflate(z_streamp strm, int flush); Advanced functions int deflateInit2(z_streamp strm, int level, int method, int windowBits, int memLevel, int strategy); Utility functions typedef voidp gzFile ; gzFile gzopen(const char *path, const char *mode);
zlib.hを見ろなんていう不親切(手抜き)な説明はしておらず、最初に関数の列挙、続いて各関数の説明、構造体の説明と、至れり尽くせりの親切さが有ったぞ。
zlib@python
gaucheもCも通な人が使う。じゃ、普通な人はどうするの? そりゃ猫も杓子もpython3でしょ。zlibサポートしてるんか?
In [2]: help("zlib") : DESCRIPTION The functions in this module allow compression and decompression using the zlib library, which is based on GNU zip. adler32(string[, start]) -- Compute an Adler-32 checksum. compress(data[, level]) -- Compress data, with compression level 0-9 or -1. compressobj([level[, ...]]) -- Return a compressor object. crc32(string[, start]) -- Compute a CRC-32 checksum. decompress(string,[wbits],[bufsize]) -- Decompresses a compressed string. decompressobj([wbits[, zdict]]]) -- Return a decompressor object. 'wbits' is window buffer size and container format. Compressor objects support compress() and flush() methods; decompressor objects support decompress() and flush().
もっと分かり易く 13.1. zlib — gzip 互換の圧縮 更に例もあると嬉しい? 13.2. gzip — gzip ファイルのサポート
原典に当たる
sakae@fb:~ % rfc 1952 The Result: 1952 GZIP file format specification version 4.3. P. Deutsch. May 1996. (Format: TXT=25036, PS=43337, PDF=43211 bytes) (Status: INFORMATIONAL) (DOI: 10.17487/RFC1952)
RFC 1952 GZIP file format specification version 4.3 日本語訳
gosh-rl @ NetBSD
NetBSDでもgoshで補完が出来ると嬉しいな。んな訳で作ってみた。勿論元となるreadlineは、変な所に置いてあったんだ。
nb$ locate readline.h /usr/include/readline/readline.h /usr/pkg/include/readline/readline.h /usr/pkgsrc/graphics/gnuplot/patches/patch-src_readline.h /usr/pkgsrc/net/tnftp/files/libedit/readline/readline.h /usr/share/man/html3/openpam_readline.html /usr/src/lib/libedit/readline/readline.h nb$ locate readline.so /usr/pkg/lib/libreadline.so /usr/pkg/lib/libreadline.so.7 /usr/pkg/lib/libreadline.so.7.0.0
ヘッダーは、リナ系を斟酌して普通の所にも置いてあった。けど、soなファイルは、特殊領域にしか置かれていない。それを例の方法で吸収して、コンパイルを成功させた。
いざ使用しようとしたら、readline.soが見つからないと言われた。 一応リンク関係を確認。
nb$ ldd term--readline.so term--readline.so: -lgauche-0.9.0.6 => not found -lcrypt.1 => /usr/lib/libcrypt.so.1 -lgcc_s.1 => /usr/lib/libgcc_s.so.1 -lc.12 => /usr/lib/libc.so.12 -lutil.7 => /usr/lib/libutil.so.7 -lrt.1 => /usr/lib/librt.so.1 -lm.0 => /usr/lib/libm.so.0 -lpthread.1 => /usr/lib/libpthread.so.1 -lreadline.7 => not found
なにこれ? readlineもgaucheも見えていないじゃん。まてまて、goshは起動してるんで、見えているはずだけど。はて、どうする? 昔の記憶を手繰り寄せて、ld.so.confを呼び覚ました。
nb$ cat /etc/ld.so.conf /usr/pkg/lib /usr/local/lib
このファイルを作るだけで、下記のように認識された。
nb$ ldd term--readline.so term--readline.so: -lgauche-0.9.0.6 => /usr/local/lib/libgauche-0.9.so.0.6 -lcrypt.1 => /usr/lib/libcrypt.so.1 -lgcc_s.1 => /usr/lib/libgcc_s.so.1 -lc.12 => /usr/lib/libc.so.12 -lutil.7 => /usr/lib/libutil.so.7 -lrt.1 => /usr/lib/librt.so.1 -lm.0 => /usr/lib/libm.so.0 -lpthread.1 => /usr/lib/libpthread.so.1 -lreadline.7 => /usr/pkg/lib/libreadline.so.7 -lterminfo.1 => /usr/lib/libterminfo.so.1
正式には、どういう手を打つのだろうか?