#!/usr/bin/env ruby # ================================================================ # Poki: A poor man's wiki generator # Code: https://github.com/johnkerl/poki # Docs: http://johnkerl.org/poki/doc # Customized for Miller 2019-09-15 # John Kerl # kerl.john.r@gmail.com # 2015-04-26 # ================================================================ $us = File.basename $0 $default_config_file_name = "poki-cfg.json" $output_file_mode_during_edit = 0644; $output_file_mode_after_edit = 0444; require 'getoptlong' require 'fileutils' require 'json' # ================================================================ def usage() $stderr.puts <" output_handle.puts "" # Template directive: For navbar. elsif template_line =~ /POKI_PUT_NAVBAR_HERE/ output_handle.puts(template_line.sub('POKI_PUT_NAVBAR_HERE', '')) generate_navbar(overall_config, section_config, page_config, output_html_name, output_handle) # Template directive: Page titles in browser window title, as well as page-content title. elsif template_line =~ /POKI_PUT_TITLE_HERE/ output_handle.puts(template_line.sub('POKI_PUT_TITLE_HERE', page_title)) # Template directive: Page body. elsif template_line =~ /POKI_PUT_BODY_HERE/ output_handle.puts(template_line.sub('POKI_PUT_BODY_HERE', '')) generate_page_body(overall_config, section_config, page_config, input_html_path, output_handle) # Absent any other directives, copy the template file to the output file. else output_handle.write(template_line) end end FileUtils.chmod($output_file_mode_after_edit, output_html_name) puts "Generated #{output_html_name}" end end # ---------------------------------------------------------------- def generate_navbar(overall_config, section_config, page_config, output_html_name, output_handle) template_file_name = overall_config.fetch('template_file_name') output_handle.puts "" output_handle.puts("
"); for other_section_config in overall_config.fetch('sections') other_section_internal_name = other_section_config.fetch('internal_name') other_section_external_name = other_section_config.fetch('external_name') bs = '' be = '' if (other_section_internal_name == section_config.fetch('internal_name')) bs = '' be = '' end other_section_pages_config = other_section_config.fetch('pages') top_page_for_section = other_section_pages_config[0] top_page_for_section_file_name = top_page_for_section.fetch('file_name') # xxx needs css for no underlining ... output_handle.puts("#{bs}#{other_section_external_name}#{be}"); output_handle.puts(" "); end output_handle.puts("
"); for other_page_config in section_config.fetch('pages') other_page_file_name = other_page_config['file_name'] # nullable other_page_link_name = other_page_config['link'] # nullable other_page_external_name = other_page_config.fetch('external_name') if other_page_link_name != nil url = other_page_link_name.sub(/^ext:/, '') output_handle.puts "
#{other_page_link_name}" else bs = "" be = "" if File.basename(output_html_name) == other_page_file_name # Bold the current file in the navbar. bs = "" be = "" end output_handle.puts "
#{bs}#{other_page_external_name}#{be}" end end end # ---------------------------------------------------------------- def generate_page_body(overall_config, section_config, page_config, input_html_path, output_handle) output_handle.puts "" File.readlines(input_html_path).each do |content_line| # Page-content directive: table of contents. if content_line =~ /POKI_PUT_TOC_HERE/ generate_toc(overall_config, section_config, page_config, input_html_path, output_handle) # Page-content directive: include other file (do HTML escapes) elsif content_line =~ /POKI_INCLUDE_ESCAPED\(([^)]+)\)HERE/ included_file_name = $1 include_escaped(included_file_name, output_handle) # Page-content directive: include other file (do HTML escapes) and print its output elsif content_line =~ /POKI_INCLUDE_AND_RUN_ESCAPED\(([^)]+)\)HERE/ included_file_name = $1 cmd = File.readlines(included_file_name).join('') run_command(cmd, output_handle, true) # Page-content directive: run a script which generates HTML and print its output elsif content_line =~ /POKI_RUN_UNESCAPED\(([^)]+)\)HERE/ included_file_name = $1 cmd = File.readlines(included_file_name).join('') run_command(cmd, output_handle, false) # Page-content directive: include other file (do HTML escapes) and print its output elsif content_line =~ /POKI_RUN_CONTENT_GENERATOR\(([^)]+)\)HERE/ cmd = $1 run_content_generator(cmd, output_handle) # Page-content directive: format as if included from a file. elsif content_line =~ /POKI_CARDIFY\(([^)]+)\)HERE/ content_line = $1 cardify(content_line, output_handle, true) elsif content_line =~ /POKI_CARDIFY{{([^)]+)}}HERE/ content_line = $1 cardify(content_line, output_handle, true) elsif content_line =~ /POKI_CARDIFY{{(.+)}}HERE/ content_line = $1 cardify(content_line, output_handle, true) # Page-content directive: include other file (do HTML escapes) elsif content_line =~ /POKI_RUN_COMMAND{{(.+)}}HERE/ cmd = $1 run_command(cmd, output_handle, true) # Page-content directive: include other file (do HTML escapes) elsif content_line =~ /POKI_RUN_COMMAND_TOLERATING_ERROR{{(.+)}}HERE/ cmd = $1 run_command_tolerating_error(cmd, output_handle) # Page-content directive: link to sibling in pageset. elsif content_line =~ /POKI_PUT_LINK_FOR_PAGE\(([^)]+)\)HERE/ other_page_link = $1 other_page_name = other_page_link.sub(/#.*/, '') other_page_title = nil for loop_section_config in overall_config.fetch('sections') for loop_page_config in loop_section_config.fetch('pages') loop_page_file_name = loop_page_config['file_name'] # nullable loop_page_external_name = loop_page_config.fetch('external_name') if other_page_name == loop_page_file_name other_page_title = loop_page_external_name end end end if other_page_title.nil? raise "Couldn't find page title for \"#{other_page_name}\"." end href = "#{other_page_title}" content_line.sub!(/POKI_PUT_LINK_FOR_PAGE\([^)]+\)HERE/, href) output_handle.puts(content_line) # Page-content directive: automark

,

, etc. with jump-to tags so they can be found by the # table of contents. Example:

Title

becomes

Title

elsif content_line =~ /(.*)<\/h[1-9]>/ tag = $2.strip # If there's a section with a space in it (e.g. "Naming conventions") then # make the href text the same but underscorify the link itself (e.g. # "Naming_conventions"). output_handle.print("") output_handle.write(content_line) else output_handle.write(content_line) end end end # ---------------------------------------------------------------- def generate_toc(overall_config, section_config, page_config, input_html_path, output_handle) # Read all the

,

,

, etc. from the current file and create internal links tags = [] depths = {} File.readlines(input_html_path).each do |scan_line| if scan_line =~ /^(.*)<\/h[1-9]>/ depth = $1 tag = $2.strip tags << tag depths[tag] = depth.to_i - 1 end end page_title = page_config.fetch('external_name') page_subtitle = page_config['external_subname'] # nullable output_handle.puts('") output_handle.puts("

") end # ---------------------------------------------------------------- def include_escaped(included_file_name, output_handle) write_card(File.readlines(included_file_name), output_handle, true) end # ---------------------------------------------------------------- def run_command(cmd, output_handle, do_escape) cmd_output = `#{cmd} 2>&1` status = $?.to_i if status != 0 raise "\"#{cmd}\" exited with non-zero code #{status}." end write_card(['$ '+cmd] + cmd_output.split(/\n/), output_handle, do_escape) end # ---------------------------------------------------------------- def run_command_tolerating_error(cmd, output_handle) cmd_output = `#{cmd} 2>&1` write_card(['$ '+cmd] + cmd_output.split(/\n/), output_handle, true) end # ---------------------------------------------------------------- def run_content_generator(cmd, output_handle) cmd_output = `#{cmd} 2>&1` status = $?.to_i if status != 0 raise "\"#{cmd}\" exited with non-zero code #{status}." end output_handle.puts(cmd_output) end # ---------------------------------------------------------------- def cardify(content_line, output_handle, do_escape) write_card([content_line], output_handle, do_escape) end # ---------------------------------------------------------------- def write_card(content_lines, output_handle, do_escape) output_handle.puts('

') output_handle.puts('

') output_handle.puts('
')
  content_lines.each do |content_line|
    if do_escape
      output_handle.puts(html_escape_line(content_line))
    else
      output_handle.puts(content_line)
    end
  end
  output_handle.puts('
') output_handle.puts('
') output_handle.puts('

') end # ---------------------------------------------------------------- def html_escape_line(line) line.gsub("&", "&").gsub("<", "<").gsub(">", ">").rstrip end # ================================================================ # Top-down programming style, please. main()