2 # -*- coding: euc-jp -*-
5 # Convert ~/howm/ to HTML or other formats.
6 # Only RD format is supported unless you will give me patches. :p
8 #############################################################
13 name = File::basename $0
15 #{name}: howm ¥á¥â¤ò HTML ²½
16 ¡¦¤ä¤Ð¤¤Ê¸»ú¤ò¥¨¥¹¥±¡¼¥×
17 ¡¦¥¡¼¥ï¡¼¥É¤ò¥ê¥ó¥¯¤ËÊÑ´¹
18 ¡¦¥Ø¥Ã¥À¤È¥Õ¥Ã¥¿¤ò¤Ä¤±¤ë
20 #{name} ~/howm/ ~/converted/
21 ls ~/howm/*/*/*7-*.txt | #{name} -list ~/converted/
22 grep -rl '¤Û¤²' ~/howm/ | #{name} -list ~/converted/
24 -list ¢ª ¥á¥â¥Õ¥¡¥¤¥ë¤Î¥ê¥¹¥È¤òɸ½àÆþÎϤ«¤éÆɤà
25 -exclude='^[.]|CVS' ¢ª Âоݳ°¤Î¥Õ¥¡¥¤¥ë¤òÀµµ¬É½¸½¤Ç»ØÄê
26 -home='index.html' ¢ª ¡ÖHome¡×¤Î¥ê¥ó¥¯Àè
27 -silent ¤Þ¤¿¤Ï -s ¢ª ¿ÊĽɽ¼¨¤ò¤·¤Ê¤¤
28 -help ¤Þ¤¿¤Ï -h ¢ª ¤³¤Î¥á¥Ã¥»¡¼¥¸¤òɽ¼¨
29 (-debug ¢ª ¥Ç¥Ð¥Ã¥°ÍѽÐÎÏ)
33 argv_len = $list ? 1 : 2
34 if ($help || $h || ARGV.length != argv_len)
39 #############################################################
41 $exclude ||= "^[.\#]|CVS|~$"
43 $summary_length ||= 70
44 $come_from ||= "<<< *(.+)$"
47 #############################################################
50 STDERR.print str if !$silent
55 Dir::open(dir){|d| d.each{|f| a.push f}} # map doesn't work??
58 next if f =~ /#$exclude/
59 path = File::expand_path f, dir
60 b.push f if FileTest::file? path
61 b += ls_R(path).map{|g| "#{f}/#{g}"} if FileTest::directory? path
68 parent = File::dirname path
69 return true if parent == path # root dir
71 if !FileTest::exist? path
77 def split_base(file_list)
78 fs = file_list.map{|f| File::expand_path f}
79 ds = fs.map{|f| File::dirname f}
83 if common.length <= d.length
86 common = File::dirname common
90 rs = fs.map{|f| f[(common.length + 1)..-1]} # +1 for '/'
96 self[key] ||= Array::new
104 # self.each{|x| ret += x}
113 # necessary for use of last_match. sigh...
114 pattern = Regexp::new(Regexp::quote(pattern)) if pattern.is_a? String
115 while (i = index pattern, pos)
116 a.push Regexp.last_match.offset(0)
124 def expand_readlines(f)
125 open(File::expand_path(f, base_dir), 'r'){|io| io.readlines.join}
126 # open(File::expand_path(f, base_dir), 'r'){|io| io.read} # for ruby-1.7?
129 open(File::expand_path(f, base_dir), 'r'){|io| io.gets.chop}
131 def link_tag(f) # Clean me. ²¿ÅÙ¤â¸Æ¤Ö¤Î¤Ï¤«¤Ã¤³°¤¤.
133 [:link, f + '.b', f + ': ' + fline[0, $summary_length]]
137 #############################################################
141 def initialize(home = nil)
148 com.each{|c| put_one c}
154 items = command.shift
157 @result += "</pre>\n"
159 @result += CGI::escapeHTML(command.shift)
162 url = link.is_a?(String) ? link + '.html' : link[1]
163 @result += %!<a href="#{url}">#{CGI::escapeHTML str}</a>!
165 items = command.shift
167 items.each{|i| @result += ' <li>'; put_one i; @result += "\n"}
171 def wrapped_result(title)
172 etitle = CGI::escapeHTML title
175 <head><title>#{etitle}</title></head>
181 <a href="#{@home}">Home</a>
182 <a href="book.h.html">Files</a>
183 <a href="index.h.html">Keywords</a>
188 def write(title, file, dir)
189 f = File::expand_path(file, dir) + '.html'
190 mkdir_p File::dirname(f)
191 open(f, 'w'){|io| io.puts wrapped_result(title)}
197 attr_accessor :files, :base_dir
198 def initialize(files, base_dir)
205 def write(dest_dir, formatter)
206 index = Index::new @files, @base_dir
207 write_each dest_dir, formatter, index
208 write_list dest_dir, formatter
209 index.write dest_dir, formatter
211 def write_list(dest_dir, formatter)
213 formatter.put [:list, @files.sort.map{|f|
215 # first_line = open(File::expand_path f, @base_dir){|io| io.gets.chop}
216 # [:link, f + '.b', f + ': ' + first_line[0, $summary_length]]
218 formatter.write 'Files', 'book.h', dest_dir
221 def write_each(dest_dir, formatter, index)
224 formatter.put [:pre, interpret(expand_readlines(f), index, f)]
225 formatter.write first_line(f), f + '.b', dest_dir
226 # formatter.write f, f + '.b', dest_dir
231 def interpret(src, index, f)
232 hit = search src, index, f
239 when -1 # eat until beginning of this hit, and retry
240 ret.push [:as_is, src[cursor...b]]
243 when 0 # expand this hit
249 w = Regexp::last_match[$come_from_pos]
250 link = CGI::escape(CGI::escape(w)) + '.i'
252 decl = index.decl[key]
253 link = decl.member?(f) ? nil : decl[0] + '.b'
255 ret.push(link ? [:link, link, s] : [:as_is, s])
257 when 1 # discard this hit
260 ret.push [:as_is, src[cursor..-1]]
263 def search(src, index, f)
265 index.decl.each_key{|k|
266 offsets = src.offsets k
267 index.used.cons k, f if !offsets.empty? && !index.decl[k].member?(f)
268 hit += offsets.map{|o| o.push k}
270 hit += src.offsets(%r{http://[-!@#\$%^&*()_+|=:~/?a-zA-Z0-9.,;]*[-!@#\$%^&*()_+|=:~/?a-zA-Z0-9]+}).map{|o| o.push :url}
271 hit += src.offsets($come_from).map{|o| o.push :decl}
272 hit.sort{|h1, h2| earlier_longer h1, h2}
274 def earlier_longer(h1, h2)
275 [h1[0], - h1[1]] <=> [h2[0], - h2[1]]
281 attr_accessor :files, :base_dir
282 attr_reader :decl, :used
283 def initialize(files, base_dir)
286 @decl = HashList::new
287 @used = HashList::new
292 expand_readlines(f).scan($come_from){|hit| @decl.cons hit[0], f}
295 def write(dest_dir, formatter)
296 write_each dest_dir, formatter
297 write_list dest_dir, formatter
299 def write_list(dest_dir, formatter)
303 @decl.keys.sort.map{|key|
304 [:link, CGI::escape(CGI::escape(key)) + '.i', key + " (#{(@used[key]||[]).length})"]
307 formatter.write 'Keywords', 'index.h', dest_dir
310 def write_each(dest_dir, formatter)
312 f = CGI::escape(key) + '.i'
313 to_decl = @decl[key].map{|g| link_tag g}
314 to_used = (@used[key] || []).map{|g| link_tag g}
315 to_rel = related_keys(key).map{|g| [:link, @decl[g][0] + '.b', g]}
316 # to_decl = @decl[key].map{|g| [:link, g + '.b', g]}
317 # to_used = (@used[key] || []).map{|g| [:link, g + '.b', g]}
318 # to_rel = related_keys(key).map{|g| [:link, @decl[g][0] + '.b', g]}
321 [:as_is, "Declared:\n"],
323 [:as_is, "Linked:\n"],
325 [:as_is, "Related:\n"],
329 formatter.write key, f, dest_dir
334 def related_keys(key)
335 sub = included_keys key
336 sub.map{|k| @decl.keys.select{|x| x.include? k and x != key}}.flatten.uniq.sort
338 def included_keys(key)
339 @decl.keys.select{|k| key.include? k}
343 #############################################################
346 dest_dir = ARGV.shift
347 src_dir, files = split_base(STDIN.readlines.map{|s| s.chomp})
349 src_dir, dest_dir = ARGV
352 notice "#{files.length} files "
354 b = Book::new files, src_dir
355 i = Index::new files, src_dir
356 notice "(#{i.decl.length} entries)\n"
358 $home ||= b.first_page[1] + '.html'
359 fmt = Formatter::new $home
360 b.write dest_dir, fmt