diff options
Diffstat (limited to 'scripts/context/ruby/base/ctx.rb')
-rw-r--r-- | scripts/context/ruby/base/ctx.rb | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/scripts/context/ruby/base/ctx.rb b/scripts/context/ruby/base/ctx.rb new file mode 100644 index 000000000..13b4045af --- /dev/null +++ b/scripts/context/ruby/base/ctx.rb @@ -0,0 +1,462 @@ +# module : base/ctx +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# todo: write systemcall for mpost to file so that it can be run +# faster + +# report ? + +require 'base/system' +require 'base/file' +require 'base/switch' # has needsupdate, bad place + +require 'rexml/document' + +class CtxRunner + + attr_reader :environments, :modules, :filters, :flags, :modes + + @@suffix = 'prep' + + def initialize(jobname=nil,logger=nil) + if @logger = logger then + def report(str='') + @logger.report(str) + end + else + def report(str='') + puts(str) + end + end + @jobname = jobname + @ctxname = nil + @xmldata = nil + @prepfiles = Hash.new + @environments = Array.new + @modules = Array.new + @filters = Array.new + @flags = Array.new + @modes = Array.new + @local = false + @paths = Array.new + end + + def register_path(str) + @paths << str + end + + def manipulate(ctxname=nil,defaultname=nil) + + if ctxname then + @ctxname = ctxname + @jobname = File.suffixed(@ctxname,'tex') unless @jobname + else + @ctxname = File.suffixed(@jobname,'ctx') if @jobname + end + + if not @ctxname then + report('no ctx file specified') + return + end + + if @ctxname !~ /\.[a-z]+$/ then + @ctxname += ".ctx" + end + + # name can be kpse:res-make.ctx + if not FileTest.file?(@ctxname) then + fullname, done = '', false + if @ctxname =~ /^kpse:/ then + begin + if fullname = Kpse.found(@ctxname.sub(/^kpse:/,'')) then + @ctxname, done = fullname, true + end + rescue + # should not happen + end + else + ['..','../..'].each do |path| + begin + fullname = File.join(path,@ctxname) + if FileTest.file?(fullname) then + @ctxname, done = fullname, true + end + rescue + # probably strange join + end + break if done + end + if ! done then + fullname = Kpse.found(@ctxname) + if FileTest.file?(fullname) then + @ctxname, done = fullname, true + end + end + end + if ! done && defaultname && FileTest.file?(defaultname) then + report("using default ctxfile #{defaultname}") + @ctxname, done = defaultname, true + end + if not done then + report('no ctx file found') + return false + end + end + + if FileTest.file?(@ctxname) then + @xmldata = IO.read(@ctxname) + else + report('no ctx file found') + return false + end + + unless @xmldata =~ /^.*<\?xml.*?\?>/moi then + report("ctx file #{@ctxname} is no xml file, skipping") + return + else + report("loading ctx file #{@ctxname}") + end + + if @xmldata then + # out if a sudden rexml started to be picky about namespaces + @xmldata.gsub!(/<ctx:job>/,"<ctx:job xmlns:ctx='http://www.pragma-ade.com/rng/ctx.rng'>") + end + + begin + @xmldata = REXML::Document.new(@xmldata) + rescue + report('provide valid ctx file (xml error)') + return + else + include(@xmldata,'ctx:include','name') + end + + begin + variables = Hash.new + if @jobname then + variables['job'] = @jobname + end + root = @xmldata.root + REXML::XPath.each(root,"/ctx:job//ctx:flags/ctx:flag") do |flg| + @flags << justtext(flg) + end + REXML::XPath.each(root,"/ctx:job//ctx:resources/ctx:environment") do |sty| + @environments << justtext(sty) + end + REXML::XPath.each(root,"/ctx:job//ctx:resources/ctx:module") do |mod| + @modules << justtext(mod) + end + REXML::XPath.each(root,"/ctx:job//ctx:resources/ctx:filter") do |fil| + @filters << justtext(fil) + end + REXML::XPath.each(root,"/ctx:job//ctx:resources/ctx:mode") do |fil| + @modes << justtext(fil) + end + begin + REXML::XPath.each(root,"//ctx:block") do |blk| + if @jobname && blk.attributes['pattern'] then + root.delete(blk) unless @jobname =~ /#{blk.attributes['pattern']}/ + else + root.delete(blk) + end + end + rescue + end + REXML::XPath.each(root,"//ctx:value[@name='job']") do |val| + substititute(val,variables['job']) + end + REXML::XPath.each(root,"/ctx:job//ctx:message") do |mes| + report("preprocessing: #{justtext(mes)}") + end + REXML::XPath.each(root,"/ctx:job//ctx:process/ctx:resources/ctx:environment") do |sty| + @environments << justtext(sty) + end + REXML::XPath.each(root,"/ctx:job//ctx:process/ctx:resources/ctx:module") do |mod| + @modules << justtext(mod) + end + REXML::XPath.each(root,"/ctx:job//ctx:process/ctx:resources/ctx:filter") do |fil| + @filters << justtext(fil) + end + REXML::XPath.each(root,"/ctx:job//ctx:process/ctx:resources/ctx:mode") do |fil| + @modes << justtext(fil) + end + REXML::XPath.each(root,"/ctx:job//ctx:process/ctx:flags/ctx:flag") do |flg| + @flags << justtext(flg) + end + commands = Hash.new + REXML::XPath.each(root,"/ctx:job//ctx:preprocess/ctx:processors/ctx:processor") do |pre| + begin + commands[pre.attributes['name']] = pre + rescue + end + end + suffix = @@suffix + begin + suffix = REXML::XPath.match(root,"/ctx:job//ctx:preprocess/@suffix").to_s + rescue + suffix = @@suffix + else + if suffix && suffix.empty? then suffix = @@suffix end + end + if (REXML::XPath.first(root,"/ctx:job//ctx:preprocess/ctx:processors/@local").to_s =~ /(yes|true)/io rescue false) then + @local = true + else + @local = false + end + REXML::XPath.each(root,"/ctx:job//ctx:preprocess/ctx:files") do |files| + REXML::XPath.each(files,"ctx:file") do |pattern| + suffix = @@suffix + begin + suffix = REXML::XPath.match(root,"/ctx:job//ctx:preprocess/@suffix").to_s + rescue + suffix = @@suffix + else + if suffix && suffix.empty? then suffix = @@suffix end + end + preprocessor = pattern.attributes['processor'] + if preprocessor and not preprocessor.empty? then + begin + variables['old'] = @jobname + variables['new'] = "" + REXML::XPath.each(pattern,"ctx:value") do |value| + if name = value.attributes['name'] then + substititute(value,variables[name.to_s]) + end + end + rescue + report('unable to resolve file pattern') + return + end + pattern = justtext(pattern) + oldfiles = Dir.glob(pattern) + pluspath = false + if oldfiles.length == 0 then + report("no files match #{pattern}") + if @paths.length > 0 then + @paths.each do |p| + oldfiles = Dir.glob("#{p}/#{pattern}") + if oldfiles.length > 0 then + pluspath = true + break + end + end + if oldfiles.length == 0 then + report("no files match #{pattern} on path") + end + end + end + oldfiles.each do |oldfile| + newfile = "#{oldfile}.#{suffix}" + newfile = File.basename(newfile) if @local # or pluspath + if File.expand_path(oldfile) != File.expand_path(newfile) && File.needsupdate(oldfile,newfile) then + report("#{oldfile} needs preprocessing") + begin + File.delete(newfile) + rescue + # hope for the best + end + # there can be a sequence of processors + preprocessor.split(',').each do |pp| + if command = commands[pp] then + # a lie: no <?xml ...?> + command = REXML::Document.new(command.to_s) # don't infect original + # command = command.deep_clone() # don't infect original + command = command.elements["ctx:processor"] + if suf = command.attributes['suffix'] then + newfile = "#{oldfile}.#{suf}" + end + begin + newfile = File.basename(newfile) if @local + rescue + end + REXML::XPath.each(command,"ctx:old") do |value| replace(value,oldfile) end + REXML::XPath.each(command,"ctx:new") do |value| replace(value,newfile) end + report("preprocessing #{oldfile} into #{newfile} using #{pp}") + variables['old'] = oldfile + variables['new'] = newfile + REXML::XPath.each(command,"ctx:value") do |value| + if name = value.attributes['name'] then + substititute(value,variables[name.to_s]) + end + end + command = justtext(command) + report(command) + unless ok = System.run(command) then + report("error in preprocessing file #{oldfile}") + end + begin + oldfile = File.basename(oldfile) if @local + rescue + end + end + end + if FileTest.file?(newfile) then + File.syncmtimes(oldfile,newfile) + else + report("check target location of #{newfile}") + end + else + report("#{oldfile} needs no preprocessing (same file)") + end + @prepfiles[oldfile] = FileTest.file?(newfile) + end + end + end + end + rescue + report("fatal error in preprocessing #{@ctxname}: #{$!}") + end + end + + def savelog(ctlname=nil) + unless ctlname then + if @jobname then + ctlname = File.suffixed(@jobname,'ctl') + elsif @ctxname then + ctlname = File.suffixed(@ctxname,'ctl') + else + return + end + end + if @prepfiles.length > 0 then + if log = File.open(ctlname,'w') then + log << "<?xml version='1.0' standalone='yes'?>\n\n" + if @local then + log << "<ctx:preplist local='yes'>\n" + else + log << "<ctx:preplist local='no'>\n" + end + @prepfiles.keys.sort.each do |prep| + # log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{File.basename(prep)}</ctx:prepfile>\n" + log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{prep}</ctx:prepfile>\n" + end + log << "</ctx:preplist>\n" + log.close + end + else + begin + File.delete(ctlname) + rescue + end + end + end + + private + + def include(xmldata,element='ctx:include',attribute='name') + loop do + begin + more = false + REXML::XPath.each(xmldata.root,element) do |e| + begin + name = e.attributes.get_attribute(attribute).to_s + name = e.text.to_s if name.empty? + name.strip! if name + done = false + if name and not name.empty? then + ['.',File.dirname(@ctxname),'..','../..'].each do |path| + begin + fullname = if path == '.' then name else File.join(path,name) end + if FileTest.file?(fullname) then + if f = File.open(fullname,'r') and i = REXML::Document.new(f) then + report("including ctx file #{name}") + REXML::XPath.each(i.root,"*") do |ii| + xmldata.root.insert_before(e,ii) + more = true + end + end + done = true + end + rescue + end + break if done + end + end + report("no valid ctx inclusion file #{name}") unless done + rescue Exception + # skip this file + ensure + xmldata.root.delete(e) + end + end + break unless more + rescue Exception + break # forget about inclusion + end + end + end + + private + + def yes_or_no(b) + if b then 'yes' else 'no' end + end + + private # copied from rlxtools.rb + + def justtext(str) + str = str.to_s + str.gsub!(/<[^>]*?>/o, '') + str.gsub!(/\s+/o, ' ') + str.gsub!(/</o, '<') + str.gsub!(/>/o, '>') + str.gsub!(/&/o, '&') + str.gsub!(/"/o, '"') + str.gsub!(/[\/\\]+/o, '/') + return str.strip + end + + def substititute(value,str) + if str then + begin + if value.attributes.key?('method') then + str = filtered(str.to_s,value.attributes['method'].to_s) + end + if str.empty? && value.attributes.key?('default') then + str = value.attributes['default'].to_s + end + value.insert_after(value,REXML::Text.new(str.to_s)) + rescue Exception + end + end + end + + def replace(value,str) + if str then + begin + value.insert_after(value,REXML::Text.new(str.to_s)) + rescue Exception + end + end + end + + def filtered(str,method) + str = str.to_s # to be sure + case method + when 'name' then # no path, no suffix + case str + when /^.*[\\\/](.+?)\..*?$/o then $1 + when /^.*[\\\/](.+?)$/o then $1 + when /^(.*)\..*?$/o then $1 + else str + end + when 'path' then if str =~ /^(.+)([\\\/])(.*?)$/o then $1 else '' end + when 'suffix' then if str =~ /^.*\.(.*?)$/o then $1 else '' end + when 'nosuffix' then if str =~ /^(.*)\..*?$/o then $1 else str end + when 'nopath' then if str =~ /^.*[\\\/](.*?)$/o then $1 else str end + when 'base' then if str =~ /^.*[\\\/](.*?)$/o then $1 else str end + when 'full' then str + when 'complete' then str + when 'expand' then File.expand_path(str).gsub(/\\/,"/") + else str + end + end + +end |