-*- mode: org -*-
#+TITLE:       sisu param
#+DESCRIPTION: documents - structuring, various output representations & search
#+FILETAGS:    :sisu:param:
#+AUTHOR:      Ralph Amissah
#+EMAIL:       [[mailto:ralph.amissah@gmail.com][ralph.amissah@gmail.com]]
#+COPYRIGHT:   Copyright (C) 2015 - 2021 Ralph Amissah
#+LANGUAGE:    en
#+STARTUP:     content hideblocks hidestars noindent entitiespretty
#+OPTIONS:     H:3 num:nil toc:t \n:nil @:t ::t |:t ^:nil _:nil -:t f:t *:t <:t
#+PROPERTY:    header-args  :exports code
#+PROPERTY:    header-args+ :noweb yes
#+PROPERTY:    header-args+ :eval no
#+PROPERTY:    header-args+ :results no
#+PROPERTY:    header-args+ :cache no
#+PROPERTY:    header-args+ :padline no

* dp.rb

#+BEGIN_SRC ruby  :tangle "../lib/sisu/dp.rb"
# <<sisu_document_header>>
module SiSU_Param
  begin
    require 'uri'
    require 'pstore'
  rescue LoadError
    SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
      error('uri or pstore NOT FOUND (LoadError)')
  end
  require_relative 'se'                                 # se.rb
    include SiSU_Env
  require_relative 'dp_make'                            # dp_make.rb
  require_relative 'dp_identify_markup'                 # dp_identify_markup.rb
  @@date=SiSU_Env::InfoDate.new
  @doc={
    initialise: nil,
    markup: '',
    lnks: '',
    stmp: '',
    req: {},
  }
  @@trigger=nil
  @@lv,@@flag={},{}
  @@tex_backslash="\\\\"
  class Parameters
    @@publisher='SiSU scribe'
    @@md=@@fns=@@pth=nil
    def initialize(opt)
      @opt=opt
      @cX||=SiSU_Screen::Ansi.new(@opt.act[:color_state][:set])
      @fns=if @opt.act[:psql][:set] == [:on] #revisit CHECK
        opt.fns
      else opt.fns.gsub(/\.ssm$/,'.ssm.sst')
      end
      SiSU_Param::Instantiate.new.param_instantiate
      @env=SiSU_Env::InfoEnv.new(@fns)
      @pstorefile="#{@env.processing_path.ao}/#{@fns}.pstore"
    end
    def get
      if @opt.f_pth \
      and @opt.f_pth[:pth] != Dir.pwd #BUG check
        # you may need to change Dir.pwd to @opt.f_pth[:pth] where the latter
        # has a path value that is different, however, f_pth is not always set!
        Dir.chdir(@opt.f_pth[:pth])
      end
      if @@fns !=@fns \
      or @@pth !=Dir.pwd               #@opt.f_pth[:pth]
        @@fns,@@pth=@fns,Dir.pwd       #@opt.f_pth[:pth]
        @@md=nil
      end
      if @@md.nil? \
      or @opt.act[:maintenance][:set]==:on #not particularly helpful, as current cycle is through output types, with files changing, only helpful if deal with a file all output types before going to next file
        if File.exist?(@pstorefile)
          param_msg='Parameters from pstore'
          store=PStore.new(@pstorefile)
          store.transaction do
            @md=store['md']
          end
          @md
        else
          param_msg='Parameters extracted'
          fns_array=@env.read_source_file(@opt.fns)
          @md=SiSU_Param::Parameters::Instructions.new(fns_array,@opt).extract
          @md
        end
        if defined? @md.title.main # on removal check problems with -U
          if (@opt.act[:verbose][:set]==:on \
          || @opt.act[:verbose_plus][:set]==:on \
          || @opt.act[:maintenance][:set]==:on)
            SiSU_Screen::Ansi.new(
              @opt.act[:color_state][:set],
              param_msg,
              @md.title.main
            ).txt_grey
          end
        end
        @@md=@md
      else @@md
      end
      begin
        @@md.opt=@opt
        @@md
      rescue
        SiSU_Utils::CodeMarker.new(__LINE__,__FILE__,:fuchsia).
          mark('has an existing option been selected?')
        exit
      end
    end
    class MdDefault
      def rights(author,date)
        @author,@date=author,date
        def assignment(author)
          'copyright not explicitly stated, ' \
          + 'program "assigning" copyright to author: ' \
          + author
        end
        def all
          s=nil
          if @author
            #puts assignment(@author)
            s ||=((@date =~/((?:1[4-9]|2[01])\d{2})/ ) \
            ? ("Copyright (C) #{$1} #{@author}")
            : ('Copyright (C)' + @author))                     #matches years 1400 through 21\d\d
          end
          s
        end
        def copyright_and_license
          s=nil
          if @author
            #puts assignment(@author)
            s ||=((@date =~/((?:1[4-9]|2[01])\d{2})/ ) \
            ? ("Copyright (C) #{$1} #{@author}")
            : ('Copyright (C)' + @author))                     #matches years 1400 through 21\d\d
          end
          s
        end
        def text
          all
        end
        def copyright
          def all
            s=nil
            if @author
              s ||=((@date =~/((?:1[4-9]|2[01])\d{2})/ ) \
              ? ("Copyright (C) #{$1} #{@author}")
              : ('Copyright (C)' + @author))                     #matches years 1400 through 21\d\d
            end
            s
          end
          def text
            all
          end
          self
        end
        self
      end
    end
    class MdMake < SiSU_Param_Make::MdMake
    end
    class Md
      def initialize(str,opt,env)
        @s,@opt,@env=str,opt,env
      end
      def validate_length(s,l,n)
        #s=(s.length <= l) ? s : nil
        s=if s.is_a?(String) \
        and s.length <= l
          s
        elsif s.is_a?(NilClass)
          nil
        elsif s.class !=String
          STDERR.puts "#{n} is #{s.class}: programming error, String expected #{__FILE__}:#{__LINE__}"
          s
        else
          SiSU_Screen::Ansi.new(
            'v',
            "*WARN* #{n} length #{s.length} exceeds set db field length #{l}, metadata dropped",
            @opt.fns
          ).warn unless @opt.act[:quiet][:set]==:on
          nil
        end
      end
      def name_format(name)
        if name
          name=name.strip
          @name_a_h=[]
          authors=name.scan(/[^;]+/)
          authors.each_with_index do |a,i|
            b=((a =~/\s*\|\s*/) ? (a.split(/\|/)) : [a])
            if b[0] =~/"(.+?)"/
              @name_a_h << { the: $1 }
            else
              x=b[0].scan(/[^,]+/)
              if x.length==1
                @name_a_h << { the: x[0].strip }
              elsif x.length==2
                @name_a_h << { the: x[0].strip, others: x[1].strip }
              else #p x.length
              end
            end
            b.delete_at(0)
            b.each do |d|
              k,c=nil
              k,c=/^(\S+)\s+(.*)/.match(d)[1,2] if d
              @name_a_h[i][:hon]=c.strip if k=='hon'
              @name_a_h[i][:affiliation]=c.strip if k=='affiliation'
              @name_a_h[i][:nationality]=c.strip if k=='nationality'
            end
          end
          l=@name_a_h.length
          name_str=''
          @name_a_h.each_with_index do |a,i|
            name_str += if a[:others]
              z=(((l - i) > 1) ? ', ' : '')
              "#{a[:others].strip} #{a[:the].strip}" + z
            else
              z=(((l - i) > 2) ? ', ' : '')
              "#{a[:the].strip}" + z
            end
          end
          { name_a_h: @name_a_h, name_str: name_str }
        else nil
        end
      end
      def build_hash(arr)
        @h={}
        arr.each_with_index do |x,i|
          a,b=nil,nil
          if x =~/^%\s/ #ignore comment
          elsif x =~/:(\S+?):\s+(.+)/
            a,b=/:(\S+?):\s+(.+)\Z/m.match(x)[1,2]
            b=b.gsub(/\s*<br(?: \/)?>\s*/,' \\\\\\ ')
            b=if b =~/\n/m
              (b =~/;\n/m) \
              ? (b.split(/;\s*\n\s*/).join(';'))
              : (b.split(/\s*\n\s*/).join(' '))
            else
              b
            end
          elsif i == 0
            a='main'
            b=x
          else
          end
          @h[a]=b
        end
        @h
      end
      def title
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def main
          s=@h['main']
          l,n=Db[:col_title_part],'title.main'
          validate_length(s,l,n)
        end
        def sub
          s=@h['subtitle']
          l,n=Db[:col_title_part],'title.subtitle'
          validate_length(s,l,n)
        end
        def edition
          s=@h['edition']
          l,n=Db[:col_title_edition],'title.edition'
          validate_length(s,l,n)
        end
        def note
          s=@h['note']
          l,n=Db[:col_info_note],'title.note'
          validate_length(s,l,n)
        end
        def short
          s=@h['short'] \
          ? @h['short']
          : @h['main']
          l,n=Db[:col_title_part],'title.short'
          validate_length(s,l,n)
        end
        def full
          s=@h['subtitle'] \
          ? (@h['main'] + ' - ' + @h['subtitle'])
          : @h['main']
          l,n=Db[:col_title],'title.full'
          validate_length(s,l,n)
        end
        def language
          s=@h['language']
          l,n=Db[:col_language],'title.language'
          validate_length(s,l,n)
        end
        def language_char # look into, this must be set, from 1 directory stub (.fi), 2 filename (~fi), [3 (not used) document header (@title:\n  :language_char: fi)]
          s=@h['language_char']
          l,n=Db[:col_language_char],'title.language_char'
          validate_length(s,l,n)
        end
        self
      end
      def creator #there are sub categories that need to be catered for and sometimes more than one author etc.; implement array.to_s.length validation test later, current test on string approximate as string is not used
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def author
          @h['author']=(@h['author'] \
          ? @h['author']
          : @h['main'])
          names=name_format(@h['author'])
          s=names[:name_str]
          l,n=Db[:col_name],'creator.author'
          validate_length(s,l,n)
        end
        def author_detail
          s=@h['author'] \
          ? @h['author']
          : @h['main']
          names=name_format(s)
          names[:name_a_h]
        end
        def email #revisit
          s=@h['email']
        end
        def editor
          names=@h['editor'] \
          ? name_format(@h['editor'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.editor'
            validate_length(s,l,n)
          else nil
          end
        end
        def editor_detail
          names=@h['editor'] \
          ? name_format(@h['editor'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def contributor
          names=@h['contributor'] \
          ? name_format(@h['contributor'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.author'
            validate_length(s,l,n)
          else nil
          end
        end
        def contributor_detail
          names=@h['contributor'] \
          ? name_format(@h['contributor'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def illustrator
          names=@h['illustrator'] \
          ? name_format(@h['illustrator'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.illustrator'
            validate_length(s,l,n)
          else nil
          end
        end
        def illustrator_detail
          names=@h['illustrator'] \
          ? name_format(@h['illustrator'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def photographer
          names=@h['photographer'] \
          ? name_format(@h['photographer'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.photographer'
            validate_length(s,l,n)
          else nil
          end
        end
        def photographer_detail
          names=@h['photographer'] \
          ? name_format(@h['photographer'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def translator
          names=@h['translator'] \
          ? name_format(@h['translator'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.translator'
            validate_length(s,l,n)
          else nil
          end
        end
        def translator_detail
          names=@h['translator'] \
          ? name_format(@h['translator'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def audio
          names=@h['audio'] \
          ? name_format(@h['audio'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.audio'
            validate_length(s,l,n)
          else nil
          end
        end
        def audio_detail
          names=@h['audio'] \
          ? name_format(@h['audio'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def digitized_by
          names=@h['digitized_by'] \
          ? name_format(@h['digitized_by'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.digitized_by'
            validate_length(s,l,n)
          else nil
          end
        end
        def digitized_by_detail
          names=@h['digitized_by'] \
          ? name_format(@h['digitized_by'])
          : nil
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        def prepared_by
          names=@h['prepared_by'] \
          ? name_format(@h['prepared_by'])
          : nil
          s=(names.is_a?(Hash)) \
          ? names[:name_str]
          : nil
          s=if s
            l,n=Db[:col_name],'creator.prepared_by'
            validate_length(s,l,n)
          else nil
          end
        end
        def prepared_by_detail
          names=@h['prepared_by'] \
          ? name_format(@h['prepared_by'])
          : nil
          names=name_format(@h['prepared_by'])
          (names.is_a?(Hash)) \
          ? names[:name_a_h]
          : nil
        end
        self
      end
      def rights
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def copyright
          def text #you may wish to expand to take from all
            s=if @h['copyright'] then @h['copyright']
            elsif @h['text']     then @h['text']
            elsif @h['main']     then @h['main']
            else
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                'WARNING Document Copyright missing; provide @rights: :copyright:'
              ).warn if (@opt.act[:verbose][:set]==:on \
                || @opt.act[:verbose_plus][:set]==:on \
                || @opt.act[:maintenance][:set]==:on)
              ''
            end
            l,n=Db[:col_info_note],'rights.copyright.text'
            validate_length(s,l,n)
          end
          def translation
            s=@h['translation'] \
            ? @h['translation']
            : nil
            l,n=Db[:col_info_note],'rights.copyright.translation'
            validate_length(s,l,n)
          end
          def illustrations
            s=@h['illustrations'] \
            ? @h['illustrations']
            : nil
            l,n=Db[:col_info_note],'rights.copyright.illustrations'
            validate_length(s,l,n)
          end
          def photographs
            s=@h['photographs'] \
            ? @h['photographs']
            : nil
            l,n=Db[:col_info_note],'rights.copyright.photographs'
            validate_length(s,l,n)
          end
          def digitization
            s=@h['digitization'] \
            ? @h['digitization']
            : nil
            l,n=Db[:col_info_note],'rights.copyright.digitization'
            validate_length(s,l,n)
          end
          def audio
            s=@h['audio'] \
            ? @h['audio']
            : nil
            l,n=Db[:col_info_note],'rights.copyright.audio'
            validate_length(s,l,n)
          end
          self
        end
        def license
          s=@h['license'] \
          ? @h['license']
          : nil
          l,n=Db[:col_info_note],'rights.license'
          validate_length(s,l,n)
        end
        def sep(str)
          ' \\\\ '
        end
        def copyright_and_license
          s=if @h['copyright_and_license'] then @h['copyright_and_license']
          else
            s=''
            if defined? copyright.text \
            and copyright.text \
            and not copyright.text.empty?
              v=sep(copyright.text)
              s +=copyright.text + v
            end
            if defined? copyright.license \
            and copyright.license \
            and not copyright.license.empty?
              s +=copyright.license
            end
            if s.empty?
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                'WARNING Document Rights information missing; provide @rights: :copyright:'
              ).warn if (@opt.act[:verbose][:set]==:on \
                || @opt.act[:verbose_plus][:set]==:on \
                || @opt.act[:maintenance][:set]==:on)
            else
              l,n=Db[:col_info_note],'rights.all'
              validate_length(s,l,n)
            end
            s=s.gsub(/ [\\]+\s+$/,'')
          end
          s
        end
        def all
          s=if @h['all'] then @h['all']
          else
            s=''
            if defined? copyright.text \
            and copyright.text \
            and not copyright.text.empty?
              v=sep(copyright.text)
              s +='Copyright: ' + copyright.text + v
            end
            if defined? copyright.translation \
            and copyright.translation \
            and not copyright.translation.empty?
              v=sep(copyright.translation)
              s +='translation: ' + copyright.translation + v
            end
            if defined? copyright.illustrations \
            and copyright.illustrations \
            and not copyright.illustrations.empty?
              v=sep(copyright.illustrations)
              s +='illustrations: ' + copyright.illustrations + v
            end
            if defined? copyright.photographs \
            and copyright.photographs \
            and not copyright.photographs.empty?
              v=sep(copyright.photographs)
              s +='photographs: ' + copyright.photographs + v
            end
            if defined? copyright.digitization \
            and copyright.digitization \
            and not copyright.digitization.empty?
              v=sep(copyright.digitization)
              s +='digitization: ' + copyright.digitization + v
            end
            if defined? copyright.audio \
            and copyright.audio \
            and not copyright.audio.empty?
              v=sep(copyright.audio)
              s +='audio: ' + copyright.audio + v
            end
            if defined? copyright.license \
            and copyright.license \
            and not copyright.license.empty?
              s +='License: ' + copyright.license
            end
            if s.empty?
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                'WARNING Document Rights information missing; provide @rights: :copyright:'
              ).warn if (@opt.act[:verbose][:set]==:on \
                || @opt.act[:verbose_plus][:set]==:on \
                || @opt.act[:maintenance][:set]==:on)
            else
              l,n=Db[:col_info_note],'rights.all'
              validate_length(s,l,n)
            end
            s=s.gsub(/ [\\]+\s+$/,'')
          end
          s
        end
        self
      end
      def identifier
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def oclc
          s=@h['oclc']
          l,n=Db[:col_library],'identifier.oclc'
          validate_length(s,l,n)
        end
        def isbn
          s=@h['isbn']
          l,n=Db[:col_small],'identifier.isbn'
          validate_length(s,l,n)
        end
        def pg
          s=@h['pg']
          l,n=Db[:col_small],'identifier.pg'
          validate_length(s,l,n)
        end
        self
      end
      def classify
        a=@s.split(/(\n%\s.+?$|[ ]*)(?:\n[ ]*(?=:)|\Z)/m)
        @h=build_hash(a)
        def topic_register
          s=@h['topic_register']
          l,n=Db[:col_info_note],'classify.topic_register'
          validate_length(s,l,n)
        end
        def subject
          s=@h['subject']
          l,n=Db[:col_txt_long],'classify.subject'
          validate_length(s,l,n)
        end
        def keywords
          s=@h['keywords']
          l,n=Db[:col_txt_long],'classify.keywords'
          validate_length(s,l,n)
        end
        def loc
          s=@h['loc']
          l,n=Db[:col_library],'classify.loc'
          validate_length(s,l,n)
        end
        def dewey
          s=@h['dewey']
          l,n=Db[:col_library],'classify.dewey'
          validate_length(s,l,n)
        end
        self
      end
      def publisher
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        s=@h['main']
        l,n=Db[:col_name],'publisher'
        validate_length(s,l,n)
      end
      def date
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def added_to_site
          s=@h['added_to_site']
          l,n=Db[:col_date_text],'date.added_to_site'
          validate_length(s,l,n)
        end
        def available
          s=@h['available']
          l,n=Db[:col_date_text],'date.available'
          validate_length(s,l,n)
        end
        def created
          s=@h['created']
          l,n=Db[:col_date_text],'date.created'
          validate_length(s,l,n)
        end
        def issued
          s=@h['issued']
          l,n=Db[:col_date_text],'date.issued'
          validate_length(s,l,n)
        end
        def modified
          s=@h['modified']
          l,n=Db[:col_date_text],'date.modified'
          validate_length(s,l,n)
        end
        def published
          s=@h['published']=(@h['published'] ? @h['published'] : @h['main'])
          l,n=Db[:col_date_text],'date.published'
          validate_length(s,l,n)
        end
        def valid
          s=@h['valid']
          l,n=Db[:col_date_text],'date.valid'
          validate_length(s,l,n)
        end
        self
      end
      #def language                     # as things stand this should really be populated from title.language and original.language, resolve
      #  a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
      #  @h=build_hash(a)
      #  def document
      #    s=@h['document']=(@h['document'] ? @h['document'] : @h['main'])
      #    l,n=Db[:col_language],'language.document'
      #    validate_length(s,l,n)
      #  end
      #  def document_char
      #    s=@h['document_char']=(@h['document_char'] ? @h['document_char'] : nil)
      #    l,n=Db[:col_language_char],'language.document_char'
      #    validate_length(s,l,n)
      #  end
      #  def original
      #    s=@h['original']
      #    l,n=Db[:col_language],'language.original'
      #    validate_length(s,l,n)
      #  end
      #  def original_char
      #    s=@h['original_char']
      #    l,n=Db[:col_language_char],'language.original_char'
      #    validate_length(s,l,n)
      #  end
      #  self
      #end
      def current_publisher
        @s
      end
      def original
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def publisher
          s=@h['publisher']
          l,n=Db[:col_name],'original.publisher'
          validate_length(s,l,n)
        end
        def language
          s=@h['language']
          l,n=Db[:col_language],'original.language'
          validate_length(s,l,n)
        end
        def language_char
          s=@h['language_char']
          l,n=Db[:col_language_char],'original.language_char'
          validate_length(s,l,n)
        end
        def source
          s=@h['source']
          l,n=Db[:col_name],'original.source'
          validate_length(s,l,n)
        end
        def institution
          s=@h['institution']
          l,n=Db[:col_name],'original.institution'
          validate_length(s,l,n)
        end
        def nationality
          s=@h['nationality']
          l,n=Db[:col_language],'original.nationality'
          validate_length(s,l,n)
        end
        self
      end
      def notes
        a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
        @h=build_hash(a)
        def description
          s=@h['description']
          l,n=Db[:col_info_note],'notes.description'
          validate_length(s,l,n)
        end
        def abstract
          s=@h['abstract']
          l,n=Db[:col_info_note],'notes.abstract'
          validate_length(s,l,n)
        end
        def comment
          s=@h['comment']
          l,n=Db[:col_info_note],'notes.comment'
          validate_length(s,l,n)
        end
        def coverage
          s=@h['coverage']
          l,n=Db[:col_info_note],'notes.coverage'
          validate_length(s,l,n)
        end
        def relation
          s=@h['relation']
          l,n=Db[:col_info_note],'notes.relation'
          validate_length(s,l,n)
        end
        def source
          s=@h['source']
          l,n=Db[:col_txt_long],'notes.source'
          validate_length(s,l,n)
        end
        def history
          s=@h['history']
          l,n=Db[:col_txt_long],'notes.history'
          validate_length(s,l,n)
        end
        def type
          s=@h['type']
          l,n=Db[:col_txt_long],'notes.relation'
          validate_length(s,l,n)
        end
        def format
          s=@h['format']
          l,n=Db[:col_txt_short],'notes.format'
          validate_length(s,l,n)
        end
        def prefix
          @h['prefix']
        end
        self
      end
    end
    class Instructions
      @doc={ lv: [] }
      @doc[:fns],@doc[:fnb],@doc[:scr_suffix]='','',''
      @@publisher='SiSU scribe'
      attr_accessor :make,:env,:path,:file,:fn,:fns,:fno,:fnb,:fnn,:fnt,:fnl,:flv,:fnz,:fnstex,:ocn,:sfx_src,:pdf,:file_type,:dir_out,:dir_tex,:dir_lout,:txt_path,:sisu,:project_details,:ruby_version,:title,:subtitle,:full_title,:html_title,:subtitle_tex,:creator,:classify,:author_home,:author,:email,:author_title,:author_nationality,:authors,:authorship,:translator,:illustrator,:prepared_by,:digitized_by,:subject,:description,:publisher,:current_publisher,:contributor,:date,:date_created,:date_issued,:date_available,:date_valid,:date_modified,:date_translated,:date_added_to_site,:date_scheme,:date_created_scheme,:date_issued_scheme,:date_available_scheme,:date_valid_scheme,:date_modified_scheme,:type,:format,:identifier,:source,:language,:language_original,:relation,:coverage,:rights,:keywords,:comments,:abstract,:cls_loc,:cls_dewey,:cls_pg,:cls_isbn,:papersize,:papersize_array,:toc,:lv0,:lv1,:lv2,:lv3,:lv4,:lv5,:lv6,:lvs,:pagenew,:pagebreak,:pageline,:num_top,:bold_match_list,:italics_match_list,:substitution_match_list,:emphasis_set_to,:toc_lev_limit,:flag_biblio,:flag_auto_biblio,:flag_endnotes,:flag_auto_endnotes,:flag_glossary,:flag_separate_endnotes,:flag_separate_endnotes_make,:markup,:markup_instruction,:flag_tables,:vocabulary,:doc_css,:yaml,:lnk,:links,:prefix_a,:prefix_b,:suffix,:information,:contact,:icon,:image,:ad_url,:ad_png,:ad_alt,:ad_began,:flag_promo,:promo,:ad_home,:stmp,:stmpd,:sc_filename,:sc_number,:sc_date,:sc_time,:sc_info,:yamladdr,:locale,:wc_lines,:wc_words,:wc_bytes,:file_encoding,:filesize,:user,:home,:hostname,:pwd,:firstseg,:programs,:author_copymark,:i18n,:lang,:lang_code_insert,:en,:notes,:dgst,:generated,:tags,:tag_array,:concord_make,:seg_names,:seg_autoname_safe,:set_header_title,:set_heading_top,:set_heading_seg,:heading_seg_first,:heading_seg_first_flag,:base_program,:ec,:opt,:sem_tag,:book_idx,:topic_register,:topic_register_array,:original,:writing_focus,:audio,:daisy,:home_button_image,:home_button_links,:footer_links,:cover_image,:man_section
      def initialize(fns_array,opt)
        @env=@path,@file=@fn=@fns=@fno=@fnb=@fnn=@fnt=@fnl=@flv=@fnz=@fnstex=@ocn=@sfx_src=@pdf=@file_type=@dir_out=@dir_tex=@dir_lout=@txt_path=@make=@flag_biblio=@flag_auto_biblio=@flag_endnotes=@flag_auto_endnotes=@flag_glossary=@flag_separate_endnotes=@flag_separate_endnotes_make=@sisu=@project_details=@ruby_version=@title=@subtitle=@full_title=@html_title=@subtitle_tex=@creator=@classify=@author_home=@author=@email=@author_title=@author_nationality=@translator=@illustrator=@prepared_by=@digitized_by=@subject=@description=@publisher=@current_publisher=@contributor=@date=@date_created=@date_issued=@date_available=@date_valid=@date_modified=@date_translated=@date_added_to_site=@date_scheme=@date_created_scheme=@date_issued_scheme=@date_available_scheme=@date_valid_scheme=@date_modified_scheme=@type=@format=@identifier=@source=@language=@language_original=@relation=@coverage=@rights=@keywords=@comments=@abstract=@cls_loc=@cls_dewey=@cls_pg=@cls_isbn=@papersize=@toc=@lv0=@lv1=@lv2=@lv3=@lv4=@lv5=@lv6=@pagenew=@pagebreak=@pageline=@num_top=@bold_match_list=@italics_match_list=@substitution_match_list=@emphasis_set_to=@toc_lev_limit=@flag_tables=@vocabulary=@doc_css=@yaml=@lnk=@links=@prefix_a=@prefix_b=@suffix=@information=@contact=@icon=@ad_url=@ad_png=@ad_alt=@ad_began=@promo=@ad_home=@stmp=@stmpd=@sc_filename=@sc_number=@sc_date=@sc_time=@sc_info=@yamladdr=@locale=@wc_lines=@wc_words=@wc_bytes=@file_encoding=@filesize=@firstseg=@programs=@author_copymark=@i18n=@lang=@lang_code_insert=@en=@notes=@dgst=@generated=@heading_seg_first=@base_program=@topic_register=@original=@writing_focus=@audio=@home_button_image=@home_button_links=@cover_image=@man_section=nil
        @data,      @path,  @fns,   @fno,   @opt=
          fns_array,opt.pth,opt.fns,opt.fno,opt #@data used as data
        @flag_tables,@set_header_title,@set_heading_top,@set_heading_seg,@heading_seg_first_flag,@flag_promo,@book_idx=
          false,     false,            false,           false,           false,                  false,      false
        @seg_autoname_safe=true
        @daisy,@sem_tag=false,false
        @authorship,@markup_instruction,@image='','','','' #check which other values should be set to empty rather than nil
        @markup=@markup_instruction #use @markup_instruction
        @doc,@fn,@make_italic,@tag_hash,@ec={},{},{},{},{},{}
        @flv,@lang,@seg_names,@tags,@tag_array,@tag_a,@ec[:image],@ec[:audio],@ec[:multimedia]=Array.new(9){[]}
        @authors,@topic_register_array,@papersize_array=[],[],[]
        @lvs=[nil,0,0,0,0,0,0]
        @emphasis_set_to='bold'
        @lang_code_insert=SiSU_Env::FilenameLanguageCodeInsert.new(@opt).language_code_insert
        @footer_links= { left: { say: '', url: '' }, center: { say: '', url: '' } }
        @rgx_image=/(?:^|[^_\\])\{(?:\s*|\~\^\s+)(\S+?\.(?:png|jpg|gif)\b)/m
        @rgx_audio=/\{\s*(\S+?\.(?:mp3|ogg))/
        @rgx_mm=/\{\s*(\S+?\.(?:ogg|mpeg))/ #expand and distinguish ogg
        Dir.chdir(@opt.f_pth[:pth])
        begin
        rescue
          SiSU_Errors::Rescued.new($!,$@,@opt.selections.str,@fns).location do
            __LINE__.to_s + ':' + __FILE__
          end
        ensure
        end
        @header_make_links_append=:no
        common_makes=(defined? @opt.make_instructions_pod) \
        && @opt.make_instructions_pod !=nil \
        && @opt.make_instructions_pod[:makeset]==true \
        ? @opt.make_instructions_pod
        : @opt.make_instructions
        if common_makes[:makeset]
          @pagenew=common_makes[:pagenew]
          @pagebreak=common_makes[:pagebreak]
          @pageline=common_makes[:pageline]
          @toc=common_makes[:toc]
          @lv0=common_makes[:lv0]
          @lv1=common_makes[:lv1]
          @lv2=common_makes[:lv2]
          @lv3=common_makes[:lv3]
          @lv4=common_makes[:lv4]
          @lv5=common_makes[:lv5]
          @lv6=common_makes[:lv6]
          @num_top=common_makes[:num_top]
          @i18n=common_makes[:i18n]
          @man_section=common_makes[:man_section]
          @emphasis_set_to=common_makes[:emphasis_set_to]
          @bold_match_list=common_makes[:bold_match_list]
          @italics_match_list=common_makes[:italics_match_list]
          @substitution_match_list=common_makes[:substitution_match_list]
          @footer_links=common_makes[:footer_links]
          @home_button_links=common_makes[:home_button_links]
          @home_button_image=common_makes[:home_button_image]
          @cover_image=common_makes[:cover_image]
          @lnk=@links=common_makes[:links]
          @header_make_links_append=common_makes[:links_append]
        end
      end
      #protected
      def extract
        begin
          @user,@home,@hostname,@pwd=ENV['USER'],ENV['HOME'],ENV['HOSTNAME'],ENV['PWD']
          @programs,@wc,@language,@language_original={},{},{},{}
          @en={ sum: 0, mark: 0, note: 0, mismatch: 0 }
          @prog=SiSU_Env::InfoSettings.new
          @sys=SiSU_Env::SystemCall.new
          @env=SiSU_Env::InfoEnv.new(@fns) #watch
          if (@opt.act[:verbose_plus][:set]==:on \
          || @opt.act[:maintenance][:set]==:on)
            puts 'system locale: ' + @sys.locale
          end
          if @prog.wc \
          and @sys.wc
            wc=%x{wc #{fns}}
            wca=wc.scan(/\d+/)
            @wc_lines,@wc_words,@wc_bytes=wca[0].to_i,wca[1].to_i,wca[2].to_i
          else
            fns_a=@data.dup
            tmp=fns_a.join
            fns_a=tmp.scan(/\S+/)
            @wc_words=fns_a.length
            fns_a=tmp=nil
          end
          @concord_make=(@wc_words > @env.concord_max) ? false : true
          @locale=@sys.locale
          @file_encoding=@sys.file_encoding(fns,@opt.act)
          # programs set here for things that affect output appearance only
          @programs[:pdf]=SiSU_Env::SystemCall.new.program_found?('pdflatex')
          if @opt.act[:psql][:set] == [:ok]
            m=/((.+?)(?:\~\w\w(?:_\w\w)?)?)\.((?:-|ssm\.)?sst|ssm|ssi)$/ #watch added match for sss
            @fnn,@fnb,@fnt=@fns[m,1],@fns[m,2],@fns[m,3]
            @flv=@env.document_language_versions_found[:f]
          else
            m=/((.+?)(?:\~\w\w(?:_\w\w)?)?)\.((?:-|ssm\.)?sst|ssm)$/ #watch added match for sss
            @fnn,@fnb,@fnt=@fns[m,1],@fns[m,2],@fns[m,3]
            @flv=@env.document_language_versions_found[:f]
            @fnz=(@fns =~/\.(?:ssm\.sst|ssm)$/) ? (@fnn + '.ssm.txz') : (@fnn + '.sst.txz')
          end
          @papersize=@env.papersize #'A4' #default size #get first from SiSU_Env:: # @env is probably no longer most appropriate name! as default info is more general
          @sfx_src=@fns[m,2]
          if @fns =~ /(?:-|ssm\.)?sst$/ \
          and not @opt.act[:psql][:set] == [:ok]
            @env_out_root=@env.path.output
            @dir_out="#{@env.path.output}/#{@fnb}"
            @dir_tex=@env.processing_path.tex
            @dir_lout=@env.processing_path.lout
            @@publisher='SiSU http://www.jus.uio.no/sisu'
          end
          @txt_path=@txt_path ||= @env.path.output
          @stmp=%{#{@fns}}[/^(.+?)\..*/m,1]
          @fnstex=@fns.gsub(/_/,'\_\-').gsub(/\./,'.\-')
          @flag_endnotes,@flag_auto_endnotes,@flag_separate_endnotes=false,false,false
          @flag_separate_endnotes_make=true
          @flag_glossary=false
          @flag_biblio,@flag_auto_biblio=false,false
          ver=SiSU_Env::InfoVersion.instance
          @project_details=ver.get_version
          @ruby_version=ver.rbversion
          @generated=Time.now
          fns_array=@data.dup
          skip unless fns_array                                                    # consider
          @code_flag=false
          flag_code_curly=:not_code_curly
          flag_code_tics=:not_code_tics
          fns_array.each do |para|                                               #% scan document
            if para !~/^%+\s/ \
            and para =~/<![abcdeghijklmnopqrstuvwxyz]/i # <!f not included
              raise "Old markup style in file #{@fns}, current version #{@project_details.project} #{@project_details.version} #{@project_details.date_stamp} #{@project_details.date}:\n\t\t#{para}\n\n"
            end
            if para =~/^code\{/
              flag_code_curly=:code_curly
            elsif para =~/^\}code/
              flag_code_curly=:not_code_curly
            elsif para =~/^``` code/
              flag_code_tics=:code_tics
            elsif flag_code_tics ==:code_tics \
            and para =~/^```/
              flag_code_tics=:not_code_tics
            end
            @code_flag=if flag_code_curly==:code_curly \
            or flag_code_tics==:code_tics
              true
            else false
            end
            regx_header=/^@\S+?:[+-]?\s/
            if para =~regx_header \
            and not @code_flag #or para=~/^(?:1|:?A)~/
              case para
              when /^@title:(.+)/m                                               #% * header metadata - title
                @title=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).title
              when /^@creator:(.+)/m                                             #% * header metadata - creator
                @creator=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).creator
                @authorship=@author=@creator.author
                @authors=@creator.author_detail
              when /^@date:(.+)/m                                                #% * header metadata - date
                @date=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).date
              when /^@publisher:\s+(.+)/m                                        #% * header metadata - publisher
                @publisher=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).current_publisher
                @current_publisher=@publisher
              when /^@rights:(.+)/m                                              #% * header metadata - rights
                @rights=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).rights
              when /^@classify:(.+)/m                                            #% * header metadata - classify
                @classify=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).classify
              when /^@identifier:(.+)/m                                          #% * header metadata - identifier
                @identifier=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).identifier
              when /^@original:(.+)/m                                            #% * header metadata - original (document)
                @original=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).original
                @source=@original.source
              when /^@notes?:\s(.+)\Z/m                                          #% * header metadata - notes
                @notes=SiSU_Param::Parameters::Md.new($1.strip,@opt,@env).notes
              when /^@links:\s+(.+?)\Z/m                                         #% * header metadata - links
                links=SiSU_Param::Parameters::MdMake.new($1.strip,@opt,@env).make_links.links
                @lnk=@links=if @header_make_links_append == :yes
                  (links) \
                    ? (links + @links)
                    : @links
                else
                  (links) \
                    ? (links)
                    : @links
                end
              when /^@make:(.+)/m                                                #% * header processing - make
                @make=SiSU_Param::Parameters::MdMake.new($1.strip,@opt,@env).make
                makes=SiSU_Param_Make::MakeHead.new(@make).make_instruct
                @pagenew=(makes[:pagenew]) \
                  ? (makes[:pagenew]) \
                  : @pagenew
                @pagebreak=(makes[:pagebreak]) \
                  ? (makes[:pagebreak]) \
                  : @pagebreak
                @pageline=(makes[:pageline]) \
                  ? (makes[:pageline]) \
                  : @pageline
                @toc=(makes[:toc]) ? (makes[:toc]) : @toc
                @lv0=(makes[:lv0]) ? (makes[:lv0]) : @lv0
                @lv1=(makes[:lv1]) ? (makes[:lv1]) : @lv1
                @lv2=(makes[:lv2]) ? (makes[:lv2]) : @lv2
                @lv3=(makes[:lv3]) ? (makes[:lv3]) : @lv3
                @lv4=(makes[:lv4]) ? (makes[:lv4]) : @lv4
                @lv5=(makes[:lv5]) ? (makes[:lv5]) : @lv5
                @lv6=(makes[:lv6]) ? (makes[:lv6]) : @lv6
                @num_top=
                  (makes[:num_top]) \
                  ? (makes[:num_top]) \
                  : @num_top
                @substitution_match_list=
                  (makes[:substitution_match_list]) \
                  ? (makes[:substitution_match_list]) \
                  : @substitution_match_list
                @bold_match_list=
                  (makes[:bold_match_list]) \
                  ? (makes[:bold_match_list]) \
                  : @bold_match_list
                @italics_match_list=
                  (makes[:italics_match_list]) \
                  ? (makes[:italics_match_list]) \
                  : @italics_match_list
                @emphasis_set_to=
                  (makes[:emphasis_set_to]) \
                  ? (makes[:emphasis_set_to]) \
                  : @emphasis_set_to
                @i18n=
                  (makes[:i18n]) \
                  ? (makes[:i18n]) \
                  : @i18n
                @man_section=
                  (makes[:man_section]) \
                  ? (makes[:man_section]) \
                  : @man_section
                @footer_links=
                  (makes[:footer_links]) \
                  ? (makes[:footer_links]) \
                  : @footer_links
                @home_button_links=
                  (makes[:home_button_links]) \
                  ? (makes[:home_button_links]) \
                  : @home_button_links
                @home_button_image=
                  (makes[:home_button_image]) \
                  ? (makes[:home_button_image]) \
                  : @home_button_image
                @cover_image=
                  (makes[:cover_image]) \
                  ? (makes[:cover_image]) \
                  : @cover_image
              end
              @lv0 ||=/^0~/
              @lv1 ||=/^1~/
              @lv2 ||=/^2~/
              @lv3 ||=/^3~/
              @lv4 ||=/^4~/
              @lv5 ||=/^5~/
              @lv6 ||=/^6~/
            else                                                                 #% *
              l_0=l_1=l_2=l_3=l_4=l_5=''
              if defined? @make.headings[0]
                l_0=if defined? @make.headings[0][0] \
                and @make.headings[0][0] =~/\S+/
                  "|^#{@make.headings[0][0]}"
                end
                l_1=if defined? @make.headings[0][1] \
                and @make.headings[0][1] =~/\S+/
                  "|^#{@make.headings[0][1]}"
                end
                l_2=if defined? @make.headings[0][2] \
                and @make.headings[0][2] =~/\S+/
                  "|^#{@make.headings[0][2]}"
                end
                l_3=if defined? @make.headings[0][3] \
                and @make.headings[0][3] =~/\S+/
                  "|^#{@make.headings[0][3]}"
                end
                l_4=if defined? @make.headings[0][4] \
                and @make.headings[0][4] =~/\S+/
                  "|^#{@make.headings[0][4]}"
                end
                l_5=if defined? @make.headings[0][5] \
                and @make.headings[0][5] =~/\S+/
                  "|^#{@make.headings[0][5]}"
                end
              end
              case para
              #when /^:?A~/
              when /^:?B~#{l_0}/
                @lvs[1]=1
              when /^:?C~#{l_1}/
                @lvs[2]=1
              when /^:?D~#{l_2}/
                @lvs[3]=1
              when /^1~#{l_3}/
                @lvs[4]=1
              when /^2~#{l_4}/
                @lvs[5]=1
              when /^3~#{l_5}/
                @lvs[6]=1
              end
              if para =~ /^:?A~/                                                  #% processing
                if not defined? @title.full.nil?
                  tf=para[/^:A~\S*(.+)$/m,1]
                  tf="@title: #{tf}"
                  @title=SiSU_Param::Parameters::Md.new(tf.strip,@opt,@env).title
                end
                creator=(@creator.is_a?(SiSU_Param::Parameters::Md) \
                && defined? @creator.author \
                && @creator.author.is_a?(String)) \
                ? " #{@creator.author}"
                : ''
                title=@title.full.gsub(/\s*(?:<p>|<p \/>|<br>|<br \/>)\s*/,' ').
                  gsub(/~\{.+?\}~/,'')
                SiSU_Screen::Ansi.new(
                  @opt.act[:color_state][:set],
                  'Document Parameters',
                  %{#{title}#{creator}}
                ).txt_grey if @opt.act[:verbose][:set]==:on
              end
              unless @code_flag
                if para =~/^1~!biblio(?:graphy)?/
                  @flag_auto_biblio,@flag_biblio=false,true
                  #@flag_biblio=true
                elsif @flag_biblio ==true \
                and @flag_auto_biblio ==false \
                and para =~/^(?:au|author):/m
                  @flag_auto_biblio =true
                end
                if para =~/^1~!glossary/
                  @flag_glossary=true
                end
              end
              if not @book_idx \
              and para =~/^=\{(.+?)\}[\s`]*\Z/m
                @book_idx=true
              end
              unless @code_flag
                case para
                when /~\{\s+.+?\}~/m                                             #% processing
                  en=para.scan(/~\{.+?\}~/m)
                  en.each { |e| @en[:sum] +=1 }
                when /~\^(?:\s|$)/m                                              #% processing
                  mk=para.scan(/~\^(?:\s|$)/)
                  mk.each { |e| @en[:mark] +=1 }
                when /^\^~\s+\S/ then @en[:note] +=1                             #% processing
                end
              end
              if para =~/~\{|\^~ |~\^|\{.+?\[[1-6]\]\}\S+?\.ss[tm]/m
                @flag_auto_endnotes,@flag_endnotes=true,true
              end
              if para =~/^(?:table\{|\{table)/i
                @flag_tables=true
              end
            end
            if para =~/^:?A~/
              @set_heading_top=true
            end
            if para =~/^1~/
              m=nil
              if para =~/^1~(\S+)\s+(.+)$/
                m,t=$1,$2
              elsif para =~/^1~\s+(.+)$/
                t=$1
              end
              unless @heading_seg_first_flag                                     # extract first segment name
                @heading_seg_first=t
                @heading_seg_first_flag=true
              end
              if m                                                               # list all segment names
                @seg_names << m
                @set_heading_seg=true
                if m=~/^\d{1,3}/ \
                and m !~/^0/
                  @seg_autoname_safe=false
                end
              end
            end
            para=para.gsub(/<:=(\S+?)>/,'{ c_\1.png 14x14 }image')               # embedded symbol (image)
            if para !~/^%+\s/ \
            and para =~@rgx_image
              @ec[:image] << para.scan(@rgx_image).uniq
            end
            @ec[:audio] << para.scan(@rgx_audio).uniq if para =~@rgx_audio #embedded content
            @ec[:multimedia] << para.scan(@rgx_mm).uniq if para =~@rgx_mm #embedded content
            unless @sem_tag
              @sem_tag=true if para=~/[:;]\{.+?\}[:;][a-z+]/ #refix later
            end
          end                                                                    #% here endeth the document loop
          unless @make
            if (@opt.act[:verbose_plus][:set]==:on \
            || @opt.act[:maintenance][:set]==:on)
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                '@make:',
                'header absent'
              ).warn
            end
            @make=SiSU_Param::Parameters::MdMake.new('@make: ',@opt,@env).make
          end
          if @cover_image \
          and @cover_image.is_a?(Hash) \
          and (@cover_image[:cover] =~@rgx_image \
            or @cover_image[:cover] =~/\S+?.(?:jpg|png|gif)/)
            @ec[:image] << @cover_image[:cover]
          end
          if @home_button_image \
          and @home_button_image.is_a?(Hash) \
          and (@home_button_image[:home] =~@rgx_image \
            or @home_button_image[:home] =~/\S+?\.(?:jpg|png|gif)/)
            @ec[:image] << @home_button_image
          end
          if @ec[:image].length > 0
            @ec[:image]=@ec[:image].flatten.uniq
            @ec[:image].delete_if {|x| x =~/https?:\/\// }
            @ec[:image]=@ec[:image].sort
          end
          @ec[:audio]=@ec[:audio].uniq.flatten.sort
          @ec[:multimedia]=@ec[:multimedia].uniq.flatten.sort
          unless @rights
            if defined? @creator.author \
            and @creator.author.is_a?(String) \
            and defined? @date.published \
            and @date.published.is_a?(String)
              @rights=SiSU_Param::Parameters::MdDefault.new.rights(@creator.author,@date.published)
            elsif defined? @creator.author \
            and @creator.author.is_a?(String)
              @rights=SiSU_Param::Parameters::MdDefault.new.rights("[#{@creator.author}]",'')
            end
          end
          if defined? @classify.topic_register \
          and @classify.topic_register.is_a?(String) \
          and @classify.topic_register.length >3
             topic_register=@classify.topic_register
             u=topic_register.scan(/[^;]+/m).sort
             v=[]
             u.each do |l|
               v << l.scan(/[^:]+/m)
             end
             v.each do |s|
               s[-1]=s[-1].scan(/[^|]+/m) if s[-1] =~/[|]/m
               @topic_register_array << s
             end
             @topic_register_array
          end
          if @i18n
            @i18n=@i18n.uniq
            @i18n << 'en' unless @i18n.find_index("en")
          else
            @i18n=[ 'en' ]
          end
          translated=[]
          translate_list=[@pagenew,@pagebreak,@pageline,@num_top,@toc_lev_limit]
          translate_list.each do |t|
            translate=t.to_s if t
            translated << if translate
              translate.gsub!(/3/,'6')
              translate.gsub!(/2/,'5')
              translate.gsub!(/1/,'4')
              translate.gsub!(/:?C/,'3')
              translate.gsub!(/:?B/,'2')
              translate.gsub!(/:?A/,'1')
              # looks like an ok substituion for the above but is not, causes problems, check why
              #translate=translate.gsub(/3/,'6').
              #  gsub(/2/,'5').
              #  gsub(/1/,'4').
              #  gsub(/:?C/,'3').
              #  gsub(/:?B/,'2').
              #  gsub(/:?A/,'1')
              translate=(translate =~/^\d+$/) \
              ? translate.to_i
              : translate
            else nil
            end
          end
          @pagenew,@pagebreak,@pageline,@num_top,@toc_lev_limit=translated
          @markup=@markup.gsub(/page_new\s*=\s*([\dA-C])/,"page_new=#{@pagenew}").
            gsub(/page_break\s*=\s*([\dA-C])/,"page_break=#{@pagebreak}").
            gsub(/page_line\s*=\s*([\dA-C])/,"page_line=#{@pageline}").
            gsub(/num_top\s*=\s*([\dA-C])/,"num_top=#{@num_top}").
            gsub(/toc_lev_limit\s*=\s*([\dA-C])/,"toc_lev_limit=#{@toc_lev_limit}")
          papersize_array_rc=@papersize.downcase.scan(/(?:a4|letter|legal|book|a5|b5)/)
          papersize_array_opt=[
            ((@opt.act[:pdf_a4][:set]==:on)     ? 'a4'     : ''),
            ((@opt.act[:pdf_a5][:set]==:on)     ? 'a5'     : ''),
            ((@opt.act[:pdf_b5][:set]==:on)     ? 'b5'     : ''),
            ((@opt.act[:pdf_letter][:set]==:on) ? 'letter' : ''),
            ((@opt.act[:pdf_legal][:set]==:on)  ? 'legal'  : ''),
          ] - [""]
          @papersize_array=(papersize_array_opt.length > 0) \
          ? papersize_array_opt
          : papersize_array_rc
          fn=@opt.fno #decide what to do a filesize on .ssm tells very little about actual document size
          @filesize=(File.size(fn)).to_s
          if @sys.openssl !=false \
          and FileTest.file?(@env.source_file_with_path)
            @dgst=[]
            case @env.digest(@opt).type
            when :sha512
              dgst=@sys.sha512(@env.source_file_with_path)
              @dgst=dgst[1].length==128 ? dgst : nil
              puts 'check document (sha512) digest' if not @dgst
            when :sha256
              dgst=@sys.sha256(@env.source_file_with_path)
              @dgst=dgst[1].length==64 ? dgst : nil
              puts 'check document (sha256) digest' if not @dgst
            when :md5
              dgst=@sys.md5(@env.source_file_with_path)
              @dgst=dgst[1].length==32 ? dgst : nil
              puts 'check document (md5) digest' if not @dgst
            else
              dgst=@sys.sha256(@env.source_file_with_path)
              @dgst=dgst[1].length==64 ? dgst : nil
              puts 'check document (sha256) digest' if not @dgst
            end
          elsif not FileTest.file?(@env.source_file_with_path)
            #puts SiSU_Utils::CodeMarker.new(__LINE__,__FILE__).set(:fuchsia)
          end
          @publisher ||= "#{@@publisher} (this copy)"
          fn_set_lang=SiSU_Env::StandardiseLanguage.new(@opt.lng).language
          unless @language[:code] \
          and @language[:name]
            lang=@env.i18n.language #default language settings for directory by name, or in sysrc.yml
            @language[:code] ||= lang.code
            @language[:name] ||= lang.title
          end
          unless fn_set_lang[:d]==true #decide, naming convention overrides other settings, within document, etc.
            @language[:code]=fn_set_lang[:c]
            @language[:name]=fn_set_lang[:n]
          end
          @fnl=@env.i18n.lang_filename(fn_set_lang[:c])
          @lang=@lang.uniq
          @fn=SiSU_Env::EnvCall.new(@fns).lang(fn_set_lang[:c])
          if @en[:note] > 0 \
          and @en[:sum] > 0
            if @en[:sum] > 0
            else
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                '*WARN* both endnote styles used',
                "~{ #{@en[:sum]} }~ and ^~ #{@en[:mark]}"
              ).warn unless @opt.act[:quiet][:set]==:on
            end
          end
          if @en[:mark] != @en[:note] \
          and @en[:note] > 0
            @en[:mismatch]=@en[:note] - @en[:mark]
            SiSU_Screen::Ansi.new(
              @opt.act[:color_state][:set],
              '*WARN* endnote number mismatch',
              "endnotes: #{@en[:note]} != endnote reference marks: #{@en[:mark]} " \
              + "(difference = #{@en[:mismatch]})"
            ).warn unless @opt.act[:quiet][:set]==:on
            footnote_conversion_errors=File.new("#{Dir.pwd}/footnote_conversion_errors.txt",'a')
            footnote_conversion_errors <<
              "#{@fns}:\n\tendnotes: #{@en[:note]} != endnote reference marks: #{@en[:mark]} " \
              + "(difference = #{@en[:mismatch]})\n"
          end
          if not @title \
          or not defined? @title.main \
          or @title.main !~/[\S]/
            if @fns =~/\.ssm$/ \
            and  @opt.inspect =~/P/
              #@title=Md.new('Text Insert',@opt,@env).title
            else
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                'WARNING: Document Title missing',
                'please provide @title:'
              ).warn if (@opt.act[:verbose][:set]==:on \
                || @opt.act[:verbose_plus][:set]==:on \
                || @opt.act[:maintenance][:set]==:on)
            end
          end
          if @author !~/[\S]/
            if @fns =~/\.ssm$/ \
            and  @opt.inspect =~/P/
              #@creator=SiSU_Param::Md.new('Text Insert',@opt,@env).creator
            else
              SiSU_Screen::Ansi.new(
                @opt.act[:color_state][:set],
                'WARNING: Document Author missing',
                'please provide @creator: :author:'
              ).warn if (@opt.act[:verbose][:set]==:on \
                || @opt.act[:verbose_plus][:set]==:on \
                || @opt.act[:maintenance][:set]==:on)
            end
          end
          @struct={}
          doc_struct=Hash.new(0)
          if @lv1.nil?
            fns_array.each do |para|
              if para =~/^(Part|Chapter|Section|Article)\b/i
                case para
                when /^(Part|PART)\b/
                  @struct[:part]=doc_struct[:part]
                  doc_struct[:part]=doc_struct[:part] + 1
                when /^(Chapter|CHAPTER)\b/
                  @struct[:chapter]=doc_struct[:chapter]
                  doc_struct[:chapter]=doc_struct[:chapter] + 1
                when /^(Section|SECTION)\b/
                  @struct[:section]=doc_struct[:section]
                  doc_struct[:section]=doc_struct[:section] + 1
                when /^(Article|ARTICLE)\b/
                  @struct[:article]=doc_struct[:article]
                  doc_struct[:article]=doc_struct[:article] + 1
                when /^(Clause|CLAUSE)\b/
                  @struct[:clause]=doc_struct[:clause]
                  doc_struct[:clause]=doc_struct[:clause] + 1
                when /^\d\..*[^\.]$/
                  @struct[:number]=doc_struct[:number]
                  doc_struct[:number]=doc_struct[:number] + 1
                end
              end
            end
            if doc_struct[:article] > 2                                            #%~level 4
              @lv4=/^(?:Article|ARTICLE)\b/
            elsif doc_struct[:chapter] > 2 \
            and doc_struct[:article] \
            and doc_struct[:article] < 3
              @lv4=/^(?:Chapter|CHAPTER)\b/
            elsif doc_struct[:clause] > 2
              @lv4=/^(?:Clause|CLAUSE)\b/
            elsif doc_struct[:number] > 2
              @lv4="^\d\..*[^\.]$"
            end
            if doc_struct[:section] > 2                                           #%~level 3
              @lv3=/^(?:Section|SECTION)\b/
            end
            if doc_struct[:chapter] > 2 \
            and doc_struct[:article] \
            and doc_struct[:article] > 2
              @lv2=/^(?:Chapter|CHAPTER)\b/
            end
            if doc_struct[:part] > 2 \
            and @lv[2].nil?
              @lv2=/^(?:Part|PART)\b/
            end
            if doc_struct[:part] > 2 \
            and @lv[2].inspect !~/Part/ \
            and @lv[1].nil?
              @lv1=/^(Part|PART)\b/
            end
          end
          @lnk=@lnk.compact if @lnk
          @lv0 ||=/^0~/
          @lv1 ||=/^1~/
          @lv2 ||=/^2~/
          @lv3 ||=/^3~/
          @lv4 ||=/^4~/
          @lv5 ||=/^5~/
          @lv6 ||=/^6~/
          @data=nil #else whole file's contents are stored in md pstore & is not required to be... big waste actually
          @file=SiSU_Env::FileOp.new(self) #watch
          Store.new(self,@env).store                                             #% pstore
          self
        rescue
          if @opt.act[:harvest][:set]==:on
            exit
          end
        end
      end
      private
      class Store
        def initialize(md,env)
          @md,@env=md,env
        end
        def store
          begin
            pstorefile="#{@env.processing_path.ao}/#{@md.fns}.pstore"
            File.unlink(pstorefile) if FileTest.file?(pstorefile)
            if (@md.opt.act[:verbose_plus][:set]==:on \
            || @md.opt.act[:maintenance][:set]==:on)
              SiSU_Screen::Ansi.new(
                @md.opt.act[:color_state][:set],
                "PStore -> #{pstorefile}"
              ).txt_grey
            end
            store=PStore.new(pstorefile)
            store.transaction do
              store['md']=@md
              store.commit
            end
            @@md=@md=nil
          rescue
            SiSU_Errors::Rescued.new($!,$@,@md.opt.selections.str,@md.fns).location do
              __LINE__.to_s + ':' + __FILE__
            end
          ensure
          end
        end
      end
    end
  end
  class Instantiate
    def param_instantiate
      @@date=SiSU_Env::InfoDate.new
      @doc={
       initialise: nil,
       markup: '',
       lnks: '',
       stmp: '',
       prefix_a: '',
       prefix_b: '',
       req: {}
      }
      @@flag={}
      @@publisher='SiSU scribe'
    end
  end
end
__END__
#+END_SRC

* dp_make.rb

#+BEGIN_SRC ruby  :tangle "../lib/sisu/dp_make.rb"
# <<sisu_document_header>>
module SiSU_Param_Make
  class MdMake
    def initialize(str,opt,env)
      @s,@opt,@env=str,opt,env
    end
    def validate_length(s,l,n)
      #s=(s.length <= l) ? s : nil
      s=if s.is_a?(String) \
      and s.length <= l
        s
      elsif s.is_a?(NilClass)
        nil
      elsif s.class !=String
        STDERR.puts "#{n} is #{s.class}: programming error, String expected #{__FILE__}:#{__LINE__}"
        s
      else
        SiSU_Screen::Ansi.new(
          'v',
          "*WARN* #{n} length #{s.length} exceeds set db field length #{l}, metadata dropped",
          @opt.fns
        ).warn unless @opt.act[:quiet][:set]==:on
        nil
      end
    end
    def name_format(name)
      if name
        name=name.strip
        @name_a_h=[]
        authors=name.scan(/[^;]+/)
        authors.each_with_index do |a,i|
          b=((a =~/\s*\|\s*/) ? (a.split(/\|/)) : [a])
          if b[0] =~/"(.+?)"/
            @name_a_h << { the: $1 }
          else
            x=b[0].scan(/[^,]+/)
            if x.length==1
              @name_a_h << { the: x[0].strip }
            elsif x.length==2
              @name_a_h << { the: x[0].strip, others: x[1].strip }
            else #p x.length
            end
          end
          b.delete_at(0)
          b.each do |d|
            k,c=nil
            k,c=/^(\S+)\s+(.*)/.match(d)[1,2] if d
            @name_a_h[i][:hon]=c.strip if k=='hon'
            @name_a_h[i][:affiliation]=c.strip if k=='affiliation'
            @name_a_h[i][:nationality]=c.strip if k=='nationality'
          end
        end
        l=@name_a_h.length
        name_str=''
        @name_a_h.each_with_index do |a,i|
          name_str += if a[:others]
            z=(((l - i) > 1) ? ', ' : '')
            "#{a[:others].strip} #{a[:the].strip}" + z
          else
            z=(((l - i) > 2) ? ', ' : '')
            "#{a[:the].strip}" + z
          end
        end
        { name_a_h: @name_a_h, name_str: name_str }
      else nil
      end
    end
    def build_hash(arr)
      @h={}
      arr.each_with_index do |x,i|
        a,b=nil,nil
        if x =~/^%[:\s]/ #ignore comment
        elsif x =~/:(\S+?):\s+(.+)/
          a,b=/:(\S+?):\s+(.+)\Z/m.match(x)[1,2]
        elsif i == 0
          a='main'
          b=x
        else
        end
        @h[a]=b
      end
      @h
    end
    def make
      a=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
      @h=build_hash(a)
      def headings
        lv=[]
        x=@h['headings']
        x=((x =~/;/) ? (x.split(/;\s*/)) : [ x ])
        lv[0]=x
        lv0 ||='A~ '             #root level, single document apex, document title
        lv1=x[0] ||='B~ '
        lv[1]=/^#{lv1}/
        lv2=x[1] ||='C~ '
        lv[2]=/^#{lv2}/
        lv3=x[2] ||='D~ '
        lv[3]=/^#{lv3}/
        lv4=x[3] ||='1~ '
        lv[4]=/^#{lv4}/
        lv5=x[4] ||='2~ '
        lv[5]=/^#{lv5}/
        lv6=x[5] ||='3~ '
        lv[6]=/^#{lv6}/
        lv
      end
      def num_top
        @h['num_top']
      end
      def breaks
        pagebreaks=((@h['breaks'] =~/;/) \
        ? (@h['breaks'].split(/;\s*/))
        : [ @h['breaks'] ])
        page_new,page_break,page_line=nil,nil,nil
        pagebreaks.each do |x|
          page_new=x[/(:?[\dA-C],?)+/] if x=~/new|clear/
          page_break=x[/(:?[\dA-C],?)+/] if x =~/break/
          page_line=x[/(:?[\dA-C],?)+/] if x =~/line/
        end
        { page_new: page_new, page_break: page_break, page_line: page_line }
      end
      def language
        if @h['language'] && (@h['language']=~/\S{2,}/)
          ((@h['language'] =~/,/) \
          ? (@h['language'].split(/,\s*/))
          : [ @h['language'] ])
        else [ 'en' ]
        end
      end
      def bold
        m=@h['bold']
        i=(m=~/\/i$/)? 'i' : ''
        if m
          x=m.gsub(/^\/(.+?)\/i?/,'\1').
            gsub(/\((?:\?:)?/,'(?:')                                         # avoid need to escape use of brackets within regex provided
          rgx='\b(' + x + ')\b'
          y=((i =~/i/) ? (/#{rgx}/i) : (/#{rgx}/))
          { str: '\b(?:' + x + ')\b', regx: y, i: i }
        else nil
        end
      end
      def italics
        m=@h['italics']
        i=((m=~/\/i$/) ? 'i' : '')
        if m
          x=m.gsub(/^\/(.+?)\/i?/,'\1').
            gsub(/\((?:\?:)?/,'(?:')                                         # avoid need to escape use of brackets within regex provided
          rgx='\b(' + x + ')\b'
          y=((i =~/i/) ? (/#{rgx}/i) : (/#{rgx}/))
          { str: '\b(?:' + x + ')\b', regx: y, i: i }
        else nil
        end
      end
      def emphasis
        if @h['emphasis'] =~/bold/                   then 'bold'
        elsif @h['emphasis'] =~/italics?/            then 'italics'
        elsif @h['emphasis'] =~/under(?:line|score)/ then 'underscore'
        else nil
        end
      end
      def substitute
        m=@h['substitute']
        if m
          w=m.scan(/\/(.+?)\/(i?,)\s*'(.+?)'(?:\s+|\s*;\s*|$)/)
          arr_hash=[]
          matches=''
          w.each do |x|
            c=(x[1] =~/[i],/) ? :i : :s
            matches=matches + x[0].gsub(/([${}])/,'\\\\\1') + '|'
            arr_hash << {
              match: x[0].gsub(/([${}])/,'\\\\\1'),
              replace: x[2],
              case_s: c
            }
          end
          matches.chop!
          { match_and_replace: arr_hash, matches: matches }
        else nil
        end
      end
      def plaintext_wrap
        if @h['plaintext_wrap'].to_s =~/\d\d+/ \
        and @h['plaintext_wrap'].to_i > 19 \
        and @h['plaintext_wrap'].to_i < 201
          @h['plaintext_wrap'].to_i
        else nil
        end
      end
      def omit
        m=@h['omit']
        @m=m ? (m.split(/,\s+/)) : nil
        def list
          @m
        end
        self
      end
      def ocn?
        (omit.list.inspect =~/"ocn"/) \
        ? :off
        : :na
      end
      def toc?
        (omit.list.inspect =~/"toc"/) \
        ? :off
        : :na
      end
      def manifest?
        (omit.list.inspect =~/"manifest"/) \
        ? :off
        : :na
      end
      def links_to_manifest?
        (omit.list.inspect =~/"manifest_links"|"links_to_manifest"/) \
        ? :off
        : :na
      end
      def metadata?
        (omit.list.inspect =~/"metadata"/) \
        ? :off
        : :na
      end
      def minitoc?
        (omit.list.inspect =~/"minitoc"/) \
        ? :off
        : :na
      end
      def html_minitoc?
        (omit.list.inspect =~/"html_minitoc"/) \
        ? :off
        : :na
      end
      def html_top_band?
        (omit.list.inspect =~/"html_top_band"/) \
        ? :off
        : :na
      end
      def html_navigation?
        (omit.list.inspect =~/"html_navigation"/) \
        ? :off
        : :na
      end
      def html_navigation_bar?
        (omit.list.inspect =~/"html_navigation_bar"/) \
        ? :off
        : :na
      end
      def segsubtoc?
        (omit.list.inspect =~/"segsubtoc"/) \
        ? :off
        : :na
      end
      def search_form?
        (omit.list.inspect =~/"search_form"/) \
        ? :off
        : :na
      end
      def html_search_form?
        (omit.list.inspect =~/"html_search_form"/) \
        ? :off
        : :na
      end
      def html_right_pane?
        (omit.list.inspect =~/"html_right_column"|"html_right_pane"/) \
        ? :off
        : :na
      end
      def manifest_minitoc?
        (omit.list.inspect =~/"manifest_minitoc"/) \
        ? :off
        : :na
      end
      def cover_image?
        (omit.list.inspect =~/"cover_image"/) \
        ? :off
        : :na
      end
      def home_button_image?
        (omit.list.inspect =~/"home_button_image"/) \
        ? :off
        : :na
      end
      def texpdf_font
        def main
          @h['texpdf_font'] \
          && (@h['texpdf_font']=~/\S{3,}/) \
          ? @h['texpdf_font']
          : @env.font.texpdf.main
        end
        def sans                                                             # not used
          @h['texpdf_font_sans'] \
          && (@h['texpdf_font_sans']=~/\S{3,}/) \
          ? @h['texpdf_font_sans']
          : @env.font.texpdf.sans
        end
        def serif                                                            # not used
          @h['texpdf_font_serif'] \
          && (@h['texpdf_font_serif']=~/\S{3,}/) \
          ? @h['texpdf_font_serif']
          : @env.font.texpdf.serif
        end
        def mono
          @h['texpdf_font_mono'] \
          && (@h['texpdf_font_mono']=~/\S{3,}/) \
          ? @h['texpdf_font_mono']
          : @env.font.texpdf.mono
        end
        def cjk
          @h['texpdf_font_cjk'] \
          && (@h['texpdf_font_cjk']=~/\S{3,}/) \
          ? @h['texpdf_font_cjk']
          : @env.font.texpdf.cjk
        end
        def cjk_zh
          @h['texpdf_font_cjk_zh'] \
          && (@h['texpdf_font_cjk_zh']=~/\S{3,}/) \
          ? @h['texpdf_font_cjk_zh']
          : @env.font.texpdf.cjk_zh
        end
        def cjk_ja
          @h['texpdf_font_cjk_ja'] \
          && (@h['texpdf_font_cjk_ja']=~/\S{3,}/) \
          ? @h['texpdf_font_cjk_ja']
          : @env.font.texpdf.cjk_ja
        end
        def cjk_ko
          @h['texpdf_font_cjk_ko'] \
          && (@h['texpdf_font_cjk_ko']=~/\S{3,}/) \
          ? @h['texpdf_font_cjk_ko']
          : @env.font.texpdf.cjk_ko
        end
        self
      end
      def promo
        @h['promo']
      end
      def ad
        @h['ad']
      end
      def manpage
        manpage={}
        if @h['manpage']
          if @h['manpage'] =~/;/m
            man=@h['manpage'].split(/;/m)
            man.each do |x|
              m=(x=~/=/m) ? x.split(/=/m) : nil
              if m
                manpage[m[0].strip] = m[1].split(/ \. /)
              end
            end
          end
        end
        if manpage['name']
          manpage['name']=manpage['name'].join("\n.br\n").
            gsub(/(-)/m,"\\\\\\1").
            gsub(/\A/,"\n.br\n.SH NAME\n.br\n")
        else
          manpage['name']='man page "name/whatis" information not provided, set in header @man: name=[whatis information]'
        end
        if manpage['synopsis']
          manpage['synopsis']=manpage['synopsis'].join("\n\n.br\n").
            gsub(/(-)/m,"\\\\\\1").
            gsub(/\A/,"\n.br\n.SH SYNOPSIS\n.br\n")
        else
          manpage['synopsis']=''
        end
        unless manpage['section']
          manpage['section']=1
        end
        manpage
      end
      def get_image_dimensions(img)
        imgk=SiSU_Env::SystemCall.new.imagemagick
        gmgk=SiSU_Env::SystemCall.new.graphicsmagick
        img_pth={
          sst: @env.path.image_source_include,
          pod: File.expand_path("../../../sisupod/image" )
        }
        path_img=if FileTest.file?("#{img_pth[:pod]}/#{img}")
          "#{img_pth[:pod]}/#{img}"
        elsif FileTest.file?("#{img_pth[:sst]}/#{img}")
          "#{img_pth[:sst]}/#{img}"
        else nil
        end
        if path_img
          if imgk or gmgk
            if imgk
              imgsys=`identify #{path_img}`.strip                           #system call
            elsif gmgk
              imgsys=`gm identify #{path_img}`.strip                        #system call
            end
            w,h=/(\d+)x(\d+)/m.match(imgsys)[1,2]
          else
            w,h='600','800'
          end
        else
          w,h=nil,nil
        end
        {w: w, h: h}
      end
      def home_button_text
        if @h['home_button_text']
          @h['home_button_text'].split(/\s*;\s*/)
        else nil
        end
      end
      def home_button_image
        s=nil
        s=if @h['home_button_image']
          s=@h['home_button_image'].split(/\s*;\s*/)
          s0=s[0] #if
          image={}
          s=if s0 =~/{(\S+\.(?:jpg|png|gif))(?:\s+(\d+x\d+))?\s*}(?:(http:\/\/\S+)|image)/
            image[:home_button]=$1
            if $2
              image[:dimensions]=$2
              image[:w],image[:h]=/(\d+)x(\d+)/m.match(image[:dimensions])[1,2]
            else
              d=get_image_dimensions(image[:home_button])
              image[:w],image[:h]=d[:w],d[:h]
              image[:dimensions]="#{d[:w]}x#{d[:h]}"
            end
            image[:link]=$3
            image
          end
        else nil
        end
      end
      def cover_image
        s=nil
        if @h['cover_image']
          s=@h['cover_image'].split(/\s*;\s*/)
          s=s[0] #if
          image={}
          if s =~/{\s*(\S+\.(?:jpg|png|gif))(?:\s+(\d+x\d+))?(?:\s+"(.+?)")?\s*}image/
            image[:cover]=$1
            if $2
              image[:dimensions]=$2
              image[:w],image[:h]=/(\d+)x(\d+)/m.match(image[:dimensions])[1,2]
            else
              d=get_image_dimensions(image[:cover])
              image[:w],image[:h]=d[:w],d[:h]
              image[:dimensions]="#{d[:w]}x#{d[:h]}"
            end
            image[:note]=$3
          elsif s =~/(\S+\.(?:jpg|png|gif))/
            image[:cover]=$1
            d=get_image_dimensions(image[:cover])
            image[:w],image[:h]=d[:w],d[:h]
            image[:dimensions]="#{d[:w]}x#{d[:h]}"
            image[:note]=nil
          end
          image
        else nil
        end
      end
      def footer
        if @h['footer']
          @h['footer'].split(/\s*;\s*/)
        else nil
        end
      end
      self
    end
    def make_links
      @doc_links=@s.split(/\n%\s.+?$|[ ]*\n[ ]*/m)
      def links
        lnks,a_idx=[],0
        @doc_links.each do |doc_link|
          if doc_link=~/\{.+?\}(?:(?:https?|file|ftp):\/|\.\.)\/\S+(?:\s|$)/
            say,url=/\{\s*(.+?)\s*\}((?:(?:https?|file|ftp):\/|\.\.)\/\S+)/im.match(doc_link)[1,2]
            lnks[a_idx]={ say: say, url: url  }
            a_idx +=1
          end
        end
        lnks
      end
      def append?
        (@doc_links[0]=='+') \
          ? :yes
          : :no
      end
      self
    end
  end
  class MakeHead
    attr_accessor :pagenew,:pagebreak,:pageline,:toc,:lv1,:lv2,:lv3,:lv4,:lv5,:lv6,:num_top,:i18n,:man_section,:substitution_match_list,:bold_match_list,:italics_match_list,:emphasis_set_to,:footer_links,:home_button_links,:home_button_image,:cover_image
    def initialize(make)
      @make=make
    end
    def clear
      @pagenew=@pagebreak=@pageline=@toc=@lv1=@lv2=@lv3=@lv4=@lv5=@lv6=@num_top=@i18n=@man_section=@footer_links=@substitution_match_list=@bold_match_list=@italics_match_list=@emphasis_set_to=@home_button_links=@home_button_image=@cover_image=nil
    end
    def make_instruct
      clear
      if defined? @make.breaks \
      and @make.breaks[:page_new]                  #clearpage
        @pagenew=@make.breaks[:page_new]
      end
      if defined? @make.breaks \
      and @make.breaks[:page_break]                #newpage
        @pagebreak=@make.breaks[:page_break]
      end
      if defined? @make.breaks \
      and @make.breaks[:page_line]                 #page line across
        @pagebreak=@make.breaks[:page_line]
      end
      if defined? @make.headings \
      and @make.headings
        @toc=@make.headings[0]
        @lv1=@make.headings[1]
        @lv2=@make.headings[2]
        @lv3=@make.headings[3]
        @lv4=@make.headings[4]
        @lv5=@make.headings[5]
        @lv6=@make.headings[6]
      end
      if defined? @make.num_top \
      and @make.num_top
        @num_top=@make.num_top # remove @num_top
      end
      if defined? @make.language \
      and @make.language[0]
        @i18n=@make.language
      end
      if defined? @make.manpage \
      and @make.manpage
        @man_section=(defined? @make.manpage.section) \
        ? @make.manpage.section
        : 1
      end
      if defined? @make.substitute \
      and @make.substitute
        @substitution_match_list=@make.substitute
      end
      if defined? @make.bold \
      and @make.bold
        @bold_match_list=@make.bold
      end
      if defined? @make.italics \
      and @make.italics
        @italics_match_list=@make.italics
      end
      if defined? @make.emphasis \
      and @make.emphasis
        @emphasis_set_to=@make.emphasis
      end
      if defined? @make.footer \
      and @make.footer.is_a?(Array)
        @footer_links= { left: { say: '', url: '' }, center: { say: '', url: '' } } #already set
        @footer_links[:left]=if @make.footer[0]=~/\{.+?\}(?:(?:https?|file|ftp):\/|\.\.)\/\S+(?:\s|$)/
          say,url=/\{\s*(.+?)\s*\}((?:(?:https?|file|ftp):\/|\.\.)\/\S+)/im.match(@make.footer[0])[1,2]
          { say: say, url: url }
        else
          { say: '', url: '' }
        end
        @footer_links[:center]=if @make.footer[1]=~/\{.+?\}(?:(?:https?|file|ftp):\/|\.\.)\/\S+(?:\s|$)/
          say,url=/\{\s*(.+?)\s*\}((?:(?:https?|file|ftp):\/|\.\.)\/\S+)/im.match(@make.footer[1])[1,2]
          { say: say, url: url }
        else
          { say: '', url: '' }
        end
        @footer_links
      else #already set
        @footer_links= { left: { say: '', url: '' }, center: { say: '', url: '' } }
      end
      if defined? @make.home_button_text \
      and @make.home_button_text.is_a?(Array)
        a_idx=0
        @home_button_links=[]
        @make.home_button_text.each do |doc_link|
          if doc_link=~/\{.+?\}(?:(?:https?|file|ftp):\/|\.\.)\/\S+(?:\s|$)/
            say,url=/\{\s*(.+?)\s*\}((?:(?:https?|file|ftp):\/|\.\.)\/\S+)/im.match(doc_link)[1,2]
            @home_button_links[a_idx]= { say: say, url: url }
            a_idx +=1
          end
        end
        @home_button_links
      end
      if defined? @make.home_button_image \
      and @make.home_button_image.is_a?(Hash)
        @home_button_image=@make.home_button_image
      end
      if defined? @make.cover_image \
      and @make.cover_image.is_a?(Hash)
        @cover_image=@make.cover_image
      end
      { pagenew: @pagenew,
        pagebreak: @pagebreak,
        pageline: @pageline,
        toc: @toc,
        lv1: @lv1,
        lv2: @lv2,
        lv3: @lv3,
        lv4: @lv4,
        lv5: @lv5,
        lv6: @lv6,
        num_top: @num_top,
        i18n: @i18n,
        emphasis_set_to: @emphasis_set_to,
        bold_match_list: @bold_match_list,
        italics_match_list: @italics_match_list,
        substitution_match_list: @substitution_match_list,
        man_section: @man_section,
        footer_links: @footer_links,
        home_button_links: @home_button_links,
        home_button_image: @home_button_image,
        cover_image: @cover_image,
      }
    end
  end
end
__END__
#+END_SRC

* dp_identify_markup.rb

#+BEGIN_SRC ruby  :tangle "../lib/sisu/dp_identify_markup.rb"
# <<sisu_document_header>>
module SiSU_MarkupType
  class MarkupIdentify
    @@version={}
    @@fns,@@version[:determined],@@version[:declared],@@declared_doc_type='','','','[text?]'
    attr_accessor :version,:declared_doc_type
    def initialize(content,opt)
      @cont,@opt=content,opt
    end
    def identify
      @version,@declared_doc_type=@@version,@@declared_doc_type
      if @opt.fns != @@fns
        if @cont[0] =~ /^(?:%\s+)?SiSU\s+(text|master|insert)\s+([0-9](?:\.[0-9]+){1,2})/ \
        or @cont[0] =~ /^(?:%\s+)?sisu-([0-9](?:\.[0-9]+){1,2})/
          @declared_doc_type,@version[:declared]=$1,$2
        elsif @cont[0] =~ /^(?:%\s+)?SiSU\s+([0-9](?:\.[0-9]+){1,2})/ \
        or @cont[0] =~ /^(?:%\s+)?sisu-([0-9](?:\.[0-9]+){1,2})/
          @version[:declared]=$1
        end
        @flag_2_0,@flag_66,@flag_57,@flag_38=false,false,false,false
        @cont.each_with_index do |y,i|
          if y =~/^@make:|^@classify|^\s\s?:\S+?:\s+\S/
            version=2.0.to_f
            @version[:determined]=version
            @flag_2_0=true
            break
          end
          unless @flag_38
            if y =~/^:?A~/
              version=0.38.to_f
              @version[:determined]=version
              @flag_38=true
            end
          end
          if @flag_38
            if @flag_69 \
            or y =~/^=\{.+?\}\s*$/
              version=0.69.to_f
              @flag_69=true
              @version[:determined]=version
              break
            end
            if @flag_66 \
            or y =~/[:;]\{.+?\}[:;][a-z+]/
              version=0.66.to_f
              @flag_66=true
              @version[:determined]=version
              break
            end
          end
        end
        @flag_57,@flag_38=false,false
        unless @flag_2_0 \
        or @flag_66 \
        or @flag_69
          @cont.each_with_index do |y,i|
            if @flag_57 \
            or y =~/^:?A~\?? @title/
              @version[:determined]=0.57.to_f
              @flag_57=true
              break
            end
            if @flag_38 \
            or y =~/^:?A~/
              @version[:determined]=0.38.to_f
              @flag_38=true
              break if i >= 200
              if y =~ /(?:~{\*+|~\[\*|~\[\+)\s/
                @version[:determined]=0.42 #0.38 can safely be treated as 0.42
                break
              end
            end
            if y =~/^0~/ \
            and not @flag_38
              @version[:determined]=0.16.to_f
              break
            end
          end
        end
        @@fns=@opt.fns
        @@version,@@declared_doc_type=@version,@declared_doc_type
      end
      self
    end
    def markup_version?
      def determined
        identify.version[:determined].to_f
      end
      def series
        s=case identify.version[:determined].to_s
        when /^[01]\./ then '1.0'
        when /^[2]\./  then '2.0'
        else '2.0'
        end
        "series #{s}"
      end
      def declared
        identify.version[:declared].to_f
      end
      self
    end
  end
end
__END__
#+END_SRC

* document header

#+NAME: sisu_document_header
#+BEGIN_SRC text
encoding: utf-8
- Name: SiSU

  - Description: documents, structuring, processing, publishing, search
    param

  - Author: Ralph Amissah
    <ralph.amissah@gmail.com>

  - Copyright: (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
    2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
    2020, 2021, Ralph Amissah,
    All Rights Reserved.

  - License: GPL 3 or later:

    SiSU, a framework for document structuring, publishing and search

    Copyright (C) Ralph Amissah

    This program is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the Free
    Software Foundation, either version 3 of the License, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
    more details.

    You should have received a copy of the GNU General Public License along with
    this program. If not, see <http://www.gnu.org/licenses/>.

    If you have Internet connection, the latest version of the GPL should be
    available at these locations:
    <http://www.fsf.org/licensing/licenses/gpl.html>
    <http://www.gnu.org/licenses/gpl.html>

    <http://www.sisudoc.org/sisu/en/manifest/gpl.fsf.html>

  - SiSU uses:
    - Standard SiSU markup syntax,
    - Standard SiSU meta-markup syntax, and the
    - Standard SiSU object citation numbering and system

  - Homepages:
    <http://www.sisudoc.org>

  - Git
    <https://git.sisudoc.org/projects/>
    <https://git.sisudoc.org/projects/?p=software/sisu.git;a=summary>
    <https://git.sisudoc.org/projects/?p=markup/sisu-markup-samples.git;a=summary>
#+END_SRC