ごとけんです magic(5) のパーザーを書いてみました。ほんとは file(1) を 書こうと思ったんですけど力尽きました。だれか育てて下さい(__;; クラス MagicFile MagicFile は magic(5) のパーザークラスです。 クラスメソッド new([filename]) filename で与えられたファイルを読み、 MagicFile オブジェクトを返します。 MagicFile オブジェクトは配列のように扱うことができ、この配列の各要素は、 オフセット、型、テスト、メッセージ、追加テストの5つの要素からなる配列です。 オフセット、型、テスト、メッセージは文字列で、追加テストは MagicFile と 同じ構造の配列です。filename が与えられない場合は /etc/magic を読みます。 -- gotoken
# # class MagicFile # # MagicFile is a class as magic(5) parser. # # class methods # # new([filname]) # # Reads filename and return MagicFile object. A MagicFile object can be # treat as an array, which consists of arrays. Each member of MagicFile # consists of five members -- offset, type, test, message and more test. # Each of offset, type, test and message is a string. The more test is # an array which is structured as same as MagicFile. If filename is # omitted the /etc/magic is read. # class MagicFile TraditionalMagicFile = "/etc/magic" def initialize(file = TraditionalMagicFile) if file.kind_of?(String) @file = open(File.expand_path(file)).read.split("\n"). delete_if{|i| i =~ /^\s*(#.*)?$/} # delete comment or null line else raise ArgumentError.new("filename") end @magic = [] pat = /^(>*)(.*)/ shellword = /(?:[^\\\s]|\\.)+/ level = 0 stack = [] @file.each_with_index do |line,nr| os, type, data, *msg = line.scan(shellword) message = msg.join(' ') lv, offset = os.scan(pat)[0] lv = lv.size cur = [offset, type, data, message, []] if lv == 0 @magic.push(stack[0]) unless nr == 0 # push!! stack = [cur] elsif lv <= level + 1 # 0 < lv (level-lv+1).times{ stack.pop } stack[-1][-1].push cur stack.push cur else raise "`>' nest illegal" end level = lv end @magic.push(stack[0]) # push!! end def method_missing(mid, *arg, &blk) @magic.__send__(mid, *arg, &blk) end def a2s(ary, lv) if ary[4].empty? ">"*lv + ary[0..3].join("\t") else ">"*lv + ary[0..3].join("\t") + "\n" + ary[4].filter{|i| a2s(i, lv+1)}.join("\n") end end def to_s @magic.collect{|i| a2s(i,0)}.join("\n") end private :a2s end if __FILE__ == $0 magic = MagicFile.new print magic, "\n" end