#!/usr/bin/env ruby # # Fixes htags' output so that files have fixed names. E.g., S/542.html -> D/whatever.c.h. # # It addresses the following problem: # # "Can htags create permanent addresses?" # http://lists.gnu.org/archive/html/help-global/2015-11/msg00002.html # require 'fileutils' require 'pp' Encoding.default_external = "BINARY" $base = '.' # Where the HTML is stored. $verbose = false $debug = false $create_subdirs = false # Experimental feature. Doesn't really work. # Bonus: highlight the target line. $css = " a:target { position: absolute; left: 0; height: 1.35em; width: 100%; background: yellow; opacity: .5; z-index: -1000; } " # # Computes pretty names for files: # # '.../S/456.html' => 'S/modules/tty.c.html' # def prettyname(full_path, prefix, kind, nid, ext) gist = nid start = IO.read(full_path, 1024) if start =~ /<title>(.*?)<\/title>/ then gist = $1 gist.gsub!(%r{/+$}, '') # remove trailing '/'s. if not $create_subdirs then gist.gsub!('/', '--') end gist.gsub!(%r{[^a-zA-Z0-9_.,/-]}, '-') # clean special characters. end return kind + '/' + gist + ext end class Counter class << self def init(total) @total = total end def reset() @count = 0 end def report() print "[#{@count}/#{@total}]\r" @count += 1 end def finish() puts end end end # # Returns a table explaining how to rename the files. E.g.: # # { # 'S/456.html' => 'S/modules/tty.c.html' # ... # } # def build_cnv_tbl() Counter.reset() cnv = {} Dir[$base + '/{[A-Z],files}/*.html'].each do |full_path| #p full_path if full_path =~ /(.*)\/((\w+)\/(\d+)(\.\w+))$/ then prefix = $1 # "/home/joe/.../HTML" path = $2 # "S/323.html" kind = $3 # "S" nid = $4 # "323" ext = $5 # ".html" pretty = prettyname(full_path, prefix, kind, nid, ext) cnv[path] = pretty end Counter.report() end Counter.finish() return cnv end # # Make all the substiutions inside the files: # # <a href='../S/456.html'> --> <a href='../S/modules/tty.c.html'> # ... # def mksub(cnv) Counter.reset() Dir[$base + '/**/*.html'].each do |path| kind = path[$base.length .. -1][/\w+/] text = IO.read(path) fixed = text.gsub(/(<a href='(?:..\/)?)([^'#]+)/) do |a| prefix = $1 target = $2 if cnv[target] then target = cnv[target] elsif cnv[kind + '/' + target] then # This is a relative link of the form: href='456.html' if cnv[kind + '/' + target].match /\/(.*)/ # converts "s/whatever.html" to "whatever.html" target = $1 end end prefix + target end # Fix a problem in Opera, where empty lines are squashed. fixed.gsub!(%r{(<a id='L\d+' name='L\d+'></a>)\n}, "\\1 \n") if text != fixed then IO.write(path, fixed) puts(path + " modified") if $debug end Counter.report() end Counter.finish() end # # Rename the files. # def rename(cnv) Counter.reset() cnv.each do |a, b| src = $base + '/' + a trg = $base + '/' + b if $create_subdirs then FileUtils.mkdir_p(File.dirname(trg)) end File.rename(src, trg) Counter.report() end Counter.finish() end def better_css(css_path) text = IO.read(css_path) IO.write(css_path, text + $css) end def main() if not File.exists?($base + '/help.html') then $base += '/HTML' if not File.exists?($base + '/help.html') then puts "Error: This doesn't look like htags' output directory." exit(1) end end Counter.init( Dir[$base + '/**/*.html'].length ) puts "Figuring out pretty names..." cnv = build_cnv_tbl() pp cnv if $verbose puts "Updating links in files..." mksub(cnv) puts "Renaming files..." rename(cnv) puts "Enhancing the css..." better_css($base + '/style.css') puts "Done." end main()