summaryrefslogtreecommitdiff
path: root/scripts/context/ruby
diff options
context:
space:
mode:
authorMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
committerMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
commit85b7bc695629926641c7cb752fd478adfdf374f3 (patch)
tree80293f5aaa7b95a500a78392c39688d8ee7a32fc /scripts/context/ruby
downloadcontext-85b7bc695629926641c7cb752fd478adfdf374f3.tar.gz
stable 2010-05-24 13:10
Diffstat (limited to 'scripts/context/ruby')
-rw-r--r--scripts/context/ruby/base/ctx.rb462
-rw-r--r--scripts/context/ruby/base/exa.rb407
-rw-r--r--scripts/context/ruby/base/file.rb150
-rw-r--r--scripts/context/ruby/base/kpse.rb389
-rw-r--r--scripts/context/ruby/base/kpse/drb.rb57
-rw-r--r--scripts/context/ruby/base/kpse/soap.rb79
-rw-r--r--scripts/context/ruby/base/kpse/trees.rb84
-rw-r--r--scripts/context/ruby/base/kpsedirect.rb34
-rw-r--r--scripts/context/ruby/base/kpsefast.rb934
-rw-r--r--scripts/context/ruby/base/kpseremote.rb116
-rw-r--r--scripts/context/ruby/base/kpserunner.rb87
-rw-r--r--scripts/context/ruby/base/logger.rb104
-rw-r--r--scripts/context/ruby/base/merge.rb139
-rw-r--r--scripts/context/ruby/base/mp.rb167
-rw-r--r--scripts/context/ruby/base/pdf.rb75
-rw-r--r--scripts/context/ruby/base/state.rb75
-rw-r--r--scripts/context/ruby/base/switch.rb635
-rw-r--r--scripts/context/ruby/base/system.rb121
-rw-r--r--scripts/context/ruby/base/tex.rb2299
-rw-r--r--scripts/context/ruby/base/texutil.rb1097
-rw-r--r--scripts/context/ruby/base/tool.rb291
-rw-r--r--scripts/context/ruby/base/variables.rb132
-rw-r--r--scripts/context/ruby/concheck.rb471
-rw-r--r--scripts/context/ruby/ctxtools.rb2785
-rw-r--r--scripts/context/ruby/fcd_start.rb472
-rw-r--r--scripts/context/ruby/graphics/gs.rb684
-rw-r--r--scripts/context/ruby/graphics/inkscape.rb112
-rw-r--r--scripts/context/ruby/graphics/magick.rb161
-rw-r--r--scripts/context/ruby/imgtopdf.rb86
-rw-r--r--scripts/context/ruby/mpstools.rb7
-rw-r--r--scripts/context/ruby/mtxtools.rb475
-rw-r--r--scripts/context/ruby/pdftools.rb861
-rw-r--r--scripts/context/ruby/pstopdf.rb533
-rw-r--r--scripts/context/ruby/rlxtools.rb368
-rw-r--r--scripts/context/ruby/rscortool.rb63
-rw-r--r--scripts/context/ruby/rsfiltool.rb341
-rw-r--r--scripts/context/ruby/rslibtool.rb114
-rw-r--r--scripts/context/ruby/runtools.rb506
-rw-r--r--scripts/context/ruby/texexec.rb792
-rw-r--r--scripts/context/ruby/texmfstart.rb1277
-rw-r--r--scripts/context/ruby/texsync.rb206
-rw-r--r--scripts/context/ruby/textools.rb1033
-rw-r--r--scripts/context/ruby/texutil.rb93
-rw-r--r--scripts/context/ruby/tmftools.rb165
-rw-r--r--scripts/context/ruby/xmltools.rb656
45 files changed, 20195 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!(/&lt;/o, '<')
+ str.gsub!(/&gt;/o, '>')
+ str.gsub!(/&amp;/o, '&')
+ str.gsub!(/&quot;/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
diff --git a/scripts/context/ruby/base/exa.rb b/scripts/context/ruby/base/exa.rb
new file mode 100644
index 000000000..7ba990cf9
--- /dev/null
+++ b/scripts/context/ruby/base/exa.rb
@@ -0,0 +1,407 @@
+# \setuplayout[width=3cm]
+#
+# tex.setup.setuplayout.width.[integer|real|dimension|string|key]
+# tex.[mp]var.whatever.width.[integer|real|dimension|string|key]
+
+require 'fileutils'
+# require 'ftools'
+require 'digest/md5'
+
+# this can become a lua thing
+
+# no .*? but 0-9a-z\:\. because other too slow (and greedy)
+
+class Hash
+
+ def subset(pattern)
+ h = Hash.new
+ r = /^#{pattern.gsub('.','\.')}/
+ self.keys.each do |k|
+ h[k] = self[k].dup if k =~ r
+ end
+ return h
+ end
+
+end
+
+module ExaEncrypt
+
+ def ExaEncrypt.encrypt_base(logger, oldfilename, newfilename)
+ if FileTest.file?(oldfilename) then
+ logger.report("checking #{oldfilename}") if logger
+ if data = IO.read(oldfilename) then
+ done = false
+ # cfg file:
+ #
+ # banner : exa configuration file
+ # user : domain, name = password, projectlist
+ #
+ if data =~ /^\s*banner\s*\:\s*exa\s*configuration\s*file/ then
+ data.gsub!(/^(\s*user\s*\:\s*.+?\s*\,\s*.+?\s*\=\s*)(.+?)(\s*\,\s*.+\s*)$/) do
+ pre, password, post = $1, $2, $3
+ unless password =~ /MD5:/i then
+ done = true
+ password = "MD5:" + Digest::MD5.hexdigest(password).upcase
+ end
+ "#{pre}#{password}#{post}"
+ end
+ else
+ data.gsub!(/<exa:password([^>]*?)>(.*?)<\/exa:password>/moi) do
+ attributes, password = $1, $2
+ unless password =~ /^([0-9A-F][0-9A-F])+$/ then
+ done = true
+ password = Digest::MD5.hexdigest(password).upcase
+ attributes = " encryption='md5'#{attributes}"
+ end
+ "<exa:password#{attributes}>#{password}</exa:password>"
+ end
+ end
+ begin
+ File.open(newfilename,'w') do |f|
+ f.puts(data)
+ end
+ rescue
+ logger.report("#{newfilename} cannot be written") if logger
+ else
+ logger.report("#{oldfilename} encrypted into #{newfilename}") if done and logger
+ end
+ end
+ end
+ end
+
+end
+
+module ExaModes
+
+ @@modefile = 'examodes.tex'
+
+ @@request = /(<exa:request.*?)(>.*?<\/exa:request>)/mo
+ @@redone = /<exa:request[^>]*?texified=([\'\"])yes\1.*?>/mo
+ @@reload = /<(exa:variable)([^>]+?label\=)([\"\'])([0-9A-Za-z\-\.\:]+?)(\3[^\/]*?)>(.*?)<(\/exa:variable)>/mo
+ @@recalc = /<(exa:variable)([^>]+?label\=)([\"\'])([0-9A-Za-z\-\.\:]+?)([\.\:]calcmath)(\3[^\/]*?)>(.*?)<(\/exa:variable)>/mo
+ @@rename = /<(exa:variable)([^>]+?label\=)([\"\'])([0-9A-Za-z\-\.\:]+?)(\3[^\/]*?)>(.*?)<(\/exa:variable)>/mo
+ @@refile = /<(exa:filename|exa:filelist)>(.*?)<(\/\1)>/mo
+
+ def ExaModes.cleanup_request(logger,filename='request.exa',modefile=@@modefile)
+ begin File.delete(filename+'.raw') ; rescue ; end
+ begin File.delete(modefile) ; rescue ; end
+ if FileTest.file?(filename) then
+ data, done = nil, false
+ begin
+ data = IO.read(filename)
+ rescue
+ data = nil
+ end
+ if data =~ @@request and data !~ @@redone then
+ data.gsub!(@@rename) do
+ done = true
+ '<' + $1 + $2 + $3 + $4 + $5 + '>' +
+ texifiedstr($4,$6) +
+ '<' + $7 + '>'
+ end
+ data.gsub!(@@refile) do
+ done = true
+ '<' + $1 + '>' +
+ cleanpath($2) +
+ '<' + $3 + '>'
+ end
+ data.gsub!(@@recalc) do
+ done = true
+ '<' + $1 + $2 + $3 + $4 + ":raw" + $6 + '>' + $7 + '<' + $8 + '>' +
+ '<' + $1 + $2 + $3 + $4 + $6 + '>' +
+ calculatortexmath($7,false) +
+ '<' + $8 + '>'
+ end
+ if done then
+ data.gsub!(@@request) do
+ $1 + " texified='yes'" + $2
+ end
+ begin File.copy(filename, filename+'.raw') ; rescue ; end
+ begin
+ logger.report("rewriting #{filename}") if logger
+ File.open(filename,'w') do |f|
+ f.puts(data)
+ end
+ rescue
+ logger.report("#{filename} cannot be rewritten") if logger
+ end
+ end
+ else
+ logger.report("#{filename} is already ok") if logger
+ end
+ @variables = Hash.new
+ data.scan(@@reload) do
+ @variables[$4] = $5
+ end
+ vars = @variables.subset('data.tex.var')
+ mpvars = @variables.subset('data.tex.mpvar')
+ modes = @variables.subset('data.tex.mode')
+ setups = @variables.subset('data.tex.setup')
+ if not (modes.empty? and setups.empty? and vars.empty? and mpvars.empty?) then
+ begin
+ File.open(modefile,'w') do |mod|
+ logger.report("saving modes and setups in #{modefile}") if logger
+ if not modes.empty? then
+ for key in modes.keys do
+ k = key.dup
+ k.gsub!(/\./,'-')
+ mod.puts("\\enablemode[#{k}-#{modes[key]}]\n")
+ if modes[key] =~ /(on|yes|start)/o then # ! ! ! ! !
+ mod.puts("\\enablemode[#{k}]\n")
+ end
+ end
+ mod.puts("\n\\readfile{cont-mod}{}{}\n")
+ end
+ if not setups.empty? then
+ for key in setups.keys
+ if key =~ /^(.+?)\.(.+?)\.(.+?)$/o then
+ command, key, type, value = $1, $2, $3, setups[key]
+ value = cleanedup(key,type,value)
+ mod.puts("\\#{$1}[#{key}=#{value}]\n")
+ elsif key =~ /^(.+?)\.(.+?)$/o then
+ command, type, value = $1, $2, setups[key]
+ mod.puts("\\#{$1}[#{value}]\n")
+ end
+ end
+ end
+ savevaroptions(vars, 'setvariables', mod)
+ savevaroptions(mpvars,'setMPvariables',mod)
+ end
+ rescue
+ logger.report("#{modefile} cannot be saved") if logger
+ end
+ else
+ logger.report("#{modefile} is not created") if logger
+ end
+ end
+ end
+
+ private
+
+ def ExaModes.autoparenthesis(str)
+ if str =~ /[\+\-]/o then '[1]' + str + '[1]' else str end
+ end
+
+ def ExaModes.cleanedup(key,type,value)
+ if type == 'dimension' then
+ unless value =~ /(cm|mm|in|bp|sp|pt|dd|em|ex)/o
+ value + 'pt'
+ else
+ value
+ end
+ elsif type == 'calcmath' then
+ '{' + calculatortexmath(value,true) + '}'
+ elsif type =~ /^filename|filelist$/ or key =~ /^filename|filelist$/ then
+ cleanpath(value)
+ else
+ value
+ end
+ end
+
+ def ExaModes.cleanpath(str)
+ (str ||'').gsub(/\\/o,'/')
+ end
+
+ def ExaModes.texifiedstr(key,val)
+ case key
+ when 'filename' then
+ cleanpath(val)
+ when 'filelist' then
+ cleanpath(val)
+ else
+ val
+ end
+ end
+
+ def ExaModes.savevaroptions(vars,setvariables,mod)
+ if not vars.empty? then
+ for key in vars.keys do
+ # var.whatever.width.dimension.value
+ if key =~ /^(.+?)\.(.+?)\.(.+?)$/o then
+ tag, key, type, value = $1, $2, $3, vars[key]
+ value = cleanedup(key,type,value)
+ mod.puts("\\#{setvariables}[#{tag}][#{key}=#{value}]\n")
+ elsif key =~ /^(.+?)\.(.+?)$/o then
+ tag, key, value = $1, $2, vars[key]
+ mod.puts("\\#{setvariables}[#{tag}][#{key}=#{value}]\n")
+ end
+ end
+ end
+ end
+
+ def ExaModes.calculatortexmath(str,tx=true)
+ if tx then
+ bdisp, edisp = "\\displaymath\{", "\}"
+ binln, einln = "\\inlinemath\{" , "\}"
+ egraf = "\\endgraf"
+ else
+ bdisp, edisp = "<displaytexmath>", "</displaytexmath>"
+ binln, einln = "<inlinetexmath>" , "</inlinetexmath>"
+ egraf = "<p/>"
+ end
+ str.gsub!(/\n\s*\n+/moi, "\\ENDGRAF ")
+ str.gsub!(/(\[\[)\s*(.*?)\s*(\]\])/mos) do
+ $1 + docalculatortexmath($2) + $3
+ end
+ str.gsub!(/(\\ENDGRAF)+\s*(\[\[)\s*(.*?)\s*(\]\])/moi) do
+ $1 + bdisp + $3 + edisp
+ end
+ str.gsub!(/(\[\[)\s*(.*?)\s*(\]\])/o) do
+ binln + $2 + einln
+ end
+ str.gsub!(/\\ENDGRAF/mos, egraf)
+ str
+ end
+
+ def ExaModes.docalculatortexmath(str)
+ str.gsub!(/\n/o) { ' ' }
+ str.gsub!(/\s+/o) { ' ' }
+ str.gsub!(/&gt;/o) { '>' }
+ str.gsub!(/&lt;/o) { '<' }
+ str.gsub!(/&.*?;/o) { }
+ level = 0
+ str.gsub!(/([\(\)])/o) do |chr|
+ if chr == '(' then
+ level = level + 1
+ chr = '[' + level.to_s + ']'
+ elsif chr == ')' then
+ chr = '[' + level.to_s + ']'
+ level = level - 1
+ end
+ chr
+ end
+ # ...E...
+ loop do
+ break unless str.gsub!(/([\d\.]+)E([\-\+]{0,1}[\d\.]+)/o) do
+ "\{\\SCINOT\{#{$1}\}\{#{$2}\}\}"
+ end
+ end
+ # ^-..
+ loop do
+ break unless str.gsub!(/\^([\-\+]*\d+)/o) do
+ "\^\{#{$1}\}"
+ end
+ end
+ # ^(...)
+ loop do
+ break unless str.gsub!(/\^(\[\d+\])(.*?)\1/o) do
+ "\^\{#{$2}\}"
+ end
+ end
+ # 1/x^2
+ loop do
+ break unless str.gsub!(/([\d\w\.]+)\/([\d\w\.]+)\^([\d\w\.]+)/o) do
+ "@\{#{$1}\}\{#{$2}\^\{#{$3}\}\}"
+ end
+ end
+ # int(a,b,c)
+ loop do
+ break unless str.gsub!(/(int|sum|prod)(\[\d+\])(.*?),(.*?),(.*?)\2/o) do
+ "\\#{$1.upcase}\^\{#{$4}\}\_\{#{$5}\}\{#{autoparenthesis($3)}\}"
+ end
+ end
+ # int(a,b)
+ loop do
+ break unless str.gsub!(/(int|sum|prod)(\[\d+\])(.*?),(.*?)\2/o) do
+ "\\#{$1.upcase}\_\{#{$4}\}\{#{autoparenthesis($3)}\}"
+ end
+ end
+ # int(a)
+ loop do
+ break unless str.gsub!(/(int|sum|prod)(\[\d+\])(.*?)\2/o) do
+ "\\#{$1.upcase}\{#{autoparenthesis($3)}\}"
+ end
+ end
+ # sin(x) => {\sin(x)}
+ loop do
+ break unless str.gsub!(/(median|min|max|round|sqrt|sin|cos|tan|sinh|cosh|tanh|ln|log)\s*(\[\d+\])(.*?)\2/o) do
+ "\{\\#{$1.upcase}\{#{$2}#{$3}#{$2}\}\}"
+ end
+ end
+ # mean
+ str.gsub!(/(mean)(\[\d+\])(.*?)\2/o) do
+ "\{\\OVERLINE\{#{$3}\}\}"
+ end
+ # sin x => {\sin(x)}
+ # ...
+ # (1+x)/(1+x) => \frac{1+x}{1+x}
+ loop do
+ break unless str.gsub!(/(\[\d+\])(.*?)\1\/(\[\d+\])(.*?)\3/o) do
+ "@\{#{$2}\}\{#{$4}\}"
+ end
+ end
+ # (1+x)/x => \frac{1+x}{x}
+ loop do
+ break unless str.gsub!(/(\[\d+\])(.*?)\1\/([a-zA-Z0-9]+)/o) do
+ "@\{#{$2}\}\{#{$3}\}"
+ end
+ end
+ # 1/(1+x) => \frac{1}{1+x}
+ loop do
+ break unless str.gsub!(/([a-zA-Z0-9]+)\/(\[\d+\])(.*?)\2/o) do
+ "@\{#{$1}\}\{#{$3}\}"
+ end
+ end
+ # 1/x => \frac{1}{x}
+ loop do
+ break unless str.gsub!(/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)/o) do
+ "@\{#{$1}\}\{#{$2}\}"
+ end
+ end
+ #
+ str.gsub!(/\@/o) do
+ "\\FRAC "
+ end
+ str.gsub!(/\*/o) do
+ " "
+ end
+ str.gsub!(/\<\=/o) do
+ "\\LE "
+ end
+ str.gsub!(/\>\=/o) do
+ "\\GE "
+ end
+ str.gsub!(/\=/o) do
+ "\\EQ "
+ end
+ str.gsub!(/\</o) do
+ "\\LT "
+ end
+ str.gsub!(/\>/) do
+ "\\GT "
+ end
+ str.gsub!(/(D)(\[\d+\])(.*?)\2/o) do
+ "\{\\FRAC\{\\MBOX{d}\}\{\\MBOX{d}x\}\{#{$2}#{$3}#{$2}\}\}"
+ end
+ str.gsub!(/(exp)(\[\d+\])(.*?)\2/o) do
+ "\{e^\{#{$3}\}\}"
+ end
+ str.gsub!(/(abs)(\[\d+\])(.*?)\2/o) do
+ "\{\\left\|#{$3}\\right\|\}"
+ end
+ str.gsub!(/D([x|y])/o) do
+ "\\FRAC\{\{\\rm d\}#{$1}\}\{\{\\rm d\}x\}"
+ end
+ str.gsub!(/D([f|g])(\[\d+\])(.*?)\2/o) do
+ "\{\\rm #{$1}\}'#{$2}#{$3}#{$2}"
+ end
+ str.gsub!(/([f|g])(\[\d+\])(.*?)\2/o) do
+ "\{\\rm #{$1}\}#{$2}#{$3}#{$2}"
+ end
+ str.gsub!(/(pi|inf)/io) do
+ "\\#{$1} "
+ end
+ loop do
+ break unless str.gsub!(/(\[\d+?\])(.*?)\1/o) do
+ "\\left(#{$2}\\right)"
+ end
+ end
+ str.gsub!(/\\([A-Z]+?)([\s\{\^\_\\])/io) do
+ "\\#{$1.downcase}#{$2}"
+ end
+ str
+ end
+
+end
+
+# ExaModes.cleanup_request()
diff --git a/scripts/context/ruby/base/file.rb b/scripts/context/ruby/base/file.rb
new file mode 100644
index 000000000..1aeac5fd6
--- /dev/null
+++ b/scripts/context/ruby/base/file.rb
@@ -0,0 +1,150 @@
+# module : base/file
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+require 'fileutils'
+# require 'ftools'
+
+class File
+
+ def File.suffixed(name,sufa,sufb=nil)
+ if sufb then
+ if sufa.empty? then
+ unsuffixed(name) + ".#{sufb}"
+ else
+ unsuffixed(name) + "-#{sufa}.#{sufb}"
+ end
+ else
+ unsuffixed(name) + ".#{sufa}"
+ end
+ end
+
+ def File.unsuffixed(name)
+ name.sub(/\.[^\.]*?$/o, '')
+ end
+
+ def File.suffix(name,default='')
+ if name =~ /\.([^\.]*?)$/o then
+ $1
+ else
+ default
+ end
+ end
+
+ def File.splitname(name,suffix='')
+ if name =~ /^(.*)\.([^\.]*?)$/o then
+ [$1, $2]
+ else
+ [name, suffix]
+ end
+ end
+
+end
+
+class File
+
+ def File.silentopen(name,method='r')
+ begin
+ f = File.open(name,method)
+ rescue
+ return nil
+ else
+ return f
+ end
+ end
+
+ def File.silentread(name)
+ begin
+ data = IO.read(name)
+ rescue
+ return nil
+ else
+ return data
+ end
+ end
+
+ def File.atleast?(name,n=0)
+ begin
+ size = FileTest.size(name)
+ rescue
+ return false
+ else
+ return size > n
+ end
+ end
+
+ def File.appended(name,str='')
+ if FileTest.file?(name) then
+ begin
+ if f = File.open(name,'a') then
+ f << str
+ f.close
+ return true
+ end
+ rescue
+ end
+ end
+ return false
+ end
+
+ def File.written(name,str='')
+ begin
+ if f = File.open(name,'w') then
+ f << str
+ f.close
+ return true
+ end
+ rescue
+ end
+ return false
+ end
+
+ def File.silentdelete(filename)
+ File.delete(filename) rescue false
+ end
+
+ def File.silentcopy(oldname,newname)
+ return if File.expand_path(oldname) == File.expand_path(newname)
+ FileUtils.makedirs(File.dirname(newname)) rescue false
+ File.copy(oldname,newname) rescue false
+ end
+
+ def File.silentrename(oldname,newname)
+ # in case of troubles, we just copy the file; we
+ # maybe working over multiple file systems or
+ # apps may have mildly locked files (like gs does)
+ return if File.expand_path(oldname) == File.expand_path(newname)
+ File.delete(newname) rescue false
+ begin
+ File.rename(oldname,newname)
+ rescue
+ FileUtils.makedirs(File.dirname(newname)) rescue false
+ File.copy(oldname,newname) rescue false
+ end
+ end
+
+end
+
+class File
+
+ # handles "c:\tmp\test.tex" as well as "/${TEMP}/test.tex")
+
+ def File.unixfied(filename)
+ begin
+ str = filename.gsub(/\$\{*([a-z0-9\_]+)\}*/oi) do
+ if ENV.key?($1) then ENV[$1] else $1 end
+ end
+ str.gsub(/[\/\\]+/o, '/')
+ rescue
+ filename
+ end
+ end
+
+end
+
diff --git a/scripts/context/ruby/base/kpse.rb b/scripts/context/ruby/base/kpse.rb
new file mode 100644
index 000000000..0f9868784
--- /dev/null
+++ b/scripts/context/ruby/base/kpse.rb
@@ -0,0 +1,389 @@
+# module : base/kpse
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# rename this one to environment
+#
+# todo: web2c vs miktex module and include in kpse
+
+require 'rbconfig'
+require 'fileutils'
+
+# beware $engine is lowercase in kpse
+#
+# miktex has mem|fmt|base paths
+
+class String
+
+ def split_path
+ if self =~ /\;/o || self =~ /^[a-z]\:/io then
+ self.split(";")
+ else
+ self.split(":")
+ end
+ end
+
+ def sane_path
+ self.gsub(/\\/,'/')
+ end
+
+end
+
+class Array
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+ def non_empty
+ self.delete_if do |i|
+ (i == nil || i.empty?) rescue false
+ end
+ end
+
+end
+
+module Kpse
+
+ @@located = Hash.new
+ @@paths = Hash.new
+ @@scripts = Hash.new
+ @@formats = ['tex','texmfscripts','other text files']
+ @@progname = 'context'
+ @@ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ @@problems = false
+ @@tracing = false
+ @@distribution = 'web2c'
+ @@crossover = true
+ @@mswindows = Config::CONFIG['host_os'] =~ /mswin/
+
+ # @@distribution = 'miktex' if ENV['PATH'] =~ /miktex[\\\/]bin/o
+
+ # if ENV['PATH'] =~ /(.*?)miktex[\\\/]bin/i then
+ # @@distribution = 'miktex' unless $1 =~ /(texmf\-mswin[\/\\]bin|bin[\/\\]win32)/i
+ # end
+
+ if @@mswindows && (ENV['PATH'] =~ /(.*?)miktex[\\\/]bin/i) then
+ @@distribution = 'miktex' unless $1 =~ /(texmf\-mswin[\/\\]bin|bin[\/\\]win32)/i
+ end
+
+ @@re_true = /yes|on|true|1/i
+
+ if (ENV['KPSEFAST'] =~ @@re_true) || (ENV['CTXMINIMAL'] =~ @@re_true) then
+ @@usekpserunner = true
+ require 'base/kpsefast'
+ require 'base/kpserunner'
+ else
+ @@usekpserunner = false
+ end
+
+ if @@crossover then
+ ENV.keys.each do |k|
+ case k
+ when /\_CTX\_KPSE\_V\_(.*?)\_/io then @@located[$1] = ENV[k].dup
+ when /\_CTX\_KPSE\_P\_(.*?)\_/io then @@paths [$1] = ENV[k].dup.split(';')
+ when /\_CTX\_KPSE\_S\_(.*?)\_/io then @@scripts[$1] = ENV[k].dup
+ end
+ end
+ end
+
+ def Kpse.distribution
+ @@distribution
+ end
+
+ def Kpse.miktex?
+ @@distribution == 'miktex'
+ end
+
+ def Kpse.web2c?
+ @@distribution == 'web2c'
+ end
+
+ def Kpse.inspect
+ @@located.keys.sort.each do |k| puts("located : #{k} -> #{@@located[k]}\n") end
+ @@paths .keys.sort.each do |k| puts("paths : #{k} -> #{@@paths [k]}\n") end
+ @@scripts.keys.sort.each do |k| puts("scripts : #{k} -> #{@@scripts[k]}\n") end
+ end
+
+ def Kpse.used_path(varname)
+ begin
+ if @@mswindows then
+ path = run("--expand-path=\$#{varname}") rescue ''
+ else
+ path = run("--expand-path='$#{varname}'") rescue ''
+ end
+ rescue
+ path = ''
+ end
+ return path.sane_path
+ end
+
+ def Kpse.found(filename, progname=nil, format=nil)
+ begin
+ tag = Kpse.key(filename) # all
+ if @@located.key?(tag) then
+ return @@located[tag].sane_path
+ elsif FileTest.file?(filename) then
+ setvariable(tag,filename)
+ return filename
+ elsif FileTest.file?(File.join(@@ownpath,filename)) then
+ setvariable(tag,File.join(@@ownpath,filename))
+ return @@located[tag].sane_path
+ else
+ [progname,@@progname].flatten.compact.uniq.each do |prg|
+ [format,@@formats].flatten.compact.uniq.each do |fmt|
+ begin
+ tag = Kpse.key(filename,prg,fmt)
+ if @@located.key?(tag) then
+ return @@located[tag].sane_path
+ elsif p = Kpse.kpsewhich(filename,prg,fmt) then
+ setvariable(tag,p.chomp)
+ return @@located[tag].sane_path
+ end
+ rescue
+ end
+ end
+ end
+ setvariable(tag,filename)
+ return filename.sane_path
+ end
+ rescue
+ filename.sane_path
+ end
+ end
+
+ def Kpse.kpsewhich(filename,progname,format)
+ p = if progname && ! progname.empty? then "-progname=#{progname}" else '' end
+ f = if format && ! format.empty? then "-format=\"#{format}\"" else '' end
+ Kpse.run("#{p} #{f} #{filename}")
+ end
+
+ def Kpse.which
+ Kpse.kpsewhich
+ end
+
+ def Kpse.run(arguments)
+ puts arguments if @@tracing
+ begin
+ if @@problems then
+ results = ''
+ elsif @@usekpserunner then
+ results = KpseRunner.kpsewhich(arguments).chomp
+ else
+ results = `kpsewhich #{arguments}`.chomp
+ end
+ rescue
+ puts "unable to run kpsewhich" if @@tracing
+ @@problems, results = true, ''
+ end
+ puts results if @@tracing
+ return results
+ end
+
+
+ def Kpse.formatpaths
+ # maybe we should check for writeability
+ unless @@paths.key?('formatpaths') then
+ begin
+ setpath('formatpaths',run("--show-path=fmt").sane_path.split_path)
+ rescue
+ setpath('formatpaths',[])
+ end
+ end
+ return @@paths['formatpaths']
+ end
+
+ def Kpse.key(filename='',progname='all',format='all')
+ [progname,format,filename].join('-')
+ end
+
+ def Kpse.formatpath(engine='pdftex',enginepath=true)
+
+ # because engine support in distributions is not always
+ # as we expect, we need to check for it;
+
+ # todo: miktex
+
+ if miktex? then
+ return '.'
+ else
+ unless @@paths.key?(engine) then
+ # savedengine = ENV['engine']
+ if ENV['TEXFORMATS'] && ! ENV['TEXFORMATS'].empty? then
+ # make sure that we have a lowercase entry
+ ENV['TEXFORMATS'] = ENV['TEXFORMATS'].sub(/\$engine/io,"\$engine")
+ # well, we will append anyway, so we could also strip it
+ # ENV['TEXFORMATS'] = ENV['TEXFORMATS'].sub(/\$engine/io,"")
+ end
+ # use modern method
+ if enginepath then
+ formatpath = run("--engine=#{engine} --show-path=fmt")
+ else
+ # ENV['engine'] = engine if engine
+ formatpath = run("--show-path=fmt")
+ end
+ # use ancient method
+ if formatpath.empty? then
+ if enginepath then
+ if @@mswindows then
+ formatpath = run("--engine=#{engine} --expand-path=\$TEXFORMATS")
+ else
+ formatpath = run("--engine=#{engine} --expand-path=\\\$TEXFORMATS")
+ end
+ end
+ # either no enginepath or failed run
+ if formatpath.empty? then
+ if @@mswindows then
+ formatpath = run("--expand-path=\$TEXFORMATS")
+ else
+ formatpath = run("--expand-path=\\\$TEXFORMATS")
+ end
+ end
+ end
+ # locate writable path
+ if ! formatpath.empty? then
+ formatpaths, done = formatpath.split_path, false
+ formatpaths.collect! do |fp|
+ fp.gsub!(/\\/o,'/')
+ fp.gsub!(/\/\/$/o,'/')
+ # remove funny patterns
+ fp.sub!(/^!!/o,'')
+ fp.sub!(/\/+$/o,'')
+ fp.sub!(/(unsetengine|unset)/o,if enginepath then engine else '' end)
+ fp
+ end
+ formatpaths.delete_if do |fp|
+ fp.empty? || fp == '.'
+ end
+ # the engine path may not yet be present, find first writable
+ formatpaths.each do |fp|
+ # strip (possible engine) and test for writeability
+ fpp = fp.sub(/#{engine}\/*$/o,'')
+ if FileTest.directory?(fpp) && FileTest.writable?(fpp) then
+ # use this path
+ formatpath, done = fp.dup, true
+ break
+ end
+ end
+ unless done then
+ formatpaths.each do |fp|
+ fpp = fp.sub(/#{engine}\/*$/o,'')
+ FileUtils.makedirs(fpp) rescue false # maybe we don't have an path yet
+ if FileTest.directory?(fpp) && FileTest.writable?(fpp) then
+ # use this path
+ formatpath, done = fp.dup, true
+ break
+ end
+ end
+ end
+ unless done then
+ formatpath = '.'
+ end
+ end
+ # needed !
+ FileUtils.makedirs(formatpath) rescue false
+ # fall back to current path
+ formatpath = '.' if formatpath.empty? || ! FileTest.writable?(formatpath)
+ # append engine but prevent duplicates
+ formatpath = File.join(formatpath.sub(/\/*#{engine}\/*$/,''), engine) if enginepath
+ FileUtils.makedirs(formatpath) rescue false
+ setpath(engine,formatpath)
+ # ENV['engine'] = savedengine
+ end
+ return @@paths[engine].first
+ end
+ end
+
+ def Kpse.update
+ system('initexmf -u') if Kpse.miktex?
+ system('mktexlsr')
+ end
+
+ # engine support is either broken of not implemented in some
+ # distributions, so we need to take care of it ourselves (without
+ # delays due to kpse calls); there can be many paths in the string
+ #
+ # in a year or so, i will drop this check
+
+ def Kpse.fixtexmfvars(engine=nil)
+ ENV['ENGINE'] = engine if engine
+ texformats = if ENV['TEXFORMATS'] then ENV['TEXFORMATS'].dup else '' end
+ if texformats.empty? then
+ if engine then
+ if @@mswindows then
+ texformats = `kpsewhich --engine=#{engine} --expand-var=\$TEXFORMATS`.chomp
+ else
+ texformats = `kpsewhich --engine=#{engine} --expand-var=\\\$TEXFORMATS`.chomp
+ end
+ else
+ if @@mswindows then
+ texformats = `kpsewhich --expand-var=\$TEXFORMATS`.chomp
+ else
+ texformats = `kpsewhich --expand-var=\\\$TEXFORMATS`.chomp
+ end
+ end
+ end
+ if engine then
+ texformats.sub!(/unsetengine/,engine)
+ else
+ texformats.sub!(/unsetengine/,"\$engine")
+ end
+ if engine && (texformats =~ /web2c[\/\\].*#{engine}/o) then
+ # ok, engine is seen
+ return false
+ elsif texformats =~ /web2c[\/\\].*\$engine/io then
+ # shouldn't happen
+ return false
+ else
+ ENV['TEXFORMATS'] = texformats.gsub(/(web2c\/\{)(,\})/o) do
+ "#{$1}\$engine#{$2}"
+ end
+ if texformats !~ /web2c[\/\\].*\$engine/io then
+ ENV['TEXFORMATS'] = texformats.gsub(/web2c\/*/, "web2c/{\$engine,}")
+ end
+ return true
+ end
+ end
+
+ def Kpse.runscript(name,filename=[],options=[])
+ setscript(name,`texmfstart --locate #{name}`) unless @@scripts.key?(name)
+ cmd = "#{@@scripts[name]} #{[options].flatten.join(' ')} #{[filename].flatten.join(' ')}"
+ system(cmd)
+ end
+
+ def Kpse.pipescript(name,filename=[],options=[])
+ setscript(name,`texmfstart --locate #{name}`) unless @@scripts.key?(name)
+ cmd = "#{@@scripts[name]} #{[options].flatten.join(' ')} #{[filename].flatten.join(' ')}"
+ `#{cmd}`
+ end
+
+ def Kpse.searchmethod
+ if @@usekpserunner then 'kpsefast' else 'kpsewhich' end
+ end
+
+ private
+
+ def Kpse.setvariable(key,value)
+ @@located[key] = value
+ ENV["_CTX_K_V_#{key}_"] = @@located[key] if @@crossover
+ end
+
+ def Kpse.setscript(key,value)
+ @@scripts[key] = value
+ ENV["_CTX_K_S_#{key}_"] = @@scripts[key] if @@crossover
+ end
+
+ def Kpse.setpath(key,value)
+ @@paths[key] = [value].flatten.uniq.collect do |p|
+ p.sub(/^!!/,'').sub(/\/*$/,'')
+ end
+ ENV["_CTX_K_P_#{key}_"] = @@paths[key].join(';') if @@crossover
+ end
+
+end
diff --git a/scripts/context/ruby/base/kpse/drb.rb b/scripts/context/ruby/base/kpse/drb.rb
new file mode 100644
index 000000000..db1ce0eec
--- /dev/null
+++ b/scripts/context/ruby/base/kpse/drb.rb
@@ -0,0 +1,57 @@
+require 'drb'
+require 'base/kpse/trees'
+
+class KpseServer
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ end
+
+ def start
+ puts "starting drb service at port #{@port}"
+ DRb.start_service("druby://localhost:#{@port}", KpseTrees.new)
+ trap(:INT) do
+ DRb.stop_service
+ end
+ DRb.thread.join
+ end
+
+ def stop
+ # todo
+ end
+
+end
+
+class KpseClient
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @kpse = nil
+ end
+
+ def start
+ # only needed when callbacks are used / slow, due to Socket::getaddrinfo
+ # DRb.start_service
+ end
+
+ def object
+ @kpse = DRbObject.new(nil,"druby://localhost:#{@port}")
+ end
+
+end
+
+
+# SERVER_URI="druby://localhost:8787"
+#
+# # Start a local DRbServer to handle callbacks.
+# #
+# # Not necessary for this small example, but will be required
+# # as soon as we pass a non-marshallable object as an argument
+# # to a dRuby call.
+# DRb.start_service
+#
+# timeserver = DRbObject.new_with_uri(SERVER_URI)
diff --git a/scripts/context/ruby/base/kpse/soap.rb b/scripts/context/ruby/base/kpse/soap.rb
new file mode 100644
index 000000000..c9ed75c44
--- /dev/null
+++ b/scripts/context/ruby/base/kpse/soap.rb
@@ -0,0 +1,79 @@
+require 'soap/rpc/standaloneServer'
+require 'soap/rpc/driver'
+
+require 'base/kpse/trees'
+
+class KpseService < SOAP::RPC::StandaloneServer
+
+ def on_init
+ kpse = KpseTrees.new
+ add_method(kpse, 'choose', 'files', 'environment')
+ add_method(kpse, 'load', 'files', 'environment')
+ add_method(kpse, 'expand_variables', 'tree')
+ add_method(kpse, 'expand_braces', 'tree', 'str')
+ add_method(kpse, 'expand_path', 'tree', 'str')
+ add_method(kpse, 'expand_var', 'tree', 'str')
+ add_method(kpse, 'show_path', 'tree', 'str')
+ add_method(kpse, 'var_value', 'tree', 'str')
+ add_method(kpse, 'find_file', 'tree', 'filename')
+ add_method(kpse, 'find_files', 'tree', 'filename', 'first')
+ end
+
+end
+
+class KpseServer
+
+ @@url = 'http://kpse.thismachine.org/KpseService'
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @server = nil
+ end
+
+ def start
+ puts "starting soap service at port #{@port}"
+ @server = KpseService.new('KpseServer', @@url, '0.0.0.0', @port.to_i)
+ trap(:INT) do
+ @server.shutdown
+ end
+ status = @server.start
+ end
+
+ def stop
+ @server.shutdown rescue false
+ end
+
+end
+
+class KpseClient
+
+ @@url = 'http://kpse.thismachine.org/KpseService'
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @kpse = nil
+ end
+
+ def start
+ @kpse = SOAP::RPC::Driver.new("http://localhost:#{port}/", @@url)
+ @kpse.add_method('choose','files', 'environment')
+ @kpse.add_method('load','files', 'environment')
+ @kpse.add_method('expand_variables', 'tree')
+ @kpse.add_method('expand_braces', 'tree', 'str')
+ @kpse.add_method('expand_path', 'tree', 'str')
+ @kpse.add_method('expand_var', 'tree', 'str')
+ @kpse.add_method('show_path', 'tree', 'str')
+ @kpse.add_method('var_value', 'tree', 'str')
+ @kpse.add_method('find_file', 'tree', 'filename')
+ @kpse.add_method('find_files', 'tree', 'filename', 'first')
+ end
+
+ def object
+ @kpse
+ end
+
+end
diff --git a/scripts/context/ruby/base/kpse/trees.rb b/scripts/context/ruby/base/kpse/trees.rb
new file mode 100644
index 000000000..9c872eb18
--- /dev/null
+++ b/scripts/context/ruby/base/kpse/trees.rb
@@ -0,0 +1,84 @@
+require 'monitor'
+require 'base/kpsefast'
+
+class KpseTrees < Monitor
+
+ def initialize
+ @trees = Hash.new
+ end
+
+ def pattern(filenames)
+ filenames.join('|').gsub(/\\+/o,'/').downcase
+ end
+
+ def choose(filenames,environment)
+ current = pattern(filenames)
+ load(filenames,environment) unless @trees[current]
+ puts "enabling tree #{current}"
+ current
+ end
+
+ def fetch(filenames,environment) # will send whole object !
+ current = pattern(filenames)
+ load(filenames,environment) unless @trees[current]
+ puts "fetching tree #{current}"
+ @trees[current]
+ end
+
+ def load(filenames,environment)
+ current = pattern(filenames)
+ puts "loading tree #{current}"
+ @trees[current] = KpseFast.new
+ @trees[current].push_environment(environment)
+ @trees[current].load_cnf(filenames)
+ @trees[current].expand_variables
+ @trees[current].load_lsr
+ end
+
+ def set(tree,key,value)
+ case key
+ when 'progname' then @trees[tree].progname = value
+ when 'engine' then @trees[tree].engine = value
+ when 'format' then @trees[tree].format = value
+ end
+ end
+ def get(tree,key)
+ case key
+ when 'progname' then @trees[tree].progname
+ when 'engine' then @trees[tree].engine
+ when 'format' then @trees[tree].format
+ end
+ end
+
+ def load_cnf(tree)
+ @trees[tree].load_cnf
+ end
+ def load_lsr(tree)
+ @trees[tree].load_lsr
+ end
+ def expand_variables(tree)
+ @trees[tree].expand_variables
+ end
+ def expand_braces(tree,str)
+ @trees[tree].expand_braces(str)
+ end
+ def expand_path(tree,str)
+ @trees[tree].expand_path(str)
+ end
+ def expand_var(tree,str)
+ @trees[tree].expand_var(str)
+ end
+ def show_path(tree,str)
+ @trees[tree].show_path(str)
+ end
+ def var_value(tree,str)
+ @trees[tree].var_value(str)
+ end
+ def find_file(tree,filename)
+ @trees[tree].find_file(filename)
+ end
+ def find_files(tree,filename,first)
+ @trees[tree].find_files(filename,first)
+ end
+
+end
diff --git a/scripts/context/ruby/base/kpsedirect.rb b/scripts/context/ruby/base/kpsedirect.rb
new file mode 100644
index 000000000..6fa8c8601
--- /dev/null
+++ b/scripts/context/ruby/base/kpsedirect.rb
@@ -0,0 +1,34 @@
+class KpseDirect
+
+ attr_accessor :progname, :format, :engine
+
+ def initialize
+ @progname, @format, @engine = '', '', ''
+ end
+
+ def expand_path(str)
+ clean_name(`kpsewhich -expand-path=#{str}`.chomp)
+ end
+
+ def expand_var(str)
+ clean_name(`kpsewhich -expand-var=#{str}`.chomp)
+ end
+
+ def find_file(str)
+ clean_name(`kpsewhich #{_progname_} #{_format_} #{str}`.chomp)
+ end
+
+ def _progname_
+ if @progname.empty? then '' else "-progname=#{@progname}" end
+ end
+ def _format_
+ if @format.empty? then '' else "-format=\"#{@format}\"" end
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
diff --git a/scripts/context/ruby/base/kpsefast.rb b/scripts/context/ruby/base/kpsefast.rb
new file mode 100644
index 000000000..8a9f89593
--- /dev/null
+++ b/scripts/context/ruby/base/kpsefast.rb
@@ -0,0 +1,934 @@
+# module : base/kpsefast
+# 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: multiple cnf files
+#
+# todo: cleanup, string or table store (as in lua variant)
+
+class String
+
+ def split_path
+ if self =~ /\;/o || self =~ /^[a-z]\:/io then
+ self.split(";")
+ else
+ self.split(":")
+ end
+ end
+
+end
+
+class Array
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+end
+
+class File
+
+ def File.locate_file(path,name)
+ begin
+ files = Dir.entries(path)
+ if files.include?(name) then
+ fullname = File.join(path,name)
+ return fullname if FileTest.file?(fullname)
+ end
+ files.each do |p|
+ fullname = File.join(path,p)
+ if p != '.' and p != '..' and FileTest.directory?(fullname) and result = locate_file(fullname,name) then
+ return result
+ end
+ end
+ rescue
+ # bad path
+ end
+ return nil
+ end
+
+ def File.glob_file(pattern)
+ return Dir.glob(pattern).first
+ end
+
+end
+
+module KpseUtil
+
+ # to be adapted, see loading cnf file
+
+ @@texmftrees = ['texmf-local','texmf.local','../..','texmf'] # '../..' is for gwtex
+ @@texmfcnf = 'texmf.cnf'
+
+ def KpseUtil::identify
+ # we mainly need to identify the local tex stuff and wse assume that
+ # the texmfcnf variable is set; otherwise we need to expand the
+ # TEXMF variable and that takes time since it may involve more
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ ENV['SELFAUTOPARENT'] = ownpath
+ else
+ ENV['SELFAUTOPARENT'] = '.' # fall back
+ # may be too tricky:
+ #
+ # (ENV['PATH'] ||'').split_path.each do |p|
+ # if p.gsub!(/texmf.*?$/o, '') then
+ # ENV['SELFAUTOPARENT'] = p
+ # break
+ # end
+ # end
+ end
+ filenames = Array.new
+ if ENV['TEXMFCNF'] && ! ENV['TEXMFCNF'].empty? then
+ ENV['TEXMFCNF'].to_s.split_path.each do |path|
+ filenames << File.join(path,@@texmfcnf)
+ end
+ elsif ENV['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.',@@texmfcnf)
+ else
+ @@texmftrees.each do |tree|
+ filenames << File.join(ENV['SELFAUTOPARENT'],tree,'web2c',@@texmfcnf)
+ end
+ end
+ loop do
+ busy = false
+ filenames.collect! do |f|
+ f.gsub(/\$([a-zA-Z0-9\_\-]+)/o) do
+ if (! ENV[$1]) || (ENV[$1] == $1) then
+ "$#{$1}"
+ else
+ busy = true
+ ENV[$1]
+ end
+ end
+ end
+ break unless busy
+ end
+ filenames.delete_if do |f|
+ ! FileTest.file?(f)
+ end
+ return filenames
+ end
+
+ def KpseUtil::environment
+ Hash.new.merge(ENV)
+ end
+
+end
+
+class KpseFast
+
+ # formats are an incredible inconsistent mess
+
+ @@suffixes = Hash.new
+ @@formats = Hash.new
+ @@suffixmap = Hash.new
+
+ @@texmfcnf = 'texmf.cnf'
+
+ @@suffixes['gf'] = ['.<resolution>gf'] # todo
+ @@suffixes['pk'] = ['.<resolution>pk'] # todo
+ @@suffixes['tfm'] = ['.tfm']
+ @@suffixes['afm'] = ['.afm']
+ @@suffixes['base'] = ['.base']
+ @@suffixes['bib'] = ['.bib']
+ @@suffixes['bst'] = ['.bst']
+ @@suffixes['cnf'] = ['.cnf']
+ @@suffixes['ls-R'] = ['ls-R', 'ls-r']
+ @@suffixes['fmt'] = ['.fmt', '.efmt', '.efm', '.ofmt', '.ofm', '.oft', '.eofmt', '.eoft', '.eof', '.pfmt', '.pfm', '.epfmt', '.epf', '.xpfmt', '.xpf', '.afmt', '.afm']
+ @@suffixes['map'] = ['.map']
+ @@suffixes['mem'] = ['.mem']
+ @@suffixes['mf'] = ['.mf']
+ @@suffixes['mfpool'] = ['.pool']
+ @@suffixes['mft'] = ['.mft']
+ @@suffixes['mp'] = ['.mp']
+ @@suffixes['mppool'] = ['.pool']
+ @@suffixes['ocp'] = ['.ocp']
+ @@suffixes['ofm'] = ['.ofm', '.tfm']
+ @@suffixes['opl'] = ['.opl']
+ @@suffixes['otp'] = ['.otp']
+ @@suffixes['ovf'] = ['.ovf']
+ @@suffixes['ovp'] = ['.ovp']
+ @@suffixes['graphic/figure'] = ['.eps', '.epsi']
+ @@suffixes['tex'] = ['.tex']
+ @@suffixes['texpool'] = ['.pool']
+ @@suffixes['PostScript header'] = ['.pro']
+ @@suffixes['type1 fonts'] = ['.pfa', '.pfb']
+ @@suffixes['vf'] = ['.vf']
+ @@suffixes['ist'] = ['.ist']
+ @@suffixes['truetype fonts'] = ['.ttf', '.ttc']
+ @@suffixes['web'] = ['.web', '.ch']
+ @@suffixes['cweb'] = ['.w', '.web', '.ch']
+ @@suffixes['enc files'] = ['.enc']
+ @@suffixes['cmap files'] = ['.cmap']
+ @@suffixes['subfont definition files'] = ['.sfd']
+ @@suffixes['lig files'] = ['.lig']
+ @@suffixes['bitmap font'] = []
+ @@suffixes['MetaPost support'] = []
+ @@suffixes['TeX system documentation'] = []
+ @@suffixes['TeX system sources'] = []
+ @@suffixes['Troff fonts'] = []
+ @@suffixes['dvips config'] = []
+ @@suffixes['type42 fonts'] = []
+ @@suffixes['web2c files'] = []
+ @@suffixes['other text files'] = []
+ @@suffixes['other binary files'] = []
+ @@suffixes['misc fonts'] = []
+ @@suffixes['opentype fonts'] = []
+ @@suffixes['pdftex config'] = []
+ @@suffixes['texmfscripts'] = []
+
+ # replacements
+
+ @@suffixes['fmt'] = ['.fmt']
+ @@suffixes['type1 fonts'] = ['.pfa', '.pfb', '.pfm']
+ @@suffixes['tex'] = ['.tex', '.xml']
+ @@suffixes['texmfscripts'] = ['rb','lua','py','pl']
+
+ @@suffixes.keys.each do |k| @@suffixes[k].each do |s| @@suffixmap[s] = k end end
+
+ # TTF2TFMINPUTS
+ # MISCFONTS
+ # TEXCONFIG
+ # DVIPDFMINPUTS
+ # OTFFONTS
+
+ @@formats['gf'] = ''
+ @@formats['pk'] = ''
+ @@formats['tfm'] = 'TFMFONTS'
+ @@formats['afm'] = 'AFMFONTS'
+ @@formats['base'] = 'MFBASES'
+ @@formats['bib'] = ''
+ @@formats['bst'] = ''
+ @@formats['cnf'] = ''
+ @@formats['ls-R'] = ''
+ @@formats['fmt'] = 'TEXFORMATS'
+ @@formats['map'] = 'TEXFONTMAPS'
+ @@formats['mem'] = 'MPMEMS'
+ @@formats['mf'] = 'MFINPUTS'
+ @@formats['mfpool'] = 'MFPOOL'
+ @@formats['mft'] = ''
+ @@formats['mp'] = 'MPINPUTS'
+ @@formats['mppool'] = 'MPPOOL'
+ @@formats['ocp'] = 'OCPINPUTS'
+ @@formats['ofm'] = 'OFMFONTS'
+ @@formats['opl'] = 'OPLFONTS'
+ @@formats['otp'] = 'OTPINPUTS'
+ @@formats['ovf'] = 'OVFFONTS'
+ @@formats['ovp'] = 'OVPFONTS'
+ @@formats['graphic/figure'] = ''
+ @@formats['tex'] = 'TEXINPUTS'
+ @@formats['texpool'] = 'TEXPOOL'
+ @@formats['PostScript header'] = 'TEXPSHEADERS'
+ @@formats['type1 fonts'] = 'T1FONTS'
+ @@formats['vf'] = 'VFFONTS'
+ @@formats['ist'] = ''
+ @@formats['truetype fonts'] = 'TTFONTS'
+ @@formats['web'] = ''
+ @@formats['cweb'] = ''
+ @@formats['enc files'] = 'ENCFONTS'
+ @@formats['cmap files'] = 'CMAPFONTS'
+ @@formats['subfont definition files'] = 'SFDFONTS'
+ @@formats['lig files'] = 'LIGFONTS'
+ @@formats['bitmap font'] = ''
+ @@formats['MetaPost support'] = ''
+ @@formats['TeX system documentation'] = ''
+ @@formats['TeX system sources'] = ''
+ @@formats['Troff fonts'] = ''
+ @@formats['dvips config'] = ''
+ @@formats['type42 fonts'] = 'T42FONTS'
+ @@formats['web2c files'] = 'WEB2C'
+ @@formats['other text files'] = ''
+ @@formats['other binary files'] = ''
+ @@formats['misc fonts'] = ''
+ @@formats['opentype fonts'] = 'OPENTYPEFONTS'
+ @@formats['pdftex config'] = 'PDFTEXCONFIG'
+ @@formats['texmfscripts'] = 'TEXMFSCRIPTS'
+
+ attr_accessor :progname, :engine, :format, :rootpath, :treepath,
+ :verbose, :remember, :scandisk, :diskcache, :renewcache
+
+ @@cacheversion = '1'
+
+ def initialize
+ @rootpath = ''
+ @treepath = ''
+ @progname = 'kpsewhich'
+ @engine = 'pdftex'
+ @variables = Hash.new
+ @expansions = Hash.new
+ @files = Hash.new
+ @found = Hash.new
+ @kpsevars = Hash.new
+ @lsrfiles = Array.new
+ @cnffiles = Array.new
+ @verbose = true
+ @remember = true
+ @scandisk = true
+ @diskcache = true
+ @renewcache = false
+ @isolate = false
+
+ @diskcache = false
+ @cachepath = nil
+ @cachefile = 'tmftools.log'
+
+ @environment = ENV
+ end
+
+ def set(key,value)
+ case key
+ when 'progname' then @progname = value
+ when 'engine' then @engine = value
+ when 'format' then @format = value
+ end
+ end
+
+ def push_environment(env)
+ @environment = env
+ end
+
+ # {$SELFAUTOLOC,$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}
+ #
+ # $SELFAUTOLOC : /usr/tex/bin/platform
+ # $SELFAUTODIR : /usr/tex/bin
+ # $SELFAUTOPARENT : /usr/tex
+ #
+ # since we live in scriptpath we need a slightly different method
+
+ def load_cnf(filenames=nil)
+ unless filenames then
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ @environment['SELFAUTOPARENT'] = ownpath
+ else
+ @environment['SELFAUTOPARENT'] = '.'
+ end
+ unless @treepath.empty? then
+ unless @rootpath.empty? then
+ @treepath = @treepath.split(',').collect do |p| File.join(@rootpath,p) end.join(',')
+ end
+ @environment['TEXMF'] = @treepath
+ # only the first one
+ @environment['TEXMFCNF'] = File.join(@treepath.split(',').first,'texmf/web2c')
+ end
+ unless @rootpath.empty? then
+ @environment['TEXMFCNF'] = File.join(@rootpath,'texmf/web2c')
+ @environment['SELFAUTOPARENT'] = @rootpath
+ @isolate = true
+ end
+ filenames = Array.new
+ if @environment['TEXMFCNF'] and not @environment['TEXMFCNF'].empty? then
+ @environment['TEXMFCNF'].to_s.split_path.each do |path|
+ filenames << File.join(path,@@texmfcnf)
+ end
+ elsif @environment['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.',@@texmfcnf)
+ else
+ ['texmf-local','texmf'].each do |tree|
+ filenames << File.join(@environment['SELFAUTOPARENT'],tree,'web2c',@@texmfcnf)
+ end
+ end
+ end
+ # <root>/texmf/web2c/texmf.cnf
+ filenames = _expanded_path_(filenames)
+ @rootpath = filenames.first
+ 3.times do
+ @rootpath = File.dirname(@rootpath)
+ end
+ filenames.collect! do |f|
+ f.gsub("\\", '/')
+ end
+ filenames.each do |fname|
+ if FileTest.file?(fname) and f = File.open(fname) then
+ @cnffiles << fname
+ while line = f.gets do
+ loop do
+ # concatenate lines ending with \
+ break unless line.sub!(/\\\s*$/o) do
+ f.gets || ''
+ end
+ end
+ case line
+ when /^[\%\#]/o then
+ # comment
+ when /^\s*(.*?)\s*\=\s*(.*?)\s*$/o then
+ key, value = $1, $2
+ unless @variables.key?(key) then
+ value.sub!(/\%.*$/,'')
+ value.sub!(/\~/, "$HOME")
+ @variables[key] = value
+ end
+ @kpsevars[key] = true
+ end
+ end
+ f.close
+ end
+ end
+ end
+
+ def load_lsr
+ @lsrfiles = []
+ simplified_list(expansion('TEXMF')).each do |p|
+ ['ls-R','ls-r'].each do |f|
+ filename = File.join(p,f)
+ if FileTest.file?(filename) then
+ @lsrfiles << [filename,File.size(filename)]
+ break
+ end
+ end
+ end
+ @files = Hash.new
+ if @diskcache then
+ ['HOME','TEMP','TMP','TMPDIR'].each do |key|
+ if @environment[key] then
+ if FileTest.directory?(@environment[key]) then
+ @cachepath = @environment[key]
+ @cachefile = [@rootpath.gsub(/[^A-Z0-9]/io, '-').gsub(/\-+/,'-'),File.basename(@cachefile)].join('-')
+ break
+ end
+ end
+ end
+ if @cachepath and not @renewcache and FileTest.file?(File.join(@cachepath,@cachefile)) then
+ begin
+ if f = File.open(File.join(@cachepath,@cachefile)) then
+ cacheversion = Marshal.load(f)
+ if cacheversion == @@cacheversion then
+ lsrfiles = Marshal.load(f)
+ if lsrfiles == @lsrfiles then
+ @files = Marshal.load(f)
+ end
+ end
+ f.close
+ end
+ rescue
+ @files = Hash.new
+ end
+ end
+ end
+ return if @files.size > 0
+ @lsrfiles.each do |filedata|
+ filename, filesize = filedata
+ filepath = File.dirname(filename)
+ begin
+ path = '.'
+ data = IO.readlines(filename)
+ if data[0].chomp =~ /% ls\-R \-\- filename database for kpathsea\; do not change this line\./io then
+ data.each do |line|
+ case line
+ when /^[a-zA-Z0-9]/o then
+ line.chomp!
+ if @files[line] then
+ @files[line] << path
+ else
+ @files[line] = [path]
+ end
+ when /^\.\/(.*?)\:$/o then
+ path = File.join(filepath,$1)
+ end
+ end
+ end
+ rescue
+ # sorry
+ end
+ end
+ if @diskcache and @cachepath and f = File.open(File.join(@cachepath,@cachefile),'wb') then
+ f << Marshal.dump(@@cacheversion)
+ f << Marshal.dump(@lsrfiles)
+ f << Marshal.dump(@files)
+ f.close
+ end
+ end
+
+ def expand_variables
+ @expansions = Hash.new
+ if @isolate then
+ @variables['TEXMFCNF'] = @environment['TEXMFCNF'].dup
+ @variables['SELFAUTOPARENT'] = @environment['SELFAUTOPARENT'].dup
+ else
+ @environment.keys.each do |e|
+ if e =~ /^([a-zA-Z]+)\_(.*)\s*$/o then
+ @expansions["#{$1}.#{$2}"] = (@environment[e] ||'').dup
+ else
+ @expansions[e] = (@environment[e] ||'').dup
+ end
+ end
+ end
+ @variables.keys.each do |k|
+ @expansions[k] = @variables[k].dup unless @expansions[k]
+ end
+ loop do
+ busy = false
+ @expansions.keys.each do |k|
+ @expansions[k].gsub!(/\$([a-zA-Z0-9\_\-]*)/o) do
+ busy = true
+ @expansions[$1] || ''
+ end
+ @expansions[k].gsub!(/\$\{([a-zA-Z0-9\_\-]*)\}/o) do
+ busy = true
+ @expansions[$1] || ''
+ end
+ end
+ break unless busy
+ end
+ @expansions.keys.each do |k|
+ @expansions[k] = @expansions[k].gsub("\\", '/')
+ end
+ end
+
+ def variable(name='')
+ (name and not name.empty? and @variables[name.sub('$','')]) or ''
+ end
+
+ def expansion(name='')
+ (name and not name.empty? and @expansions[name.sub('$','')]) or ''
+ end
+
+ def variable?(name='')
+ name and not name.empty? and @variables.key?(name.sub('$',''))
+ end
+
+ def expansion?(name='')
+ name and not name.empty? and @expansions.key?(name.sub('$',''))
+ end
+
+ def simplified_list(str)
+ lst = str.gsub(/^\{/o,'').gsub(/\}$/o,'').split(",")
+ lst.collect do |l|
+ l.sub(/^[\!]*/,'').sub(/[\/\\]*$/o,'')
+ end
+ end
+
+ def original_variable(variable)
+ if variable?("#{@progname}.#{variable}") then
+ variable("#{@progname}.#{variable}")
+ elsif variable?(variable) then
+ variable(variable)
+ else
+ ''
+ end
+ end
+
+ def expanded_variable(variable)
+ if expansion?("#{variable}.#{@progname}") then
+ expansion("#{variable}.#{@progname}")
+ elsif expansion?(variable) then
+ expansion(variable)
+ else
+ ''
+ end
+ end
+
+ def original_path(filename='')
+ _expanded_path_(original_variable(var_of_format_or_suffix(filename)).split(";"))
+ end
+
+ def expanded_path(filename='')
+ _expanded_path_(expanded_variable(var_of_format_or_suffix(filename)).split(";"))
+ end
+
+ def _expanded_path_(pathlist)
+ i, n = 0, 0
+ pathlist.collect! do |mainpath|
+ mainpath.gsub(/([\{\}])/o) do
+ if $1 == "{" then
+ i += 1 ; n = i if i > n ; "<#{i}>"
+ else
+ i -= 1 ; "</#{i+1}>"
+ end
+ end
+ end
+ n.times do |i|
+ loop do
+ more = false
+ newlist = []
+ pathlist.each do |path|
+ unless path.sub!(/^(.*?)<(#{n-i})>(.*?)<\/\2>(.*?)$/) do
+ pre, mid, post = $1, $3, $4
+ mid.gsub!(/\,$/,',.')
+ mid.split(',').each do |m|
+ more = true
+ if m == '.' then
+ newlist << "#{pre}#{post}"
+ else
+ newlist << "#{pre}#{m}#{post}"
+ end
+ end
+ end then
+ newlist << path
+ end
+ end
+ if more then
+ pathlist = [newlist].flatten # copy -)
+ else
+ break
+ end
+ end
+ end
+ pathlist = pathlist.uniq.collect do |path|
+ p = path
+ # p.gsub(/^\/+/o) do '' end
+ # p.gsub!(/(.)\/\/(.)/o) do "#{$1}/#{$2}" end
+ # p.gsub!(/\/\/+$/o) do '//' end
+ p.gsub!(/\/\/+/o) do '//' end
+ p
+ end
+ pathlist
+ end
+
+ # todo: ignore case
+
+ def var_of_format(str)
+ @@formats[str] || ''
+ end
+
+ def var_of_suffix(str) # includes .
+ if @@suffixmap.key?(str) then @@formats[@@suffixmap[str]] else '' end
+ end
+
+ def var_of_format_or_suffix(str)
+ if @@formats.key?(str) then
+ @@formats[str]
+ elsif @@suffixmap.key?(File.extname(str)) then # extname includes .
+ @@formats[@@suffixmap[File.extname(str)]] # extname includes .
+ else
+ ''
+ end
+ end
+
+end
+
+class KpseFast
+
+ # test things
+
+ def list_variables(kpseonly=true)
+ @variables.keys.sort.each do |k|
+ if kpseonly then
+ puts("#{k} = #{@variables[k]}") if @kpsevars[k]
+ else
+ puts("#{if @kpsevars[k] then 'K' else 'E' end} #{k} = #{@variables[k]}")
+ end
+ end
+ end
+
+ def list_expansions(kpseonly=true)
+ @expansions.keys.sort.each do |k|
+ if kpseonly then
+ puts("#{k} = #{@expansions[k]}") if @kpsevars[k]
+ else
+ puts("#{if @kpsevars[k] then 'K' else 'E' end} #{k} = #{@expansions[k]}")
+ end
+ end
+ end
+
+ def list_lsr
+ puts("files = #{@files.size}")
+ end
+
+ def set_test_patterns
+ @variables["KPSE_TEST_PATTERN_A"] = "foo/{1,2}/bar//"
+ @variables["KPSE_TEST_PATTERN_B"] = "!!x{A,B{1,2}}y"
+ @variables["KPSE_TEST_PATTERN_C"] = "x{A,B//{1,2}}y"
+ @variables["KPSE_TEST_PATTERN_D"] = "x{A,B//{1,2,}}//y"
+ end
+
+ def show_test_patterns
+ ['A','B','D'].each do |i|
+ puts ""
+ puts @variables ["KPSE_TEST_PATTERN_#{i}"]
+ puts ""
+ puts expand_path("KPSE_TEST_PATTERN_#{i}").split_path
+ puts ""
+ end
+ end
+
+end
+
+class KpseFast
+
+ # kpse stuff
+
+ def expand_braces(str) # output variable and brace expansion of STRING.
+ _expanded_path_(original_variable(str).split_path).join_path
+ end
+
+ def expand_path(str) # output complete path expansion of STRING.
+ _expanded_path_(expanded_variable(str).split_path).join_path
+ end
+
+ def expand_var(str) # output variable expansion of STRING.
+ expanded_variable(str)
+ end
+
+ def show_path(str) # output search path for file type NAME
+ expanded_path(str).join_path
+ end
+
+ def var_value(str) # output the value of variable $STRING.
+ original_variable(str)
+ end
+
+end
+
+class KpseFast
+
+ def _is_cnf_?(filename)
+ filename == File.basename((@cnffiles.first rescue @@texmfcnf) || @@texmfcnf)
+ end
+
+ def find_file(filename)
+ if _is_cnf_?(filename) then
+ @cnffiles.first rescue ''
+ else
+ [find_files(filename,true)].flatten.first || ''
+ end
+ end
+
+ def find_files(filename,first=false)
+ if _is_cnf_?(filename) then
+ result = @cnffiles.dup
+ else
+ if @remember then
+ # stamp = "#{filename}--#{@format}--#{@engine}--#{@progname}"
+ stamp = "#{filename}--#{@engine}--#{@progname}"
+ return @found[stamp] if @found.key?(stamp)
+ end
+ pathlist = expanded_path(filename)
+ result = []
+ filelist = if @files.key?(filename) then @files[filename].uniq else nil end
+ done = false
+ if pathlist.size == 0 then
+ if FileTest.file?(filename) then
+ done = true
+ result << '.'
+ end
+ else
+ pathlist.each do |path|
+ doscan = if path =~ /^\!\!/o then false else true end
+ recurse = if path =~ /\/\/$/o then true else false end
+ pathname = path.dup
+ pathname.gsub!(/^\!+/o, '')
+ done = false
+ if not done and filelist then
+ # checking for exact match
+ if filelist.include?(pathname) then
+ result << pathname
+ done = true
+ end
+ if not done and recurse then
+ # checking for fuzzy //
+ pathname.gsub!(/\/+$/o, '/.*')
+ # pathname.gsub!(/\/\//o,'/[\/]*/')
+ pathname.gsub!(/\/\//o,'/.*?/')
+ re = /^#{pathname}/
+ filelist.each do |f|
+ if re =~ f then
+ result << f # duplicates will be filtered later
+ done = true
+ end
+ break if done
+ end
+ end
+ end
+ if not done and doscan then
+ # checking for path itself
+ pname = pathname.sub(/\.\*$/,'')
+ if not pname =~ /\*/o and FileTest.file?(File.join(pname,filename)) then
+ result << pname
+ done = true
+ end
+ end
+ break if done and first
+ end
+ end
+ if not done and @scandisk then
+ pathlist.each do |path|
+ pathname = path.dup
+ unless pathname.gsub!(/^\!+/o, '') then # !! prevents scan
+ recurse = pathname.gsub!(/\/+$/o, '')
+ complex = pathname.gsub!(/\/\//o,'/*/')
+ if recurse then
+ if complex then
+ if ok = File.glob_file("#{pathname}/**/#{filename}") then
+ result << File.dirname(ok)
+ done = true
+ end
+ elsif ok = File.locate_file(pathname,filename) then
+ result << File.dirname(ok)
+ done = true
+ end
+ elsif complex then
+ if ok = File.glob_file("#{pathname}/#{filename}") then
+ result << File.dirname(ok)
+ done = true
+ end
+ elsif FileTest.file?(File.join(pathname,filename)) then
+ result << pathname
+ done = true
+ end
+ break if done and first
+ end
+ end
+ end
+ result = result.uniq.collect do |pathname|
+ File.join(pathname,filename)
+ end
+ @found[stamp] = result if @remember
+ end
+ return result # redundant
+ end
+
+end
+
+class KpseFast
+
+ class FileData
+ attr_accessor :tag, :name, :size, :date
+ def initialize(tag=0,name=nil,size=nil,date=nil)
+ @tag, @name, @size, @date = tag, name, size, date
+ end
+ def FileData.sizes(a)
+ a.collect do |aa|
+ aa.size
+ end
+ end
+ def report
+ case @tag
+ when 1 then "deleted | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 2 then "present | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 3 then "obsolete | #{' '*8} | #{' '*16} | #{@name}"
+ end
+ end
+ end
+
+ def analyze_files(filter='',strict=false,sort='',delete=false)
+ puts("command line = #{ARGV.join(' ')}")
+ puts("number of files = #{@files.size}")
+ puts("filter pattern = #{filter}")
+ puts("loaded cnf files = #{@cnffiles.join(' ')}")
+ puts('')
+ if filter.gsub!(/^not:/,'') then
+ def the_same(filter,filename)
+ not filter or filter.empty? or /#{filter}/ !~ filename
+ end
+ else
+ def the_same(filter,filename)
+ not filter or filter.empty? or /#{filter}/ =~ filename
+ end
+ end
+ @files.keys.each do |name|
+ if @files[name].size > 1 then
+ data = Array.new
+ @files[name].each do |path|
+ filename = File.join(path,name)
+ # if not filter or filter.empty? or /#{filter}/ =~ filename then
+ if the_same(filter,filename) then
+ if FileTest.file?(filename) then
+ if delete then
+ data << FileData.new(1,filename,File.size(filename),File.mtime(filename))
+ begin
+ File.delete(filename) if delete
+ rescue
+ end
+ else
+ data << FileData.new(2,filename,File.size(filename),File.mtime(filename))
+ end
+ else
+ # data << FileData.new(3,filename)
+ end
+ end
+ end
+ if data.length > 1 then
+ if strict then
+ # if data.collect do |d| d.size end.uniq! then
+ # data.sort! do |a,b| b.size <=> a.size end
+ # data.each do |d| puts d.report end
+ # puts ''
+ # end
+ data.sort! do |a,b|
+ if a.size and b.size then
+ b.size <=> a.size
+ else
+ 0
+ end
+ end
+ bunch = Array.new
+ done = false
+ data.each do |d|
+ if bunch.size == 0 then
+ bunch << d
+ elsif bunch[0].size == d.size then
+ bunch << d
+ else
+ if bunch.size > 1 then
+ bunch.each do |b|
+ puts b.report
+ end
+ done = true
+ end
+ bunch = [d]
+ end
+ end
+ puts '' if done
+ else
+ case sort
+ when 'size' then data.sort! do |a,b| a.size <=> b.size end
+ when 'revsize' then data.sort! do |a,b| b.size <=> a.size end
+ when 'date' then data.sort! do |a,b| a.date <=> b.date end
+ when 'revdate' then data.sort! do |a,b| b.date <=> a.date end
+ end
+ data.each do |d| puts d.report end
+ puts ''
+ end
+ end
+ end
+ end
+ end
+
+end
+
+# if false then
+
+ # k = KpseFast.new # (root)
+ # k.set_test_patterns
+ # k.load_cnf
+ # k.expand_variables
+ # k.load_lsr
+
+ # k.show_test_patterns
+
+ # puts k.list_variables
+ # puts k.list_expansions
+ # k.list_lsr
+ # puts k.expansion("$TEXMF")
+ # puts k.expanded_path("TEXINPUTS","context")
+
+ # k.progname, k.engine, k.format = 'context', 'pdftex', 'tfm'
+ # k.scandisk = false # == must_exist
+ # k.expand_variables
+
+ # 10.times do |i| puts k.find_file('texnansi-lmr10.tfm') end
+
+ # puts "expand braces $TEXMF"
+ # puts k.expand_braces("$TEXMF")
+ # puts "expand path $TEXMF"
+ # puts k.expand_path("$TEXMF")
+ # puts "expand var $TEXMF"
+ # puts k.expand_var("$TEXMF")
+ # puts "expand path $TEXMF"
+ # puts k.show_path('tfm')
+ # puts "expand value $TEXINPUTS"
+ # puts k.var_value("$TEXINPUTS")
+ # puts "expand value $TEXINPUTS.context"
+ # puts k.var_value("$TEXINPUTS.context")
+
+ # exit
+
+# end
diff --git a/scripts/context/ruby/base/kpseremote.rb b/scripts/context/ruby/base/kpseremote.rb
new file mode 100644
index 000000000..9a73b88b0
--- /dev/null
+++ b/scripts/context/ruby/base/kpseremote.rb
@@ -0,0 +1,116 @@
+require 'base/kpsefast'
+
+case ENV['KPSEMETHOD']
+ when /soap/o then require 'base/kpse/soap'
+ when /drb/o then require 'base/kpse/drb'
+ else require 'base/kpse/drb'
+end
+
+class KpseRemote
+
+ @@port = ENV['KPSEPORT'] || 7000
+ @@method = ENV['KPSEMETHOD'] || 'drb'
+
+ def KpseRemote::available?
+ @@method && @@port
+ end
+
+ def KpseRemote::start_server(port=nil)
+ kpse = KpseServer.new(port || @@port)
+ kpse.start
+ end
+
+ def KpseRemote::start_client(port=nil) # keeps object in server
+ kpseclient = KpseClient.new(port || @@port)
+ kpseclient.start
+ kpse = kpseclient.object
+ tree = kpse.choose(KpseUtil::identify, KpseUtil::environment)
+ [kpse, tree]
+ end
+
+ def KpseRemote::fetch(port=nil) # no need for defining methods but slower, send whole object
+ kpseclient = KpseClient.new(port || @@port)
+ kpseclient.start
+ kpseclient.object.fetch(KpseUtil::identify, KpseUtil::environment) rescue nil
+ end
+
+ def initialize(port=nil)
+ if KpseRemote::available? then
+ begin
+ @kpse, @tree = KpseRemote::start_client(port)
+ rescue
+ @kpse, @tree = nil, nil
+ end
+ else
+ @kpse, @tree = nil, nil
+ end
+ end
+
+ def progname=(value)
+ @kpse.set(@tree,'progname',value)
+ end
+ def format=(value)
+ @kpse.set(@tree,'format',value)
+ end
+ def engine=(value)
+ @kpse.set(@tree,'engine',value)
+ end
+
+ def progname
+ @kpse.get(@tree,'progname')
+ end
+ def format
+ @kpse.get(@tree,'format')
+ end
+ def engine
+ @kpse.get(@tree,'engine')
+ end
+
+ def load
+ @kpse.load(KpseUtil::identify, KpseUtil::environment)
+ end
+ def okay?
+ @kpse && @tree
+ end
+ def set(key,value)
+ @kpse.set(@tree,key,value)
+ end
+ def load_cnf
+ @kpse.load_cnf(@tree)
+ end
+ def load_lsr
+ @kpse.load_lsr(@tree)
+ end
+ def expand_variables
+ @kpse.expand_variables(@tree)
+ end
+ def expand_braces(str)
+ clean_name(@kpse.expand_braces(@tree,str))
+ end
+ def expand_path(str)
+ clean_name(@kpse.expand_path(@tree,str))
+ end
+ def expand_var(str)
+ clean_name(@kpse.expand_var(@tree,str))
+ end
+ def show_path(str)
+ clean_name(@kpse.show_path(@tree,str))
+ end
+ def var_value(str)
+ clean_name(@kpse.var_value(@tree,str))
+ end
+ def find_file(filename)
+ clean_name(@kpse.find_file(@tree,filename))
+ end
+ def find_files(filename,first=false)
+ # dodo: each filename
+ @kpse.find_files(@tree,filename,first)
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
diff --git a/scripts/context/ruby/base/kpserunner.rb b/scripts/context/ruby/base/kpserunner.rb
new file mode 100644
index 000000000..cfc2ad4fb
--- /dev/null
+++ b/scripts/context/ruby/base/kpserunner.rb
@@ -0,0 +1,87 @@
+require 'base/kpsefast'
+
+module KpseRunner
+
+ @@kpse = nil
+
+ def KpseRunner.kpsewhich(arg='')
+ options, arguments = split_args(arg)
+ unless @@kpse then
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ require 'base/kpseremote'
+ @@kpse = KpseRemote.new
+ else
+ @@kpse = nil
+ end
+ if @@kpse && @@kpse.okay? then
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ else
+ require 'base/kpsefast'
+ @@kpse = KpseFast.new
+ @@kpse.load_cnf
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ @@kpse.expand_variables
+ @@kpse.load_lsr
+ end
+ else
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ @@kpse.expand_variables
+ end
+ if option = options['expand-braces'] and not option.empty? then
+ @@kpse.expand_braces(option)
+ elsif option = options['expand-path'] and not option.empty? then
+ @@kpse.expand_path(option)
+ elsif option = options['expand-var'] and not option.empty? then
+ @@kpse.expand_var(option)
+ elsif option = options['show-path'] and not option.empty? then
+ @@kpse.show_path(option)
+ elsif option = options['var-value'] and not option.empty? then
+ @@kpse.expand_var(option)
+ elsif arguments.size > 0 then
+ files = Array.new
+ arguments.each do |option|
+ if file = @@kpse.find_file(option) and not file.empty? then
+ files << file
+ end
+ end
+ files.join("\n")
+ else
+ ''
+ end
+ end
+
+ def KpseRunner.kpsereset
+ @@kpse = nil
+ end
+
+ private
+
+ def KpseRunner.split_args(arg)
+ vars, args = Hash.new, Array.new
+ arg.gsub!(/([\"\'])(.*?)\1/o) do
+ $2.gsub(' ','<space/>')
+ end
+ arg = arg.split(/\s+/o)
+ arg.collect! do |a|
+ a.gsub('<space/>',' ')
+ end
+ arg.each do |a|
+ if a =~ /^(.*?)\=(.*?)$/o then
+ k, v = $1, $2
+ vars[k.sub(/^\-+/,'')] = v
+ else
+ args << a
+ end
+ end
+ # puts vars.inspect
+ # puts args.inspect
+ return vars, args
+ end
+
+end
diff --git a/scripts/context/ruby/base/logger.rb b/scripts/context/ruby/base/logger.rb
new file mode 100644
index 000000000..2526cdb0e
--- /dev/null
+++ b/scripts/context/ruby/base/logger.rb
@@ -0,0 +1,104 @@
+# module : base/logger
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+require 'thread'
+
+# The next calls are valid:
+
+# @log.report('a','b','c', 'd')
+# @log.report('a','b',"c #{d}")
+# @log.report("a b c #{d}")
+
+# Keep in mind that "whatever #{something}" is two times faster than
+# 'whatever ' + something or ['whatever',something].join and that
+# when verbosity is not needed the following is much faster too:
+
+# @log.report('a','b','c', 'd') if @log.verbose?
+# @log.report('a','b',"c #{d}") if @log.verbose?
+# @log.report("a b c #{d}") if @log.verbose?
+
+# The last three cases are equally fast when verbosity is turned off.
+
+# Under consideration: verbose per instance
+
+class Logger
+
+ @@length = 0
+ @@verbose = false
+
+ def initialize(tag=nil,length=0,verbose=false)
+ @tag = tag || ''
+ @@verbose = @@verbose || verbose
+ @@length = @tag.length if @tag.length > @@length
+ @@length = length if length > @@length
+ end
+
+ def report(*str)
+ begin
+ case str.length
+ when 0
+ print("\n")
+ return true
+ when 1
+ message = str.first
+ else
+ message = [str].flatten.collect{|s| s.to_s}.join(' ').chomp
+ end
+ if @tag.empty? then
+ print("#{message}\n")
+ else
+ # try to avoid too many adjustments
+ @tag = @tag.ljust(@@length) unless @tag.length == @@length
+ print("#{@tag} | #{message}\n")
+ end
+ rescue
+ end
+ return true
+ end
+
+ def reportlines(*str)
+ unless @tag.empty? then
+ @tag = @tag.ljust(@@length) unless @tag.length == @@length
+ end
+ report([str].flatten.collect{|s| s.gsub(/\n/,"\n#{@tag} | ")}.join(' '))
+ end
+
+ def debug(*str)
+ report(str) if @@verbose
+ end
+
+ def error(*str)
+ if ! $! || $!.to_s.empty? then
+ report(str)
+ else
+ report(str,$!)
+ end
+ end
+
+ def verbose
+ @@verbose = true
+ end
+
+ def silent
+ @@verbose = false
+ end
+
+ def verbose?
+ @@verbose
+ end
+
+ # attr_reader :tag
+
+ # alias fatal error
+ # alias info debug
+ # alias warn debug
+ # alias debug? :verbose?
+
+end
diff --git a/scripts/context/ruby/base/merge.rb b/scripts/context/ruby/base/merge.rb
new file mode 100644
index 000000000..a66b97e91
--- /dev/null
+++ b/scripts/context/ruby/base/merge.rb
@@ -0,0 +1,139 @@
+# module : base/merge
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# --selfmerg ewill create stand alone script (--selfcleanup does the opposite)
+
+# this module will package all the used modules in the file itself
+# so that we can relocate the file at wish, usage:
+#
+# merge:
+#
+# unless SelfMerge::ok? && SelfMerge::merge then
+# puts("merging should happen on the path were the base inserts reside")
+# end
+#
+# cleanup:
+#
+# unless SelfMerge::cleanup then
+# puts("merging should happen on the path were the base inserts reside")
+# end
+
+module SelfMerge
+
+ @@kpsemergestart = "\# kpse_merge_start"
+ @@kpsemergestop = "\# kpse_merge_stop"
+ @@kpsemergefile = "\# kpse_merge_file: "
+ @@kpsemergedone = "\# kpse_merge_done: "
+
+ @@filename = File.basename($0)
+ @@ownpath = File.expand_path(File.dirname($0))
+ @@modroot = '(base|graphics|rslb|www)' # needed in regex in order not to mess up SelfMerge
+ @@modules = $".collect do |file| File.expand_path(file) end
+
+ @@modules.delete_if do |file|
+ file !~ /^#{@@ownpath}\/#{@@modroot}.*$/i
+ end
+
+ def SelfMerge::ok?
+ begin
+ @@modules.each do |file|
+ return false unless FileTest.file?(file)
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::merge
+ begin
+ if SelfMerge::ok? && rbfile = IO.read(@@filename) then
+ begin
+ inserts = "#{@@kpsemergestart}\n\n"
+ @@modules.each do |file|
+ inserts << "#{@@kpsemergefile}'#{file}'\n\n"
+ inserts << IO.read(file).gsub(/^#.*?\n$/,'')
+ inserts << "\n\n"
+ end
+ inserts << "#{@@kpsemergestop}\n\n"
+ # no gsub! else we end up in SelfMerge
+ rbfile.sub!(/#{@@kpsemergestart}\s*#{@@kpsemergestop}/moi) do
+ inserts
+ end
+ rbfile.gsub!(/^(.*)(require [\"\'].*?#{@@modroot}.*)$/) do
+ pre, post = $1, $2
+ if pre =~ /#{@@kpsemergedone}/ then
+ "#{pre}#{post}"
+ else
+ "#{pre}#{@@kpsemergedone}#{post}"
+ end
+ end
+ rescue
+ return false
+ else
+ begin
+ File.open(@@filename,'w') do |f|
+ f << rbfile
+ end
+ rescue
+ return false
+ end
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::cleanup
+ begin
+ if rbfile = IO.read(@@filename) then
+ begin
+ rbfile.sub!(/#{@@kpsemergestart}(.*)#{@@kpsemergestop}\s*/moi) do
+ "#{@@kpsemergestart}\n\n#{@@kpsemergestop}\n\n"
+ end
+ rbfile.gsub!(/^(.*#{@@kpsemergedone}.*)$/) do
+ str = $1
+ if str =~ /require [\"\']/ then
+ str.gsub(/#{@@kpsemergedone}/, '')
+ else
+ str
+ end
+ end
+ rescue
+ return false
+ else
+ begin
+ File.open(@@filename,'w') do |f|
+ f << rbfile
+ end
+ rescue
+ return false
+ end
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::replace
+ if SelfMerge::ok? then
+ SelfMerge::cleanup
+ SelfMerge::merge
+ end
+ end
+
+end
diff --git a/scripts/context/ruby/base/mp.rb b/scripts/context/ruby/base/mp.rb
new file mode 100644
index 000000000..d168bde1d
--- /dev/null
+++ b/scripts/context/ruby/base/mp.rb
@@ -0,0 +1,167 @@
+# module : base/mp
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2005-2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+module MPTools
+
+ @@definitions, @@start, @@stop, @@before, @@after = Hash.new, Hash.new, Hash.new, Hash.new, Hash.new
+
+
+ @@definitions['plain'] = <<EOT
+\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup
+ \\setbox0=\\hbox\\bgroup}
+
+\\gdef\\stopmpxshipout{\\egroup \\dimen0=\\ht0 \\advance\\dimen0\\dp0
+ \\dimen1=\\ht0 \\dimen2=\\dp0
+ \\setbox0=\\hbox\\bgroup
+ \\box0
+ \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2
+ \\else \\vrule width1sp height1sp depth0sp\\relax
+ \\fi\\egroup
+ \\ht0=0pt \\dp0=0pt \\box0 \\egroup}
+EOT
+
+ @@start ['plain'] = ""
+ @@before['plain'] = "\\mpxshipout"
+ @@after ['plain'] = "\\stopmpxshipout"
+ @@stop ['plain'] = "\\end{document}"
+
+ @@definitions['context'] = <<EOT
+\\ifx\\startMPXpage\\undefined
+
+ \\ifx\\loadallfontmapfiles\\undefined \\let\\loadallfontmapfiles\\relax \\fi
+
+ \\gdef\\startMPXpage
+ {\\shipout\\hbox
+ \\bgroup
+ \\setbox0=\\hbox
+ \\bgroup}
+
+ \\gdef\\stopMPXpage
+ {\\egroup
+ \\dimen0=\\ht0
+ \\advance\\dimen0\\dp0
+ \\dimen1=\\ht0
+ \\dimen2=\\dp0
+ \\setbox0=\\hbox\\bgroup
+ \\box0
+ \\ifnum\\dimen0>0
+ \\vrule width 1sp height \\dimen1 depth \\dimen2
+ \\else
+ \\vrule width 1sp height 1sp depth 0sp \\relax
+ \\fi
+ \\egroup
+ \\ht0=0pt
+ \\dp0=0pt
+ \\loadallfontmapfiles
+ \\box0
+ \\egroup}
+
+\\fi
+
+\\ifx\\starttext\\undefined
+
+ \\let\\starttext\\relax
+ \\def\\stoptext{\\end{document}}
+
+\\fi
+EOT
+
+ @@start ['context'] = "\\starttext"
+ @@before['context'] = "\\startMPXpage"
+ @@after ['context'] = "\\stopMPXpage"
+ @@stop ['context'] = "\\stoptext"
+
+ # todo: \usemodule[m-mpx ] and test fo defined
+
+ def MPTools::mptotex(from,to=nil,method='plain')
+ begin
+ if from && data = IO.read(from) then
+ f = if to then File.open(to,'w') else $stdout end
+ f.puts("% file: #{from}")
+ f.puts("")
+ f.puts(@@definitions[method])
+ unless @@start[method].empty? then
+ f.puts("")
+ f.puts(@@start[method])
+ end
+ data.gsub!(/([^\\])%.*?$/mo) do
+ $1
+ end
+ data.scan(/(verbatim|b)tex\s*(.*?)\s*etex/mo) do
+ tag, text = $1, $2
+ f.puts("")
+ if tag == 'b' then
+ f.puts(@@before[method])
+ f.puts("#{text}%")
+ f.puts(@@after [method])
+ else
+ f.puts("#{text}")
+ end
+ f.puts("")
+ end
+ f.puts("")
+ f.puts(@@stop[method])
+ f.close
+ else
+ return false
+ end
+ rescue
+ File.delete(to) rescue false
+ return false
+ else
+ return true
+ end
+ end
+
+ @@splitMPlines = false
+
+ def MPTools::splitmplines(str)
+ if @@splitMPlines then
+ btex, verbatimtex, strings, result = Array.new, Array.new, Array.new, str.dup
+ # protect texts
+ result.gsub!(/btex\s*(.*?)\s*etex/) do
+ btex << $1
+ "btex(#{btex.length-1})"
+ end
+ result.gsub!(/verbatimtex\s*(.*?)\s*etex/) do
+ verbatimtex << $1
+ "verbatimtex(#{verbatimtex.length-1})"
+ end
+ result.gsub!(/\"(.*?)\"/) do
+ strings << $1
+ "\"#{strings.length-1}\""
+ end
+ result.gsub!(/\;/) do
+ ";\n"
+ end
+ result.gsub!(/(.{80,})(\-\-\-|\-\-|\.\.\.|\.\.)/) do
+ "#{$1}#{$2}\n"
+ end
+ result.gsub!(/\n[\s\n]+/moi) do
+ "\n"
+ end
+ result.gsub!(/btex\((\d+)\)/) do
+ "btex #{btex[$1.to_i]} etex"
+ end
+ result.gsub!(/verbatimtex\((\d+)\)/) do
+ "verbatimtex #{verbatimtex[$1.to_i]} etex"
+ end
+ result.gsub!(/\"(\d+)\"/) do
+ "\"#{strings[$1.to_i]}\""
+ end
+ # return result # let's catch xetex bug
+ return result.gsub(/\^\^(M|J)/o, "\n")
+ else
+ # return str # let's catch xetex bug
+ return str.gsub(/\^\^(M|J)/o, "\n")
+ end
+ end
+
+end
diff --git a/scripts/context/ruby/base/pdf.rb b/scripts/context/ruby/base/pdf.rb
new file mode 100644
index 000000000..5aec06fc5
--- /dev/null
+++ b/scripts/context/ruby/base/pdf.rb
@@ -0,0 +1,75 @@
+module PDFview
+
+ @files = Hash.new
+ @opencalls = Hash.new
+ @closecalls = Hash.new
+ @allcalls = Hash.new
+
+ @method = 'default' # 'xpdf'
+
+ @opencalls['default'] = "pdfopen --file" # "pdfopen --back --file"
+ @opencalls['xpdf'] = "xpdfopen"
+
+ @closecalls['default'] = "pdfclose --file"
+ @closecalls['xpdf'] = nil
+
+ @allcalls['default'] = "pdfclose --all"
+ @allcalls['xpdf'] = nil
+
+ def PDFview.setmethod(method)
+ @method = method
+ end
+
+ def PDFview.open(*list)
+ begin
+ [*list].flatten.each do |file|
+ filename = fullname(file)
+ if FileTest.file?(filename) then
+ if @opencalls[@method] then
+ result = `#{@opencalls[@method]} #{filename} 2>&1`
+ @files[filename] = true
+ end
+ end
+ end
+ rescue
+ end
+ end
+
+ def PDFview.close(*list)
+ [*list].flatten.each do |file|
+ filename = fullname(file)
+ begin
+ if @files.key?(filename) then
+ if @closecalls[@method] then
+ result = `#{@closecalls[@method]} #{filename} 2>&1`
+ end
+ else
+ closeall
+ return
+ end
+ rescue
+ end
+ @files.delete(filename)
+ end
+ end
+
+ def PDFview.closeall
+ begin
+ if @allcalls[@method] then
+ result = `#{@allcalls[@method]} 2>&1`
+ end
+ rescue
+ end
+ @files.clear
+ end
+
+ def PDFview.fullname(name)
+ name + if name =~ /\.pdf$/ then '' else '.pdf' end
+ end
+
+end
+
+# PDFview.open("t:/document/show-exa.pdf")
+# PDFview.open("t:/document/show-gra.pdf")
+# PDFview.close("t:/document/show-exa.pdf")
+# PDFview.close("t:/document/show-gra.pdf")
diff --git a/scripts/context/ruby/base/state.rb b/scripts/context/ruby/base/state.rb
new file mode 100644
index 000000000..76ef50b25
--- /dev/null
+++ b/scripts/context/ruby/base/state.rb
@@ -0,0 +1,75 @@
+require 'digest/md5'
+
+# todo: register omissions per file
+
+class FileState
+
+ def initialize
+ @states = Hash.new
+ @omiter = Hash.new
+ end
+
+ def reset
+ @states.clear
+ @omiter.clear
+ end
+
+ def register(filename,omit=nil)
+ unless @states.key?(filename) then
+ @states[filename] = Array.new
+ @omiter[filename] = omit
+ end
+ @states[filename] << checksum(filename,@omiter[filename])
+ end
+
+ def update(filename=nil)
+ [filename,@states.keys].flatten.compact.uniq.each do |fn|
+ register(fn)
+ end
+ end
+
+ def inspect(filename=nil)
+ result = ''
+ [filename,@states.keys].flatten.compact.uniq.sort.each do |fn|
+ if @states.key?(fn) then
+ result += "#{fn}: #{@states[fn].inspect}\n"
+ end
+ end
+ result
+ end
+
+ def changed?(filename)
+ if @states.key?(filename) then
+ n = @states[filename].length
+ if n>1 then
+ changed = @states[filename][n-1] != @states[filename][n-2]
+ else
+ changed = true
+ end
+ else
+ changed = true
+ end
+ return changed
+ end
+
+ def checksum(filename,omit=nil)
+ sum = ''
+ begin
+ if FileTest.file?(filename) && (data = IO.read(filename)) then
+ data.gsub!(/\n.*?(#{[omit].flatten.join('|')}).*?\n/) do "\n" end if omit
+ sum = Digest::MD5.hexdigest(data).upcase
+ end
+ rescue
+ sum = ''
+ end
+ return sum
+ end
+
+ def stable?
+ @states.keys.each do |s|
+ return false if changed?(s)
+ end
+ return true
+ end
+
+end
diff --git a/scripts/context/ruby/base/switch.rb b/scripts/context/ruby/base/switch.rb
new file mode 100644
index 000000000..19eced424
--- /dev/null
+++ b/scripts/context/ruby/base/switch.rb
@@ -0,0 +1,635 @@
+# module : base/switch
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# we cannot use getoptlong because we want to be more
+# tolerant; also we want to be case insensitive (2002).
+
+# we could make each option a class itself, but this is
+# simpler; also we can put more in the array
+
+# beware: regexps/o in methods are optimized globally
+
+require "rbconfig"
+
+$mswindows = Config::CONFIG['host_os'] =~ /mswin/
+$separator = File::PATH_SEPARATOR
+
+class String
+
+ def has_suffix?(suffix)
+ self =~ /\.#{suffix}$/i
+ end
+
+end
+
+# may move to another module
+
+class File
+
+ @@update_eps = 1
+
+ def File.needsupdate(oldname,newname)
+ begin
+ oldtime = File.stat(oldname).mtime.to_i
+ newtime = File.stat(newname).mtime.to_i
+ if newtime >= oldtime then
+ return false
+ elsif oldtime-newtime < @@update_eps then
+ return false
+ else
+ return true
+ end
+ rescue
+ return true
+ end
+ end
+
+ def File.syncmtimes(oldname,newname)
+ return
+ begin
+ if $mswindows then
+ # does not work (yet) / gives future timestamp
+ # t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
+ # File.utime(0,t,oldname,newname)
+ else
+ t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
+ File.utime(0,t,oldname,newname)
+ end
+ rescue
+ end
+ end
+
+ def File.timestamp(name)
+ begin
+ "#{File.stat(name).mtime}"
+ rescue
+ return 'unknown'
+ end
+ end
+
+end
+
+# main thing
+
+module CommandBase
+
+ # this module can be used as a mixin in a command handler
+
+ $stdout.sync = true
+
+ def initialize(commandline,logger,banner)
+ @commandline, @logger, @banner = commandline, logger, banner
+ @forcenewline, @versiondone, @error = false, false, false
+ version if @commandline.option('version')
+ end
+
+ def reportlines(*str)
+ @logger.reportlines(str)
+ end
+
+ # only works in 1.8
+ #
+ # def report(*str)
+ # @logger.report(str)
+ # end
+ #
+ # def version # just a bit of playing with defs
+ # report(@banner.join(' - '))
+ # def report(*str)
+ # @logger.report
+ # @logger.report(str)
+ # def report(*str)
+ # @logger.report(str)
+ # end
+ # end
+ # def version
+ # end
+ # end
+
+ def report(*str)
+ initlogger ; @logger.report(str)
+ end
+
+ def seterror
+ @error = true
+ end
+
+ def error?
+ return @error
+ end
+
+ def exit
+ if @error then Kernel.exit(1) else Kernel.exit(0) end
+ end
+
+ def execute(str=nil)
+ send(str || action || 'main')
+ exit
+ end
+
+ def debug(*str)
+ initlogger ; @logger.debug(str)
+ end
+
+ def error(*str)
+ initlogger ; @logger.error(str)
+ end
+
+ def initlogger
+ if @forcenewline then
+ @logger.report
+ @forcenewline = false
+ end
+ end
+
+ def logger
+ @logger
+ end
+
+ def version # just a bit of playing with defs
+ unless @versiondone then
+ report(@banner.join(' - '))
+ @forcenewline = true
+ @versiondone = true
+ end
+ end
+
+ def help
+ version # is nilled when already given
+ @commandline.helpkeys.each do |k|
+ if @commandline.help?(k) then
+ kstr = ('--'+k).ljust(@commandline.helplength+2)
+ message = @commandline.helptext(k)
+ message = '' if message == CommandLine::NOHELP
+ message = message.split(/\s*\n\s*/)
+ loop do
+ report("#{kstr} #{message.shift}")
+ kstr = ' '*kstr.length
+ break if message.length == 0
+ end
+ end
+ end
+ end
+
+ def option(key)
+ @commandline.option(key)
+ end
+ def oneof(*key)
+ @commandline.oneof(*key)
+ end
+
+ def globfiles(pattern='*',suffix=nil)
+ @commandline.setarguments([pattern].flatten)
+ if files = findfiles(suffix) then
+ @commandline.setarguments(files)
+ else
+ @commandline.setarguments
+ end
+ end
+
+ private
+
+ def findfiles(suffix=nil)
+
+ if @commandline.arguments.length>1 then
+ return @commandline.arguments
+ else
+ pattern = @commandline.argument('first')
+ pattern = '*' if pattern.empty?
+ if suffix && ! pattern.match(/\..+$/o) then
+ suffix = '.' + suffix
+ pattern += suffix unless pattern =~ /#{suffix}$/
+ end
+ # not {} safe
+ pattern = '**/' + pattern if @commandline.option('recurse')
+ files = Dir[pattern]
+ if files && files.length>0 then
+ return files
+ else
+ pattern = @commandline.argument('first')
+ if FileTest.file?(pattern) then
+ return [pattern]
+ else
+ report("no files match pattern #{pattern}")
+ return nil
+ end
+ end
+ end
+
+ end
+
+ def globbed(pattern,recurse=false)
+
+ files = Array.new
+ pattern.split(' ').each do |p|
+ if recurse then
+ if p =~ /^(.*)(\/.*?)$/i then
+ p = $1 + '/**' + $2
+ else
+ p = '**/' + p
+ end
+ p.gsub!(/[\\\/]+/, '/')
+ end
+ files.push(Dir.glob(p))
+ end
+ files.flatten.sort do |a,b|
+ pathcompare(a,b)
+ end
+ end
+
+ def pathcompare(a,b)
+
+ aa, bb = a.split('/'), b.split('/')
+ if aa.length == bb.length then
+ aa.each_index do |i|
+ if aa[i]<bb[i] then
+ return -1
+ elsif aa[i]>bb[i] then
+ return +1
+ end
+ end
+ return 0
+ else
+ return aa.length <=> bb.length
+ end
+
+ end
+
+end
+
+class CommandLine
+
+ VALUE, FLAG = 1, 2
+ NOHELP = 'no arguments'
+
+ def initialize(prefix='-')
+
+ @registered = Array.new
+ @options = Hash.new
+ @unchecked = Hash.new
+ @arguments = Array.new
+ @original = ARGV.join(' ')
+ @helptext = Hash.new
+ @mandated = Hash.new
+ @provided = Hash.new
+ @prefix = prefix
+ @actions = Array.new
+
+ # The quotes in --switch="some value" get lost in ARGV, so we need to do some trickery here.
+
+ @original = ''
+ ARGV.each do |a|
+ aa = a.strip.gsub(/^([#{@prefix}]+\w+\=)([^\"].*?\s+.*[^\"])$/) do
+ $1 + "\"" + $2 + "\""
+ end
+ @original += if @original.empty? then '' else ' ' end + aa
+ end
+
+ end
+
+ def setarguments(args=[])
+ @arguments = if args then args else [] end
+ end
+
+ def register(option,shortcut,kind,default=false,action=false,helptext='')
+ if kind == FLAG then
+ @options[option] = default
+ elsif not default then
+ @options[option] = ''
+ else
+ @options[option] = default
+ end
+ @registered.push([option,shortcut,kind])
+ @mandated[option] = false
+ # @provided[option] = false
+ @helptext[option] = helptext
+ @actions.push(option) if action
+ end
+
+ def registerflag(option,default=false,helptext='')
+ if default.class == String then
+ register(option,'',FLAG,false,false,default)
+ else
+ register(option,'',FLAG,false,false,helptext)
+ end
+ end
+
+ def registervalue(option,default='',helptext='')
+ register(option,'',VALUE,default,false,helptext)
+ end
+
+ def registeraction(option,helptext='')
+ register(option,'',FLAG,false,true,helptext)
+ end
+
+ def registermandate(*option)
+ [*option].each do |o|
+ [o].each do |oo|
+ @mandated[oo] = true
+ end
+ end
+ end
+
+ def actions
+ a = @actions.delete_if do |t|
+ ! option(t)
+ end
+ if a && a.length>0 then
+ return a
+ else
+ return nil
+ end
+ end
+
+ def action
+ @actions.each do |t|
+ return t if option(t)
+ end
+ return nil
+ end
+
+ def forgotten
+ @mandated.keys.sort - @provided.keys.sort
+ end
+
+ def registerhelp(option,text='')
+ @helptext['unknown'] = if text.empty? then option else text end
+ end
+
+ def helpkeys(option='.*')
+ @helptext.keys.sort.grep(/#{option}/)
+ end
+
+ def helptext(option)
+ @helptext.fetch(option,'')
+ end
+
+ def help?(option)
+ @helptext[option] && ! @helptext[option].empty?
+ end
+
+ def helplength
+ n = 0
+ @helptext.keys.each do |h|
+ n = h.length if h.length>n
+ end
+ return n
+ end
+
+ def expand
+
+ # todo : '' or false, depending on type
+ # @options.clear
+ # @arguments.clear
+
+ dirtyvalue(@original).split(' ').each do |arg|
+ case arg
+ when /^[#{@prefix}][#{@prefix}](.+?)\=(.*?)$/ then locatedouble($1,$2)
+ when /^[#{@prefix}][#{@prefix}](.+?)$/ then locatedouble($1,false)
+ when /^[#{@prefix}](.)\=(.)$/ then locatesingle($1,$2)
+ when /^[#{@prefix}](.+?)$/ then locateseries($1,false)
+ when /^[\+\-]+/o then # do nothing
+ else
+ arguments.push(arg)
+ end
+ end
+
+ @options or @unchecked or @arguments
+
+ end
+
+ def extend (str)
+ @original = @original + ' ' + str
+ end
+
+ def replace (str)
+ @original = str
+ end
+
+ def show
+ # print "-- options --\n"
+ @options.keys.sort.each do |key|
+ print "option: #{key} -> #{@options[key]}\n"
+ end
+ # print "-- arguments --\n"
+ @arguments.each_index do |key|
+ print "argument: #{key} -> #{@arguments[key]}\n"
+ end
+ end
+
+ def option(str,default=nil)
+ if @options.key?(str) then
+ @options[str]
+ elsif default then
+ default
+ else
+ @options[str]
+ end
+ end
+
+ def checkedoption(str,default='')
+ if @options.key?(str) then
+ if @options[str].empty? then default else @options[str] end
+ else
+ default
+ end
+ end
+
+ def foundoption(str,default='')
+ str = str.split(',') if str.class == String
+ str.each do |s|
+ return str if @options.key?(str)
+ end
+ return default
+ end
+
+ def oneof(*key)
+ [*key].flatten.compact.each do |k|
+ return true if @options.key?(k) && @options[k]
+ end
+ return false
+ end
+
+ def setoption(str,value)
+ @options[str] = value
+ end
+
+ def getoption(str,value='') # value ?
+ @options[str]
+ end
+
+ def argument(n=0)
+ if n.class == String then
+ case n
+ when 'first' then argument(0)
+ when 'second' then argument(1)
+ when 'third' then argument(2)
+ else
+ argument(0)
+ end
+ elsif @arguments[n] then
+ @arguments[n]
+ else
+ ''
+ end
+ end
+
+ # a few local methods, cannot be defined nested (yet)
+
+ private
+
+ def dirtyvalue(value)
+ if value then
+ value.gsub(/([\"\'])(.*?)\1/) do
+ $2.gsub(/\s+/o, "\xFF")
+ end
+ else
+ ''
+ end
+ end
+
+ def cleanvalue(value)
+ if value then
+ # value.sub(/^([\"\'])(.*?)\1$/) { $2.gsub(/\xFF/o, ' ') }
+ value.gsub(/\xFF/o, ' ')
+ else
+ ''
+ end
+ end
+
+ def locatedouble(key, value)
+
+ foundkey, foundkind = nil, nil
+
+ @registered.each do |option, shortcut, kind|
+ if option == key then
+ foundkey, foundkind = option, kind
+ break
+ end
+ end
+ unless foundkey then
+ @registered.each do |option, shortcut, kind|
+ n = 0
+ begin
+ re = /^#{key}/i
+ rescue
+ key = key.inspect.sub(/^\"(.*)\"$/) do $1 end
+ re = /^#{key}/i
+ ensure
+ if option =~ re then
+ case n
+ when 0
+ foundkey, foundkind, n = option, kind, 1
+ when 1
+ # ambiguous matches, like --fix => --fixme --fixyou
+ foundkey, foundkind = nil, nil
+ break
+ end
+ end
+ end
+ end
+ end
+ if foundkey then
+ @provided[foundkey] = true
+ if foundkind == VALUE then
+ @options[foundkey] = cleanvalue(value)
+ else
+ @options[foundkey] = true
+ end
+ else
+ if value.class == FalseClass then
+ @unchecked[key] = true
+ else
+ @unchecked[key] = cleanvalue(value)
+ end
+ end
+
+ end
+
+ def locatesingle(key, value)
+
+ @registered.each do |option, shortcut, kind|
+ if shortcut == key then
+ @provided[option] = true
+ @options[option] = if kind == VALUE then '' else cleanvalue(value) end
+ break
+ end
+ end
+
+ end
+
+ def locateseries(series, value)
+
+ series.each do |key|
+ locatesingle(key,cleanvalue(value))
+ end
+
+ end
+
+ public
+
+ attr_reader :arguments, :options, :original, :unchecked
+
+end
+
+# options = CommandLine.new
+#
+# options.register("filename", "f", CommandLine::VALUE)
+# options.register("request" , "r", CommandLine::VALUE)
+# options.register("verbose" , "v", CommandLine::FLAG)
+#
+# options.expand
+# options.extend(str)
+# options.show
+#
+# c = CommandLine.new
+#
+# c.registervalue('aaaa')
+# c.registervalue('test')
+# c.registervalue('zzzz')
+#
+# c.registerhelp('aaaa','some aaaa to enter')
+# c.registerhelp('test','some text to enter')
+# c.registerhelp('zzzz','some zzzz to enter')
+#
+# c.registermandate('test')
+#
+# c.expand
+#
+# class CommandLine
+#
+# def showhelp (banner,*str)
+# if helpkeys(*str).length>0
+# print banner
+# helpkeys(*str).each do |h|
+# print helptext(h) + "\n"
+# end
+# true
+# else
+# false
+# end
+# end
+#
+# def showmandate(banner)
+# if forgotten.length>0
+# print banner
+# forgotten.each do |f|
+# print helptext(f) + "\n"
+# end
+# true
+# else
+# false
+# end
+# end
+#
+# end
+#
+# c.showhelp("you can provide:\n\n")
+# c.showmandate("you also need to provide:\n\n")
diff --git a/scripts/context/ruby/base/system.rb b/scripts/context/ruby/base/system.rb
new file mode 100644
index 000000000..c3fb08645
--- /dev/null
+++ b/scripts/context/ruby/base/system.rb
@@ -0,0 +1,121 @@
+# module : base/system
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+require "rbconfig"
+
+module System
+
+ @@mswindows = Config::CONFIG['host_os'] =~ /mswin/
+ @@binpaths = ENV['PATH'].split(File::PATH_SEPARATOR)
+ @@binsuffixes = if $mswindows then ['.exe','.com','.bat'] else ['','.sh','.csh'] end
+ @@located = Hash.new
+ @@binnames = Hash.new
+
+ if @@mswindows then
+ @@binnames['ghostscript'] = ['gswin32c.exe','gs.cmd','gs.bat']
+ @@binnames['imagemagick'] = ['imagemagick.exe','convert.exe']
+ @@binnames['inkscape'] = ['inkscape.exe']
+ else
+ @@binnames['ghostscript'] = ['gs']
+ @@binnames['imagemagick'] = ['convert']
+ @@binnames['inkscape'] = ['inkscape']
+ end
+
+
+ def System.null
+ if @@mswindows then 'nul' else '/dev/null' end
+ end
+
+ def System.unix?
+ not @@mswindows
+ end
+ def System.mswin?
+ @@mswindows
+ end
+
+ def System.binnames(str)
+ if @@binnames.key?(str) then
+ @@binnames[str]
+ else
+ [str]
+ end
+ end
+
+ def System.prependengine(str)
+ if str =~ /^\S+\.(pl|rb|lua|py)/io then
+ case $1
+ when 'pl' then return "perl #{str}"
+ when 'rb' then return "ruby #{str}"
+ when 'lua' then return "lua #{str}"
+ when 'py' then return "python #{str}"
+ end
+ end
+ return str
+ end
+
+ def System.locatedprogram(program)
+ if @@located.key?(program) then
+ return @@located[program]
+ else
+ System.binnames(program).each do |binname|
+ if binname =~ /\..*$/io then
+ @@binpaths.each do |path|
+ if FileTest.file?(str = File.join(path,binname)) then
+ return @@located[program] = System.prependengine(str)
+ end
+ end
+ end
+ binname.gsub!(/\..*$/io, '')
+ @@binpaths.each do |path|
+ @@binsuffixes.each do |suffix|
+ if FileTest.file?(str = File.join(path,"#{binname}#{suffix}")) then
+ return @@located[program] = System.prependengine(str)
+ end
+ end
+ end
+ end
+ end
+ return @@located[program] = "texmfstart #{program}"
+ end
+
+ def System.command(program,arguments='')
+ if program =~ /^(.*?) (.*)$/ then
+ program = System.locatedprogram($1) + ' ' + $2
+ else
+ program = System.locatedprogram(program)
+ end
+ program = program + ' ' + arguments if ! arguments.empty?
+ program.gsub!(/\s+/io, ' ')
+ #program.gsub!(/(\/\.\/)+/io, '/')
+ program.gsub!(/\\/io, '/')
+ return program
+ end
+
+ def System.run(program,arguments='',pipe=false,collect=false)
+ if pipe then
+ if collect then
+ `#{System.command(program,arguments)} 2>&1`
+ else
+ `#{System.command(program,arguments)}`
+ end
+ else
+ system(System.command(program,arguments))
+ end
+ end
+
+ def System.pipe(program,arguments='',collect=false)
+ System.run(program,arguments,true)
+ end
+
+ def System.safepath(path)
+ if path.match(/ /o) then "\"#{path}\"" else path end
+ end
+
+end
diff --git a/scripts/context/ruby/base/tex.rb b/scripts/context/ruby/base/tex.rb
new file mode 100644
index 000000000..84025693b
--- /dev/null
+++ b/scripts/context/ruby/base/tex.rb
@@ -0,0 +1,2299 @@
+# module : base/tex
+# 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
+# - use -8bit and -progname
+#
+
+# report ?
+
+require 'fileutils'
+
+require 'base/variables'
+require 'base/kpse'
+require 'base/system'
+require 'base/state'
+require 'base/pdf'
+require 'base/file'
+require 'base/ctx'
+require 'base/mp'
+
+class String
+
+ def standard?
+ begin
+ self == 'standard'
+ rescue
+ false
+ end
+ end
+
+end
+
+# class String
+ # def utf_bom?
+ # self.match(/^\357\273\277/o).length>0 rescue false
+ # end
+# end
+
+class Array
+
+ def standard?
+ begin
+ self.include?('standard')
+ rescue
+ false
+ end
+ end
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+end
+
+class TEX
+
+ # The make-part of this class was made on a rainy day while listening
+ # to "10.000 clowns on a rainy day" by Jan Akkerman. Unfortunately the
+ # make method is not as swinging as this live cd.
+
+ include Variables
+
+ @@texengines = Hash.new
+ @@mpsengines = Hash.new
+ @@backends = Hash.new
+ @@mappaths = Hash.new
+ @@runoptions = Hash.new
+ @@tcxflag = Hash.new
+ @@draftoptions = Hash.new
+ @@synctexcoptions = Hash.new
+ @@texformats = Hash.new
+ @@mpsformats = Hash.new
+ @@prognames = Hash.new
+ @@texmakestr = Hash.new
+ @@texprocstr = Hash.new
+ @@mpsmakestr = Hash.new
+ @@mpsprocstr = Hash.new
+ @@texmethods = Hash.new
+ @@mpsmethods = Hash.new
+ @@pdftex = 'pdftex' # new default, pdfetex is gone
+ @@luafiles = "luafiles.tmp"
+ @@luatarget = "lua/context"
+
+ @@platformslash = if System.unix? then "\\\\" else "\\" end
+
+ ['tex','etex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdftex' end
+ ['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end
+ ['xetex'] .each do |e| @@texengines[e] = 'xetex' end
+ ['petex'] .each do |e| @@texengines[e] = 'petex' end
+ ['luatex'] .each do |e| @@texengines[e] = 'luatex' end
+
+ ['metapost','mpost', 'standard'] .each do |e| @@mpsengines[e] = 'mpost' end
+
+ ['pdfetex','pdftex','pdf','pdftex','standard'] .each do |b| @@backends[b] = 'pdftex' end
+ ['dvipdfmx','dvipdfm','dpx','dpm'] .each do |b| @@backends[b] = 'dvipdfmx' end
+ ['xetex','xtx'] .each do |b| @@backends[b] = 'xetex' end
+ ['petex'] .each do |b| @@backends[b] = 'dvipdfmx' end
+ ['aleph'] .each do |b| @@backends[b] = 'dvipdfmx' end
+ ['dvips','ps','dvi'] .each do |b| @@backends[b] = 'dvips' end
+ ['dvipsone'] .each do |b| @@backends[b] = 'dvipsone' end
+ ['acrobat','adobe','distiller'] .each do |b| @@backends[b] = 'acrobat' end
+ ['xdv','xdv2pdf'] .each do |b| @@backends[b] = 'xdv2pdf' end
+
+ ['tex','standard'] .each do |b| @@mappaths[b] = 'dvips' end
+ ['pdftex','pdfetex'] .each do |b| @@mappaths[b] = 'pdftex' end
+ ['aleph','omega','xetex','petex'] .each do |b| @@mappaths[b] = 'dvipdfmx' end
+ ['dvipdfm', 'dvipdfmx', 'xdvipdfmx'] .each do |b| @@mappaths[b] = 'dvipdfmx' end
+ ['xdv','xdv2pdf'] .each do |b| @@mappaths[b] = 'dvips' end
+
+ # todo norwegian (no)
+
+ ['plain'] .each do |f| @@texformats[f] = 'plain' end
+ ['cont-en','en','english','context','standard'].each do |f| @@texformats[f] = 'cont-en' end
+ ['cont-nl','nl','dutch'] .each do |f| @@texformats[f] = 'cont-nl' end
+ ['cont-de','de','german'] .each do |f| @@texformats[f] = 'cont-de' end
+ ['cont-it','it','italian'] .each do |f| @@texformats[f] = 'cont-it' end
+ ['cont-fr','fr','french'] .each do |f| @@texformats[f] = 'cont-fr' end
+ ['cont-cs','cs','cont-cz','cz','czech'] .each do |f| @@texformats[f] = 'cont-cs' end
+ ['cont-ro','ro','romanian'] .each do |f| @@texformats[f] = 'cont-ro' end
+ ['cont-gb','gb','cont-uk','uk','british'] .each do |f| @@texformats[f] = 'cont-gb' end
+ ['cont-pe','pe','persian'] .each do |f| @@texformats[f] = 'cont-pe' end
+ ['cont-xp','xp','experimental'] .each do |f| @@texformats[f] = 'cont-xp' end
+ ['mptopdf'] .each do |f| @@texformats[f] = 'mptopdf' end
+
+ ['latex'] .each do |f| @@texformats[f] = 'latex.ltx' end
+
+ ['plain','mpost'] .each do |f| @@mpsformats[f] = 'mpost' end
+ ['metafun','context','standard'] .each do |f| @@mpsformats[f] = 'metafun' end
+
+ ['pdftex','pdfetex','aleph','omega','petex',
+ 'xetex','luatex'] .each do |p| @@prognames[p] = 'context' end
+ ['mpost'] .each do |p| @@prognames[p] = 'metafun' end
+ ['latex','pdflatex'] .each do |p| @@prognames[p] = 'latex' end
+
+ ['plain','default','standard','mptopdf'] .each do |f| @@texmethods[f] = 'plain' end
+ ['cont-en','cont-nl','cont-de','cont-it',
+ 'cont-fr','cont-cs','cont-ro','cont-gb',
+ 'cont-pe','cont-xp'] .each do |f| @@texmethods[f] = 'context' end
+ ['latex','pdflatex'] .each do |f| @@texmethods[f] = 'latex' end
+
+ ['plain','default','standard'] .each do |f| @@mpsmethods[f] = 'plain' end
+ ['metafun'] .each do |f| @@mpsmethods[f] = 'metafun' end
+
+ @@texmakestr['plain'] = @@platformslash + "dump"
+ @@mpsmakestr['plain'] = @@platformslash + "dump"
+
+ ['cont-en','cont-nl','cont-de','cont-it',
+ 'cont-fr','cont-cs','cont-ro','cont-gb',
+ 'cont-pe','cont-xp'] .each do |f| @@texprocstr[f] = @@platformslash + "emergencyend" end
+
+ @@runoptions['aleph'] = ['--8bit']
+ @@runoptions['luatex'] = ['--file-line-error']
+ @@runoptions['mpost'] = ['--8bit']
+ @@runoptions['pdfetex'] = ['--8bit'] # obsolete
+ @@runoptions['pdftex'] = ['--8bit'] # pdftex is now pdfetex
+ # @@runoptions['petex'] = []
+ @@runoptions['xetex'] = ['--8bit','-output-driver="xdvipdfmx -E -d 4 -V 5"']
+ @@draftoptions['pdftex'] = ['--draftmode']
+ @@synctexcoptions['pdftex'] = ['--synctex=1']
+ @@synctexcoptions['luatex'] = ['--synctex=1']
+ @@synctexcoptions['xetex'] = ['--synctex=1']
+
+ @@tcxflag['aleph'] = true
+ @@tcxflag['luatex'] = false
+ @@tcxflag['mpost'] = false
+ @@tcxflag['pdfetex'] = true
+ @@tcxflag['pdftex'] = true
+ @@tcxflag['petex'] = false
+ @@tcxflag['xetex'] = false
+
+ @@mainbooleanvars = [
+ 'batchmode', 'nonstopmode', 'fast', 'final',
+ 'paranoid', 'notparanoid', 'nobanner', 'once', 'allpatterns', 'draft',
+ 'nompmode', 'nomprun', 'automprun', 'combine',
+ 'nomapfiles', 'local',
+ 'arrange', 'noarrange',
+ 'forcexml', 'foxet',
+ 'alpha', 'beta', 'luatex',
+ 'mpyforce', 'forcempy',
+ 'forcetexutil', 'texutil',
+ 'globalfile', 'autopath',
+ 'purge', 'purgeall', 'keep', 'autopdf', 'xpdf', 'simplerun', 'verbose',
+ 'nooptionfile', 'nobackend', 'noctx', 'utfbom',
+ 'mkii','mkiv',
+ 'synctex',
+ ]
+ @@mainstringvars = [
+ 'modefile', 'result', 'suffix', 'response', 'path',
+ 'filters', 'usemodules', 'environments', 'separation', 'setuppath',
+ 'arguments', 'input', 'output', 'randomseed', 'modes', 'mode', 'filename',
+ 'ctxfile', 'printformat', 'paperformat', 'paperoffset',
+ 'timeout', 'passon'
+ ]
+ @@mainstandardvars = [
+ 'mainlanguage', 'bodyfont', 'language'
+ ]
+ @@mainknownvars = [
+ 'engine', 'distribution', 'texformats', 'mpsformats', 'progname', 'interface',
+ 'runs', 'backend'
+ ]
+
+ @@extrabooleanvars = []
+ @@extrastringvars = []
+
+ def booleanvars
+ [@@mainbooleanvars,@@extrabooleanvars].flatten.uniq
+ end
+ def stringvars
+ [@@mainstringvars,@@extrastringvars].flatten.uniq
+ end
+ def standardvars
+ [@@mainstandardvars].flatten.uniq
+ end
+ def knownvars
+ [@@mainknownvars].flatten.uniq
+ end
+ def allbooleanvars
+ [@@mainbooleanvars,@@extrabooleanvars].flatten.uniq
+ end
+ def allstringvars
+ [@@mainstringvars,@@extrastringvars,@@mainstandardvars,@@mainknownvars].flatten.uniq
+ end
+
+ def setextrastringvars(vars)
+ # @@extrastringvars << vars -- problems in 1.9
+ @@extrastringvars = [@@extrastringvars,vars].flatten
+ end
+ def setextrabooleanvars(vars)
+ # @@extrabooleanvars << vars -- problems in 1.9
+ @@extrabooleanvars = [@@extrabooleanvars,vars].flatten
+ end
+
+ # def jobvariables(names=nil)
+ # if [names ||[]].flatten.size == 0 then
+ # names = [allbooleanvars,allstringvars].flatten
+ # end
+ # data = Hash.new
+ # names.each do |name|
+ # if allbooleanvars.include?(name) then
+ # data[name] = if getvariable(name) then "yes" else "no" end
+ # else
+ # data[name] = getvariable(name)
+ # end
+ # end
+ # data
+ # end
+
+ # def setjobvariables(names=nil)
+ # assignments = Array.new
+ # jobvariables(names).each do |k,v|
+ # assignments << "#{k}=\{#{v}\}"
+ # end
+ # "\setvariables[exe][#{assignments.join(", ")}]"
+ # end
+
+ @@temprunfile = 'texexec'
+ @@temptexfile = 'texexec.tex'
+
+ def initialize(logger=nil)
+ if @logger = logger then
+ def report(str='')
+ @logger.report(str)
+ end
+ else
+ def report(str='')
+ puts(str)
+ end
+ end
+ @cleanups = Array.new
+ @variables = Hash.new
+ @startuptime = Time.now
+ # options
+ booleanvars.each do |k|
+ setvariable(k,false)
+ end
+ stringvars.each do |k|
+ setvariable(k,'')
+ end
+ standardvars.each do |k|
+ setvariable(k,'standard')
+ end
+ setvariable('distribution', Kpse.distribution)
+ setvariable('texformats', defaulttexformats)
+ setvariable('mpsformats', defaultmpsformats)
+ setvariable('progname', 'standard') # or ''
+ setvariable('interface', 'standard')
+ setvariable('engine', 'standard') # replaced by tex/mpsengine
+ setvariable('backend', 'pdftex')
+ setvariable('runs', '8')
+ setvariable('randomseed', rand(1440).to_s) # we want the same seed for one run
+ # files
+ setvariable('files', [])
+ # defaults
+ setvariable('texengine', 'standard')
+ setvariable('mpsengine', 'standard')
+ setvariable('backend', 'standard')
+ setvariable('error', '')
+ end
+
+ def error?
+ not getvariable('error').empty?
+ end
+
+ def runtime
+ Time.now - @startuptime
+ end
+
+ def reportruntime
+ report("runtime: #{runtime}")
+ end
+
+ def runcommand(something)
+ command = [something].flatten.join(' ')
+ report("running: #{command}") if getvariable('verbose')
+ system(command)
+ end
+
+ def inspect(name=nil)
+ if ! name || name.empty? then
+ name = [booleanvars,stringvars,standardvars,knownvars]
+ end
+ str = '' # allocate
+ [name].flatten.each do |n|
+ if str = getvariable(n) then
+ str = str.join(" ") if str.class == Array
+ unless (str.class == String) && str.empty? then
+ report("option '#{n}' is set to '#{str}'")
+ end
+ end
+ end
+ end
+
+ def tempfilename(suffix='')
+ @@temprunfile + if suffix.empty? then '' else ".#{suffix}" end
+ end
+
+ def cleanup
+ @cleanups.each do |name|
+ begin
+ File.delete(name) if FileTest.file?(name)
+ rescue
+ report("unable to delete #{name}")
+ end
+ end
+ end
+
+ def cleanuptemprunfiles
+ begin
+ Dir.glob("#{@@temprunfile}*").each do |name|
+ if File.file?(name) && (File.splitname(name)[1] !~ /(pdf|dvi)/o) then
+ File.delete(name) rescue false
+ end
+ end
+ rescue
+ end
+ ['mpgraph.mp'].each do |file|
+ (File.delete(file) if (FileTest.size?(file) rescue 10) < 10) rescue false
+ end
+ end
+
+ def backends() @@backends.keys.sort end
+
+ def texengines() @@texengines.keys.sort end
+ def mpsengines() @@mpsengines.keys.sort end
+ def texformats() @@texformats.keys.sort end
+ def mpsformats() @@mpsformats.keys.sort end
+
+ def defaulttexformats() ['en','nl','mptopdf'] end
+ def defaultmpsformats() ['metafun'] end
+
+ def texmakeextras(format) @@texmakestr[format] || '' end
+ def mpsmakeextras(format) @@mpsmakestr[format] || '' end
+ def texprocextras(format) @@texprocstr[format] || '' end
+ def mpsprocextras(format) @@mpsprocstr[format] || '' end
+
+ def texmethod(format) @@texmethods[str] || @@texmethods['standard'] end
+ def mpsmethod(format) @@mpsmethods[str] || @@mpsmethods['standard'] end
+
+ def runoptions(engine)
+ options = if getvariable('draft') then @@draftoptions[engine] else [] end
+ options = if getvariable('synctex') then @@synctexcoptions[engine] else [] end
+ begin
+ if str = getvariable('passon') then
+ options = [options,str.split(' ')].flatten
+ end
+ rescue
+ end
+ if @@runoptions.key?(engine) then
+ [options,@@runoptions[engine]].flatten.join(' ')
+ else
+ options.join(' ')
+ end
+ end
+
+ # private
+
+ def cleanuplater(name)
+ begin
+ @cleanups.push(File.expand_path(name))
+ rescue
+ @cleanups.push(name)
+ end
+ end
+
+ def openedfile(name)
+ begin
+ f = File.open(name,'w')
+ rescue
+ report("file '#{File.expand_path(name)}' cannot be opened for writing")
+ return nil
+ else
+ cleanuplater(name) if f
+ return f
+ end
+ end
+
+ def prefixed(format,engine)
+ # format
+ case engine
+ when /etex|pdftex|pdfetex|aleph|xetex|luatex/io then
+ "*#{format}"
+ else
+ format
+ end
+ end
+
+ def quoted(str)
+ if str =~ /^[^\"].* / then "\"#{str}\"" else str end
+ end
+
+ def getarrayvariable(str='')
+ str = getvariable(str)
+ if str.class == String then str.split(',') else str.flatten end
+ end
+
+ def validtexformat(str) validsomething(str,@@texformats,'tex') end
+ def validmpsformat(str) validsomething(str,@@mpsformats,'mp' ) end
+ def validtexengine(str) validsomething(str,@@texengines,'pdftex') end
+ def validmpsengine(str) validsomething(str,@@mpsengines,'mpost' ) end
+
+ def validtexmethod(str) [validsomething(str,@@texmethods)].flatten.first end
+ def validmpsmethod(str) [validsomething(str,@@mpsmethods)].flatten.first end
+
+ def validsomething(str,something,type=nil)
+ if str then
+ list = [str].flatten.collect do |s|
+ if something[s] then
+ something[s]
+ elsif type && s =~ /\.#{type}$/ then
+ s
+ else
+ nil
+ end
+ end .compact.uniq
+ if list.length>0 then
+ if str.class == String then list.first else list end
+ else
+ false
+ end
+ else
+ false
+ end
+ end
+
+ def validbackend(str)
+ if str && @@backends.key?(str) then
+ @@backends[str]
+ else
+ @@backends['standard']
+ end
+ end
+
+ def validprogname(str)
+ if str then
+ [str].flatten.each do |s|
+ s = s.sub(/\.\S*/,"")
+ return @@prognames[s] if @@prognames.key?(s)
+ end
+ return str[0].sub(/\.\S*/,"")
+ else
+ return nil
+ end
+ end
+
+ # we no longer support the & syntax
+
+ def formatflag(engine=nil,format=nil)
+ case getvariable('distribution')
+ when 'standard' then prefix = "--fmt"
+ when /web2c/io then prefix = web2cformatflag(engine)
+ when /miktex/io then prefix = "--undump"
+ else return ""
+ end
+ if format then
+ "#{prefix}=#{format.sub(/\.\S+$/,"")}"
+ else
+ prefix
+ end
+ end
+
+ def web2cformatflag(engine=nil)
+ # funny that we've standardized on the fmt suffix (at the cost of
+ # upward compatibility problems) but stuck to the bas/mem/fmt flags
+ if engine then
+ case validmpsengine(engine)
+ when /mpost/ then "-mem"
+ when /mfont/ then "-bas"
+ else "-fmt"
+ end
+ else
+ "-fmt"
+ end
+ end
+
+ def prognameflag(progname=nil)
+ case getvariable('distribution')
+ when 'standard' then prefix = "-progname"
+ when /web2c/io then prefix = "-progname"
+ when /miktex/io then prefix = "-alias"
+ else return ""
+ end
+ if progname and not progname.empty? then
+ "#{prefix}=#{progname}"
+ else
+ prefix
+ end
+ end
+
+ def iniflag() # should go to kpse and kpse should become texenv
+ if Kpse.miktex? then
+ "-initialize"
+ else
+ "--ini"
+ end
+ end
+ def tcxflag(engine)
+ if @@tcxflag[engine] then
+ file = "natural.tcx"
+ if Kpse.miktex? then
+ "-tcx=#{file}"
+ else
+ "-translate-file=#{file}"
+ end
+ else
+ ""
+ end
+ end
+
+ def filestate(file)
+ File.mtime(file).strftime("%d/%m/%Y %H:%M:%S")
+ end
+
+ # will go to context/process context/listing etc
+
+ def contextversion # ook elders gebruiken
+ filename = Kpse.found('context.tex')
+ version = 'unknown'
+ begin
+ if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+.*?)\}/) then
+ version = $1
+ end
+ rescue
+ end
+ return version
+ end
+
+ def cleanupluafiles
+ File.delete(@@luafiles) rescue false
+ end
+
+ def compileluafiles
+ begin
+ Dir.glob("lua/context/*.luc").each do |luc|
+ File.delete(luc) rescue false
+ end
+ rescue
+ end
+ if data = (IO.readlines(@@luafiles) rescue nil) then
+ report("compiling lua files (using #{File.expand_path(@@luafiles)})")
+ begin
+ FileUtils.makedirs(@@luatarget) rescue false
+ data.each do |line|
+ luafile = line.chomp
+ lucfile = File.basename(luafile).gsub(/\..*?$/,'') + ".luc"
+ if runcommand(["luac","-s","-o",quoted(File.join(Dir.getwd,@@luatarget,lucfile)),quoted(luafile)]) then
+ report("#{File.basename(luafile)} converted to #{File.basename(lucfile)}")
+ else
+ report("#{File.basename(luafile)} not converted to #{File.basename(lucfile)}")
+ end
+ end
+ rescue
+ report("fatal error in compilation")
+ end
+ else
+ report("no lua compilations needed")
+ end
+ File.delete(@@luafiles) rescue false
+ end
+
+ # we need engine methods
+
+ def makeformats
+
+ checktestversion
+
+ report("using search method '#{Kpse.searchmethod}'")
+ if getvariable('fast') then
+ report('using existing database')
+ else
+ report('updating file database')
+ Kpse.update # obsolete here
+ if getvariable('luatex') then
+ begin
+ runcommand(["luatools","--generate","--verbose"])
+ rescue
+ report("run 'luatools --generate' manualy")
+ exit
+ end
+ end
+ end
+ # goody
+ if getvariable('texformats') == 'standard' then
+ setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty?
+ end
+ # prepare
+ texformats = validtexformat(getarrayvariable('texformats'))
+ mpsformats = validmpsformat(getarrayvariable('mpsformats'))
+ texengine = validtexengine(getvariable('texengine'))
+ mpsengine = validmpsengine(getvariable('mpsengine'))
+ # save current path
+ savedpath = Dir.getwd
+ # generate tex formats
+ unless texformats || mpsformats then
+ report('provide valid format (name.tex, name.mp, ...) or format id (metafun, en, nl, ...)')
+ setvariable('error','no format specified')
+ end
+ if texformats && texengine then
+ report("using tex engine #{texengine}")
+ texformatpath = if getvariable('local') then '.' else Kpse.formatpath(texengine,true) end
+ # can be empty, to do
+ report("using tex format path #{texformatpath}")
+ Dir.chdir(texformatpath) rescue false
+ if FileTest.writable?(texformatpath) then
+ # from now on we no longer support this; we load
+ # all patterns and if someone wants another
+ # interface language ... cook up a fmt or usr file
+ #
+ # if texformats.length > 0 then
+ # makeuserfile
+ # makeresponsefile
+ # end
+ if texengine == 'luatex' then
+ cleanupluafiles
+ texformats.each do |texformat|
+ report("generating tex format #{texformat}")
+ flags = ['--ini','--compile']
+ flags << '--verbose' if getvariable('verbose')
+ flags << '--mkii' if getvariable('mkii')
+ run_luatools("#{flags.join(" ")} #{texformat}")
+ end
+ compileluafiles
+ else
+ texformats.each do |texformat|
+ report("generating tex format #{texformat}")
+ progname = validprogname([getvariable('progname'),texformat,texengine])
+ runcommand([quoted(texengine),prognameflag(progname),iniflag,tcxflag(texengine),prefixed(texformat,texengine),texmakeextras(texformat)])
+ end
+ end
+ else
+ report("unable to make format due to lack of permissions")
+ texformatpath = ''
+ setvariable('error','no permissions to write')
+ end
+ if not mpsformats then
+ # we want metafun to be in sync
+ setvariable('mpsformats',defaultmpsformats)
+ mpsformats = validmpsformat(getarrayvariable('mpsformats'))
+ end
+ else
+ texformatpath = ''
+ end
+ # generate mps formats
+ if mpsformats && mpsengine then
+ report("using mp engine #{mpsengine}")
+ mpsformatpath = if getvariable('local') then '.' else Kpse.formatpath(mpsengine,false) end
+ report("using mps format path #{mpsformatpath}")
+ Dir.chdir(mpsformatpath) rescue false
+ if FileTest.writable?(mpsformatpath) then
+ mpsformats.each do |mpsformat|
+ report("generating mps format #{mpsformat}")
+ progname = validprogname([getvariable('progname'),mpsformat,mpsengine])
+ # if not runcommand([quoted(mpsengine),prognameflag(progname),iniflag,tcxflag(mpsengine),runoptions(mpsengine),mpsformat,mpsmakeextras(mpsformat)]) then
+ if not runcommand([quoted(mpsengine),prognameflag(progname),iniflag,runoptions(mpsengine),mpsformat,mpsmakeextras(mpsformat)]) then
+ setvariable('error','no format made')
+ end
+ end
+ else
+ report("unable to make format due to lack of permissions")
+ mpsformatpath = ''
+ setvariable('error','file permission problem')
+ end
+ else
+ mpsformatpath = ''
+ end
+ # check for problems
+ report("")
+ report("tex engine path: #{texformatpath}") unless texformatpath.empty?
+ report("mps engine path: #{mpsformatpath}") unless mpsformatpath.empty?
+ report("")
+ [['fmt','tex'],['mem','mps']].each do |f|
+ [[texformatpath,'global'],[mpsformatpath,'global'],[savedpath,'current']].each do |p|
+ begin
+ Dir.chdir(p[0])
+ rescue
+ else
+ Dir.glob("*.#{f[0]}").each do |file|
+ report("#{f[1]}: #{filestate(file)} > #{File.expand_path(file)} (#{File.size(file)})")
+ end
+ end
+ end
+ end
+ begin
+ lucdir = File.join(texformatpath,@@luatarget)
+ Dir.chdir(lucdir)
+ rescue
+ else
+ Dir.glob("*.luc").each do |file|
+ report("luc: #{filestate(file)} > #{File.expand_path(file)} (#{File.size(file)})")
+ end
+ end
+ # to be sure, go back to current path
+ begin
+ Dir.chdir(savedpath)
+ rescue
+ end
+ # finalize
+ cleanup
+ report("")
+ reportruntime
+ end
+
+ def checkcontext
+
+ # todo : report texmf.cnf en problems
+
+ # basics
+ report("current distribution: #{Kpse.distribution}")
+ report("context source date: #{contextversion}")
+ formatpaths = Kpse.formatpaths
+ globpattern = "**/{#{formatpaths.join(',')}}/*/*.{fmt,efmt,ofmt,xfmt,mem}"
+ report("format path: #{formatpaths.join(' ')}")
+ # utilities
+ report('start of analysis')
+ results = Array.new
+ # ['texexec','texutil','ctxtools'].each do |program|
+ ['texexec'].each do |program|
+ result = `texmfstart #{program} --help`
+ result.sub!(/.*?(#{program}[^\n]+)\n.*/mi) do $1 end
+ results.push("#{result}")
+ end
+ # formats
+ cleanuptemprunfiles
+ if formats = Dir.glob(globpattern) then
+ formats.sort.each do |name|
+ cleanuptemprunfiles
+ if f = open(tempfilename('tex'),'w') then
+ # kind of aleph-run-out-of-par safe
+ f << "\\starttext\n"
+ f << " \\relax test \\relax\n"
+ f << "\\stoptext\n"
+ f << "\\endinput\n"
+ f.close
+ if FileTest.file?(tempfilename('tex')) then
+ format = File.basename(name)
+ engine = if name =~ /(pdftex|pdfetex|aleph|xetex|luatex)[\/\\]#{format}/ then $1 else '' end
+ if engine.empty? then
+ engineflag = ""
+ else
+ engineflag = "--engine=#{$1}"
+ end
+ case format
+ when /cont\-([a-z]+)/ then
+ interface = $1.sub(/cont\-/,'')
+ results.push('')
+ results.push("testing interface #{interface}")
+ flags = ['--noctx','--process','--batch','--once',"--interface=#{interface}",engineflag]
+ # result = Kpse.pipescript('texexec',tempfilename,flags)
+ result = runtexexec([tempfilename], flags, 1)
+ if FileTest.file?("#{@@temprunfile}.log") then
+ logdata = IO.read("#{@@temprunfile}.log")
+ if logdata =~ /^\s*This is (.*?)[\s\,]+(.*?)$/moi then
+ if validtexengine($1.downcase) then
+ results.push("#{$1} #{$2.gsub(/\(format.*$/,'')}".strip)
+ end
+ end
+ if logdata =~ /^\s*(ConTeXt)\s+(.*int:\s+[a-z]+.*?)\s*$/moi then
+ results.push("#{$1} #{$2}".gsub(/\s+/,' ').strip)
+ end
+ else
+ results.push("format #{format} does not work")
+ end
+ when /metafun/ then
+ # todo
+ when /mptopdf/ then
+ # todo
+ end
+ else
+ results.push("error in creating #{tempfilename('tex')}")
+ end
+ end
+ cleanuptemprunfiles
+ end
+ end
+ report('end of analysis')
+ report
+ results.each do |line|
+ report(line)
+ end
+ cleanuptemprunfiles
+
+ end
+
+ private
+
+ def makeuserfile # not used in luatex (yet)
+ language = getvariable('language')
+ mainlanguage = getvariable('mainlanguage')
+ bodyfont = getvariable('bodyfont')
+ if f = openedfile("cont-fmt.tex") then
+ f << "\\unprotect\n"
+ case language
+ when 'all' then
+ f << "\\preloadallpatterns\n"
+ when '' then
+ f << "% no language presets\n"
+ when 'standard'
+ f << "% using defaults\n"
+ else
+ languages = language.split(',')
+ languages.each do |l|
+ f << "\\installlanguage[\\s!#{l}][\\c!state=\\v!start]\n"
+ end
+ mainlanguage = languages.first
+ end
+ unless mainlanguage == 'standard' then
+ f << "\\setupcurrentlanguage[\\s!#{mainlanguage}]\n";
+ end
+ unless bodyfont == 'standard' then
+ # ~ will become obsolete when lmr is used
+ f << "\\definetypescriptsynonym[cmr][#{bodyfont}]"
+ # ~ is already obsolete for some years now
+ f << "\\definefilesynonym[font-cmr][font-#{bodyfont}]\n"
+ end
+ f << "\\protect\n"
+ f << "\\endinput\n"
+ f.close
+ end
+ end
+
+ def makeresponsefile
+ interface = getvariable('interface')
+ if f = openedfile("mult-def.tex") then
+ case interface
+ when 'standard' then
+ f << "% using default response interface"
+ else
+ f << "\\def\\currentresponses\{#{interface}\}\n"
+ end
+ f << "\\endinput\n"
+ f.close
+ end
+ end
+
+ private # will become baee/context
+
+ @@preamblekeys = [
+ ['tex','texengine'],
+ ['engine','texengine'],
+ ['program','texengine'],
+ ['translate','tcxfilter'],
+ ['tcx','tcxfilter'],
+ ['output','backend'],
+ ['mode','mode'],
+ ['ctx','ctxfile'],
+ ['version','contextversion'],
+ ['format','texformats'],
+ ['interface','texformats'],
+ ]
+
+ @@re_utf_bom = /^\357\273\277/o
+
+ def scantexpreamble(filename)
+ begin
+ if FileTest.file?(filename) and tex = File.open(filename,'rb') then
+ bomdone = false
+ while str = tex.gets and str.chomp! do
+ unless bomdone then
+ if str.sub!(@@re_utf_bom, '')
+ report("utf mode forced (bom found)")
+ setvariable('utfbom',true)
+ end
+ bomdone = true
+ end
+ if str =~ /^\%\s*(.*)/o then
+ # we only accept lines with key=value pairs
+ vars, ok = Hash.new, true
+ $1.split(/\s+/o).each do |s|
+ k, v = s.split('=')
+ if k && v then
+ vars[k] = v
+ else
+ ok = false
+ break
+ end
+ end
+ if ok then
+ # we have a valid line
+
+ @@preamblekeys.each do |v|
+ setvariable(v[1],vars[v[0]]) if vars.key?(v[0]) && vars[v[0]]
+ end
+
+if getvariable('given.backend') == "standard" or getvariable('given.backend') == "" then
+ setvariable('backend',@@backends[getvariable('texengine')] || 'standard')
+end
+ break
+ end
+ else
+ break
+ end
+ end
+ tex.close
+ end
+ rescue
+ # well, let's not worry too much
+ end
+ end
+
+ def scantexcontent(filename)
+ if FileTest.file?(filename) and tex = File.open(filename,'rb') then
+ while str = tex.gets do
+ case str.chomp
+ when /^\%/o then
+ # next
+ # when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then
+ when /\\(starttekst|stoptekst|startonderdeel|startoverzicht)/o then
+ setvariable('texformats','nl') ; break
+ when /\\(stelle|verwende|umgebung|benutze)/o then
+ setvariable('texformats','de') ; break
+ when /\\(stel|gebruik|omgeving)/o then
+ setvariable('texformats','nl') ; break
+ when /\\(use|setup|environment)/o then
+ setvariable('texformats','en') ; break
+ when /\\(usa|imposta|ambiente)/o then
+ setvariable('texformats','it') ; break
+ when /(height|width|style)=/o then
+ setvariable('texformats','en') ; break
+ when /(hoehe|breite|schrift)=/o then
+ setvariable('texformats','de') ; break
+ when /(hoogte|breedte|letter)=/o then
+ setvariable('texformats','nl') ; break
+ when /(altezza|ampiezza|stile)=/o then
+ setvariable('texformats','it') ; break
+ when /externfiguur/o then
+ setvariable('texformats','nl') ; break
+ when /externalfigure/o then
+ setvariable('texformats','en') ; break
+ when /externeabbildung/o then
+ setvariable('texformats','de') ; break
+ when /figuraesterna/o then
+ setvariable('texformats','it') ; break
+ end
+ end
+ tex.close
+ end
+
+ end
+
+ private # will become base/context
+
+ def pushresult(filename,resultname)
+ fname = File.unsuffixed(filename)
+ rname = File.unsuffixed(resultname)
+ if ! rname.empty? && (rname != fname) then
+ report("outputfile #{rname}")
+ ['tuo','tuc','log','dvi','pdf'].each do |s|
+ File.silentrename(File.suffixed(fname,s),File.suffixed('texexec',s))
+ end
+ ['tuo','tuc'].each do |s|
+ File.silentrename(File.suffixed(rname,s),File.suffixed(fname,s)) if FileTest.file?(File.suffixed(rname,s))
+ end
+ end
+ end
+
+ def popresult(filename,resultname)
+ fname = File.unsuffixed(filename)
+ rname = File.unsuffixed(resultname)
+ if ! rname.empty? && (rname != fname) then
+ report("renaming #{fname} to #{rname}")
+ ['tuo','tuc','log','dvi','pdf'].each do |s|
+ File.silentrename(File.suffixed(fname,s),File.suffixed(rname,s))
+ end
+ report("restoring #{fname}")
+ unless $fname == 'texexec' then
+ ['tuo','tuc','log','dvi','pdf'].each do |s|
+ File.silentrename(File.suffixed('texexec',s),File.suffixed(fname,s))
+ end
+ end
+ end
+ end
+
+ def makestubfile(rawname,rawbase,forcexml=false)
+ if tmp = openedfile(File.suffixed(rawbase,'run')) then
+ tmp << "\\starttext\n"
+ if forcexml then
+ # tmp << checkxmlfile(rawname)
+ if getvariable('mkiv') then
+ tmp << "\\xmlprocess{\\xmldocument}{#{rawname}}{}\n"
+ else
+ tmp << "\\processXMLfilegrouped{#{rawname}}\n"
+ end
+ else
+ tmp << "\\processfile{#{rawname}}\n"
+ end
+ tmp << "\\stoptext\n"
+ tmp.close
+ return "run"
+ else
+ return File.splitname(rawname)[1]
+ end
+ end
+
+ # def checkxmlfile(rawname)
+ # tmp = ''
+ # if FileTest.file?(rawname) && (xml = File.open(rawname)) then
+ # xml.each do |line|
+ # case line
+ # when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then
+ # category, key, value, rest = $1, $2, $3, $4
+ # case category
+ # when 'job' then
+ # case key
+ # when 'control' then
+ # setvariable(value,if rest.empty? then true else rest end)
+ # when 'mode', 'modes' then
+ # tmp << "\\enablemode[#{value}]\n"
+ # when 'stylefile', 'environment' then
+ # tmp << "\\environment #{value}\n"
+ # when 'module' then
+ # tmp << "\\usemodule[#{value}]\n"
+ # when 'interface' then
+ # contextinterface = value
+ # when 'ctxfile' then
+ # setvariable('ctxfile', value)
+ # report("using source driven ctxfile #{value}")
+ # end
+ # end
+ # when /<[a-z]+/io then # beware of order, first pi test
+ # break
+ # end
+ # end
+ # xml.close
+ # end
+ # return tmp
+ # end
+
+ def extendvariable(name,value)
+ set = getvariable(name).split(',')
+ set << value
+ str = set.uniq.join(',')
+ setvariable(name,str)
+ end
+
+ def checkxmlfile(rawname)
+ if FileTest.file?(rawname) && (xml = File.open(rawname,'rb')) then
+ xml.each do |line|
+ case line
+ when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then
+ category, key, value, rest = $1, $2, $3, $4
+ case category
+ when 'job' then
+ case key
+ when 'control' then
+ setvariable(value,if rest.empty? then true else rest end)
+ when /^(mode)(s|)$/ then
+ extendvariable('modes',value)
+ when /^(stylefile|environment)(s|)$/ then
+ extendvariable('environments',value)
+ when /^(use|)(module)(s|)$/ then
+ extendvariable('usemodules',value)
+ when /^(filter)(s|)$/ then
+ extendvariable('filters',value)
+ when 'interface' then
+ contextinterface = value
+ when 'ctxfile' then
+ setvariable('ctxfile', value)
+ report("using source driven ctxfile #{value}")
+ end
+ end
+ when /<[a-z]+/io then # beware of order, first pi test
+ break
+ end
+ end
+ xml.close
+ end
+ end
+
+end
+
+class TEX
+
+ def timedrun(delay, &block)
+ delay = delay.to_i rescue 0
+ if delay > 0 then
+ begin
+ report("job started with timeout '#{delay}'")
+ timeout(delay) do
+ yield block
+ end
+ rescue TimeoutError
+ report("job aborted due to timeout '#{delay}'")
+ setvariable('error','timeout')
+ rescue
+ report("job aborted due to error")
+ setvariable('error','fatal error')
+ else
+ report("job finished within timeout '#{delay}'")
+ end
+ else
+ yield block
+ end
+ end
+
+ def processtex # much to do: mp, xml, runs etc
+ setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty?
+ getarrayvariable('files').each do |filename|
+ setvariable('filename',filename)
+ report("processing document '#{filename}'")
+ timedrun(getvariable('timeout')) do
+ processfile
+ end
+ end
+ reportruntime
+ end
+
+ def processmptex
+ getarrayvariable('files').each do |filename|
+ setvariable('filename',filename)
+ report("processing graphic '#{filename}'")
+ runtexmp(filename)
+ end
+ reportruntime
+ end
+
+ private
+
+ def load_map_files(filename) # tui basename
+ # c \usedmapfile{=}{lm-texnansi}
+ begin
+ str = ""
+ IO.read(filename).scan(/^c\s+\\usedmapfile\{(.*?)\}\{(.*?)\}\s*$/o) do
+ str << "\\loadmapfile[#{$2}.map]\n"
+ end
+ rescue
+ return ""
+ else
+ return str
+ end
+ end
+
+ public
+
+ # def run_luatools(args)
+ # dirty trick: we know that the lua path is relative to the ruby path; of course this
+ # will not work well when stubs are used
+ # [(ENV["_CTX_K_S_texexec_"] or ENV["_CTX_K_S_THREAD_"] or ENV["TEXMFSTART.THREAD"]), File.dirname($0)].each do |path|
+ # if path then
+ # script = "#{path}/../lua/luatools.lua"
+ # if FileTest.file?(script) then
+ # return runcommand("luatex --luaonly #{script} #{args}")
+ # end
+ # end
+ # end
+ # return runcommand("texmfstart luatools #{args}")
+ # end
+
+ def run_luatools(args)
+ return runcommand("luatools #{args}")
+ end
+
+ def processmpgraphic
+ getarrayvariable('files').each do |filename|
+ setvariable('filename',filename)
+ report("processing graphic '#{filename}'")
+ runtexmp(filename,'',false) # no purge
+ mapspecs = load_map_files(File.suffixed(filename,'temp','tui'))
+ unless getvariable('keep') then
+ # not enough: purge_mpx_files(filename)
+ Dir.glob(File.suffixed(filename,'temp*','*')).each do |fname|
+ File.delete(fname) unless File.basename(filename) == File.basename(fname)
+ end
+ end
+ begin
+ data = IO.read(File.suffixed(filename,'log'))
+ basename = filename.sub(/\.mp$/, '')
+ if data =~ /output files* written\:\s*(.*)$/moi then
+ files, number, range, list = $1.split(/\s+/), 0, false, []
+ files.each do |fname|
+ if fname =~ /^.*\.(\d+)$/ then
+ if range then
+ (number+1 .. $1.to_i).each do |i|
+ list << i
+ end
+ range = false
+ else
+ number = $1.to_i
+ list << number
+ end
+ elsif fname =~ /\.\./ then
+ range = true
+ else
+ range = false
+ next
+ end
+ end
+ begin
+ if getvariable('combine') then
+ fullname = "#{basename}.#{number}"
+ File.open("texexec.tex",'w') do |f|
+ f << "\\setupoutput[pdftex]\n"
+ f << "\\setupcolors[state=start]\n"
+ f << mapspecs
+ f << "\\starttext\n"
+ list.each do |number|
+ f << "\\startTEXpage\n"
+ f << "\\convertMPtoPDF{#{fullname}}{1}{1}"
+ f << "\\stopTEXpage\n"
+ end
+ f << "\\stoptext\n"
+ end
+ report("converting graphic '#{fullname}'")
+ runtex("texexec.tex")
+ pdffile = File.suffixed(basename,'pdf')
+ File.silentrename("texexec.pdf",pdffile)
+ report ("#{basename}.* converted to #{pdffile}")
+ else
+ list.each do |number|
+ begin
+ fullname = "#{basename}.#{number}"
+ File.open("texexec.tex",'w') do |f|
+ f << "\\setupoutput[pdftex]\n"
+ f << "\\setupcolors[state=start]\n"
+ f << mapspecs
+ f << "\\starttext\n"
+ f << "\\startTEXpage\n"
+ f << "\\convertMPtoPDF{#{fullname}}{1}{1}"
+ f << "\\stopTEXpage\n"
+ f << "\\stoptext\n"
+ end
+ report("converting graphic '#{fullname}'")
+ runtex("texexec.tex")
+ if files.length>1 then
+ pdffile = File.suffixed(basename,number.to_s,'pdf')
+ else
+ pdffile = File.suffixed(basename,'pdf')
+ end
+ File.silentrename("texexec.pdf",pdffile)
+ report ("#{fullname} converted to #{pdffile}")
+ end
+ end
+ end
+ rescue
+ report ("error when converting #{fullname} (#{$!})")
+ end
+ end
+ rescue
+ report("error in converting #{filename}")
+ end
+ end
+ reportruntime
+ end
+
+ def processmpstatic
+ if filename = getvariable('filename') then
+ filename += ".mp" unless filename =~ /\..+?$/
+ if FileTest.file?(filename) then
+ begin
+ data = IO.read(filename)
+ File.open("texexec.tex",'w') do |f|
+ f << "\\setupoutput[pdftex]\n"
+ f << "\\setupcolors[state=start]\n"
+ data.sub!(/^%mpenvironment\:\s*(.*?)$/moi) do
+ f << $1
+ "\n"
+ end
+ f << "\\starttext\n"
+ f << "\\startMPpage\n"
+ f << data.gsub(/end\.*\s*$/m, '') # a bit of a hack
+ f << "\\stopMPpage\n"
+ f << "\\stoptext\n"
+ end
+ report("converting static '#{filename}'")
+ runtex("texexec.tex")
+ pdffile = File.suffixed(filename,'pdf')
+ File.silentrename("texexec.pdf",pdffile)
+ report ("#{filename} converted to #{pdffile}")
+ rescue
+ report("error in converting #{filename} (#{$!}")
+ end
+ end
+ end
+ reportruntime
+ end
+
+ def processmpxtex
+ getarrayvariable('files').each do |filename|
+ setvariable('filename',filename)
+ report("processing text of graphic '#{filename}'")
+ processmpx(filename,false,true,true)
+ end
+ reportruntime
+ end
+
+ def deleteoptionfile(rawname)
+ ['top','top.keep'].each do |suffix|
+ begin
+ File.delete(File.suffixed(rawname,suffix))
+ rescue
+ end
+ end
+ end
+
+ def makeoptionfile(rawname, jobname, jobsuffix, finalrun, fastdisabled, kindofrun, currentrun=1)
+ begin
+ # jobsuffix = orisuffix
+ if topname = File.suffixed(rawname,'top') and opt = File.open(topname,'w') then
+ report("writing option file #{topname}")
+ # local handies
+ opt << "\% #{topname}\n"
+ opt << "\\unprotect\n"
+ #
+ # feedback and basic control
+ #
+ if getvariable('batchmode') then
+ opt << "\\batchmode\n"
+ end
+ if getvariable('nonstopmode') then
+ opt << "\\nonstopmode\n"
+ end
+ if getvariable('paranoid') then
+ opt << "\\def\\maxreadlevel{1}\n"
+ end
+ if getvariable('nomapfiles') then
+ opt << "\\disablemapfiles\n"
+ end
+ if getvariable('nompmode') || getvariable('nomprun') || getvariable('automprun') then
+ opt << "\\runMPgraphicsfalse\n"
+ end
+ if getvariable('utfbom') then
+ opt << "\\enableregime[utf]"
+ end
+ progname = validprogname(['metafun']) # [getvariable('progname'),mpsformat,mpsengine]
+ opt << "\\def\\MPOSTformatswitch\{#{prognameflag(progname)} #{formatflag('mpost')}=\}\n"
+ #
+ # process info
+ #
+ opt << "\\setupsystem[\\c!n=#{kindofrun},\\c!m=#{currentrun}]\n"
+ if (str = File.unixfied(getvariable('modefile'))) && ! str.empty? then
+ opt << "\\readlocfile{#{str}}{}{}\n"
+ end
+ if (str = File.unixfied(getvariable('result'))) && ! str.empty? then
+ opt << "\\setupsystem[file=#{str}]\n"
+ elsif (str = getvariable('suffix')) && ! str.empty? then
+ opt << "\\setupsystem[file=#{jobname}.#{str}]\n"
+ end
+ opt << "\\setupsystem[\\c!method=2]\n" # 1=oldtexexec 2=newtexexec (obsolete)
+ opt << "\\setupsystem[\\c!type=#{Tool.ruby_platform()}]\n"
+ if (str = File.unixfied(getvariable('path'))) && ! str.empty? then
+ opt << "\\usepath[#{str}]\n" unless str.empty?
+ end
+ if (str = getvariable('mainlanguage').downcase) && ! str.empty? && ! str.standard? then
+ opt << "\\setuplanguage[#{str}]\n"
+ end
+ if (str = getvariable('arguments')) && ! str.empty? then
+ opt << "\\setupenv[#{str}]\n"
+ end
+ if (str = getvariable('setuppath')) && ! str.empty? then
+ opt << "\\setupsystem[\\c!directory=\{#{str}\}]\n"
+ end
+ if (str = getvariable('randomseed')) && ! str.empty? then
+ report("using randomseed #{str}")
+ opt << "\\setupsystem[\\c!random=#{str}]\n"
+ end
+ if (str = getvariable('input')) && ! str.empty? then
+ opt << "\\setupsystem[inputfile=#{str}]\n"
+ else
+ opt << "\\setupsystem[inputfile=#{rawname}]\n"
+ end
+ #
+ # modes
+ #
+ # we handle both "--mode" and "--modes", else "--mode" is mapped onto "--modefile"
+ if (str = getvariable('modes')) && ! str.empty? then
+ opt << "\\enablemode[#{str}]\n"
+ end
+ if (str = getvariable('mode')) && ! str.empty? then
+ opt << "\\enablemode[#{str}]\n"
+ end
+ #
+ # options
+ #
+ opt << "\\startsetups *runtime:options\n"
+ if str = validbackend(getvariable('backend')) then
+ opt << "\\setupoutput[#{str}]\n"
+ elsif str = validbackend(getvariable('output')) then
+ opt << "\\setupoutput[#{str}]\n"
+ end
+ if getvariable('color') then
+ opt << "\\setupcolors[\\c!state=\\v!start]\n"
+ end
+ if (str = getvariable('separation')) && ! str.empty? then
+ opt << "\\setupcolors[\\c!split=#{str}]\n"
+ end
+ if (str = getvariable('paperformat')) && ! str.empty? && ! str.standard? then
+ if str =~ /^([a-z]+\d+)([a-z]+\d+)$/io then # A5A4 A4A3 A2A1 ...
+ opt << "\\setuppapersize[#{$1.upcase}][#{$2.upcase}]\n"
+ else # ...*...
+ pf = str.upcase.split(/[x\*]/o)
+ pf << pf[0] if pf.size == 1
+ opt << "\\setuppapersize[#{pf[0]}][#{pf[1]}]\n"
+ end
+ end
+ if (str = getvariable('background')) && ! str.empty? then
+ opt << "\\defineoverlay[whatever][{\\externalfigure[#{str}][\\c!factor=\\v!max]}]\n"
+ opt << "\\setupbackgrounds[\\v!page][\\c!background=whatever]\n"
+ end
+ if getvariable('centerpage') then
+ opt << "\\setuplayout[\\c!location=\\v!middle,\\c!marking=\\v!on]\n"
+ end
+ if getvariable('noarrange') then
+ opt << "\\setuparranging[\\v!disable]\n"
+ elsif getvariable('arrange') then
+ arrangement = Array.new
+ if finalrun then
+ arrangement << "\\v!doublesided" unless getvariable('noduplex')
+ case getvariable('printformat')
+ when '' then arrangement << "\\v!normal"
+ when /.*up/oi then arrangement << ["2UP","\\v!rotated"]
+ when /.*down/oi then arrangement << ["2DOWN","\\v!rotated"]
+ when /.*side/oi then arrangement << ["2SIDE","\\v!rotated"]
+ end
+ else
+ arrangement << "\\v!disable"
+ end
+ opt << "\\setuparranging[#{arrangement.flatten.join(',')}]\n" if arrangement.size > 0
+ end
+ if (str = getvariable('pages')) && ! str.empty? then
+ if str.downcase == 'odd' then
+ opt << "\\chardef\\whichpagetoshipout=1\n"
+ elsif str.downcase == 'even' then
+ opt << "\\chardef\\whichpagetoshipout=2\n"
+ else
+ pagelist = Array.new
+ str.split(/\,/).each do |page|
+ pagerange = page.split(/\D+/o)
+ if pagerange.size > 1 then
+ pagerange.first.to_i.upto(pagerange.last.to_i) do |p|
+ pagelist << p.to_s
+ end
+ else
+ pagelist << page
+ end
+ end
+ opt << "\\def\\pagestoshipout\{#{pagelist.join(',')}\}\n";
+ end
+ end
+ opt << "\\stopsetups\n"
+ #
+ # styles and modules
+ #
+ opt << "\\startsetups *runtime:modules\n"
+ begin getvariable('filters' ).split(',').uniq.each do |f| opt << "\\useXMLfilter[#{f}]\n" end ; rescue ; end
+ begin getvariable('usemodules' ).split(',').uniq.each do |m| opt << "\\usemodule [#{m}]\n" end ; rescue ; end
+ begin getvariable('environments').split(',').uniq.each do |e| opt << "\\environment #{e} \n" end ; rescue ; end
+ opt << "\\stopsetups\n"
+ #
+ opt << "\\protect \\endinput\n"
+ #
+ opt.close
+ else
+ report("unable to write option file #{topname}")
+ end
+ rescue
+ report("fatal error in writing option file #{topname} (#{$!})")
+ end
+ end
+
+ def takeprecautions
+ ENV['MPXCOMAND'] = '0' # else loop
+ if getvariable('paranoid') then
+ ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 'f'
+ ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'p'
+ ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'p'
+ elsif getvariable('notparanoid') then
+ ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 't'
+ ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'a'
+ ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'a'
+ end
+ if ENV['OPENIN_ANY'] && (ENV['OPENIN_ANY'] == 'p') then # first test redundant
+ setvariable('paranoid', true)
+ end
+ if ENV.key?('SHELL_ESCAPE') && (ENV['SHELL_ESCAPE'] == 'f') then
+ setvariable('automprun',true)
+ end
+ done = false
+ ['TXRESOURCES','MPRESOURCES','MFRESOURCES'].each do |res|
+ [getvariable('runpath'),getvariable('path')].each do |pat|
+ unless pat.empty? then
+ if ENV.key?(res) then
+ # ENV[res] = if ENV[res].empty? then pat else pat + ":" + ENV[res] end
+if ENV[res].empty? then
+ ENV[res] = pat
+elsif ENV[res] == pat || ENV[res] =~ /^#{pat}\:/ || ENV[res] =~ /\:#{pat}\:/ then
+ # skip
+else
+ ENV[res] = pat + ":" + ENV[res]
+end
+ else
+ ENV[res] = pat
+ end
+ report("setting #{res} to #{ENV[res]}") unless done
+ end
+ end
+ done = true
+ end
+ end
+
+ def checktestversion
+ #
+ # one can set TEXMFALPHA and TEXMFBETA for test versions
+ # but keep in mind that the format as well as the test files
+ # then need the --alpha or --beta flag
+ #
+ done, tree = false, ''
+ ['alpha', 'beta'].each do |what|
+ if getvariable(what) then
+ if ENV["TEXMF#{what.upcase}"] then
+ done, tree = true, ENV["TEXMF#{what.upcase}"]
+ elsif ENV["TEXMFLOCAL"] then
+ done, tree = true, File.join(File.dirname(ENV['TEXMFLOCAL']), "texmf-#{what}")
+ end
+ end
+ break if done
+ end
+ if done then
+ tree = tree.strip
+ ENV['TEXMFPROJECT'] = tree
+ report("using test tree '#{tree}'")
+ ['MP', 'MF', 'TX'].each do |ctx|
+ ENV['CTXDEV#{ctx}PATH'] = ''
+ end
+ unless (FileTest.file?(File.join(tree,'ls-r')) || FileTest.file?(File.join(tree,'ls-R'))) then
+ report("no ls-r/ls-R file for tree '#{tree}' (run: mktexlsr #{tree})")
+ end
+ end
+ # puts `kpsewhich --expand-path=$TEXMF`
+ # exit
+ end
+
+ def runtex(filename)
+ checktestversion
+ texengine = validtexengine(getvariable('texengine'))
+ texformat = validtexformat(getarrayvariable('texformats').first)
+ report("tex engine: #{texengine}")
+ report("tex format: #{texformat}")
+ if texengine && texformat then
+ fixbackendvars(@@mappaths[texengine])
+ if texengine == "luatex" then
+ # currently we use luatools to start luatex but some day we should
+ # find a clever way to directly call luatex (problem is that we need
+ # to feed the explicit location of the format and lua initialization
+ # file)
+ run_luatools("--fmt=#{texformat} #{filename}")
+ else
+ progname = validprogname([getvariable('progname'),texformat,texengine])
+ runcommand([quoted(texengine),prognameflag(progname),formatflag(texengine,texformat),tcxflag(texengine),runoptions(texengine),filename,texprocextras(texformat)])
+ end
+ # true
+ else
+ false
+ end
+ end
+
+ def runmp(mpname,mpx=false)
+ checktestversion
+ mpsengine = validmpsengine(getvariable('mpsengine'))
+ mpsformat = validmpsformat(getarrayvariable('mpsformats').first)
+ if mpsengine && mpsformat then
+ ENV["MPXCOMMAND"] = "0" unless mpx
+ progname = validprogname([getvariable('progname'),mpsformat,mpsengine])
+ mpname.gsub!(/\.mp$/,"") # temp bug in mp
+ # runcommand([quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),tcxflag(mpsengine),runoptions(mpsengine),mpname,mpsprocextras(mpsformat)])
+ runcommand([quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),runoptions(mpsengine),mpname,mpsprocextras(mpsformat)])
+ true
+ else
+ false
+ end
+ end
+
+ def runtexmp(filename,filetype='',purge=true)
+ checktestversion
+ mpname = File.suffixed(filename,filetype,'mp')
+ if File.atleast?(mpname,10) then
+ # first run needed
+ File.silentdelete(File.suffixed(mpname,'mpt'))
+ doruntexmp(mpname,nil,true,purge)
+ mpgraphics = checkmpgraphics(mpname)
+ mplabels = checkmplabels(mpname)
+ if mpgraphics || mplabels then
+ # second run needed
+ doruntexmp(mpname,mplabels,true,purge)
+ else
+ # no labels
+ end
+ end
+ end
+
+ def runtexmpjob(filename,filetype='')
+ checktestversion
+ mpname = File.suffixed(filename,filetype,'mp')
+ if File.atleast?(mpname,25) && (data = File.silentread(mpname)) then
+ textranslation = if data =~ /^\%\s+translate.*?\=([\w\d\-]+)/io then $1 else '' end
+ mpjobname = if data =~ /collected graphics of job \"(.+?)\"/io then $1 else '' end
+ if ! mpjobname.empty? and File.unsuffixed(filename) =~ /#{mpjobname}/ then # don't optimize
+ options = Array.new
+ options.push("--mptex")
+ options.push("--nomp")
+ options.push("--mpyforce") if getvariable('forcempy') || getvariable('mpyforce')
+ options.push("--translate=#{textranslation}") unless textranslation.empty?
+ options.push("--batch") if getvariable('batchmode')
+ options.push("--nonstop") if getvariable('nonstopmode')
+ options.push("--output=ps") # options.push("--dvi")
+ options.push("--nobackend")
+ return runtexexec(mpname,options,2)
+ end
+ end
+ return false
+ end
+
+ def runtexutil(filename=[], options=['--ref','--ij','--high'], old=false)
+ [filename].flatten.each do |fname|
+ if old then
+ Kpse.runscript('texutil',fname,options)
+ else
+ begin
+ logger = Logger.new('TeXUtil')
+ if tu = TeXUtil::Converter.new(logger) and tu.loaded(fname) then
+ ok = tu.processed && tu.saved && tu.finalized
+ end
+ rescue
+ Kpse.runscript('texutil',fname,options)
+ end
+ end
+ end
+ end
+
+ def runluacheck(jobname)
+ if false then
+ # test-pos.tex / 6 meg tua file: 18.6 runtime
+ old, new = File.suffixed(jobname,'tua'), File.suffixed(jobname,'tuc')
+ if FileTest.file?(old) then
+ report("converting #{old} into #{new}")
+ system("luac -s -o #{new} #{old}")
+ end
+ else
+ # test-pos.tex / 6 meg tua file: 17.5 runtime
+ old, new = File.suffixed(jobname,'tua'), File.suffixed(jobname,'tuc')
+ if FileTest.file?(old) then
+ report("renaming #{old} into #{new}")
+ File.rename(old,new) rescue false
+ end
+ end
+ end
+
+ # 1=tex 2=mptex 3=mpxtex 4=mpgraphic 5=mpstatic
+
+ def runtexexec(filename=[], options=[], mode=nil)
+ begin
+ if mode and job = TEX.new(@logger) then
+ options.each do |option|
+ case option
+ when /^\-*(.*?)\=(.*)$/o then
+ job.setvariable($1,$2)
+ when /^\-*(.*?)$/o then
+ job.setvariable($1,true)
+ end
+ end
+ job.setvariable("files",filename)
+ case mode
+ when 1 then job.processtex
+ when 2 then job.processmptex
+ when 3 then job.processmpxtex
+ when 4 then job.processmpgraphic
+ when 5 then job.processmpstatic
+ end
+ job.inspect && Kpse.inspect if getvariable('verbose')
+ return true
+ else
+ Kpse.runscript('texexec',filename,options)
+ end
+ rescue
+ Kpse.runscript('texexec',filename,options)
+ end
+ end
+
+ def fixbackendvars(backend)
+ if backend then
+ ENV['backend'] = backend ;
+ ENV['progname'] = backend unless validtexengine(backend)
+ ENV['TEXFONTMAPS'] = ['.',"\$TEXMF/fonts/{data,map}/{#{backend},pdftex,dvips,}//",'./fonts//'].join_path
+ report("fixing backend map path for #{backend}: #{ENV['TEXFONTMAPS']}") if getvariable('verbose')
+ else
+ report("unable to fix backend map path") if getvariable('verbose')
+ end
+ end
+
+ def runbackend(rawname)
+ unless getvariable('nobackend') then
+ case validbackend(getvariable('backend'))
+ when 'dvipdfmx' then
+ fixbackendvars('dvipdfm')
+ runcommand("dvipdfmx -d 4 -V 5 #{File.unsuffixed(rawname)}")
+ when 'xetex' then
+ # xetex now runs its own backend
+ xdvfile = File.suffixed(rawname,'xdv')
+ if FileTest.file?(xdvfile) then
+ fixbackendvars('dvipdfm')
+ runcommand("xdvipdfmx -q -d 4 -V 5 -E #{xdvfile}")
+ end
+ when 'xdv2pdf' then
+ xdvfile = File.suffixed(rawname,'xdv')
+ if FileTest.file?(xdvfile) then
+ fixbackendvars('xdv2pdf')
+ runcommand("xdv2pdf #{xdvfile}")
+ end
+ when 'dvips' then
+ fixbackendvars('dvips')
+ mapfiles = ''
+ begin
+ if tuifile = File.suffixed(rawname,'tui') and FileTest.file?(tuifile) then
+ IO.read(tuifile).scan(/^c \\usedmapfile\{.\}\{(.*?)\}\s*$/o) do
+ mapfiles += "-u +#{$1} " ;
+ end
+ end
+ rescue
+ mapfiles = ''
+ end
+ runcommand("dvips #{mapfiles} #{File.unsuffixed(rawname)}")
+ when 'pdftex' then
+ # no need for postprocessing
+ else
+ report("no postprocessing needed")
+ end
+ end
+ end
+
+ def processfile
+
+ takeprecautions
+ report("using search method '#{Kpse.searchmethod}'") if getvariable('verbose')
+
+ rawname = getvariable('filename')
+ jobname = getvariable('filename')
+
+ if getvariable('autopath') then
+ jobname = File.basename(jobname)
+ inppath = File.dirname(jobname)
+ else
+ inppath = ''
+ end
+
+ jobname, jobsuffix = File.splitname(jobname,'tex')
+
+ jobname = File.unixfied(jobname)
+ inppath = File.unixfied(inppath)
+
+ orisuffix = jobsuffix # still needed ?
+
+ if jobsuffix =~ /^(htm|html|xhtml|xml|fo|fox|rlg|exa)$/io then
+ setvariable('forcexml',true)
+ end
+
+ dummyfile = false
+
+ # fuzzy code snippet: (we kunnen kpse: prefix gebruiken)
+
+ unless FileTest.file?(File.suffixed(jobname,jobsuffix)) then
+ if FileTest.file?(rawname + '.tex') then
+ jobname = rawname.dup
+ jobsuffix = 'tex'
+ end
+ end
+
+ # we can have funny names, like 2005.10.10 (given without suffix)
+
+ rawname = jobname + '.' + jobsuffix
+ rawpath = File.dirname(rawname)
+ rawbase = File.basename(rawname)
+
+ unless FileTest.file?(rawname) then
+ inppath.split(',').each do |ip|
+ break if dummyfile = FileTest.file?(File.join(ip,rawname))
+ end
+ end
+
+ forcexml = getvariable('forcexml')
+
+ if dummyfile || forcexml then # after ctx?
+ jobsuffix = makestubfile(rawname,rawbase,forcexml)
+ checkxmlfile(rawname)
+ end
+
+ # preprocess files
+
+ unless getvariable('noctx') then
+ ctx = CtxRunner.new(rawname,@logger)
+ if pth = getvariable('path') then
+ pth.split(',').each do |p|
+ ctx.register_path(p)
+ end
+ end
+ if getvariable('ctxfile').empty? then
+ if rawname == rawbase then
+ ctx.manipulate(File.suffixed(rawname,'ctx'),'jobname.ctx')
+ else
+ ctx.manipulate(File.suffixed(rawname,'ctx'),File.join(rawpath,'jobname.ctx'))
+ end
+ else
+ ctx.manipulate(File.suffixed(getvariable('ctxfile'),'ctx'))
+ end
+ ctx.savelog(File.suffixed(rawbase,'ctl'))
+
+ envs = ctx.environments
+ mods = ctx.modules
+ flags = ctx.flags
+ mdes = ctx.modes
+
+ flags.each do |f|
+ f.sub!(/^\-+/,'')
+ if f =~ /^(.*?)=(.*)$/ then
+ setvariable($1,$2)
+ else
+ setvariable(f,true)
+ end
+ end
+
+ report("using flags #{flags.join(' ')}") if flags.size > 0
+
+ # merge environment and module specs
+
+ envs << getvariable('environments') unless getvariable('environments').empty?
+ mods << getvariable('usemodules') unless getvariable('usemodules') .empty?
+ mdes << getvariable('modes') unless getvariable('modes') .empty?
+
+ envs = envs.uniq.join(',')
+ mods = mods.uniq.join(',')
+ mdes = mdes.uniq.join(',')
+
+ report("using search method '#{Kpse.searchmethod}'") if getvariable('verbose')
+
+ report("using environments #{envs}") if envs.length > 0
+ report("using modules #{mods}") if mods.length > 0
+ report("using modes #{mdes}") if mdes.length > 0
+
+ setvariable('environments', envs)
+ setvariable('usemodules', mods)
+ setvariable('modes', mdes)
+ end
+
+ # end of preprocessing and merging
+
+ setvariable('nomprun',true) if orisuffix == 'mpx' # else cylic run
+ PDFview.setmethod('xpdf') if getvariable('xpdf')
+ PDFview.closeall if getvariable('autopdf')
+
+ runonce = getvariable('once')
+ finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange'))
+ suffix = getvariable('suffix')
+ result = getvariable('result')
+ globalfile = getvariable('globalfile')
+ forcexml = getvariable('forcexml') # can be set in ctx file
+
+if dummyfile || forcexml then # after ctx?
+ jobsuffix = makestubfile(rawname,rawbase,forcexml)
+ checkxmlfile(rawname)
+end
+
+ result = File.unixfied(result)
+
+ if globalfile || FileTest.file?(rawname) then
+
+ if not dummyfile and not globalfile and not forcexml then
+ scantexpreamble(rawname)
+ scantexcontent(rawname) if getvariable('texformats').standard?
+ end
+ result = File.suffixed(rawname,suffix) unless suffix.empty?
+
+ pushresult(rawbase,result)
+
+ method = validtexmethod(validtexformat(getvariable('texformats')))
+
+ report("tex processing method: #{method}")
+
+ case method
+
+ when 'context' then
+ if getvariable('simplerun') || runonce then
+ makeoptionfile(rawbase,jobname,orisuffix,true,true,3,1) unless getvariable('nooptionfile')
+ ok = runtex(if dummyfile || forcexml then rawbase else rawname end)
+ if ok then
+ ok = runtexutil(rawbase) if getvariable('texutil') || getvariable('forcetexutil')
+ runluacheck(rawbase)
+ runbackend(rawbase)
+ popresult(rawbase,result)
+ end
+ if getvariable('keep') then
+ ['top','log','run'].each do |suffix|
+ File.silentrename(File.suffixed(rawbase,suffix),File.suffixed(rawbase,suffix+'.keep'))
+ end
+ end
+ else
+# goto tmp/jobname when present
+ mprundone, ok, stoprunning = false, true, false
+ texruns, nofruns = 0, getvariable('runs').to_i
+ state = FileState.new
+ ['tub','tuo','tuc'].each do |s|
+ state.register(File.suffixed(rawbase,s))
+ end
+ if getvariable('automprun') then # check this
+ ['mprun','mpgraph'].each do |s|
+ state.register(File.suffixed(rawbase,s,'mp'),'randomseed')
+ end
+ end
+ while ! stoprunning && (texruns < nofruns) && ok do
+ texruns += 1
+ report("TeX run #{texruns}")
+ unless getvariable('nooptionfile') then
+ if texruns == nofruns then
+ makeoptionfile(rawbase,jobname,orisuffix,false,false,4,texruns) # last
+ elsif texruns == 1 then
+ makeoptionfile(rawbase,jobname,orisuffix,false,false,1,texruns) # first
+ else
+ makeoptionfile(rawbase,jobname,orisuffix,false,false,2,texruns) # unknown
+ end
+ end
+# goto .
+
+ ok = runtex(File.suffixed(if dummyfile || forcexml then rawbase else rawname end,jobsuffix))
+
+if getvariable('texengine') == "xetex" then
+ ok = true
+end
+
+############################
+
+# goto tmp/jobname when present
+ if ok && (nofruns > 1) then
+ unless getvariable('nompmode') then
+ mprundone = runtexmpjob(rawbase, "mpgraph")
+ mprundone = runtexmpjob(rawbase, "mprun")
+ end
+ ok = runtexutil(rawbase)
+ runluacheck(rawbase)
+ state.update
+ stoprunning = state.stable?
+ end
+ end
+ if not ok then
+ setvariable('error','error in tex file')
+ end
+ if (nofruns == 1) && getvariable('texutil') then
+ ok = runtexutil(rawbase)
+ runluacheck(rawbase)
+ end
+ if ok && finalrun && (nofruns > 1) then
+ makeoptionfile(rawbase,jobname,orisuffix,true,finalrun,4,texruns) unless getvariable('nooptionfile')
+ report("final TeX run #{texruns}")
+# goto .
+ ok = runtex(File.suffixed(if dummyfile || forcexml then rawbase else rawname end,jobsuffix))
+# goto tmp/jobname when present
+ end
+ if getvariable('keep') then
+ ['top','log','run'].each do |suffix|
+ File.silentrename(File.suffixed(rawbase,suffix),File.suffixed(rawbase,suffix+'.keep'))
+ end
+ else
+ File.silentrename(File.suffixed(rawbase,'top'),File.suffixed(rawbase,'tmp'))
+ end
+ # ['tmp','top','log'].each do |s| # previous tuo file / runtime option file / log file
+ # File.silentdelete(File.suffixed(rawbase,s))
+ # end
+ if ok then
+# goto .
+ runbackend(rawbase)
+ popresult(rawbase,result)
+# goto tmp/jobname when present
+# skip next
+ end
+ if true then # autopurge
+ begin
+ File.open(File.suffixed(rawbase, 'tuo'),'rb') do |f|
+ ok = 0
+ f.each do |line|
+ case ok
+ when 1 then
+ # next line is empty
+ ok = 2
+ when 2 then
+ if line =~ /^\%\s+\>\s+(.*?)\s+(\d+)/moi then
+ filename, n = $1, $2
+ done = File.delete(filename) rescue false
+ if done && getvariable('verbose') then
+ report("deleting #{filename} (#{n} times used)")
+ end
+ else
+ break
+ end
+ else
+ if line =~ /^\%\s+temporary files\:\s+(\d+)/moi then
+ if $1.to_i == 0 then
+ break
+ else
+ ok = 1
+ end
+ end
+ end
+ end
+ end
+ rescue
+ # report("fatal error #{$!}")
+ end
+ end
+ end
+
+ Kpse.runscript('ctxtools',rawbase,'--purge') if getvariable('purge')
+ Kpse.runscript('ctxtools',rawbase,'--purge --all') if getvariable('purgeall')
+
+ # runcommand('mtxrun','--script','ctxtools',rawbase,'--purge') if getvariable('purge')
+ # runcommand('mtxrun','--script','ctxtools',rawbase,'--purge --all') if getvariable('purgeall')
+
+ when 'latex' then
+
+ ok = runtex(rawname)
+
+ else
+
+ ok = runtex(rawname)
+
+ end
+
+ if (dummyfile or forcexml) and FileTest.file?(rawbase) then
+ begin
+ File.delete(File.suffixed(rawbase,'run'))
+ rescue
+ report("unable to delete stub file")
+ end
+ end
+
+ if ok and getvariable('autopdf') then
+ PDFview.open(File.suffixed(if result.empty? then rawbase else result end,'pdf'))
+ end
+
+ else
+ report("nothing to process")
+ end
+
+ end
+
+ # The labels are collected in the mergebe hash. Here we merge the relevant labels
+ # into beginfig/endfig. We could as well do this in metafun itself. Maybe some
+ # day ... (it may cost a bit of string space but that is cheap nowadays).
+
+ def doruntexmp(mpname,mergebe=nil,context=true,purge=true)
+ texfound = false
+ mpname = File.suffixed(mpname,'mp')
+ mpcopy = File.suffixed(mpname,'mp.copy')
+ mpkeep = File.suffixed(mpname,'mp.keep')
+ setvariable('mp.file',mpname)
+ setvariable('mp.line','')
+ setvariable('mp.error','')
+ if mpdata = File.silentread(mpname) then
+ # mpdata.gsub!(/^\%.*\n/o,'')
+ File.silentrename(mpname,mpcopy)
+ texfound = mergebe || (mpdata =~ /btex .*? etex/mo)
+ if mp = openedfile(mpname) then
+ if mergebe then
+ mpdata.gsub!(/beginfig\s*\((\d+)\)\s*\;(.+?)endfig\s*\;/mo) do
+ n, str = $1, $2
+ if str =~ /^(.*?)(verbatimtex.*?etex)\s*\;(.*)$/mo then
+ "beginfig(#{n})\;\n#{$1}#{$2}\;\n#{mergebe[n]}\n#{$3}\;endfig\;\n"
+ else
+ "beginfig(#{n})\;\n#{mergebe[n]}\n#{str}\;endfig\;\n"
+ end
+ end
+ unless mpdata =~ /beginfig\s*\(\s*0\s*\)/o then
+ mp << mergebe['0'] if mergebe.key?('0')
+ end
+ end
+ # mp << MPTools::splitmplines(mpdata)
+ mp << mpdata
+ mp << "\n"
+ # mp << "end"
+ # mp << "\n"
+ mp.close
+ end
+ processmpx(mpname,true,true,purge) if texfound
+ if getvariable('batchmode') then
+ options = ' --interaction=batch'
+ elsif getvariable('nonstopmode') then
+ options = ' --interaction=nonstop'
+ else
+ options = ''
+ end
+ # todo plain|mpost|metafun
+ begin
+ ok = runmp(mpname)
+ rescue
+ end
+ if f = File.silentopen(File.suffixed(mpname,'log')) then
+ while str = f.gets do
+ if str =~ /^l\.(\d+)\s(.*?)\n/o then
+ setvariable('mp.line',$1)
+ setvariable('mp.error',$2)
+ break
+ end
+ end
+ f.close
+ end
+ File.silentrename(mpname, mpkeep)
+ File.silentrename(mpcopy, mpname)
+ end
+ end
+
+ # todo: use internal mptotext function and/or turn all btex/etex into textexts
+
+ def processmpx(mpname,force=false,context=true,purge=true)
+ unless force then
+ mpname = File.suffixed(mpname,'mp')
+ if File.atleast?(mpname,10) && (data = File.silentread(mpname)) then
+ if data =~ /(btex|etex|verbatimtex|textext)/o then
+ force = true
+ end
+ end
+ end
+ if force then
+ begin
+ mptex = File.suffixed(mpname,'temp','tex')
+ mpdvi = File.suffixed(mpname,'temp','dvi')
+ mplog = File.suffixed(mpname,'temp','log')
+ mpmpx = File.suffixed(mpname,'mpx')
+ File.silentdelete(mptex)
+ if true then
+ report("using internal mptotex converter")
+ ok = MPTools::mptotex(mpname,mptex,'context')
+ else
+ command = "mpto #{mpname} > #{mptex}"
+ report(command) if getvariable('verbose')
+ ok = system(command)
+ end
+ # not "ok && ..." because of potential problem with return code and redirect (>)
+ if FileTest.file?(mptex) && File.appended(mptex, "\\end\n") then
+ # to be replaced by runtexexec([filenames],options,1)
+ if localjob = TEX.new(@logger) then
+ localjob.setvariable('files',mptex)
+ localjob.setvariable('backend','dvips')
+ localjob.setvariable('engine',getvariable('engine')) unless getvariable('engine').empty?
+ localjob.setvariable('once',true)
+ localjob.setvariable('nobackend',true)
+ if context then
+ localjob.setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty?
+ elsif getvariable('interface').empty? then
+ localjob.setvariable('texformats',['plain'])
+ else
+ localjob.setvariable('texformats',[getvariable('interface')])
+ end
+ localjob.processtex
+ ok = true # todo
+ else
+ ok = false
+ end
+ # so far
+ command = "dvitomp #{mpdvi} #{mpmpx}"
+ report(command) if getvariable('verbose')
+ ok = ok && FileTest.file?(mpdvi) && system(command)
+ purge_mpx_files(mpname) if purge
+ end
+ rescue
+ # error in processing mpx file
+ end
+ end
+ end
+
+ def purge_mpx_files(mpname)
+ unless getvariable('keep') then
+ ['tex', 'log', 'tui', 'tuo', 'tuc', 'top'].each do |suffix|
+ File.silentdelete(File.suffixed(mpname,'temp',suffix))
+ end
+ end
+ end
+
+ def checkmpgraphics(mpname)
+ # in practice the checksums will differ because of multiple instances
+ # ok, we could save the mpy/mpo files by number, but not now
+ mpoptions = ''
+ if getvariable('makempy') then
+ mpoptions += " --makempy "
+ end
+ mponame = File.suffixed(mpname,'mpo')
+ mpyname = File.suffixed(mpname,'mpy')
+ pdfname = File.suffixed(mpname,'pdf')
+ tmpname = File.suffixed(mpname,'tmp')
+ if getvariable('mpyforce') || getvariable('forcempy') then
+ mpoptions += " --force "
+ else
+ return false unless File.atleast?(mponame,32)
+ mpochecksum = FileState.new.checksum(mponame)
+ return false if mpochecksum.empty?
+ # where does the checksum get into the file?
+ # maybe let texexec do it?
+ # solution: add one if not present or update when different
+ begin
+ mpydata = IO.read(mpyname)
+ if mpydata then
+ if mpydata =~ /^\%\s*mpochecksum\s*\:\s*([A-Z0-9]+)$/mo then
+ checksum = $1
+ if mpochecksum == checksum then
+ return false
+ end
+ end
+ end
+ rescue
+ # no file
+ end
+ end
+ # return Kpse.runscript('makempy',mpname)
+ # only pdftex
+ flags = ['--noctx','--process','--batch','--once']
+ result = runtexexec([mponame], flags, 1)
+ runcommand(["pstoedit","-ssp -dt -f mpost", pdfname,tmpname])
+ tmpdata = IO.read(tmpname)
+ if tmpdata then
+ if mpy = openedfile(mpyname) then
+ mpy << "% mpochecksum: #{mpochecksum}\n"
+ tmpdata.scan(/beginfig(.*?)endfig/mo) do |s|
+ mpy << "begingraphictextfig#{s}endgraphictextfig\n"
+ end
+ mpy.close()
+ end
+ end
+ File.silentdelete(tmpname)
+ File.silentdelete(pdfname)
+ return true
+ end
+
+ def checkmplabels(mpname)
+ mpname = File.suffixed(mpname,'mpt')
+ if File.atleast?(mpname,10) && (mp = File.silentopen(mpname)) then
+ labels = Hash.new
+ while str = mp.gets do
+ t = if str =~ /^%\s*setup\s*:\s*(.*)$/o then $1 else '' end
+ if str =~ /^%\s*figure\s*(\d+)\s*:\s*(.*)$/o then
+ labels[$1] = labels[$1] || ''
+ unless t.empty? then
+ labels[$1] += "#{t}\n"
+ t = ''
+ end
+ labels[$1] += "#{$2}\n"
+ end
+ end
+ mp.close
+ if labels.size>0 then
+ return labels
+ else
+ return nil
+ end
+ end
+ return nil
+ end
+
+end
diff --git a/scripts/context/ruby/base/texutil.rb b/scripts/context/ruby/base/texutil.rb
new file mode 100644
index 000000000..868e3ca16
--- /dev/null
+++ b/scripts/context/ruby/base/texutil.rb
@@ -0,0 +1,1097 @@
+require "base/file"
+require "base/logger"
+
+class String
+
+ # real dirty, but inspect does a pretty good escaping but
+ # unfortunately puts quotes around the string so we need
+ # to strip these
+
+ # def escaped
+ # self.inspect[1,self.inspect.size-2]
+ # end
+
+ def escaped
+ str = self.inspect ; str[1,str.size-2]
+ end
+
+ def splitdata
+ if self =~ /^\s*(.*?)\s*\{(.*)\}\s*$/o then
+ first, second = $1, $2
+ if first.empty? then
+ [second.split(/\} \{/o)].flatten
+ else
+ [first.split(/\s+/o)] + [second.split(/\} \{/o)]
+ end
+ else
+ []
+ end
+ end
+
+end
+
+class Logger
+ def banner(str)
+ report(str)
+ return "%\n% #{str}\n%\n"
+ end
+end
+
+class TeXUtil
+
+ class Plugin
+
+ # we need to reset module data for each run; persistent data is
+ # possible, just don't reinitialize the data structures that need
+ # to be persistent; we reset afterwards becausethen we know what
+ # plugins are defined
+
+ def initialize(logger)
+ @plugins = Array.new
+ @logger = logger
+ end
+
+ def report(str)
+ @logger.report("fatal error in plugin (#{str}): #{$!}")
+ puts("\n")
+ $@.each do |line|
+ puts(" #{line}")
+ end
+ puts("\n")
+ end
+
+ def reset(name)
+ if @plugins.include?(name) then
+ begin
+ eval("#{name}").reset(@logger)
+ rescue Exception
+ report("resetting")
+ end
+ else
+ @logger.report("no plugin #{name}")
+ end
+ end
+
+ def resets
+ @plugins.each do |p|
+ reset(p)
+ end
+ end
+
+ def register(name, file=nil) # maybe also priority
+ if file then
+ begin
+ require("#{file.downcase.sub(/\.rb$/,'')}.rb")
+ rescue Exception
+ @logger.report("no plugin file #{file} for #{name}")
+ else
+ @plugins.push(name)
+ end
+ else
+ @plugins.push(name)
+ end
+ return self
+ end
+
+ def reader(name, data=[])
+ if @plugins.include?(name) then
+ begin
+ eval("#{name}").reader(@logger,data.flatten)
+ rescue Exception
+ report("reading")
+ end
+ else
+ @logger.report("no plugin #{name}")
+ end
+ end
+
+ def readers(data=[])
+ @plugins.each do |p|
+ reader(p,data.flatten)
+ end
+ end
+
+ def writers(handle)
+ @plugins.each do |p|
+ begin
+ eval("#{p}").writer(@logger,handle)
+ rescue Exception
+ report("writing")
+ end
+ end
+ end
+
+ def processors
+ @plugins.each do |p|
+ begin
+ eval("#{p}").processor(@logger)
+ rescue Exception
+ report("processing")
+ end
+ end
+ end
+
+ def finalizers
+ @plugins.each do |p|
+ begin
+ eval("#{p}").finalizer(@logger)
+ rescue Exception
+ report("finalizing")
+ end
+ end
+ end
+
+ end
+
+ class Sorter
+
+ @@downcase = true
+
+ def initialize(max=12)
+ @rep, @map, @exp, @div = Hash.new, Hash.new, Hash.new, Hash.new
+ @max = max
+ @rexa, @rexb = nil, nil
+ end
+
+ def replacer(from,to='') # and expand
+ @max = [@max,to.length+1].max if to
+ @rep[from.escaped] = to || ''
+ end
+
+ # sorter.reducer('ch', 'c')
+ # sorter.reducer('ij', 'y')
+
+ def reducer(from,to='')
+ @max = [@max,to.length+1].max if to
+ @map[from] = to || ''
+ end
+
+ # sorter.expander('aeligature', 'ae')
+ # sorter.expander('ijligature', 'y')
+
+ def expander(from,to=nil)
+ to = converted(to) # not from !!!
+ @max = [@max,to.length+1].max if to
+ @exp[from] = to || from || ''
+ end
+
+ def division(from,to=nil)
+ from, to = converted(from), converted(to)
+ @max = [@max,to.length+1].max if to
+ @div[from] = to || from || ''
+ end
+
+ # shortcut("\\ab\\cd\\e\\f", 'iacute')
+ # shortcut("\\\'\\i", 'iacute')
+ # shortcut("\\\'i", 'iacute')
+ # shortcut("\\\"e", 'ediaeresis')
+ # shortcut("\\\'o", 'oacute')
+
+ def hextoutf(str)
+ str.gsub(/^(0x[A-F\d]+)$/) do
+ [$1.hex()].pack("U")
+ end
+ end
+
+ def shortcut(from,to)
+ from = hextoutf(from)
+ replacer(from,to)
+ expander(to)
+ end
+
+ def prepare
+ if @rep.size > 0 then
+ @rexa = /(#{@rep.keys.join('|')})/ # o
+ else
+ @rexa = nil
+ end
+ if @map.size > 0 then
+ # watch out, order of match matters
+ if @@downcase then
+ @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/i # o
+ else
+ @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/ # o
+ end
+ else
+ if @@downcase then
+ @rexb = /(\\[a-zA-Z]+|.)\s*/io
+ else
+ @rexb = /(\\[a-zA-Z]+|.)\s*/o
+ end
+ end
+ if false then
+ @exp.keys.each do |e|
+ @exp[e].downcase!
+ end
+ end
+ end
+
+ def replace(str)
+ if @rexa then
+ str.gsub(@rexa) do
+ @rep[$1.escaped]
+ end
+ else
+ str
+ end
+ end
+
+ def normalize(str)
+ # replace(str).gsub(/ +/,' ')
+ replace(str).gsub(/\s\s+/," \\space")
+ end
+
+ def tokenize(str)
+ if str then
+ str.gsub(/\\strchr\{(.*?)\}/o) do "\\#{$1}" end
+ else
+ ""
+ end
+ end
+
+ def remap(str)
+ s = str.dup
+ if true then # numbers are treated special
+ s.gsub!(/(\d+)/o) do
+ $1.rjust(10,'a') # rest is b .. k
+ end
+ end
+ if @rexa then
+ s.gsub!(@rexa) do
+ @rep[$1.escaped]
+ end
+ end
+ if @rexb then
+ s.gsub!(@rexb) do
+ token = $1.sub(/\\/o, '')
+ if @@downcase then
+ token.downcase!
+ end
+ if @exp.key?(token) then
+ @exp[token].ljust(@max,' ')
+ elsif @map.key?(token) then
+ @map[token].ljust(@max,' ')
+ else
+ ''
+ end
+ end
+ end
+ s
+ end
+
+ def preset(shortcuts=[],expansions=[],reductions=[],divisions=[],language='')
+ 'a'.upto('z') do |c| expander(c) ; division(c) end
+ 'A'.upto('Z') do |c| expander(c) ; division(c) end
+ expander('1','b') ; expander('2','c') ; expander('3','e') ; expander('4','f')
+ expander('5','g') ; expander('6','h') ; expander('7','i') ; expander('8','i')
+ expander('9','j') ; expander('0','a') ; expander('-','-') ;
+ shortcuts.each do |s| shortcut(s[1],s[2]) if s[0] == '' || s[0] == language end
+ expansions.each do |e| expander(e[1],e[2]) if e[0] == '' || e[0] == language end
+ reductions.each do |r| reducer(r[1],r[2]) if r[0] == '' || r[0] == language end
+ divisions.each do |d| division(d[1],d[2]) if d[0] == '' || d[0] == language end
+ end
+
+ def simplify(str)
+ s = str.dup
+ # ^^
+ # s.gsub!(/\^\^([a-f0-9][a-f0-9])/o, $1.hex.chr)
+ # \- ||
+ s.gsub!(/(\\\-|\|\|)/o) do '-' end
+ # {}
+ s.gsub!(/\{\}/o) do '' end
+ # <*..> (internal xml entity)
+ s.gsub!(/<\*(.*?)>/o) do $1 end
+ # entities
+ s.gsub!(/\\getXMLentity\s*\{(.*?)\}/o) do $1 end
+ # elements
+ s.gsub!(/\<.*?>/o) do '' end
+ # what to do with xml and utf-8
+ # \"e etc
+ # unknown \cs
+ s.gsub!(/\\[a-zA-Z][a-zA-Z]+\s*\{(.*?)\}/o) do $1 end
+ return s
+ end
+
+ def getdivision(str)
+ @div[str] || str
+ end
+
+ def division?(str)
+ @div.key?(str)
+ end
+
+ private
+
+ def converted(str)
+ if str then
+ # puts str
+ str.gsub(/([\+\-]*\d+)/o) do
+ n = $1.to_i
+ if n > 0 then
+ 'z'*n
+ elsif n < 0 then
+ '-'*(-n) # '-' precedes 'a'
+ else
+ ''
+ end
+ end
+ else
+ nil
+ end
+ end
+
+ end
+
+ class Plugin
+
+ module MyFiles
+
+ @@files, @@temps = Hash.new, Hash.new
+
+ def MyFiles::reset(logger)
+ @@files, @@temps = Hash.new, Hash.new
+ end
+
+ def MyFiles::reader(logger,data)
+ case data[0]
+ when 'b', 'e' then
+ @@files[data[1]] = (@@files[data[1]] ||0) + 1
+ when 't' then # temporary file
+ @@temps[data[1]] = (@@temps[data[1]] ||0) + 1
+ end
+ end
+
+ def MyFiles::writer(logger,handle)
+ handle << logger.banner("loaded files: #{@@files.size}")
+ @@files.keys.sort.each do |k|
+ handle << "% > #{k} #{@@files[k]/2}\n"
+ end
+ handle << logger.banner("temporary files: #{@@temps.size}")
+ @@temps.keys.sort.each do |k|
+ handle << "% > #{k} #{@@temps[k]}\n"
+ end
+ end
+
+ def MyFiles::processor(logger)
+ @@files.keys.sort.each do |k|
+ unless (@@files[k] % 2) == 0 then
+ logger.report("check loading of file '#{k}', begin/end problem")
+ end
+ end
+ @@temps.keys.sort.each do |k|
+ # logger.report("temporary file '#{k}' can be deleted")
+ end
+ end
+
+ def MyFiles::finalizer(logger)
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MyCommands
+
+ @@commands = []
+
+ def MyCommands::reset(logger)
+ @@commands = []
+ end
+
+ def MyCommands::reader(logger,data)
+ @@commands.push(data.shift+data.collect do |d| "\{#{d}\}" end.join)
+ end
+
+ def MyCommands::writer(logger,handle)
+ handle << logger.banner("commands: #{@@commands.size}")
+ @@commands.each do |c|
+ handle << "#{c}%\n"
+ end
+ end
+
+ def MyCommands::processor(logger)
+ end
+
+ def MyCommands::finalizer(logger)
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MyExtras
+
+ @@programs = []
+
+ def MyExtras::reset(logger)
+ @@programs = []
+ end
+
+ def MyExtras::reader(logger,data)
+ case data[0]
+ when 'p' then
+ @@programs.push(data[1]) if data[0]
+ end
+ end
+
+ def MyExtras::writer(logger,handle)
+ handle << logger.banner("programs: #{@@programs.size}")
+ @@programs.each_with_index do |cmd, p|
+ handle << "% #{p+1} (#{cmd})\n"
+ end
+ end
+
+ def MyExtras::processor(logger)
+ @@programs.each do |p|
+ # cmd = @@programs[p.to_i]
+ # logger.report("running #{cmd}")
+ # system(cmd)
+ end
+ end
+
+ def MyExtras::finalizer(logger)
+ unless (ENV["CTX.TEXUTIL.EXTRAS"] =~ /^(no|off|false|0)$/io) || (ENV["CTX_TEXUTIL_EXTRAS"] =~ /^(no|off|false|0)$/io) then
+ @@programs.each do |cmd|
+ logger.report("running #{cmd}")
+ system(cmd)
+ end
+ end
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MySynonyms
+
+ class Synonym
+
+ @@debug = false
+
+ def initialize(t, c, k, d)
+ @type, @command, @key, @sortkey, @data = t, c, k, c, d
+ end
+
+ attr_reader :type, :command, :key, :data
+ attr_reader :sortkey
+ attr_writer :sortkey
+
+ # def build(sorter)
+ # if @key then
+ # @sortkey = sorter.normalize(sorter.tokenize(@sortkey))
+ # @sortkey = sorter.remap(sorter.simplify(@key.downcase)) # ??
+ # if @sortkey.empty? then
+ # @sortkey = sorter.remap(@command.downcase)
+ # end
+ # else
+ # @key = ""
+ # @sortkey = ""
+ # end
+ # end
+
+ def build(sorter)
+ if @sortkey and not @sortkey.empty? then
+ @sortkey = sorter.normalize(sorter.tokenize(@sortkey))
+ @sortkey = sorter.remap(sorter.simplify(@sortkey.downcase)) # ??
+ end
+ if not @sortkey or @sortkey.empty? then
+ @sortkey = sorter.normalize(sorter.tokenize(@key))
+ @sortkey = sorter.remap(sorter.simplify(@sortkey.downcase)) # ??
+ end
+ if not @sortkey or @sortkey.empty? then
+ @sortkey = @key.dup
+ end
+ end
+
+ def <=> (other)
+ @sortkey <=> other.sortkey
+ end
+
+ def Synonym.flush(list,handle)
+ if @@debug then
+ list.each do |entry|
+ handle << "% [#{entry.sortkey}]\n"
+ end
+ end
+ list.each do |entry|
+ handle << "\\synonymentry{#{entry.type}}{#{entry.command}}{#{entry.key}}{#{entry.data}}%\n"
+ end
+ end
+
+ end
+
+ @@synonyms = Hash.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
+
+ def MySynonyms::reset(logger)
+ @@synonyms = Hash.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
+ end
+
+ def MySynonyms::reader(logger,data)
+ case data[0]
+ when 'e' then
+ @@synonyms[data[1]] = Array.new unless @@synonyms.key?(data[1])
+ @@synonyms[data[1]].push(Synonym.new(data[1],data[2],data[3],data[4]))
+ when 'l' then
+ @@languages[data[1]] = data[2] || ''
+ end
+ end
+
+ def MySynonyms::writer(logger,handle)
+ if @@synonyms.size > 0 then
+ @@synonyms.keys.sort.each do |s|
+ handle << logger.banner("synonyms: #{s} #{@@synonyms[s].size}")
+ Synonym.flush(@@synonyms[s],handle)
+ end
+ end
+ end
+
+ def MySynonyms::processor(logger)
+ @@synonyms.keys.each do |s|
+ @@sorter[s] = Sorter.new
+ @@sorter[s].preset(
+ eval("MyKeys").shortcuts,
+ eval("MyKeys").expansions,
+ eval("MyKeys").reductions,
+ eval("MyKeys").divisions,
+ @@languages[s] || '')
+ @@sorter[s].prepare
+ @@synonyms[s].each_index do |i|
+ @@synonyms[s][i].build(@@sorter[s])
+ end
+ @@synonyms[s] = @@synonyms[s].sort
+ end
+ end
+
+ def MySynonyms::finalizer(logger)
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MyRegisters
+
+ class Register
+
+ @@specialsymbol = "\000"
+ @@specialbanner = "" # \\relax"
+
+ @@debug = false
+
+ @@howto = /^(.*?)\:\:(.*)$/o
+ @@split = ' && '
+
+ def initialize(state, t, l, k, e, s, p, r)
+ @state, @type, @location, @key, @entry, @seetoo, @page, @realpage = state, t, l, k, e, s, p, r
+ if @key =~ @@howto then @pagehowto, @key = $1, $2 else @pagehowto = '' end
+ if @entry =~ @@howto then @texthowto, @entry = $1, $2 else @texthowto = '' end
+ @key = @entry.dup if @key.empty?
+ @sortkey = @key.dup
+ @nofentries, @nofpages = 0, 0
+ @normalizeentry = false
+ end
+
+ attr_reader :state, :type, :location, :key, :entry, :seetoo, :page, :realpage, :texthowto, :pagehowto
+ attr_reader :sortkey
+ attr_writer :sortkey
+
+ def build(sorter)
+ # @entry, @key = sorter.normalize(@entry), sorter.normalize(sorter.tokenize(@key))
+ @entry = sorter.normalize(sorter.tokenize(@entry)) if @normalizeentry
+ @key = sorter.normalize(sorter.tokenize(@key))
+ if false then
+ @entry, @key = [@entry, @key].collect do |target|
+ # +a+b+c &a&b&c a+b+c a&b&c
+ case target[0,1]
+ when '&' then target = target.sub(/^./o,'').gsub(/([^\\])\&/o) do "#{$1}#{@@split}" end
+ when '+' then target = target.sub(/^./o,'').gsub(/([^\\])\+/o) do "#{$1}#{@@split}" end
+ else target = target .gsub(/([^\\])[\&\+]/o) do "#{$1}#{@@split}" end
+ end
+ # {a}{b}{c}
+ # if target =~ /^\{(.*)\}$/o then
+ # $1.split(/\} \{/o).join(@@split) # space between } { is mandate
+ # else
+ target
+ # end
+ end
+ else
+ # @entry, @key = cleanupsplit(@entry), cleanupsplit(@key)
+ @entry, @key = cleanupsplit(@entry), xcleanupsplit(@key)
+ end
+ @sortkey = sorter.simplify(@key)
+ # special = @sortkey =~ /^([^a-zA-Z\\])/o
+ special = @sortkey =~ /^([\`\~\!\@\#\$\%\^\&\*\(\)\_\-\+\=\{\}\[\]\:\;\"\'\|\<\,\>\.\?\/\d])/o
+ @sortkey = @sortkey.split(@@split).collect do |c| sorter.remap(c) end.join(@@split)
+ if special then
+ @sortkey = "#{@@specialsymbol}#{@sortkey}"
+ end
+ if @realpage == 0 then
+ @realpage = 999999
+ end
+ @sortkey = [
+ @sortkey.downcase,
+ @sortkey,
+ @entry,
+ @texthowto.ljust(10,' '),
+ # @state, # no, messes up things
+ (@realpage.to_s || '').rjust(6,' ').gsub(/0/,' '),
+ # (@realpage ||'').rjust(6,' '),
+ @pagehowto
+ ].join(@@split)
+ end
+
+ def cleanupsplit(target)
+ # +a+b+c &a&b&c a+b+c a&b&c
+ case target[0,1]
+ when '&' then target.sub(/^./o,'').gsub(/([^\\])\&/o) do "#{$1}#{@@split}" end
+ when '+' then target.sub(/^./o,'').gsub(/([^\\])\+/o) do "#{$1}#{@@split}" end
+ else target .gsub(/([^\\])[\&\+]/o) do "#{$1}#{@@split}" end
+ end
+ end
+
+ def xcleanupsplit(target) # +a+b+c &a&b&c a+b+c a&b&c
+ t = Array.new
+ case target[0,1]
+ when '&' then
+ t = target.sub(/^./o,'').split(/([^\\])\&/o)
+ when '+' then
+ t = target.sub(/^./o,'').split(/([^\\])\+/o)
+ else
+ # t = target.split(/([^\\])[\&\+]/o)
+ # t = target.split(/[\&\+]/o)
+ t = target.split(/(?!\\)[\&\+]/o) # lookahead
+ end
+ if not t[1] then t[1] = " " end # we need some entry else we get subentries first
+ if not t[2] then t[2] = " " end # we need some entry else we get subentries first
+ if not t[3] then t[3] = " " end # we need some entry else we get subentries first
+ return t.join(@@split)
+ end
+ def <=> (other)
+ @sortkey <=> other.sortkey
+ end
+
+ # more module like
+
+ @@savedhowto, @@savedfrom, @@savedto, @@savedentry = '', '', '', '', ''
+ @@collapse = false
+
+ def Register.flushsavedline(handle)
+ if @@collapse && ! @@savedfrom.empty? then
+ if ! @@savedto.empty? then
+ handle << "\\registerfrom#{@@savedfrom}%"
+ handle << "\\registerto#{@@savedto}%"
+ else
+ handle << "\\registerpage#{@@savedfrom}%"
+ end
+ end
+ @@savedhowto, @@savedfrom, @@savedto, @@savedentry = '', '', '', ''
+ end
+
+ def Register.flush(list,handle,sorter)
+ # a bit messy, quite old mechanism, maybe some day ...
+ # alphaclass can go, now flushed per class
+ if list.size > 0 then
+ @nofentries, @nofpages = 0, 0
+ current, previous, howto = Array.new, Array.new, Array.new
+ lastpage, lastrealpage = '', ''
+ alphaclass, alpha = '', ''
+ @@savedhowto, @@savedfrom, @@savedto, @@savedentry = '', '', '', ''
+ if @@debug then
+ list.each do |entry|
+ handle << "% [#{entry.sortkey.gsub(/#{@@split}/o,'] [')}]\n"
+ end
+ end
+ list.each do |entry|
+# puts(entry.sortkey.gsub(/\s+/,""))
+ if entry.sortkey =~ /^(\S+)/o then
+ if sorter.division?($1) then
+ testalpha = sorter.getdivision($1)
+ else
+ testalpha = entry.sortkey[0,1].downcase
+ end
+ else
+ testalpha = entry.sortkey[0,1].downcase
+ end
+ if (testalpha != alpha.downcase) || (alphaclass != entry.class) then
+ alpha = testalpha
+ alphaclass = entry.class
+ if alpha != ' ' then
+ flushsavedline(handle)
+ if alpha =~ /^[a-zA-Z]$/o then
+ character = alpha.dup
+ elsif alpha == @@specialsymbol then
+ character = @@specialbanner
+ elsif alpha.length > 1 then
+ # character = "\\getvalue\{#{alpha}\}"
+ character = "\\#{alpha}"
+ else
+ character = "\\unknown"
+ end
+ handle << "\\registerentry{#{entry.type}}{#{character}}%\n"
+ end
+ end
+ current = [entry.entry.split(@@split),'','','',''].flatten
+ howto = current.collect do |e|
+ e + '::' + entry.texthowto
+ end
+ if howto[0] == previous[0] then
+ current[0] = ''
+ else
+ previous[0] = howto[0].dup
+ previous[1] = ''
+ previous[2] = ''
+ previous[3] = ''
+ end
+ if howto[1] == previous[1] then
+ current[1] = ''
+ else
+ previous[1] = howto[1].dup
+ previous[2] = ''
+ previous[3] = ''
+ end
+ if howto[2] == previous[2] then
+ current[2] = ''
+ else
+ previous[2] = howto[2].dup
+ previous[3] = ''
+ end
+ if howto[3] == previous[3] then
+ current[3] = ''
+ else
+ previous[3] = howto[3].dup
+ end
+ copied = false
+ unless current[0].empty? then
+ Register.flushsavedline(handle)
+ handle << "\\registerentrya{#{entry.type}}{#{current[0]}}%\n"
+ copied = true
+ end
+ unless current[1].empty? then
+ Register.flushsavedline(handle)
+ handle << "\\registerentryb{#{entry.type}}{#{current[1]}}%\n"
+ copied = true
+ end
+ unless current[2].empty? then
+ Register.flushsavedline(handle)
+ handle << "\\registerentryc{#{entry.type}}{#{current[2]}}%\n"
+ copied = true
+ end
+ unless current[3].empty? then
+ Register.flushsavedline(handle)
+ handle << "\\registerentryd{#{entry.type}}{#{current[3]}}%\n"
+ copied = true
+ end
+ @nofentries += 1 if copied
+ # if entry.realpage.to_i == 0 then
+ if entry.realpage.to_i == 999999 then
+ Register.flushsavedline(handle)
+ handle << "\\registersee{#{entry.type}}{#{entry.pagehowto},#{entry.texthowto}}{#{entry.seetoo}}{#{entry.page}}%\n" ;
+ lastpage, lastrealpage = entry.page, entry.realpage
+ copied = false # no page !
+ elsif @@savedhowto != entry.pagehowto and ! entry.pagehowto.empty? then
+ @@savedhowto = entry.pagehowto
+ end
+ # beware, we keep multiple page entries per realpage because of possible prefix usage
+ if copied || ! ((lastpage == entry.page) && (lastrealpage == entry.realpage)) then
+ nextentry = "{#{entry.type}}{#{previous[0]}}{#{previous[1]}}{#{previous[2]}}{#{previous[3]}}{#{entry.pagehowto},#{entry.texthowto}}"
+ savedline = "{#{entry.type}}{#{@@savedhowto},#{entry.texthowto}}{#{entry.location}}{#{entry.page}}{#{entry.realpage}}"
+ if entry.state == 1 then # from
+ Register.flushsavedline(handle)
+ handle << "\\registerfrom#{savedline}%\n"
+ elsif entry.state == 3 then # to
+ Register.flushsavedline(handle)
+ handle << "\\registerto#{savedline}%\n"
+ @@savedhowto = '' # test
+ elsif @@collapse then
+ if savedentry != nextentry then
+ savedFrom = savedline
+ else
+ savedTo, savedentry = savedline, nextentry
+ end
+ else
+ handle << "\\registerpage#{savedline}%\n"
+ @@savedhowto = '' # test
+ end
+ @nofpages += 1
+ lastpage, lastrealpage = entry.page, entry.realpage
+ end
+ end
+ Register.flushsavedline(handle)
+ end
+ end
+
+ end
+
+ @@registers = Hash.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
+
+ def MyRegisters::reset(logger)
+ @@registers = Hash.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
+ end
+
+ def MyRegisters::reader(logger,data)
+ case data[0]
+ when 'f' then
+ @@registers[data[1]] = Array.new unless @@registers.key?(data[1])
+ @@registers[data[1]].push(Register.new(1,data[1],data[2],data[3],data[4],nil,data[5],data[6]))
+ when 'e' then
+ @@registers[data[1]] = Array.new unless @@registers.key?(data[1])
+ @@registers[data[1]].push(Register.new(2,data[1],data[2],data[3],data[4],nil,data[5],data[6]))
+ when 't' then
+ @@registers[data[1]] = Array.new unless @@registers.key?(data[1])
+ @@registers[data[1]].push(Register.new(3,data[1],data[2],data[3],data[4],nil,data[5],data[6]))
+ when 's' then
+ @@registers[data[1]] = Array.new unless @@registers.key?(data[1])
+ # was this but wrong sort order (4,data[1],data[2],data[3],data[4],data[5],data[6],nil))
+ @@registers[data[1]].push(Register.new(4,data[1],data[2],data[3],data[4],data[5],data[6],0))
+ when 'l' then
+ @@languages[data[1]] = data[2] || ''
+ end
+ end
+
+ def MyRegisters::writer(logger,handle)
+ if @@registers.size > 0 then
+ @@registers.keys.sort.each do |s|
+ handle << logger.banner("registers: #{s} #{@@registers[s].size}")
+ Register.flush(@@registers[s],handle,@@sorter[s])
+ # report("register #{@@registers[s].class}: #{@@registers[s].@nofentries} entries and #{@@registers[s].@nofpages} pages")
+ end
+ end
+ end
+
+ def MyRegisters::processor(logger)
+ @@registers.keys.each do |s|
+ @@sorter[s] = Sorter.new
+ @@sorter[s].preset(
+ eval("MyKeys").shortcuts,
+ eval("MyKeys").expansions,
+ eval("MyKeys").reductions,
+ eval("MyKeys").divisions,
+ @@languages[s] || '')
+ @@sorter[s].prepare
+ @@registers[s].each_index do |i|
+ @@registers[s][i].build(@@sorter[s])
+ end
+ # @@registers[s].uniq!
+ @@registers[s] = @@registers[s].sort
+ end
+ end
+
+ def MyRegisters::finalizer(logger)
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MyPlugins
+
+ @@plugins = nil
+
+ def MyPlugins::reset(logger)
+ @@plugins = nil
+ end
+
+ def MyPlugins::reader(logger,data)
+ @@plugins = Plugin.new(logger) unless @@plugins
+ case data[0]
+ when 'r' then
+ logger.report("registering plugin #{data[1]}")
+ @@plugins.register(data[1],data[2])
+ when 'd' then
+ begin
+ @@plugins.reader(data[1],data[2,data.length-1])
+ rescue
+ @@plugins.reader(data[1],['error'])
+ end
+ end
+ end
+
+ def MyPlugins::writer(logger,handle)
+ @@plugins.writers(handle) if @@plugins
+ end
+
+ def MyPlugins::processor(logger)
+ @@plugins.processors if @@plugins
+ end
+
+ def MyPlugins::finalizer(logger)
+ @@plugins.finalizers if @@plugins
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MyKeys
+
+ @@shortcuts = Array.new
+ @@expansions = Array.new
+ @@reductions = Array.new
+ @@divisions = Array.new
+
+ def MyKeys::shortcuts
+ @@shortcuts
+ end
+ def MyKeys::expansions
+ @@expansions
+ end
+ def MyKeys::reductions
+ @@reductions
+ end
+ def MyKeys::divisions
+ @@divisions
+ end
+
+ def MyKeys::reset(logger)
+ @@shortcuts = Array.new
+ @@expansions = Array.new
+ @@reductions = Array.new
+ end
+
+ def MyKeys::reader(logger,data)
+ key = data.shift
+ # grp = data.shift # language code, todo
+ case key
+ when 's' then @@shortcuts.push(data)
+ when 'e' then @@expansions.push(data)
+ when 'r' then @@reductions.push(data)
+ when 'd' then @@divisions.push(data)
+ end
+ end
+
+ def MyKeys::writer(logger,handle)
+ end
+
+ def MyKeys::processor(logger)
+ logger.report("shortcuts : #{@@shortcuts.size}") # logger.report(@@shortcuts.inspect)
+ logger.report("expansions: #{@@expansions.size}") # logger.report(@@expansions.inspect)
+ logger.report("reductions: #{@@reductions.size}") # logger.report(@@reductions.inspect)
+ logger.report("divisions : #{@@divisions.size}") # logger.report(@@divisions.inspect)
+ end
+
+ def MyKeys::finalizer(logger)
+ end
+
+ end
+
+ end
+
+ class Converter
+
+ def initialize(logger=nil)
+ if @logger = logger then
+ def report(str)
+ @logger.report(str)
+ end
+ def banner(str)
+ @logger.banner(str)
+ end
+ else
+ @logger = self
+ def report(str)
+ puts(str)
+ end
+ def banner(str)
+ puts(str)
+ end
+ end
+ @filename = 'texutil'
+ @fatalerror = false
+ @plugins = Plugin.new(@logger)
+ ['MyFiles', 'MyCommands', 'MySynonyms', 'MyRegisters', 'MyExtras', 'MyPlugins', 'MyKeys'].each do |p|
+ @plugins.register(p)
+ end
+ end
+
+ def loaded(filename)
+ begin
+ tuifile = File.suffixed(filename,'tui')
+ if FileTest.file?(tuifile) then
+ report("parsing file #{tuifile}")
+ if f = File.open(tuifile,'rb') then
+ f.each do |line|
+ case line.chomp
+ when /^f (.*)$/o then @plugins.reader('MyFiles', $1.splitdata)
+ when /^c (.*)$/o then @plugins.reader('MyCommands', [$1])
+ when /^e (.*)$/o then @plugins.reader('MyExtras', $1.splitdata)
+ when /^s (.*)$/o then @plugins.reader('MySynonyms', $1.splitdata)
+ when /^r (.*)$/o then @plugins.reader('MyRegisters',$1.splitdata)
+ when /^p (.*)$/o then @plugins.reader('MyPlugins', $1.splitdata)
+ when /^x (.*)$/o then @plugins.reader('MyKeys', $1.splitdata)
+ when /^r (.*)$/o then # nothing, not handled here
+ else
+ # report("unknown entry #{line[0,1]} in line #{line.chomp}")
+ end
+ end
+ f.close
+ end
+ else
+ report("unable to locate #{tuifile}")
+ end
+ rescue
+ report("fatal error in parsing #{tuifile}")
+ @filename = 'texutil'
+ else
+ @filename = filename
+ end
+ end
+
+ def processed
+ @plugins.processors
+ return true # for the moment
+ end
+
+ def saved(filename=@filename)
+ if @fatalerror then
+ report("fatal error, no tuo file saved")
+ return false
+ else
+ begin
+ if f = File.open(File.suffixed(filename,'tuo'),'w') then
+ @plugins.writers(f)
+ f << "\\endinput\n"
+ f.close
+ end
+ rescue
+ report("fatal error when saving file (#{$!})")
+ return false
+ else
+ report("tuo file saved")
+ return true
+ end
+ end
+ end
+
+ def finalized
+ @plugins.finalizers
+ @plugins.resets
+ return true # for the moment
+ end
+
+ def reset
+ @plugins.resets
+ end
+
+ end
+
+end
diff --git a/scripts/context/ruby/base/tool.rb b/scripts/context/ruby/base/tool.rb
new file mode 100644
index 000000000..abf0d5ed0
--- /dev/null
+++ b/scripts/context/ruby/base/tool.rb
@@ -0,0 +1,291 @@
+# module : base/tool
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+require 'timeout'
+require 'socket'
+require 'rbconfig'
+
+module Tool
+
+ $constructedtempdir = ''
+
+ def Tool.constructtempdir(create,mainpath='',fallback='')
+ begin
+ mainpath += '/' unless mainpath.empty?
+ timeout(5) do
+ begin
+ t = Time.now
+ u = t.usec.to_s % [1..2] [0..3]
+ pth = t.strftime("#{mainpath}%Y%m%d-%H%M%S-#{u}-#{Process.pid}")
+ #
+ # problems with 1.9
+ #
+ # if pth == $constructedtempdir
+ # # sleep(0.01)
+ # retry
+ # end
+ pth == $constructedtempdir
+ #
+ Dir.mkdir(pth) if create
+ $constructedtempdir = pth
+ return pth
+ rescue
+ # sleep(0.01)
+ retry
+ end
+ end
+ rescue TimeoutError
+ # ok
+ rescue
+ # ok
+ end
+ unless fallback.empty?
+ begin
+ pth = "#{mainpath}#{fallback}"
+ mkdir(pth) if create
+ $constructedtempdir = path
+ return pth
+ rescue
+ return '.'
+ end
+ else
+ return '.'
+ end
+
+ end
+
+ def Tool.findtempdir(*vars)
+ constructtempdir(false,*vars)
+ end
+
+ def Tool.maketempdir(*vars)
+ constructtempdir(true,*vars)
+ end
+
+ # print maketempdir + "\n"
+ # print maketempdir + "\n"
+ # print maketempdir + "\n"
+ # print maketempdir + "\n"
+ # print maketempdir + "\n"
+
+
+ def Tool.ruby_platform
+ case RUBY_PLATFORM
+ when /(mswin|bccwin|mingw|cygwin)/i then 'mswin'
+ when /(linux)/i then 'linux'
+ when /(netbsd|unix)/i then 'unix'
+ when /(darwin|rhapsody|nextstep)/i then 'macosx'
+ else 'unix'
+ end
+ end
+
+ $defaultlineseparator = $/ # $RS in require 'English'
+
+ def Tool.file_platform(filename)
+
+ begin
+ if f = open(filename,'rb') then
+ str = f.read(4000)
+ str.gsub!(/(.*?)\%\!PS/mo, "%!PS") # don't look into preamble crap
+ f.close
+ nn = str.count("\n")
+ nr = str.count("\r")
+ if nn>nr then
+ return 2
+ elsif nn<nr then
+ return 3
+ else
+ return 1
+ end
+ else
+ return 0
+ end
+ rescue
+ return 0
+ end
+
+ end
+
+ def Tool.path_separator
+ return File::PATH_SEPARATOR
+ end
+
+ def Tool.line_separator(filename)
+
+ case file_platform(filename)
+ when 1 then return $defaultlineseparator
+ when 2 then return "\n"
+ when 3 then return "\r"
+ else return $defaultlineseparator
+ end
+
+ end
+
+ def Tool.default_line_separator
+ $defaultlineseparator
+ end
+
+ def Tool.simplefilename(old)
+
+ return old # too fragile
+
+ return old if not FileTest.file?(old)
+
+ new = old.downcase
+ new.gsub!(/[^A-Za-z0-9\_\-\.\\\/]/o) do # funny chars
+ '-'
+ end
+ if old =~ /[a-zA-Z]\:/o
+ # seems like we have a dos/windows drive prefix, so roll back
+ new.sub!(/^(.)\-/) do
+ $1 + ':'
+ end
+ end
+ # fragile for a.b.c.d.bla-bla.e.eps
+ # new.gsub!(/(.+?)\.(.+?)(\..+)$/o) do # duplicate .
+ # $1 + '-' + $2 + $3
+ # end
+ new.gsub!(/\-+/o) do # duplicate -
+ '-'
+ end
+ new
+
+ end
+
+ if Config::CONFIG['host_os'] =~ /mswin/ then
+
+ require 'Win32API'
+
+ GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', ['P','P','N'], 'N')
+ GetLongPathName = Win32API.new('kernel32', 'GetLongPathName', ['P','P','N'], 'N')
+
+ def Tool.dowith_pathname (filename,filemethod)
+ filename.gsub!(/\\/o,'/')
+ case filename
+ when /\;/o then
+ # could be a path spec
+ return filename
+ when /\s+/o then
+ # danger lurking
+ buffer = ' ' * 260
+ length = filemethod.call(filename,buffer,buffer.size)
+ if length>0 then
+ return buffer.slice(0..length-1)
+ else
+ # when the path or file does not exist, nothing is returned
+ # so we try to handle the path separately from the basename
+ basename = File.basename(filename)
+ pathname = File.dirname(filename)
+ length = filemethod.call(pathname,buffer,260)
+ if length>0 then
+ return buffer.slice(0..length-1) + '/' + basename
+ else
+ return filename
+ end
+ end
+ else
+ # no danger
+ return filename
+ end
+ end
+
+ def Tool.shortpathname(filename)
+ dowith_pathname(filename,GetShortPathName)
+ end
+
+ def Tool.longpathname(filename)
+ dowith_pathname(filename,GetLongPathName)
+ end
+
+ else
+
+ def Tool.shortpathname(filename)
+ filename
+ end
+
+ def Tool.longpathname(filename)
+ filename
+ end
+
+ end
+
+ # print shortpathname("C:/Program Files/ABBYY FineReader 6.0/matrix.str")+ "!\n"
+ # print shortpathname("C:/Program Files/ABBYY FineReader 6.0/matrix.strx")+ "!\n"
+
+ def Tool.checksuffix(old)
+
+ return old unless FileTest.file?(old)
+
+ new = old
+
+ unless new =~ /\./io # no suffix
+ f = open(filename,'rb')
+ if str = f.gets
+ case str
+ when /^\%\!PS/io
+ # logging.report(filename, 'analyzed as EPS')
+ new = new + '.eps'
+ when /^\%PDF/io
+ # logging.report(filename, 'analyzed as PDF')
+ new = new + '.pdf'
+ else
+ # logging.report(filename, 'fallback as TIF')
+ new = new + '.tif'
+ end
+ end
+ f.close
+ end
+
+ new.sub!(/\.jpeg$/io) do
+ '.jpg'
+ end
+ new.sub!(/\.tiff$/io) do
+ '.tif'
+ end
+ new.sub!(/\.ai$/io) do
+ '.eps'
+ end
+ new.sub!(/\.ai([a-z0-9]*)$/io) do
+ '-' + $1 + '.eps'
+ end
+ new
+
+ end
+
+ def Tool.cleanfilename(old,logging=nil)
+
+ return old if not FileTest.file?(old)
+
+ new = checksuffix(simplefilename(old))
+ unless new == old
+ begin # bugged, should only be name, not path
+ File.rename(old,new)
+ logging.report("renaming fuzzy name #{old} to #{new}") unless logging
+ return old
+ rescue
+ logging.report("unable to rename fuzzy name #{old} to #{new}") unless logging
+ end
+ end
+ return new
+
+ end
+
+ def Tool.servername
+ host = Socket::gethostname
+ begin
+ Socket::gethostbyname(host)[0]
+ rescue
+ host
+ end
+ end
+
+ # print file_platform(ARGV[0])
+
+end
diff --git a/scripts/context/ruby/base/variables.rb b/scripts/context/ruby/base/variables.rb
new file mode 100644
index 000000000..403b57716
--- /dev/null
+++ b/scripts/context/ruby/base/variables.rb
@@ -0,0 +1,132 @@
+# module : base/variables
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/tool','tool'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/tool'
+
+class Hash
+
+ def nothing?(id)
+ ! self[id] || self[id].empty?
+ end
+
+ def subset(pattern)
+ h = Hash.new
+ p = pattern.gsub(/([\.\:\-])/) do "\\#{$1}" end
+ r = /^#{p}/
+ self.keys.each do |k|
+ h[k] = self[k].dup if k =~ r
+ end
+ return h
+ end
+
+end
+
+class ExtendedHash < Hash
+
+ @@re_var_a = /\%(.*?)\%/
+ @@re_var_b = /\$\((.*?)\)/
+
+ def set(key,value='',resolve=true)
+ if value then
+ self[key] = if resolve then resolved(value.to_s) else value.to_s end
+ else
+ self[key] = ''
+ end
+ end
+
+ def replace(key,value='')
+ self[key] = value if self?(key)
+ end
+
+ def get(key,default='')
+ if self.key?(key) then self[key] else default end
+ end
+
+ def true?(key)
+ self[key] =~ /^(yes|on|true|enable|enabled|y|start)$/io rescue false
+ end
+
+ def resolved(str)
+ begin
+ str.to_s.gsub(@@re_var_a) do
+ self[$1] || ''
+ end.gsub(@@re_var_b) do
+ self[$1] || ''
+ end
+ rescue
+ str.to_s rescue ''
+ end
+ end
+
+ def check(key,default='')
+ if self.key?(key) then
+ if self[key].empty? then self[key] = (default || '') end
+ else
+ self[key] = (default || '')
+ end
+ end
+
+ def checked(key,default='')
+ if self.key?(key) then
+ if self[key].empty? then default else self[key] end
+ else
+ default
+ end
+ end
+
+ def empty?(key)
+ self[key].empty?
+ end
+
+ # def downcase(key)
+ # self[key].downcase!
+ # end
+
+end
+
+# the next one is obsolete so we need to replace things
+
+module Variables
+
+ def setvariable(key,value='')
+ @variables[key] = value
+ end
+
+ def replacevariable(key,value='')
+ @variables[key] = value if @variables.key?(key)
+ end
+
+ def getvariable(key,default='')
+ if @variables.key?(key) then @variables[key] else default end
+ end
+
+ def truevariable(key)
+ @variables[key] =~ /^(yes|on|true)$/io rescue false
+ end
+
+ def checkedvariable(str,default='')
+ if @variables.key?(key) then
+ if @variables[key].empty? then default else @variables[key] end
+ else
+ default
+ end
+ end
+
+ def report(*str)
+ @logger.report(*str)
+ end
+
+ def debug(*str)
+ @logger.debug(str)
+ end
+
+end
diff --git a/scripts/context/ruby/concheck.rb b/scripts/context/ruby/concheck.rb
new file mode 100644
index 000000000..3db3e5148
--- /dev/null
+++ b/scripts/context/ruby/concheck.rb
@@ -0,0 +1,471 @@
+# Program : concheck (tex & context syntax checker)
+# Copyright : PRAGMA ADE / Hasselt NL / www.pragma-ade.com
+# Author : Hans Hagen
+# Version : 1.1 / 2003.08.18
+
+# remarks:
+#
+# - the error messages are formatted like tex's messages so that scite can see them
+# - begin and end tags are only tested on a per line basis because we assume clean sources
+# - maybe i'll add begin{something} ... end{something} checking
+
+# # example validation file
+#
+# begin interface en
+#
+# 1 text
+# 4 Question
+# 0 endinput
+# 0 setupsomething
+# 0 chapter
+#
+# end interface en
+
+# nicer
+
+# class Interface
+
+ # def initialize (language = 'unknown')
+ # @valid = Array.new
+ # @language = language
+ # end
+
+ # def register (left, right)
+ # @valid.push([left,right])
+ # end
+
+# end
+
+# $interfaces = Hash.new
+
+# $interfaces['en'] = Interface.new('english')
+# $interfaces['nl'] = Interface.new('dutch')
+
+# $interfaces['en'].add('\\\\start','\\\\stop')
+# $interfaces['en'].add('\\\\begin','\\\\end')
+# $interfaces['en'].add('\\\\Start','\\\\Stop')
+# $interfaces['en'].add('\\\\Begin','\\\\End')
+
+# $interfaces['nl'].add('\\\\start','\\\\stop')
+# $interfaces['nl'].add('\\\\beginvan','\\\\eindvan')
+# $interfaces['nl'].add('\\\\Start','\\\\Stop')
+# $interfaces['nl'].add('\\\\BeginVan','\\\\Eindvan')
+
+# rest todo
+
+$valid = Hash.new
+
+$valid['en'] = Array.new
+$valid['nl'] = Array.new
+
+#$valid['en'].push(['',''])
+$valid['en'].push(['\\\\start','\\\\stop'])
+$valid['en'].push(['\\\\begin','\\\\end'])
+$valid['en'].push(['\\\\Start','\\\\Stop'])
+$valid['en'].push(['\\\\Begin','\\\\End'])
+
+#$valid['nl'].push(['',''])
+$valid['nl'].push(['\\\\start','\\\\stop'])
+$valid['nl'].push(['\\\\beginvan','\\\\eindvan'])
+$valid['nl'].push(['\\\\Start','\\\\Stop'])
+$valid['nl'].push(['\\\\BeginVan','\\\\Eindvan'])
+
+$valid_tex = "\\\\end\(input|insert|csname|linechar|graf|buffer|strut\)"
+$valid_mp = "(enddef||end||endinput)"
+
+$start_verbatim = Hash.new
+$stop_verbatim = Hash.new
+
+$start_verbatim['en'] = '\\\\starttyping'
+$start_verbatim['nl'] = '\\\\starttypen'
+
+$stop_verbatim['en'] = '\\\\stoptyping'
+$stop_verbatim['nl'] = '\\\\stoptypen'
+
+def message(str, filename=nil, line=nil, column=nil)
+ if filename then
+ if line then
+ if column then
+ puts("error in file #{filename} at line #{line} in column #{column}: #{str}\n")
+ else
+ puts("error in file #{filename} at line #{line}: #{str}\n")
+ end
+ else
+ puts("file #{filename}: #{str}\n")
+ end
+ else
+ puts(str+"\n")
+ end
+end
+
+def load_file (filename='')
+ begin
+ data = IO.readlines(filename)
+ data.collect! do |d|
+ if d =~ /^\s*%/o then
+ ''
+ elsif d =~ /(.*?[^\\])%.*$/o then
+ $1
+ else
+ d
+ end
+ end
+ rescue
+ message("provide proper filename")
+ return nil
+ end
+ # print data.to_s + "\n"
+ return data
+end
+
+def guess_interface(data)
+ if data.first =~ /^%.*interface\=(.*)\s*/ then
+ return $1
+ else
+ data.each do |line|
+ case line
+ when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then return 'nl'
+ when /\\(stelle|verwende|umgebung|benutze)/o then return 'de'
+ when /\\(stel|gebruik|omgeving)/ then return 'nl'
+ when /\\(use|setup|environment)/ then return 'en'
+ when /\\(usa|imposta|ambiente)/ then return 'it'
+ when /(height|width|style)=/ then return 'en'
+ when /(hoehe|breite|schrift)=/ then return 'de'
+ when /(hoogte|breedte|letter)=/ then return 'nl'
+ when /(altezza|ampiezza|stile)=/ then return 'it'
+ when /externfiguur/ then return 'nl'
+ when /externalfigure/ then return 'en'
+ when /externeabbildung/ then return 'de'
+ when /figuraesterna/ then return 'it'
+ end
+ end
+ return 'en'
+ end
+end
+
+def cleanup_data(data, interface='en')
+ verbatim = 0
+ re_start = /^\s*#{$start_verbatim[interface]}/
+ re_stop = /^\s*#{$stop_verbatim[interface]}/
+ data.collect! do |d|
+ if d =~ re_start then
+ verbatim += 1
+ if verbatim>1 then
+ ''
+ else
+ d
+ end
+ elsif d =~ re_stop then
+ verbatim -= 1
+ if verbatim>0 then
+ ''
+ else
+ d
+ end
+ elsif verbatim > 0 then
+ ''
+ else
+ d
+ end
+ end
+ return data
+end
+
+def load_valid(data, interface=nil)
+ if data && (data.first =~ /^%.*valid\=(.*)\s*/)
+ filename = $1
+ filename = '../' + filename unless test(?f,filename)
+ filename = '../' + filename unless test(?f,filename)
+ if test(?f,filename) then
+ interface = guess_interface(data) unless interface
+ if $valid.has_key?(interface) then
+ interface = $valid[interface]
+ else
+ interface = $valid['en']
+ end
+ begin
+ message("loading validation file",filename)
+ validkeys = Hash.new
+ line = 1
+ IO.readlines(filename).each do |l|
+ if l =~ /\s+[\#\%]/io then
+ # ignore line
+ elsif l =~ /^\s*(begin|end)\s+interface\s+([a-z][a-z])/o then
+ # not yet supported
+ elsif l =~ /^\s*(\d+)\s+([a-zA-Z]*)$/o then
+ type, key = $1.to_i, $2.strip
+ if interface[type] then
+ validkeys[interface[type].first+key] = true
+ validkeys[interface[type].last+key] = true
+ else
+ error_message(filename,line,nil,'wrong definition')
+ end
+ end
+ line += 1
+ end
+ if validkeys then
+ message("#{validkeys.length} validation keys loaded",filename)
+ end
+ return validkeys
+ rescue
+ message("invalid validation file",filename)
+ end
+ else
+ message("unknown validation file", filename)
+ end
+ else
+ message("no extra validation file specified")
+ end
+ return nil
+end
+
+def some_chr_error(data, filename, left, right)
+ levels = Array.new
+ for line in 0..data.length-1 do
+ str = data[line]
+ # str = data[line].gsub(/\\[\#{left}\#{right}]/,'')
+ column = 0
+ while column<str.length do
+ case str[column].chr
+ when "\%" then
+ break
+ when "\\" then
+ column += 2
+ when left then
+ levels.push([line,column])
+ column += 1
+ when right then
+ if levels.pop
+ column += 1
+ else
+ message("missing #{left} for #{right}",filename,line+1,column+1)
+ return true
+ end
+ else
+ column += 1
+ end
+ end
+ end
+ if levels && levels.length>0 then
+ levels.each do |l|
+ column = l.pop
+ line = l.pop
+ message("missing #{right} for #{left}",filename,line+1,column+1)
+ end
+ return true
+ else
+ return false
+ end
+end
+
+def some_wrd_error(data, filename, start, stop, ignore)
+ levels = Array.new
+ len = 0
+ re_start = /[^\%]*(#{start})([a-zA-Z]*)/
+ re_stop = /[^\%]*(#{stop})([a-zA-Z]*)/
+ re_ignore = /#{ignore}.*/
+ str_start = start.gsub(/\\+/,'\\')
+ str_stop = stop.gsub(/\\+/,'\\')
+ line = 0
+ while line<data.length do
+ dataline = data[line].split(/[^\\A-Za-z]/)
+ if dataline.length>0 then
+ # todo: more on one line
+ dataline.each do |dataword|
+ case dataword
+ when re_ignore then
+ # just go on
+ when re_start then
+ levels.push([line,$2])
+ # print ' '*levels.length + '>' + $2 + "\n"
+ when re_stop then
+ # print ' '*levels.length + '<' + $2 + "\n"
+ if levels && levels.last && (levels.last[1] == $2) then
+ levels.pop
+ elsif levels && levels.last then
+ message("#{str_stop}#{levels.last[1]} expected instead of #{str_stop}#{$2}",filename,line+1)
+ return true
+ else
+ message("missing #{str_start}#{$2} for #{str_stop}#{$2}",filename,line+1)
+ return true
+ end
+ else
+ # just go on
+ end
+ end
+ end
+ line += 1
+ end
+ if levels && levels.length>0 then
+ levels.each do |l|
+ text = l.pop
+ line = l.pop
+ message("missing #{str_stop}#{text} for #{str_start}#{text}",filename,line+1)
+ end
+ return true
+ else
+ return false
+ end
+end
+
+def some_sym_error (data, filename, symbol, template=false)
+ saved = Array.new
+ inside = false
+ level = 0
+ for line in 0..data.length-1 do
+ str = data[line]
+ column = 0
+ while column<str.length do
+ case str[column].chr
+ when "[" then
+ level += 1 if template
+ when "]" then
+ level -= 1 if template && level > 0
+ when "\%" then
+ break
+ when "\\" then
+ column += 1
+ when symbol then
+ if level == 0 then
+ inside = ! inside
+ saved = [line,column]
+ else
+ # we're in some kind of template or so
+ end
+ else
+ # go on
+ end
+ column += 1
+ end
+ end
+ if inside && saved && level == 0 then
+ column = saved.pop
+ line = saved.pop
+ message("missing #{symbol} for #{symbol}",filename,line+1)
+ return true
+ else
+ return false
+ end
+end
+
+def some_key_error(data, filename, valid)
+ return if (! valid) || (valid.length == 0)
+ error = false
+ # data.foreach do |line| ... end
+ for line in 0..data.length-1 do
+ data[line].scan(/\\([a-zA-Z]+)/io) do
+ unless valid.has_key?($1) then
+ message("unknown command \\#{$1}",filename,line+1)
+ error = true
+ end
+ end
+ end
+ return error
+end
+
+# todo : language dependent
+
+def check_file_tex (filename)
+ error = false
+ if data = load_file(filename) then
+ message("checking tex file", filename)
+ interface = guess_interface(data)
+ valid = load_valid(data,interface)
+ data = cleanup_data(data,interface)
+ # data.each do |d| print d end
+ $valid[interface].each do |v|
+ if some_wrd_error(data, filename, v[0], v[1] ,$valid_tex) then
+ error = true
+ break
+ end
+ end
+ # return false if some_wrd_error(data, filename, '\\\\start' , '\\\\stop' , $valid_tex)
+ # return false if some_wrd_error(data, filename, '\\\\Start' , '\\\\Stop' , $valid_tex)
+ # return false if some_wrd_error(data, filename, '\\\\beginvan', '\\\\eindvan', $valid_tex)
+ # return false if some_wrd_error(data, filename, '\\\\begin' , '\\\\end|\\\\eind', $valid_tex)
+ error = true if some_sym_error(data, filename, '$', false)
+ error = true if some_sym_error(data, filename, '|', true)
+ error = true if some_chr_error(data, filename, '{', '}')
+ error = true if some_chr_error(data, filename, '[', ']')
+ error = true if some_chr_error(data, filename, '(', ')')
+ error = true if some_key_error(data, filename, valid)
+ message("no errors in tex code", filename) unless error
+ return error
+ else
+ return false
+ end
+end
+
+def check_file_mp (filename)
+ error = false
+ if data = load_file(filename) then
+ message("checking metapost file", filename)
+ interface = guess_interface(data)
+ valid = load_valid(data,interface)
+ $valid[interface].each do |v|
+ if some_wrd_error(data, filename, v[0], v[1] ,$valid_tex) then
+ error = true
+ break
+ end
+ end
+ # return false if some_wrd_error(data, filename, '', 'begin', 'end', $valid_mp)
+ error = true if some_chr_error(data, filename, '{', '}')
+ error = true if some_chr_error(data, filename, '[', ']')
+ error = true if some_chr_error(data, filename, '(', ')')
+ error = true if some_key_error(data, filename, valid)
+ message("no errors in metapost code", filename) unless error
+ return error
+ else
+ return false
+ end
+end
+
+def check_file_text(filename='')
+ if data = load_file(filename) then
+ for line in 0..data.length-1 do
+ # case data[line]
+ # when /\s([\:\;\,\.\?\!])/ then
+ # message("space before #{$1}",filename,line+1)
+ # when /\D([\:\;\,\.\?\!])\S/ then
+ # message("no space after #{$1}",filename,line+1)
+ # end
+ if data[line] =~ /\s([\:\;\,\.\?\!])/ then
+ message("space before #{$1}",filename,line+1)
+ else
+ data[line].gsub!(/\[.*?\]/o, '')
+ data[line].gsub!(/\(.*?\)/o, '')
+ data[line].gsub!(/\[.*?$/o, '')
+ data[line].gsub!(/^.*?\]/o, '')
+ if data[line] =~ /\D([\:\;\,\.\?\!])\S/ then
+ message("no space after #{$1}",filename,line+1)
+ end
+ end
+ end
+ end
+end
+
+def check_file(filename='')
+ case filename
+ when '' then
+ message("provide filename")
+ return false
+ when /\.(tex|mk.+)$/i then
+ return check_file_tex(filename) # && check_file_text(filename)
+ when /\.mp$/i then
+ return check_file_mp(filename)
+ else
+ message("only tex and metapost files are checked")
+ return false
+ end
+end
+
+if ARGV.size > 0 then
+ someerror = false
+ ARGV.each do |filename|
+ somerror = true if check_file(filename)
+ end
+ exit (if someerror then 1 else 0 end)
+else
+ exit 1
+end
+
diff --git a/scripts/context/ruby/ctxtools.rb b/scripts/context/ruby/ctxtools.rb
new file mode 100644
index 000000000..c1cbc7d20
--- /dev/null
+++ b/scripts/context/ruby/ctxtools.rb
@@ -0,0 +1,2785 @@
+#!/usr/bin/env ruby
+#encoding: ASCII-8BIT
+
+# program : ctxtools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2004-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on context
+# related files.
+
+# todo: move scite here
+#
+# todo: move kpse call to kpse class/module, faster and better
+
+# Taco Hoekwater on patterns and lig building (see 'agr'):
+#
+# Any direct use of a ligature (as accessed by \char or through active
+# characters) is wrong and will create faulty hyphenation. Normally,
+# when TeX sees "office", it has six tokens, and it knows from the
+# patterns that it can hyphenate between the "ff". It will build an
+# internal list of four nodes, like this:
+#
+# [char, o , ffi ]
+# [lig , ffi, c ,[f,f,i]]
+# [char, c , e ]
+# [char, e , NULL]
+#
+# as you can see from the ffi line, it has remembered the original
+# characters. While hyphenating, it temporarily changes back to
+# that, then re-instates the ligature afterwards.
+#
+# If you feed it the ligature directly, like so:
+#
+# [char, o , ffi ]
+# [char, ffi , c ]
+# [char, c , e ]
+# [char, e , NULL]
+#
+# it cannot do that (it tries to hyphenate as if the "ffi" was a
+# character), and the result is wrong hyphenation.
+
+banner = ['CtxTools', 'version 1.3.5', '2004/2008', 'PRAGMA ADE']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+require 'base/system'
+require 'base/kpse'
+require 'base/file'
+
+require 'rexml/document'
+require 'net/http'
+require 'fileutils'
+# require 'ftools'
+require 'kconv'
+
+exit if defined?(REQUIRE2LIB)
+
+class String
+
+ def i_translate(element, attribute, category)
+ self.gsub!(/(<#{element}.*?#{attribute}=)([\"\'])(.*?)\2/) do
+ if category.key?($3) then
+ # puts "#{element} #{$3} -> #{category[$3]}\n" if element == 'cd:inherit'
+ # puts "#{element} #{$3} => #{category[$3]}\n" if element == 'cd:command'
+ "#{$1}#{$2}#{category[$3]}#{$2}"
+ else
+ # puts "#{element} #{$3} -> ?\n" if element == 'cd:inherit'
+ # puts "#{element} #{$3} => ?\n" if element == 'cd:command'
+ "#{$1}#{$2}#{$3}#{$2}" # unchanged
+ end
+ end
+ end
+
+ def i_load(element, category)
+ self.scan(/<#{element}.*?name=([\"\'])(.*?)\1.*?value=\1(.*?)\1/) do
+ category[$2] = $3
+ end
+ end
+
+ def nosuffix(suffix)
+ self.sub(/\.#{suffix}/,'') # no /o
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def touchcontextfile
+ dowithcontextfile(1)
+ end
+
+ def contextversion
+ dowithcontextfile(2)
+ end
+
+ private
+
+ def dowithcontextfile(action)
+ maincontextfile = 'context.tex'
+ unless FileTest.file?(maincontextfile) then
+ begin
+ maincontextfile = Kpse.found(maincontextfile,'context')
+ rescue
+ maincontextfile = ''
+ end
+ end
+ unless maincontextfile.empty? then
+ nextcontextfile = maincontextfile.sub(/context\.tex$/,"cont-new.tex")
+ case action
+ when 1 then
+ touchfile(maincontextfile)
+ touchfile(nextcontextfile,@@newcontextversion)
+ when 2 then
+ reportversion(maincontextfile)
+ reportversion(nextcontextfile,@@newcontextversion)
+ end
+ end
+
+ end
+
+ @@contextversion = "\\\\contextversion"
+ @@newcontextversion = "\\\\newcontextversion"
+
+ def touchfile(filename,command=@@contextversion)
+
+ if FileTest.file?(filename) then
+ if data = IO.read(filename) then
+ timestamp = Time.now.strftime('%Y.%m.%d %H:%M')
+ prevstamp = ''
+ begin
+ data.gsub!(/#{command}\{(\d+\.\d+\.\d+.*?)\}/) do
+ prevstamp = $1
+ "#{command.sub(/(\\)+/,"\\")}{#{timestamp}}"
+ end
+ rescue
+ else
+ begin
+ File.delete(filename+'.old')
+ rescue
+ end
+ begin
+ File.copy(filename,filename+'.old')
+ rescue
+ end
+ begin
+ if f = File.open(filename,'w') then
+ f.puts(data)
+ f.close
+ end
+ rescue
+ end
+ end
+ if prevstamp.empty? then
+ report("#{filename} is not updated, no timestamp found")
+ else
+ report("#{filename} is updated from #{prevstamp} to #{timestamp}")
+ end
+ end
+ else
+ report("#{filename} is not found")
+ end
+
+ end
+
+ def reportversion(filename,command=@@contextversion)
+
+ version = 'unknown'
+ begin
+ if FileTest.file?(filename) && IO.read(filename).match(/#{command}\{(\d+\.\d+\.\d+.*?)\}/) then
+ version = $1
+ end
+ rescue
+ end
+ if @commandline.option("pipe") then
+ print version
+ else
+ report("context version: #{version} (#{filename})")
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def jeditinterface
+ editinterface('jedit')
+ end
+
+ def bbeditinterface
+ editinterface('bbedit')
+ end
+
+ def sciteinterface
+ editinterface('scite')
+ end
+
+ def rawinterface
+ editinterface('raw')
+ end
+
+ private
+
+ def editinterface(type='raw')
+
+ return unless FileTest.file?("cont-en.xml")
+
+ interfaces = @commandline.arguments
+
+ if interfaces.empty? then
+ interfaces = ['en','cs','de','it','nl','ro','fr']
+ end
+
+ interfaces.each do |interface|
+ begin
+ collection = Hash.new
+ mappings = Hash.new
+ if f = open("keys-#{interface}.xml") then
+ while str = f.gets do
+ if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+value=\"(.*?)\".*?\>/o then
+ mappings[$1] = $2
+ end
+ end
+ f.close
+ if f = open("cont-en.xml") then
+ while str = f.gets do
+ if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+type=\"environment\".*?\>/o then
+ collection["start#{mappings[$1]}"] = ''
+ collection["stop#{mappings[$1]}"] = ''
+ elsif str =~ /\<cd\:command\s+name=\"(.*?)\".*?\>/o then
+ collection["#{mappings[$1]}"] = ''
+ end
+ end
+ f.close
+ case type
+ when 'jedit' then
+ if f = open("context-jedit-#{interface}.xml", 'w') then
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<!DOCTYPE MODE SYSTEM 'xmode.dtd'>\n\n")
+ f.puts("<MODE>\n")
+ f.puts(" <RULES>\n")
+ f.puts(" <KEYWORDS>\n")
+ collection.keys.sort.each do |name|
+ f.puts(" <KEYWORD2>\\#{name}</KEYWORD2>\n") unless name.empty?
+ end
+ f.puts(" </KEYWORDS>\n")
+ f.puts(" </RULES>\n")
+ f.puts("</MODE>\n")
+ f.close
+ end
+ when 'bbedit' then
+ if f = open("context-bbedit-#{interface}.xml", 'w') then
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<key>BBLMKeywordList</key>\n")
+ f.puts("<array>\n")
+ collection.keys.sort.each do |name|
+ f.puts(" <string>\\#{name}</string>\n") unless name.empty?
+ end
+ f.puts("</array>\n")
+ f.close
+ end
+ when 'scite' then
+ if f = open("cont-#{interface}-scite.properties", 'w') then
+ i = 0
+ f.write("keywordclass.macros.context.#{interface}=")
+ collection.keys.sort.each do |name|
+ unless name.empty? then
+ if i==0 then
+ f.write("\\\n ")
+ i = 5
+ else
+ i = i - 1
+ end
+ f.write("#{name} ")
+ end
+ end
+ f.write("\n")
+ f.close
+ end
+ else # raw
+ collection.keys.sort.each do |name|
+ puts("\\#{name}\n") unless name.empty?
+ end
+ end
+ end
+ end
+ end
+ end
+
+ end
+
+end
+
+# class Commands
+#
+# include CommandBase
+#
+# public
+#
+# def translateinterface
+#
+# # since we know what kind of file we're dealing with,
+# # we do it quick and dirty instead of using rexml or
+# # xslt
+#
+# interfaces = @commandline.arguments
+#
+# if interfaces.empty? then
+# interfaces = ['cs','de','it','nl','ro','fr']
+# else
+# interfaces.delete('en')
+# end
+#
+# interfaces.flatten.each do |interface|
+#
+# variables, constants, strings, list, data = Hash.new, Hash.new, Hash.new, '', ''
+#
+# keyfile, intfile, outfile = "keys-#{interface}.xml", "cont-en.xml", "cont-#{interface}.xml"
+#
+# report("generating #{keyfile}")
+#
+# begin
+# one = "texexec --make --all #{interface}"
+# two = "texexec --batch --silent --interface=#{interface} x-set-01"
+# if @commandline.option("force") then
+# system(one)
+# system(two)
+# elsif not system(two) then
+# system(one)
+# system(two)
+# end
+# rescue
+# end
+#
+# unless File.file?(keyfile) then
+# report("no #{keyfile} generated")
+# next
+# end
+#
+# report("loading #{keyfile}")
+#
+# begin
+# list = IO.read(keyfile)
+# rescue
+# list = empty
+# end
+#
+# if list.empty? then
+# report("error in loading #{keyfile}")
+# next
+# end
+#
+# list.i_load('cd:variable', variables)
+# list.i_load('cd:constant', constants)
+# list.i_load('cd:command' , strings)
+# # list.i_load('cd:element' , strings)
+#
+# report("loading #{intfile}")
+#
+# begin
+# data = IO.read(intfile)
+# rescue
+# data = empty
+# end
+#
+# if data.empty? then
+# report("error in loading #{intfile}")
+# next
+# end
+#
+# report("translating interface en to #{interface}")
+#
+# data.i_translate('cd:string' , 'value', strings)
+# data.i_translate('cd:variable' , 'value', variables)
+# data.i_translate('cd:parameter', 'name' , constants)
+# data.i_translate('cd:constant' , 'type' , variables)
+# data.i_translate('cd:variable' , 'type' , variables)
+# data.i_translate('cd:inherit' , 'name' , strings)
+# # data.i_translate('cd:command' , 'name' , strings)
+#
+# data.gsub!(/(\<cd\:interface[^\>]*?language=")en(")/) do
+# $1 + interface + $2
+# end
+#
+# report("saving #{outfile}")
+#
+# begin
+# if f = File.open(outfile, 'w') then
+# f.write(data)
+# f.close
+# end
+# rescue
+# end
+#
+# end
+#
+# end
+#
+# end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ # faster is to glob the whole dir and regexp over that list
+
+ def purgefiles
+
+ pattern = @commandline.arguments
+ purgeall = @commandline.option("all")
+ recurse = @commandline.option("recurse")
+
+ $dontaskprefixes.push(Dir.glob("mpx-*"))
+
+ if purgeall then
+ $dontaskprefixes.push(Dir.glob("*.tex.prep"))
+ $dontaskprefixes.push(Dir.glob("*.xml.prep"))
+ end
+
+ $dontaskprefixes.flatten!
+ $dontaskprefixes.sort!
+
+ if purgeall then
+ $forsuresuffixes.push($texnonesuffixes)
+ $texnonesuffixes = []
+ $forsuresuffixes.flatten!
+ end
+
+ if ! pattern || pattern.empty? then
+ globbed = if recurse then "**/*.*" else "*.*" end
+ files = Dir.glob(globbed)
+ report("purging#{if purgeall then ' all' end} temporary files : #{globbed}")
+ else
+ report("purging#{if purgeall then ' all' end} temporary files : #{pattern.join(' ')}")
+ pattern.each do |pat|
+ nosuf = File.unsuffixed(pat)
+ globbed = if recurse then "**/#{nosuf}-*.*" else "#{nosuf}-*.*" end
+ report("checking files that match '#{globbed}'")
+ files = Dir.glob(globbed)
+ globbed = if recurse then "**/#{nosuf}.*" else "#{nosuf}.*" end
+ report("checking files that match '#{globbed}'")
+ files.push(Dir.glob(globbed))
+ end
+ end
+ files.flatten!
+ files.sort!
+
+ $dontaskprefixes.each do |file|
+ removecontextfile(file)
+ end
+ $dontasksuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /#{suffix}$/i
+ end
+ end
+ $forsuresuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /\.#{suffix}$/i
+ end
+ end
+ files.each do |file|
+ if file =~ /(.*?)\.\d+$/o then
+ basename = $1
+ if file =~ /mp(graph|run)/o || FileTest.file?("#{basename}.mp") then
+ removecontextfile($file)
+ end
+ end
+ end
+ $dummyfiles.each do |file|
+ (File.delete(file) if (FileTest.size?(file) rescue 10) < 10) rescue false
+ end
+ $texnonesuffixes.each do |suffix|
+ files.each do |file|
+ if file =~ /(.*)\.#{suffix}$/i then
+ if FileTest.file?("#{$1}.tex") || FileTest.file?("#{$1}.xml") || FileTest.file?("#{$1}.fo") then
+ keepcontextfile(file)
+ else
+ strippedname = $1.gsub(/\-[a-z]$/io, '')
+ if FileTest.file?("#{strippedname}.tex") || FileTest.file?("#{strippedname}.xml") then
+ keepcontextfile("#{file} (potential result file)")
+ else
+ removecontextfile(file)
+ end
+ end
+ end
+ end
+ end
+
+ files = Dir.glob("*.*")
+ $dontasksuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /^#{suffix}$/i
+ end
+ end
+
+ if $removedfiles || $keptfiles || $persistentfiles then
+ report("removed files : #{$removedfiles}")
+ report("kept files : #{$keptfiles}")
+ report("persistent files : #{$persistentfiles}")
+ report("reclaimed bytes : #{$reclaimedbytes}")
+ end
+
+ end
+
+ private
+
+ $removedfiles = 0
+ $keptfiles = 0
+ $persistentfiles = 0
+ $reclaimedbytes = 0
+
+ $dontaskprefixes = [
+ # "tex-form.tex", "tex-edit.tex", "tex-temp.tex",
+ "texexec.tex", "texexec.tui", "texexec.tuo",
+ "texexec.tuc", "texexec.tua",
+ "texexec.ps", "texexec.pdf", "texexec.dvi",
+ "cont-opt.tex", "cont-opt.bak"
+ ]
+ $dontasksuffixes = [
+ "mp(graph|run)\\.mp", "mp(graph|run)\\.mpd", "mp(graph|run)\\.mpo", "mp(graph|run)\\.mpy",
+ "mp(graph|run)\\.\\d+", "mp(graph|run)\\.mp.keep",
+ "xlscript\\.xsl"
+ ]
+ $forsuresuffixes = [
+ "tui", "tua", "tup", "ted", "tes", "top",
+ "log", "tmp", "run", "bck", "rlg",
+ "mpt", "mpx", "mpd", "mpo", "mpb",
+ "ctl",
+ "pgf", "synctex.gz",
+ "tmp.md5", "tmp.out"
+ ]
+ $texonlysuffixes = [
+ "dvi", "ps", "pdf"
+ ]
+ $texnonesuffixes = [
+ "tuo", "tub", "top", "tuc"
+ ]
+ $dummyfiles = [
+ "mpgraph"
+ ]
+
+ def removecontextfile (filename)
+ if filename && FileTest.file?(filename) then
+ begin
+ filesize = FileTest.size(filename)
+ File.delete(filename)
+ rescue
+ report("problematic : #{filename}")
+ else
+ if FileTest.file?(filename) then
+ $persistentfiles += 1
+ report("persistent : #{filename}")
+ else
+ $removedfiles += 1
+ $reclaimedbytes += filesize
+ report("removed : #{filename}")
+ end
+ end
+ end
+ end
+
+ def keepcontextfile (filename)
+ if filename && FileTest.file?(filename) then
+ $keptfiles += 1
+ report("not removed : #{filename}")
+ end
+ end
+
+end
+
+#D Documentation can be woven into a source file. The next
+#D routine generates a new, \TEX\ ready file with the
+#D documentation and source fragments properly tagged. The
+#D documentation is included as comment:
+#D
+#D \starttypen
+#D %D ...... some kind of documentation
+#D %M ...... macros needed for documenation
+#D %S B begin skipping
+#D %S E end skipping
+#D \stoptypen
+#D
+#D The most important tag is \type {%D}. Both \TEX\ and \METAPOST\
+#D files use \type{%} as a comment chacacter, while \PERL, \RUBY\
+#D and alike use \type{#}. Therefore \type{#D} is also handled.
+#D
+#D The generated file gets the suffix \type{ted} and is
+#D structured as:
+#D
+#D \starttypen
+#D \startmodule[type=suffix]
+#D \startdocumentation
+#D \stopdocumentation
+#D \startdefinition
+#D \stopdefinition
+#D \stopmodule
+#D \stoptypen
+#D
+#D Macro definitions specific to the documentation are not
+#D surrounded by start||stop commands. The suffix specifaction
+#D can be overruled at runtime, but defaults to the file
+#D extension. This specification can be used for language
+#D depended verbatim typesetting.
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def documentation
+ files = @commandline.arguments
+ processtype = @commandline.option("type")
+ files.each do |fullname|
+ if fullname =~ /(.*)\.(.+?)$/o then
+ filename, filesuffix = $1, $2
+ else
+ filename, filesuffix = fullname, 'tex'
+ end
+ filesuffix = 'tex' if filesuffix.empty?
+ fullname, resultname = "#{filename}.#{filesuffix}", "#{filename}.ted"
+ if ! FileTest.file?(fullname)
+ report("empty input file #{fullname}")
+ elsif ! tex = File.open(fullname)
+ report("invalid input file #{fullname}")
+ elsif ! ted = File.open(resultname,'w') then
+ report("unable to openresult file #{resultname}")
+ else
+ report("input file : #{fullname}")
+ report("output file : #{resultname}")
+ nofdocuments, nofdefinitions, nofskips = 0, 0, 0
+ skiplevel, indocument, indefinition, skippingbang = 0, false, false, false
+ if processtype.empty? then
+ filetype = filesuffix.downcase.sub(/^mk.+$/,'tex') # make sure that mkii and mkiv files are handled
+ else
+ filetype = processtype.downcase
+ end
+ report("filetype : #{filetype}")
+ # we need to signal to texexec what interface to use
+ firstline = tex.gets
+ if firstline =~ /^\%.*interface\=/ then
+ ted.puts(firstline)
+ else
+ tex.rewind # seek(0)
+ end
+ ted.puts("\\startmodule[type=#{filetype}]\n")
+ while str = tex.gets do
+ if skippingbang then
+ skippingbang = false
+ else
+ str.chomp!
+ str.sub!(/\s*$/o, '')
+ case str
+ when /^[%\#]D($| )/io then
+ if skiplevel == 0 then
+ someline = if str.length < 3 then "" else str[3,str.length-1] end
+ if indocument then
+ ted.puts("#{someline}\n")
+ else
+ if indefinition then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ end
+ unless indocument then
+ ted.puts("\n\\startdocumentation\n")
+ end
+ ted.puts("#{someline}\n")
+ indocument = true
+ nofdocuments += 1
+ end
+ end
+ when /^[%\#]M($| )/io then
+ if skiplevel == 0 then
+ someline = if str.length < 3 then "" else str[3,str.length-1] end
+ ted.puts("#{someline}\n")
+ end
+ when /^[%\%]S B/io then
+ skiplevel += 1
+ nofskips += 1
+ when /^[%\%]S E/io then
+ skiplevel -= 1
+ when /^[%\#]/io then
+ #nothing
+ when /^eval \'\(exit \$\?0\)\' \&\& eval \'exec perl/o then
+ skippingbang = true
+ else
+ if skiplevel == 0 then
+ inlocaldocument = indocument
+ inlocaldocument = false # else first line skipped when not empty
+ someline = str
+ if indocument then
+ ted.puts("\\stopdocumentation\n")
+ indocument = false
+ end
+ if indefinition then
+ if someline.empty? then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ else
+ ted.puts("#{someline}\n")
+ end
+ elsif ! someline.empty? then
+ ted.puts("\n\\startdefinition\n")
+ indefinition = true
+ if inlocaldocument then
+ # nothing
+ else
+ nofdefinitions += 1
+ ted.puts("#{someline}\n")
+ end
+ end
+ end
+ end
+ end
+ end
+ if indocument then
+ ted.puts("\\stopdocumentation\n")
+ end
+ if indefinition then
+ ted.puts("\\stopdefinition\n")
+ end
+ ted.puts("\\stopmodule\n")
+ ted.close
+
+ if nofdocuments == 0 && nofdefinitions == 0 then
+ begin
+ File.delete(resultname)
+ rescue
+ end
+ end
+ report("documentation sections : #{nofdocuments}")
+ report("definition sections : #{nofdefinitions}")
+ report("skipped sections : #{nofskips}")
+ end
+ end
+ end
+
+end
+
+#D This feature was needed when \PDFTEX\ could not yet access page object
+#D numbers (versions prior to 1.11).
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def filterpages # temp feature / no reporting
+ filename = @commandline.argument('first')
+ filename.sub!(/\.([a-z]+?)$/io,'')
+ pdffile = "#{filename}.pdf"
+ tuofile = "#{filename}.tuo"
+ if FileTest.file?(pdffile) then
+ begin
+ prevline, n = '', 0
+ if (pdf = File.open(pdffile)) && (tuo = File.open(tuofile,'a')) then
+ report('filtering page object numbers')
+ pdf.binmode
+ while line = pdf.gets do
+ line.chomp
+ # typical pdftex search
+ if (line =~ /\/Type \/Page/o) && (prevline =~ /^(\d+)\s+0\s+obj/o) then
+ p = $1
+ n += 1
+ tuo.puts("\\objectreference{PDFP}{#{n}}{#{p}}{#{n}}\n")
+ else
+ prevline = line
+ end
+ end
+ end
+ pdf.close
+ tuo.close
+ report("number of pages : #{n}")
+ rescue
+ report("fatal error in filtering pages")
+ end
+ end
+ end
+
+end
+
+# This script is used to generate hyphenation pattern files
+# that suit ConTeXt. One reason for independent files is that
+# over the years too many uncommunicated changes took place
+# as well that inconsistency in content, naming, and location
+# in the texmf tree takes more time than I'm willing to spend
+# on it. Pattern files are normally shipped for LaTeX (and
+# partially plain). A side effect of independent files is that
+# we can make them encoding independent.
+#
+# Maybe I'll make this hyptools.tex
+
+class String
+
+ def markbraces
+ level = 0
+ self.gsub(/([\{\}])/o) do |chr|
+ if chr == '{' then
+ level = level + 1
+ chr = "((+#{level}))"
+ elsif chr == '}' then
+ chr = "((-#{level}))"
+ level = level - 1
+ end
+ chr
+ end
+ end
+
+ def unmarkbraces
+ self.gsub(/\(\(\+\d+?\)\)/o) do
+ "{"
+ end .gsub(/\(\(\-\d+?\)\)/o) do
+ "}"
+ end
+ end
+
+ def getargument(pattern)
+ if self =~ /(#{pattern})\s*\(\(\+(\d+)\)\)(.*?)\(\(\-\2\)\)/m then # no /o
+ return $3
+ else
+ return ""
+ end
+ end
+
+ def withargument(pattern, &block)
+ if self.markbraces =~ /^(.*)(#{pattern}\s*)\(\(\+(\d+)\)\)(.*?)\(\(\-\3\)\)(.*)$/m then # no /o
+ "#{$1.unmarkbraces}#{$2}{#{yield($4.unmarkbraces)}}#{$5.unmarkbraces}"
+ else
+ self
+ end
+ end
+
+ def filterargument(pattern, &block)
+ if self.markbraces =~ /^(.*)(#{pattern}\s*)\(\(\+(\d+)\)\)(.*?)\(\(\-\3\)\)(.*)$/m then # no /o
+ yield($4.unmarkbraces)
+ else
+ self
+ end
+ end
+
+end
+
+class Language
+
+ include CommandBase
+
+ def initialize(commandline=nil, language='en', filenames=nil, encoding='ec')
+ @commandline= commandline
+ @language = language
+ @filenames = filenames
+ @remapping = Array.new
+ @demapping = Array.new
+ @cloning = Array.new
+ @unicode = Hash.new
+ @encoding = encoding
+ @data = ''
+ @read = ''
+ preload_accents()
+ preload_unicode() if @commandline.option('utf8')
+ case @encoding.downcase
+ when 't1', 'ec', 'cork' then preload_vector('ec', 'enco-ec.tex')
+ when 'y', 'texnansi' then preload_vector('texnansi', 'enco-ans.tex')
+ when 'agr', 'agreek' then preload_vector('agr', 'enco-agr.tex')
+ when 't2a' then preload_vector('t2a', 'enco-cyr.tex')
+ when 'cyr' then preload_vector() # somehow loading t2a does not work out well
+ end
+ end
+
+ def report(str)
+ if @commandline then
+ @commandline.report(str)
+ else
+ puts("#{str}\n")
+ end
+ end
+
+ def remap(from, to)
+ @remapping.push([from,to])
+ end
+ def demap(from, to)
+ @demapping.push([from,to])
+ end
+ def clone(from, to)
+ @cloning.push([from,to])
+ end
+
+ def load(filenames=@filenames)
+ found = false
+ begin
+ if filenames then
+ @filenames.each do |fileset|
+ [fileset].flatten.each do |filename|
+ begin
+ if fname = located(filename) then
+ data = IO.read(fname)
+ @data += data.gsub(/\%.*$/, '').gsub(/\\message\{.*?\}/, '')
+ data.gsub!(/(\\patterns|\\hyphenation)\s*\{.*/mo) do '' end
+ @read += "\n% preamble of file #{fname}\n\n#{data}\n"
+ @data.gsub!(/^[\s\n]+$/moi, '')
+ report("file #{fname} is loaded")
+ found = true
+ break # next fileset
+ end
+ rescue
+ report("file #{filename} is not readable")
+ end
+ end
+ end
+ end
+ rescue
+ end
+ return found
+ end
+
+ def valid?
+ ! @data.empty?
+ end
+
+ def convert
+ if @data then
+ n = 0
+ if true then
+ report("")
+ ["\\patterns","\\hyphenation"].each do |what|
+ @data = @data.withargument(what) do |content|
+ report("converting #{what}")
+ report("")
+ @demapping.each_index do |i|
+ content.gsub!(@demapping[i][0], @demapping[i][1])
+ end
+ content.gsub!(/\\delete\{.*?\}/o) do '' end
+ content.gsub!(/\\keep\{(.*?)\}/o) do $1 end
+ done = false
+ @remapping.each_index do |i|
+ from, to, m = @remapping[i][0], @remapping[i][1], 0
+ content.gsub!(from) do
+ done = true
+ m += 1
+ "[#{i}]"
+ end
+ report("#{m.to_s.rjust(5)} entries remapped to #{to}") unless m == 0
+ n += m
+ end
+ content.gsub!(/\[(\d+)\]/o) do
+ @remapping[$1.to_i][1]
+ end
+ report(" nothing remapped") unless done
+ @cloning.each_index do |i|
+ c = 0
+ f, s = @cloning[i][0], @cloning[i][1]
+ str = "#{f}|#{s}"
+ str.gsub!(/([\[\]])/) do "\\" + "#{$1}" end
+ reg = /(#{str})/
+ content.gsub!(/(\S*(#{str})\S*)/) do
+ a, b = $1, $1
+ a.gsub!(reg, f)
+ b.gsub!(reg, s)
+ c = c + 1
+ "#{a} #{b}"
+ end
+ report("#{c.to_s.rjust(5)} times #{f} cloned to #{s}")
+ n += c
+ end
+ report("")
+ content.to_s
+ end
+ end
+ else
+ @remapping.each do |k|
+ from, to, m = k[0], k[1], 0
+ @data.gsub!(from) do
+ m += 1
+ to
+ end
+ report("#{m.to_s.rjust(5)} entries remapped to #{to}") unless m == 0
+ n += m
+ end
+ end
+ report("#{n} changes in patterns and exceptions")
+ if @commandline.option('utf8') then
+ n = 0
+ @data.gsub!(/\[(.*?)\]/o) do
+ n += 1
+ @unicode[$1] || $1
+ end
+ report("#{n} unicode utf8 entries")
+ end
+ return true
+ else
+ return false
+ end
+ end
+
+ def comment(str)
+ str.gsub!(/^\n/o, '')
+ str.chomp!
+ if @commandline.option('xml') then
+ "<!-- #{str.strip} -->\n\n"
+ else
+ "% #{str.strip}\n\n"
+ end
+ end
+
+ def content(tag, str)
+ lst = str.split(/\s+/)
+ lst.collect! do |l|
+ l.strip
+ end
+ if lst.length>0 then
+ lst = "\n#{lst.join("\n")}\n"
+ else
+ lst = ""
+ end
+ if @commandline.option('xml') then
+ lst.gsub!(/\[(.*?)\]/o) do
+ "&#{$1};"
+ end
+ "<#{tag}>#{lst}</#{tag}>\n\n"
+ else
+ "\\#{tag} \{#{lst}\}\n\n"
+ end
+ end
+
+ def banner
+ if @commandline.option('xml') then
+ "<?xml version='1.0' standalone='yes' ?>\n\n"
+ end
+ end
+
+ def triggerunicode
+ return
+ if @commandline.option('utf8') then
+ "% xetex needs utf8 encoded patterns and for patterns\n" +
+ "% coded as such we need to enable this regime when\n" +
+ "% not in xetex; this code will be moved into context\n" +
+ "% as soon as we've spread the generic patterns\n" +
+ "\n" +
+ "\\ifx\\XeTeXversion\\undefined \\else\n" +
+ " \\ifx\\enableregime\\undefined \\else\n" +
+ " \\enableregime[utf]\n" +
+ " \\fi\n" +
+ "\\fi\n" +
+ "\n"
+ end
+ end
+
+ def save
+ xml = @commandline.option("xml")
+
+ patname = "lang-#{@language}.pat"
+ hypname = "lang-#{@language}.hyp"
+ rmename = "lang-#{@language}.rme"
+ logname = "lang-#{@language}.log"
+
+ desname = "lang-all.xml"
+
+ @data.gsub!(/\\[nc]\{(.+?)\}/) do $1 end
+ @data.gsub!(/\{\}/) do '' end
+ @data.gsub!(/\n+/mo) do "\n" end
+ @read.gsub!(/\n+/mo) do "\n" end
+
+ description = ''
+ commentfile = rmename.dup
+
+ begin
+ desfile = Kpse.found(desname,'context')
+ if f = File.new(desfile) then
+ if doc = REXML::Document.new(f) then
+ if e = REXML::XPath.first(doc.root,"/descriptions/description[@language='#{@language}']") then
+ description = e.to_s
+ end
+ end
+ end
+ rescue
+ description = ''
+ else
+ unless description.empty? then
+ commentfile = desname.dup
+ str = "<!-- copied from lang-all.xml\n\n"
+ str << "<?xml version='1.0' standalone='yes'?>\n\n"
+ str << description.chomp
+ str << "\n\nend of copy -->\n"
+ str.gsub!(/^/io, "% ") unless @commandline.option('xml')
+ description = comment("begin description data")
+ description << str + "\n"
+ description << comment("end description data")
+ report("description found for language #{@language}")
+ end
+ end
+
+ begin
+ if description.empty? || @commandline.option('log') then
+ if f = File.open(logname,'w') then
+ report("saving #{@remapping.length} remap patterns in #{logname}")
+ @remapping.each do |m|
+ f.puts("#{m[0].inspect} => #{m[1]}\n")
+ end
+ f.close
+ end
+ else
+ File.delete(logname) if FileTest.file?(logname)
+ end
+ rescue
+ end
+
+ begin
+ if description.empty? || @commandline.option('log') then
+ if f = File.open(rmename,'w') then
+ data = @read.dup
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+ f << comment("comment copied from public hyphenation files}")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << comment("begin original comment")
+ f << "#{data}\n"
+ f << comment("end original comment")
+ f.close
+ report("comment saved in file #{rmename}")
+ else
+ report("file #{rmename} is not writable")
+ end
+ else
+ File.delete(rmename) if FileTest.file?(rmename)
+ end
+ rescue
+ end
+
+ begin
+ if f = File.open(patname,'w') then
+ data = ''
+ @data.filterargument('\\patterns') do |content|
+ report("merging patterns")
+ data += content.strip
+ end
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+
+ f << banner
+ f << comment("context pattern file, see #{commentfile} for original comment")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << description
+ f << comment("begin pattern data")
+ f << triggerunicode
+ f << content('patterns', data)
+ f << comment("end pattern data")
+ f.close
+ report("patterns saved in file #{patname}")
+ else
+ report("file #{patname} is not writable")
+ end
+ rescue
+ report("problems with file #{patname}")
+ end
+
+ begin
+ if f = File.open(hypname,'w') then
+ data = ''
+ @data.filterargument('\\hyphenation') do |content|
+ report("merging exceptions")
+ data += content.strip
+ end
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+ f << banner
+ f << comment("context hyphenation file, see #{commentfile} for original comment")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << description
+ f << comment("begin hyphenation data")
+ f << triggerunicode
+ f << content('hyphenation', data)
+ f << comment("end hyphenation data")
+ f.close
+ report("exceptions saved in file #{hypname}")
+ else
+ report("file #{hypname} is not writable")
+ end
+ rescue
+ report("problems with file #{hypname}")
+ end
+ end
+
+ def process
+ load
+ if valid? then
+ convert
+ save
+ else
+ report("aborted due to missing files")
+ end
+ end
+
+ def Language::generate(commandline, language='', filenames='', encoding='ec')
+ if ! language.empty? && ! filenames.empty? then
+ commandline.report("processing language #{language}")
+ commandline.report("")
+ language = Language.new(commandline,language,filenames,encoding)
+ if language.load then
+ language.convert
+ language.save
+ commandline.report("")
+ end
+ end
+ end
+
+ private
+
+ def located(filename)
+ begin
+ ["context","plain","latex"].each do |name| # fallbacks needed for czech patterns
+ fname = Kpse.found(filename, name)
+ if FileTest.file?(fname) then
+ report("using file #{fname}")
+ return fname
+ end
+ end
+ report("file #{filename} is not present")
+ return nil
+ rescue
+ report("file #{filename} cannot be located using kpsewhich")
+ return nil
+ end
+ end
+
+ def preload_accents
+
+ begin
+ if filename = located("enco-acc.tex") then
+ if data = IO.read(filename) then
+ report("preloading accent conversions")
+ data.scan(/\\defineaccent\s*\\*(.+?)\s*\{*(.+?)\}*\s*\{\\(.+?)\}/o) do
+ one, two, three = $1, $2, $3
+ one.gsub!(/[\`\~\!\^\*\_\-\+\=\:\;\"\'\,\.\?]/o) do
+ "\\#{one}"
+ end
+ remap(/\\#{one} #{two}/, "[#{three}]")
+ remap(/\\#{one}#{two}/, "[#{three}]") unless one =~ /[a-zA-Z]/o
+ remap(/\\#{one}\{#{two}\}/, "[#{three}]")
+ end
+ end
+ end
+ rescue
+ end
+
+ end
+
+ def preload_unicode
+
+ # \definecharacter Agrave {\uchar0{192}}
+
+ begin
+ if filename = located("enco-uc.tex") then
+ if data = IO.read(filename) then
+ report("preloading unicode conversions")
+ data.scan(/\\definecharacter\s*(.+?)\s*\{\\uchar\{*(\d+)\}*\s*\{(\d+)\}/o) do
+ one, two, three = $1, $2.to_i, $3.to_i
+ @unicode[one] = [(two*256 + three)].pack("U")
+ end
+ end
+ end
+ rescue
+ report("error in loading unicode mapping (#{$!})")
+ end
+
+ end
+
+ def preload_vector(encoding='', filename='')
+
+ # funny polish
+
+ case @language
+ when 'pl' then
+ remap(/\/a/, "[aogonek]") ; remap(/\/A/, "[Aogonek]")
+ remap(/\/c/, "[cacute]") ; remap(/\/C/, "[Cacute]")
+ remap(/\/e/, "[eogonek]") ; remap(/\/E/, "[Eogonek]")
+ remap(/\/l/, "[lstroke]") ; remap(/\/L/, "[Lstroke]")
+ remap(/\/n/, "[nacute]") ; remap(/\/N/, "[Nacute]")
+ remap(/\/o/, "[oacute]") ; remap(/\/O/, "[Oacute]")
+ remap(/\/s/, "[sacute]") ; remap(/\/S/, "[Sacute]")
+ remap(/\/x/, "[zacute]") ; remap(/\/X/, "[Zacute]")
+ remap(/\/z/, "[zdotaccent]") ; remap(/\/Z/, "[Zdotaccent]")
+ when 'sl' then
+ remap(/\"c/,"[ccaron]") ; remap(/\"C/,"[Ccaron]")
+ remap(/\"s/,"[scaron]") ; remap(/\"S/,"[Scaron]")
+ remap(/\"z/,"[zcaron]") ; remap(/\"Z/,"[Zcaron]")
+ when 'da' then
+ remap(/X/, "[aeligature]")
+ remap(/Y/, "[ostroke]")
+ remap(/Z/, "[aring]")
+ when 'hu' then
+ # nothing
+ when 'ca' then
+ demap(/\\c\{/, "\\delete{")
+ when 'de', 'deo' then
+ demap(/\\c\{/, "\\delete{")
+ demap(/\\n\{/, "\\keep{")
+ remap(/\\3/, "[ssharp]")
+ remap(/\\9/, "[ssharp]")
+ remap(/\"a/, "[adiaeresis]")
+ remap(/\"o/, "[odiaeresis]")
+ remap(/\"u/, "[udiaeresis]")
+ when 'fr' then
+ demap(/\\n\{/, "\\delete{")
+ remap(/\\ae/, "[aeligature]")
+ remap(/\\oe/, "[oeligature]")
+ when 'la' then
+ # \lccode`'=`' somewhere else, todo
+ demap(/\\c\{/, "\\delete{")
+ remap(/\\a\s*/, "[aeligature]")
+ remap(/\\o\s*/, "[oeligature]")
+ when 'agr' then
+ # bug fix
+ remap("a2|", "[greekalphaiotasub]")
+ remap("h2|", "[greeketaiotasub]")
+ remap("w2|", "[greekomegaiotasub]")
+ remap(">2r1<2r", "[2ῤ1ῥ]")
+ remap(">a2n1wdu'", "[ἀ2ν1ωδύ]")
+ remap(">e3s2ou'", "[ἐ3σ2ού]")
+ # main conversion
+ remap(/\<\'a\|/, "[greekalphaiotasubdasiatonos]")
+ # remap(/\<\'a\|/, "[greekdasiatonos][greekAlpha][greekiota]")
+ remap(/\>\'a\|/, "[greekalphaiotasubpsilitonos]")
+ remap(/\<\`a\|/, "[greekalphaiotasubdasiavaria]")
+ remap(/\>\`a\|/, "[greekalphaiotasubpsilivaria]")
+ remap(/\<\~a\|/, "[greekalphaiotasubdasiaperispomeni]")
+ remap(/\>\~a\|/, "[greekalphaiotasubpsiliperispomeni]")
+ remap(/\'a\|/, "[greekalphaiotasubtonos]")
+ remap(/\`a\|/, "[greekalphaiotasubvaria]")
+ remap(/\~a\|/, "[greekalphaiotasubperispomeni]")
+ remap(/\<a\|/, "[greekalphaiotasubdasia]")
+ remap(/\>a\|/, "[greekalphaiotasubpsili]")
+ remap(/a\|/, "[greekalphaiotasub]")
+ remap(/\<\'h\|/, "[greeketaiotasubdasiatonos]")
+ remap(/\>\'h\|/, "[greeketaiotasubpsilitonos]")
+ remap(/\<\`h\|/, "[greeketaiotasubdasiavaria]")
+ remap(/\>\`h\|/, "[greeketaiotasubpsilivaria]")
+ remap(/\<\~h\|/, "[greeketaiotasubdasiaperispomeni]")
+ remap(/\>\~h\|/, "[greeketaiotasubpsiliperispomeni]")
+ remap(/\'h\|/, "[greeketaiotasubtonos]")
+ remap(/\`h\|/, "[greeketaiotasubvaria]")
+ remap(/\~h\|/, "[greeketaiotasubperispomeni]")
+ remap(/\<h\|/, "[greeketaiotasubdasia]")
+ remap(/\>h\|/, "[greeketaiotasubpsili]")
+ remap(/h\|/, "[greeketaiotasub]")
+ remap(/\<'w\|/, "[greekomegaiotasubdasiatonos]")
+ remap(/\>'w\|/, "[greekomegaiotasubpsilitonos]")
+ remap(/\<`w\|/, "[greekomegaiotasubdasiavaria]")
+ remap(/\>`w\|/, "[greekomegaiotasubpsilivaria]")
+ remap(/\<~w\|/, "[greekomegaiotasubdasiaperispomeni]")
+ remap(/\>~w\|/, "[greekomegaiotasubpsiliperispomeni]")
+ remap(/\<w\|/, "[greekomegaiotasubdasia]")
+ remap(/\>w\|/, "[greekomegaiotasubpsili]")
+ remap(/\'w\|/, "[greekomegaiotasubtonos]")
+ remap(/\`w\|/, "[greekomegaiotasubvaria]")
+ remap(/\~w\|/, "[greekomegaiotasubperispomeni]")
+ remap(/w\|/, "[greekomegaiotasub]")
+ remap(/\<\'i/, "[greekiotadasiatonos]")
+ remap(/\>\'i/, "[greekiotapsilitonos]")
+ remap(/\<\`i/, "[greekiotadasiavaria]")
+ remap(/\>\`i/, "[greekiotapsilivaria]")
+ remap(/\<\~i/, "[greekiotadasiaperispomeni]")
+ remap(/\>\~i/, "[greekiotapsiliperispomeni]")
+ remap(/\"\'i/, "[greekiotadialytikatonos]")
+ remap(/\"\`i/, "[greekiotadialytikavaria]")
+ remap(/\"\~i/, "[greekiotadialytikaperispomeni]")
+ remap(/\<i/, "[greekiotadasia]")
+ remap(/\>i/, "[greekiotapsili]")
+ remap(/\'i/, "[greekiotaoxia]")
+ remap(/\`i/, "[greekiotavaria]")
+ remap(/\~i/, "[greekiotaperispomeni]")
+ remap(/\"i/, "[greekiotadialytika]")
+ remap(/\>\~e/, "[greekepsilonpsiliperispomeni]")
+ remap(/\<\~e/, "[greekepsilondasiaperispomeni]")
+ remap(/\<\'e/, "[greekepsilondasiatonos]")
+ remap(/\>\'e/, "[greekepsilonpsilitonos]")
+ remap(/\<\`e/, "[greekepsilondasiavaria]")
+ remap(/\>\`e/, "[greekepsilonpsilivaria]")
+ remap(/\<e/, "[greekepsilondasia]")
+ remap(/\>e/, "[greekepsilonpsili]")
+ remap(/\'e/, "[greekepsilonoxia]")
+ remap(/\`e/, "[greekepsilonvaria]")
+ remap(/\~e/, "[greekepsilonperispomeni]")
+ remap(/\<\'a/, "[greekalphadasiatonos]")
+ remap(/\>\'a/, "[greekalphapsilitonos]")
+ remap(/\<\`a/, "[greekalphadasiavaria]")
+ remap(/\>\`a/, "[greekalphapsilivaria]")
+ remap(/\<\~a/, "[greekalphadasiaperispomeni]")
+ remap(/\>\~a/, "[greekalphapsiliperispomeni]")
+ remap(/\<a/, "[greekalphadasia]")
+ remap(/\>a/, "[greekalphapsili]")
+ remap(/\'a/, "[greekalphaoxia]")
+ remap(/\`a/, "[greekalphavaria]")
+ remap(/\~a/, "[greekalphaperispomeni]")
+ remap(/\<\'h/, "[greeketadasiatonos]")
+ remap(/\>\'h/, "[greeketapsilitonos]")
+ remap(/\<\`h/, "[greeketadasiavaria]")
+ remap(/\>\`h/, "[greeketapsilivaria]")
+ remap(/\<\~h/, "[greeketadasiaperispomeni]")
+ remap(/\>\~h/, "[greeketapsiliperispomeni]")
+ remap(/\<h/, "[greeketadasia]")
+ remap(/\>h/, "[greeketapsili]")
+ remap(/\'h/, "[greeketaoxia]")
+ remap(/\`h/, "[greeketavaria]")
+ remap(/\~h/, "[greeketaperispomeni]")
+ remap(/\<\~o/, "[greekomicrondasiaperispomeni]")
+ remap(/\>\~o/, "[greekomicronpsiliperispomeni]")
+ remap(/\<\'o/, "[greekomicrondasiatonos]")
+ remap(/\>\'o/, "[greekomicronpsilitonos]")
+ remap(/\<\`o/, "[greekomicrondasiavaria]")
+ remap(/\>\`o/, "[greekomicronpsilivaria]")
+ remap(/\<o/, "[greekomicrondasia]")
+ remap(/\>o/, "[greekomicronpsili]")
+ remap(/\'o/, "[greekomicronoxia]")
+ remap(/\`o/, "[greekomicronvaria]")
+ remap(/\~o/, "[greekomicronperispomeni]")
+ remap(/\<\'u/, "[greekupsilondasiatonos]")
+ remap(/\>\'u/, "[greekupsilonpsilitonos]")
+ remap(/\<\`u/, "[greekupsilondasiavaria]")
+ remap(/\>\`u/, "[greekupsilonpsilivaria]")
+ remap(/\<\~u/, "[greekupsilondasiaperispomeni]")
+ remap(/\>\~u/, "[greekupsilonpsiliperispomeni]")
+ remap(/\"\'u/, "[greekupsilondialytikatonos]")
+ remap(/\"\`u/, "[greekupsilondialytikavaria]")
+ remap(/\"\~u/, "[greekupsilondialytikaperispomeni]")
+ remap(/\<u/, "[greekupsilondasia]")
+ remap(/\>u/, "[greekupsilonpsili]")
+ remap(/\'u/, "[greekupsilonoxia]")
+ remap(/\`u/, "[greekupsilonvaria]")
+ remap(/\~u/, "[greekupsilonperispomeni]")
+ remap(/\"u/, "[greekupsilondiaeresis]")
+ remap(/\<\'w/, "[greekomegadasiatonos]")
+ remap(/\>\'w/, "[greekomegapsilitonos]")
+ remap(/\<\`w/, "[greekomegadasiavaria]")
+ remap(/\>\`w/, "[greekomegapsilivaria]")
+ remap(/\<\~w/, "[greekomegadasiaperispomeni]")
+ remap(/\>\~w/, "[greekomegapsiliperispomeni]")
+ remap(/\<w/, "[greekomegadasia]")
+ remap(/\>w/, "[greekomegapsili]")
+ remap(/\'w/, "[greekomegaoxia]")
+ remap(/\`w/, "[greekomegavaria]")
+ remap(/\~w/, "[greekomegaperispomeni]")
+ remap(/\<r/, "[greekrhodasia]")
+ remap(/\>r/, "[greekrhopsili]")
+ remap(/\<\~/, "[greekdasiaperispomeni]")
+ remap(/\>\~/, "[greekpsiliperispomeni]")
+ remap(/\<\'/, "[greekdasiatonos]")
+ remap(/\>\'/, "[greekpsilitonos]")
+ remap(/\<\`/, "[greekdasiavaria]")
+ remap(/\>\`/, "[greekpsilivaria]")
+ remap(/\"\'/, "[greekdialytikatonos]")
+ remap(/\"\`/, "[greekdialytikavaria]")
+ remap(/\"\~/, "[greekdialytikaperispomeni]")
+ remap(/\</, "[greekdasia]")
+ remap(/\>/, "[greekpsili]")
+ remap(/\d.{0,2}''/, "")
+ remap(/\'/, "[greekoxia]")
+ remap(/\`/, "[greekvaria]")
+ remap(/\~/, "[perispomeni]")
+ remap(/\"/, "[greekdialytika]")
+ # unknown
+ # remap(/\|/, "[greekIotadialytika]")
+ # next
+ remap(/A/, "[greekAlpha]")
+ remap(/B/, "[greekBeta]")
+ remap(/D/, "[greekDelta]")
+ remap(/E/, "[greekEpsilon]")
+ remap(/F/, "[greekPhi]")
+ remap(/G/, "[greekGamma]")
+ remap(/H/, "[greekEta]")
+ remap(/I/, "[greekIota]")
+ remap(/J/, "[greekTheta]")
+ remap(/K/, "[greekKappa]")
+ remap(/L/, "[greekLambda]")
+ remap(/M/, "[greekMu]")
+ remap(/N/, "[greekNu]")
+ remap(/O/, "[greekOmicron]")
+ remap(/P/, "[greekPi]")
+ remap(/Q/, "[greekChi]")
+ remap(/R/, "[greekRho]")
+ remap(/S/, "[greekSigma]")
+ remap(/T/, "[greekTau]")
+ remap(/U/, "[greekUpsilon]")
+ remap(/W/, "[greekOmega]")
+ remap(/X/, "[greekXi]")
+ remap(/Y/, "[greekPsi]")
+ remap(/Z/, "[greekZeta]")
+ remap(/a/, "[greekalpha]")
+ remap(/b/, "[greekbeta]")
+ remap(/c/, "[greekfinalsigma]")
+ remap(/d/, "[greekdelta]")
+ remap(/e/, "[greekepsilon]")
+ remap(/f/, "[greekphi]")
+ remap(/g/, "[greekgamma]")
+ remap(/h/, "[greeketa]")
+ remap(/i/, "[greekiota]")
+ remap(/j/, "[greektheta]")
+ remap(/k/, "[greekkappa]")
+ remap(/l/, "[greeklambda]")
+ remap(/m/, "[greekmu]")
+ remap(/n/, "[greeknu]")
+ remap(/o/, "[greekomicron]")
+ remap(/p/, "[greekpi]")
+ remap(/q/, "[greekchi]")
+ remap(/r/, "[greekrho]")
+ remap(/s/, "[greeksigma]")
+ remap(/t/, "[greektau]")
+ remap(/u/, "[greekupsilon]")
+ remap(/w/, "[greekomega]")
+ remap(/x/, "[greekxi]")
+ remap(/y/, "[greekpsi]")
+ remap(/z/, "[greekzeta]")
+ clone("[greekalphatonos]", "[greekalphaoxia]")
+ clone("[greekepsilontonos]", "[greekepsilonoxia]")
+ clone("[greeketatonos]", "[greeketaoxia]")
+ clone("[greekiotatonos]", "[greekiotaoxia]")
+ clone("[greekomicrontonos]", "[greekomicronoxia]")
+ clone("[greekupsilontonos]", "[greekupsilonoxia]")
+ clone("[greekomegatonos]", "[greekomegaoxia]")
+ when 'ru' then
+ remap(/\xC1/, "[cyrillica]")
+ remap(/\xC2/, "[cyrillicb]")
+ remap(/\xD7/, "[cyrillicv]")
+ remap(/\xC7/, "[cyrillicg]")
+ remap(/\xC4/, "[cyrillicd]")
+ remap(/\xC5/, "[cyrillice]")
+ remap(/\xD6/, "[cyrilliczh]")
+ remap(/\xDA/, "[cyrillicz]")
+ remap(/\xC9/, "[cyrillici]")
+ remap(/\xCA/, "[cyrillicishrt]")
+ remap(/\xCB/, "[cyrillick]")
+ remap(/\xCC/, "[cyrillicl]")
+ remap(/\xCD/, "[cyrillicm]")
+ remap(/\xCE/, "[cyrillicn]")
+ remap(/\xCF/, "[cyrillico]")
+ remap(/\xD0/, "[cyrillicp]")
+ remap(/\xD2/, "[cyrillicr]")
+ remap(/\xD3/, "[cyrillics]")
+ remap(/\xD4/, "[cyrillict]")
+ remap(/\xD5/, "[cyrillicu]")
+ remap(/\xC6/, "[cyrillicf]")
+ remap(/\xC8/, "[cyrillich]")
+ remap(/\xC3/, "[cyrillicc]")
+ remap(/\xDE/, "[cyrillicch]")
+ remap(/\xDB/, "[cyrillicsh]")
+ remap(/\xDD/, "[cyrillicshch]")
+ remap(/\xDF/, "[cyrillichrdsn]")
+ remap(/\xD9/, "[cyrillicery]")
+ remap(/\xD8/, "[cyrillicsftsn]")
+ remap(/\xDC/, "[cyrillicerev]")
+ remap(/\xC0/, "[cyrillicyu]")
+ remap(/\xD1/, "[cyrillicya]")
+ remap(/\xA3/, "[cyrillicyo]")
+ when 'tr' then
+ remap(/\^\^11/, "[dotlessi]")
+ else
+ end
+
+ if ! encoding.empty? then
+ begin
+ filename = Kpse.found(filename, 'context')
+ if data = IO.readlines(filename.chomp) then
+ report("preloading #{encoding} character mappings")
+ accept = false
+ data.each do |line|
+ case line.chomp
+ when /\\start(en|)coding\s*\[(.*?)\]/io then
+ enc = $2
+ if accept = (enc == encoding) then
+ report("accepting vector #{enc}")
+ else
+ report("skipping vector #{enc}")
+ end
+ when /\\stop(en|)coding/io then
+ accept = false
+ when accept && /\\definecharacter\s*([a-zA-Z]+)\s*(\d+)\s*/o then
+ name, number = $1, $2
+ remap(/\^\^#{sprintf("%02x",number)}/, "[#{name}]")
+ if number.to_i > 127 then
+ remap(/#{sprintf("\\%03o",number)}/, "[#{name}]")
+ end
+ end
+ end
+ end
+ rescue
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ @@languagedata = Hash.new
+
+ def patternfiles
+ language = @commandline.argument('first')
+ if (language == 'all') || language.empty? then
+ languages = @@languagedata.keys.sort
+ elsif @@languagedata.key?(language) then
+ languages = [language]
+ else
+ languages = []
+ end
+ languages.each do |language|
+ encoding = @@languagedata[language][0] || ''
+ files = @@languagedata[language][1] || []
+ Language::generate(self,language,files,encoding)
+ end
+ end
+
+ private
+
+ # todo: filter the fallback list from context
+
+ # The first entry in the array is the encoding which will be used
+ # when interpreting the raw patterns. The second entry is a list of
+ # filesets (string|aray), each first match of a set is taken.
+
+ @@languagedata['ba' ] = [ 'ec' , ['bahyph.tex'] ]
+ @@languagedata['ca' ] = [ 'ec' , ['cahyph.tex'] ]
+ @@languagedata['cy' ] = [ 'ec' , ['cyhyph.tex'] ]
+ @@languagedata['cs' ] = [ 'ec' , ['czhyphen.tex','czhyphen.ex'] ]
+ @@languagedata['de' ] = [ 'ec' , ['dehyphn.tex'] ]
+ @@languagedata['deo'] = [ 'ec' , ['dehypht.tex'] ]
+ @@languagedata['da' ] = [ 'ec' , ['dkspecial.tex','dkcommon.tex'] ]
+ # elhyph.tex
+ @@languagedata['es' ] = [ 'ec' , ['eshyph.tex'] ]
+ @@languagedata['et' ] = [ 'ec' , ['ethyph.tex'] ]
+ @@languagedata['fi' ] = [ 'ec' , ['fihyph.tex'] ]
+ @@languagedata['fr' ] = [ 'ec' , ['frhyph.tex'] ]
+ # ghyphen.readme ghyph31.readme grphyph
+ @@languagedata['hr' ] = [ 'ec' , ['hrhyph.tex'] ]
+ @@languagedata['hu' ] = [ 'ec' , ['huhyphn.tex'] ]
+ @@languagedata['us' ] = [ 'default' , [['ushyphmax.tex'],['ushyph.tex'],['hyphen.tex']] ]
+ @@languagedata['us' ] = [ 'default' , [['ushyphmax.tex'],['ushyph.tex'],['hyphen.tex']] ]
+ # inhyph.tex
+ @@languagedata['is' ] = [ 'ec' , ['ishyph.tex'] ]
+ @@languagedata['it' ] = [ 'ec' , ['ithyph.tex'] ]
+ @@languagedata['la' ] = [ 'ec' , ['lahyph.tex'] ]
+ # mnhyph
+ @@languagedata['nl' ] = [ 'ec' , ['nehyph96.tex'] ]
+ # @@languagedata['no' ] = [ 'ec' , ['nohyphbx.tex'],['nohyphb.tex'],['nohyph2.tex'],['nohyph1.tex'],['nohyph.tex'] ]
+ @@languagedata['no' ] = [ 'ec' , [['asxsx.tex','nohyphbx.tex'],['nohyphb.tex'],['nohyph2.tex'],['nohyph1.tex'],['nohyph.tex']] ]
+ @@languagedata['agr'] = [ 'agr' , [['grahyph4.tex'], ['oldgrhyph.tex']] ] # new, todo
+ @@languagedata['pl' ] = [ 'ec' , ['plhyph.tex'] ]
+ @@languagedata['pt' ] = [ 'ec' , ['pthyph.tex'] ]
+ @@languagedata['ro' ] = [ 'ec' , ['rohyph.tex'] ]
+ @@languagedata['sl' ] = [ 'ec' , [['slhyph.tex'], ['sihyph.tex']] ]
+ @@languagedata['sk' ] = [ 'ec' , ['skhyphen.tex','skhyphen.ex'] ]
+ # sorhyph.tex / upper sorbian
+ # srhyphc.tex / cyrillic
+ @@languagedata['sv' ] = [ 'ec' , ['svhyph.tex'] ]
+ @@languagedata['tr' ] = [ 'ec' , ['tkhyph.tex'] ]
+ @@languagedata['gb' ] = [ 'default' , [['ukhyphen.tex'],['ukhyph.tex']] ]
+ # @@languagedata['ru' ] = [ 't2a' , ['ruhyphal.tex'] ] # t2a does not work
+ @@languagedata['ru' ] = [ 'cyr' , ['ruhyphal.tex'] ]
+end
+
+class Commands
+
+ include CommandBase
+
+ def dpxmapfiles
+
+ force = @commandline.option("force")
+
+ texmfroot = @commandline.argument('first')
+ texmfroot = '.' if texmfroot.empty?
+ if @commandline.option('maproot') != "" then
+ maproot = @commandline.option('maproot')
+ else
+ maproot = "#{texmfroot.gsub(/\\/,'/')}/fonts/map/pdftex/context"
+ end
+ if File.directory?(maproot) then
+ files = Dir.glob("#{maproot}/*.map")
+ if files.size > 0 then
+ files.each do |pdffile|
+ next if File.basename(pdffile) == 'pdftex.map'
+ pdffile = File.expand_path(pdffile)
+ dpxfile = File.expand_path(pdffile.sub(/(dvips|pdftex)/i,'dvipdfm'))
+ unless pdffile == dpxfile then
+ begin
+ if data = File.read(pdffile) then
+ report("< #{File.basename(pdffile)} - pdf(e)tex")
+ n = 0
+ data = data.collect do |line|
+ if line =~ /^[\%\#]+/mo then
+ ''
+ else
+ encoding = if line =~ /([a-z0-9\-]+)\.enc/io then $1 else '' end
+ fontfile = if line =~ /([a-z0-9\-]+)\.(pfb|ttf)/io then $1 else nil end
+ metrics = if line =~ /^([a-z0-9\-]+)[\s\<]+/io then $1 else nil end
+ slant = if line =~ /\"([\d\.]+)\s+SlantFont\"/io then "-s #{$1}" else '' end
+ if metrics && encoding && fontfile then
+ n += 1
+ "#{metrics} #{encoding} #{fontfile} #{slant}"
+ else
+ ''
+ end
+ end
+ end
+ data.delete_if do |line|
+ line.gsub(/\s+/,'').empty?
+ end
+ data.collect! do |line|
+ # remove line with "name name" lines
+ line.gsub(/^(\S+)\s+\1\s*$/) do
+ $1
+ end
+ end
+ begin
+ if force then
+ if n > 0 then
+ File.makedirs(File.dirname(dpxfile))
+ if f = File.open(dpxfile,'w') then
+ report("> #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
+ f.puts(data)
+ f.close
+ else
+ report("? #{File.basename(dpxfile)} - dvipdfm(x)")
+ end
+ else
+ report("- #{File.basename(dpxfile)} - dvipdfm(x) - no entries")
+ # begin File.delete(dpxname) ; rescue ; end
+ if f = File.open(dpxfile,'w') then
+ f.puts("% no map entries")
+ f.close
+ end
+ end
+ else
+ report(". #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
+ end
+ rescue
+ report("error in saving dvipdfm file")
+ end
+ else
+ report("error in loading pdftex file")
+ end
+ rescue
+ report("error in processing pdftex file")
+ end
+ end
+ end
+ if force then
+ begin
+ report("regenerating database for #{texmfroot}")
+ system("mktexlsr #{texmfroot}")
+ rescue
+ end
+ end
+ else
+ report("no mapfiles found in #{maproot}")
+ end
+ else
+ report("provide proper texmfroot")
+ end
+
+ end
+
+end
+
+class Array
+
+ def add_shebang(filename,program)
+ unless self[0] =~ /^\#/ then
+ self.insert(0,"\#!/usr/bin/env #{program}")
+ end
+ unless self[2] =~ /^\#.*?copyright\=/ then
+ self.insert(1,"\#")
+ self.insert(2,"\# copyright=pragma-ade readme=readme.pdf licence=cc-gpl")
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ self[2].gsub!(/ +/, ' ')
+ return true
+ else
+ return false
+ end
+ end
+
+ def add_directive(filename,program)
+ unless self[0] =~ /^\%/ then
+ self.insert(0,"\% content=#{program}")
+ end
+ unless self[2] =~ /^\%.*?copyright\=/ then
+ self.insert(1,"\%")
+ if File.expand_path(filename) =~ /[\\\/](doc|manuals)[\\\/]/ then
+ self.insert(2,"\% copyright=pragma-ade readme=readme.pdf licence=cc-by-nc-sa")
+ else
+ self.insert(2,"\% copyright=pragma-ade readme=readme.pdf licence=cc-gpl")
+ end
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ self[0].gsub!(/ +/, ' ')
+ return true
+ else
+ return false
+ end
+ end
+
+ def add_comment(filename)
+ if self[0] =~ /<\?xml.*?\?>/ && self[2] !~ /^<\!\-\-.*?copyright\=.*?\-\->/ then
+ self.insert(1,"")
+ if File.expand_path(filename) =~ /[\\\/](doc|manuals)[\\\/]/ then
+ self.insert(2,"<!-- copyright='pragma-ade' readme='readme.pdf' licence='cc-by-nc-sa' -->")
+ else
+ self.insert(2,"<!-- copyright='pragma-ade' readme='readme.pdf' licence='cc-gpl' -->")
+ end
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ return true
+ else
+ return false
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def brandfiles
+
+ force = @commandline.option("force")
+ files = @commandline.arguments # Dir.glob("**/*.*")
+ done = false
+
+ files.each do |filename|
+ if FileTest.file?(filename) then
+ ok = false
+ begin
+ data = IO.readlines(filename)
+ case filename
+ when /\.rb$/ then
+ ok = data.add_shebang(filename,'ruby')
+ when /\.pl$/ then
+ ok = data.add_shebang(filename,'perl')
+ when /\.py$/ then
+ ok = data.add_shebang(filename,'python')
+ when /\.lua$/ then
+ ok = data.add_shebang(filename,'lua')
+ when /\.tex$/ then
+ ok = data.add_directive(filename,'tex')
+ when /\.mp$/ then
+ ok = data.add_directive(filename,'metapost')
+ when /\.mf$/ then
+ ok = data.add_directive(filename,'metafont')
+ when /\.(xml|xsl|fo|fx|rlx|rng|exa)$/ then
+ ok = data.add_comment(filename)
+ end
+ rescue
+ report("fatal error in processing #{filename}") # maybe this catches the mac problem taco reported
+ else
+ if ok then
+ report()
+ report(filename)
+ report()
+ for i in 0..4 do
+ report(' ' + data[i].chomp)
+ end
+ if force && f = File.open(filename,'w') then
+ f.puts data
+ f.close
+ end
+ done = true
+ end
+ end
+ else
+ report("no file named #{filename}")
+ end
+ end
+ report() if done
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ # usage : ctxtools --listentities entities.xml
+ # document: <!DOCTYPE something SYSTEM "entities.xml">
+
+ def flushentities(handle,entities,doctype=nil) # 'stylesheet'
+ if doctype then
+ tab = "\t"
+ handle << "<?xml version='1.0' encoding='utf-8'?>\n\n"
+ handle << "<!-- !DOCTYPE entities SYSTEM 'entities.xml' -->\n\n"
+ handle << "<!DOCTYPE #{doctype} [\n"
+ else
+ tab = ""
+ end
+ entities.keys.sort.each do |k|
+ handle << "#{tab}<!ENTITY #{k} \"\&\#x#{entities[k]};\">\n"
+ end
+ if doctype then
+ handle << "]>\n"
+ end
+ end
+
+ def listentities
+
+ filenames = ['enco-uc.tex','contextnames.txt']
+ outputname = @commandline.argument('first')
+ doctype = @commandline.option('doctype')
+ entities = Hash.new
+
+ filenames.each do |filename|
+ filename = Kpse.found(filename, 'context')
+ if filename and not filename.empty? and FileTest.file?(filename) then
+ report("loading #{filename.gsub(/\\/,'/')}") unless outputname.empty?
+ IO.readlines(filename).each do |line|
+ case line
+ when /^[\#\%]/io then
+ # skip comment line
+ when /\\definecharacter\s+([a-z]+)\s+\{\\uchar\{*(\d+)\}*\{(\d+)\}\}/io then
+ name, code = $1, sprintf("%04X",$2.to_i*256 + $3.to_i)
+ entities[name] = code.rjust(4,'0') unless entities.key?(name)
+ when /^([A-F0-9]+)\;([a-z][a-z]+)\;(.*?)\;(.*?)\s*$/io then
+ code, name, adobe, comment = $1, $2, $3, $4
+ entities[name] = code.rjust(4,'0') unless entities.key?(name)
+ end
+ end
+ end
+ end
+ if outputname and not outputname.empty? then
+ if f = File.open(outputname,'w') then
+ report("saving #{entities.size} entities in #{outputname}")
+ flushentities(f,entities,doctype)
+ f.close
+ else
+ flushentities($stdout,entities,doctype)
+ end
+ else
+ flushentities($stdout,entities,doctype)
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def platformize
+
+ pattern = if @commandline.arguments.empty? then "*.{rb,pl,py}" else @commandline.arguments end
+ recurse = @commandline.option("recurse")
+ force = @commandline.option("force")
+ pattern = "#{if recurse then '**/' else '' end}#{pattern}"
+ Dir.glob(pattern).each do |file|
+ if File.file?(file) then
+ size = File.size(file)
+ data = IO.readlines(file)
+ if force then
+ if f = File.open(file,'w')
+ data.each do |line|
+ f.puts(line.chomp)
+ end
+ f.close
+ end
+ if File.size(file) == size then # not robust
+ report("file '#{file}' is unchanged")
+ else
+ report("file '#{file}' is platformized")
+ end
+ else
+ report("file '#{file}' is a candidate")
+ end
+ end
+ end
+ end
+
+end
+
+class TexDeps
+
+ @@cs_tex = %q/
+ above abovedisplayshortskip abovedisplayskip
+ abovewithdelims accent adjdemerits advance afterassignment
+ aftergroup atop atopwithdelims
+ badness baselineskip batchmode begingroup
+ belowdisplayshortskip belowdisplayskip binoppenalty botmark
+ box boxmaxdepth brokenpenalty
+ catcode char chardef cleaders closein closeout clubpenalty
+ copy count countdef cr crcr csname
+ day deadcycles def defaulthyphenchar defaultskewchar
+ delcode delimiter delimiterfactor delimeters
+ delimitershortfall delimeters dimen dimendef discretionary
+ displayindent displaylimits displaystyle
+ displaywidowpenalty displaywidth divide
+ doublehyphendemerits dp dump
+ edef else emergencystretch end endcsname endgroup endinput
+ endlinechar eqno errhelp errmessage errorcontextlines
+ errorstopmode escapechar everycr everydisplay everyhbox
+ everyjob everymath everypar everyvbox exhyphenpenalty
+ expandafter
+ fam fi finalhyphendemerits firstmark floatingpenalty font
+ fontdimen fontname futurelet
+ gdef global group globaldefs
+ halign hangafter hangindent hbadness hbox hfil horizontal
+ hfill horizontal hfilneg hfuzz hoffset holdinginserts hrule
+ hsize hskip hss horizontal ht hyphenation hyphenchar
+ hyphenpenalty hyphen
+ if ifcase ifcat ifdim ifeof iffalse ifhbox ifhmode ifinner
+ ifmmode ifnum ifodd iftrue ifvbox ifvmode ifvoid ifx
+ ignorespaces immediate indent input inputlineno input
+ insert insertpenalties interlinepenalty
+ jobname
+ kern
+ language lastbox lastkern lastpenalty lastskip lccode
+ leaders left lefthyphenmin leftskip leqno let limits
+ linepenalty line lineskip lineskiplimit long looseness
+ lower lowercase
+ mag mark mathaccent mathbin mathchar mathchardef mathchoice
+ mathclose mathcode mathinner mathop mathopen mathord
+ mathpunct mathrel mathsurround maxdeadcycles maxdepth
+ meaning medmuskip message mkern month moveleft moveright
+ mskip multiply muskip muskipdef
+ newlinechar noalign noboundary noexpand noindent nolimits
+ nonscript scriptscript nonstopmode nulldelimiterspace
+ nullfont number
+ omit openin openout or outer output outputpenalty over
+ overfullrule overline overwithdelims
+ pagedepth pagefilllstretch pagefillstretch pagefilstretch
+ pagegoal pageshrink pagestretch pagetotal par parfillskip
+ parindent parshape parskip patterns pausing penalty
+ postdisplaypenalty predisplaypenalty predisplaysize
+ pretolerance prevdepth prevgraf
+ radical raise read relax relpenalty right righthyphenmin
+ rightskip romannumeral
+ scriptfont scriptscriptfont scriptscriptstyle scriptspace
+ scriptstyle scrollmode setbox setlanguage sfcode shipout
+ show showbox showboxbreadth showboxdepth showlists showthe
+ skewchar skip skipdef spacefactor spaceskip span special
+ splitbotmark splitfirstmark splitmaxdepth splittopskip
+ string
+ tabskip textfont textstyle the thickmuskip thinmuskip time
+ toks toksdef tolerance topmark topskip tracingcommands
+ tracinglostchars tracingmacros tracingonline tracingoutput
+ tracingpages tracingparagraphs tracingrestores tracingstats
+ uccode uchyph underline unhbox unhcopy unkern unpenalty
+ unskip unvbox unvcopy uppercase
+ vadjust valign vbadness vbox vcenter vfil vfill vfilneg
+ vfuzz voffset vrule vsize vskip vsplit vss vtop
+ wd widowpenalty write
+ xdef xleaders xspaceskip
+ year
+ /.split
+
+ @@cs_etex = %q/
+ beginL beginR botmarks
+ clubpenalties currentgrouplevel currentgrouptype
+ currentifbranch currentiflevel currentiftype
+ detokenize dimexpr displaywidowpenalties
+ endL endR eTeXrevision eTeXversion everyeof
+ firstmarks fontchardp fontcharht fontcharic fontcharwd
+ glueexpr glueshrink glueshrinkorder gluestretch
+ gluestretchorder gluetomu
+ ifcsname ifdefined iffontchar interactionmode
+ interactionmode interlinepenalties
+ lastlinefit lastnodetype
+ marks topmarks middle muexpr mutoglue
+ numexpr
+ pagediscards parshapedimen parshapeindent parshapelength
+ predisplaydirection
+ savinghyphcodes savingvdiscards scantokens showgroups
+ showifs showtokens splitdiscards splitfirstmarks
+ TeXXeTstate tracingassigns tracinggroups tracingifs
+ tracingnesting tracingscantokens
+ unexpanded unless
+ widowpenalties
+ /.split
+
+ @@cs_pdftex = %q/
+ pdfadjustspacing pdfannot pdfavoidoverfull
+ pdfcatalog pdfcompresslevel
+ pdfdecimaldigits pdfdest pdfdestmargin
+ pdfendlink pdfendthread
+ pdffontattr pdffontexpand pdffontname pdffontobjnum pdffontsize
+ pdfhorigin
+ pdfimageresolution pdfincludechars pdfinfo
+ pdflastannot pdflastdemerits pdflastobj
+ pdflastvbreakpenalty pdflastxform pdflastximage
+ pdflastximagepages pdflastxpos pdflastypos
+ pdflinesnapx pdflinesnapy pdflinkmargin pdfliteral
+ pdfmapfile pdfmaxpenalty pdfminpenalty pdfmovechars
+ pdfnames
+ pdfobj pdfoptionpdfminorversion pdfoutline pdfoutput
+ pdfpageattr pdfpageheight pdfpageresources pdfpagesattr
+ pdfpagewidth pdfpkresolution pdfprotrudechars
+ pdfrefobj pdfrefxform pdfrefximage
+ pdfsavepos pdfsnaprefpoint pdfsnapx pdfsnapy pdfstartlink
+ pdfstartthread
+ pdftexrevision pdftexversion pdfthread pdfthreadmargin
+ pdfuniqueresname
+ pdfvorigin
+ pdfxform pdfximage
+ /.split
+
+ @@cs_omega = %q/
+ odelimiter omathaccent omathchar oradical omathchardef omathcode odelcode
+ leftghost rightghost
+ charwd charht chardp charit
+ localleftbox localrightbox
+ localinterlinepenalty localbrokenpenalty
+ pagedir bodydir pardir textdir mathdir
+ boxdir nextfakemath
+ pagewidth pageheight pagerightoffset pagebottomoffset
+ nullocp nullocplist ocp externalocp ocplist pushocplist popocplist clearocplists ocptracelevel
+ addbeforeocplist addafterocplist removebeforeocplist removeafterocplist
+ OmegaVersion
+ InputTranslation OutputTranslation DefaultInputTranslation DefaultOutputTranslation
+ noInputTranslation noOutputTranslation
+ InputMode OutputMode DefaultInputMode DefaultOutputMode
+ noInputMode noOutputMode noDefaultInputMode noDefaultOutputMode
+ /.split
+
+ @@cs_metatex = %q/
+ /.split
+
+ @@cs_xetex = %q/
+ /.split
+
+ @@cs_skip = %q/
+ v\! c\! s\! e\! m\! f\!
+ \!tf \!tt \!tq \!ta \?\?
+ csname endcsname relax
+ \!\!string[a-f] \!\!dimen[a-k] \!\!count[a-f] \!\!toks[a-e] \!\!box[a-e]
+ \!\!width[a-c] \!\!height[a-c] \!\!depth[a-c]
+ \!\!done[a-f] if\!\!done[a-f] if\:\!\!done[a-f]
+ scratch globalscratch
+ ascii[a-d] globalascii
+ @@expanded @@globalexpanded @EA @EAEA @EAEAEA
+ bgroup egroup par next nextnext docommand dodocommand dododocommand
+ \!\!width \!\!height \!\!depth \!\!plus \!\!minus \!\!to
+ /.split
+
+ @@cs_skip = %q/
+ [vcsemf]\! \?\?
+ \!t[ftqa]
+ csname endcsname relax
+ \!\!string[a-f] \!\!dimen[a-k] \!\!count[a-f] \!\!toks[a-e] \!\!box[a-e]
+ \!\!width[a-c] \!\!height[a-c] \!\!depth[a-c]
+ \!\!done[a-f] if\!\!done[a-f] if\:\!\!done[a-f]
+ scratch globalscratch
+ ascii[a-d] globalascii
+ @@expanded @@globalexpanded @(EA)+
+ [be]group par next nextnext (do)+command
+ \!\!(width|height|depth|plus|minus|to)
+ /.split
+
+ # let's ignore \dimendef etc
+
+ @@primitives_def = %q/
+ def edef xdef gdef let
+ newcount newdimen newskip newbox newtoks newmarks newif newinsert newmuskip
+ chardef mathchardef dimendef countdef toksdef
+ newconditional definecomplexorsimple definecomplexorsimpleempty
+ newcounter newpersistentmark
+ installinsertion installspecial\s*\\[* installoutput\s*\\[*
+ /.split
+
+ @@types = [['invalid','*'],['okay','='],['forward','>'],['backward','<'],['unknown','?']]
+
+ @@skips = /^(#{@@cs_skip.join('|')})/o
+
+ def initialize(logger=nil,compact=false)
+ @defined = Hash.new
+ @definitive = Hash.new
+ @used_before = Hash.new
+ @used_after = Hash.new
+ @dependencies = Hash.new
+ @fineorder = Hash.new
+ @forward = Hash.new
+ @backward = Hash.new
+ @disorder = Hash.new
+ @disordercs = Hash.new
+ @type = Hash.new
+ @filename = 'context.tex'
+ @files = Array.new # keep load order !
+ @order = Hash.new
+ @logger = logger
+ @filefilter = nil
+ @namefilter = nil
+ @compact = compact
+ #
+ @@cs_tex.each do |cs| @defined[cs] = ['-tex--------'] end
+ @@cs_etex.each do |cs| @defined[cs] = ['-etex-------'] end
+ @@cs_pdftex.each do |cs| @defined[cs] = ['-pdftex-----'] end
+ @@cs_omega.each do |cs| @defined[cs] = ['-omega------'] end
+ @@cs_xetex.each do |cs| @defined[cs] = ['-xetex------'] end
+ @@cs_metatex.each do |cs| @defined[cs] = ['-metatex----'] end
+ end
+
+ def report(str)
+ @logger.report(str) rescue false
+ end
+
+ def setfilter(data)
+ data.split(/\s*\,\s*/).each do |d|
+ if d =~ /\.tex$/ then
+ @filefilter = Array.new unless @filefilter
+ @filefilter << d
+ else
+ @namefilter = Array.new unless @namefilter
+ @namefilter << d
+ end
+ end
+ end
+
+ def load(filename='context.tex')
+ begin
+ @filename = filename
+ n = 0
+ File.open(filename) do |f|
+ f.each do |line|
+ if line =~ /^(\\input\s+|\\load[a-z]+\{)([a-z\-\.]+)(\}*)/ then
+ ante, name, post = $1, $2, $3
+ @files.push(name)
+ @order[name] = n += 1
+ end
+ end
+ end
+ rescue
+ @files = Array.new
+ @order = Hash.new
+ end
+ end
+
+ def save(filename='context.tex')
+ unless @filefilter || @namefilter then
+ begin
+ data = IO.readlines(filename).each do |line|
+ line.gsub!(/^(\\input\s+|\\load[a-z]+\{)([a-z\-\.]+)(\}*)\s*$/) do
+ ante, name, post = $1, $2, $3
+ fin = (@fineorder[name] || [])-[name]
+ dep = (@dependencies[name] || [])-[name]
+ dis = (@disorder[name] || [])-[name]
+ fin = if fin.size > 0 then " B[#{fin.join(' ')}]" else "" end
+ dep = if dep.size > 0 then " A[#{dep.join(' ')}]" else "" end
+ dis = if dis.size > 0 then " D[#{dis.join(' ')}]" else "" end
+ "#{ante}#{name}#{post} %#{fin}#{dep}#{dis}\n"
+ end
+ end
+ rescue
+ report("error: #{$!}")
+ else
+ begin
+ newname = filename.sub(/\..*$/,'.log')
+ report("")
+ report("writing to #{newname}")
+ report("")
+ File.open(newname,'w') do |f|
+ f << data
+ end
+ rescue
+ report("error: #{$!}")
+ end
+ end
+ end
+ end
+
+ def analyze
+ report('')
+ report("loading files")
+ report('')
+ n = 0
+# try tex and mkiv
+ @files.each do |filename|
+ if File.file?(filename) and f = File.open(filename) then
+ defs, uses, l = 0, 0, 0
+ n += 1
+ report("#{n.to_s.rjust(5,' ')} #{filename}")
+ f.each do |line|
+ l += 1
+ line.chomp!
+
+
+ line.sub!(/\%.*$/, '')
+ line.gsub!(/\\(unexpanded|unprotected|global|protected|long)\s*(\\)/, "\\")
+ # the superseded, overloaded, forwarded, and predefined macros
+ # are at the outer level anyway, so there we may ignore leading
+ # spaces (could be inside an \if); other definitions are only
+ # accepted when they start at the beginning of a line
+ case line
+ when /^\\ifx\s*\\[a-zA-Z\@\!\?]+\s*\\undefined\s*(\\else)*(.*?)$/ then
+ if $2 =~ /^\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})/o then
+ pushdef(filename,l,$2,5) # kind of auto-predefined
+ end
+ when /^\s*\\superseded\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,1)
+ moreuse(filename,l,rest)
+ when /^\s*\\overloaded\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,2)
+ moreuse(filename,l,rest)
+ when /^\s*\\forwarded\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,3)
+ moreuse(filename,l,rest)
+ when /^\s*\\predefined\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,4)
+ moreuse(filename,l,rest)
+ when /^\\(#{@@primitives_def.join('|')})[\=\s]*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3 # \=* catches the \let \a = \b
+ pushdef(filename,l,name,0)
+ moreuse(filename,l,rest)
+ when /\\newevery\s*\\([a-zA-Z\@\?\!]+)\s*\\([a-zA-Z\@\?\!]+)/ then
+ a, b = $1, $2
+ pushdef(filename,l,a,0)
+ pushdef(filename,l,b,0)
+ else
+ moreuse(filename,l,line)
+ end
+ end
+ f.close
+ end
+ end
+ @used_after.each do |cs,files|
+ (@defined[cs] || []).each do |name|
+ @dependencies[name] = Array.new unless @dependencies[name]
+ files.each do |file|
+ @dependencies[name] << file unless @dependencies[name].include?(file)
+ end
+ end
+ end
+ @used_before.each do |cs,files|
+ (@defined[cs] || []).each do |name|
+ @disorder[name] = Array.new unless @disorder[name]
+ @disordercs[name] = Array.new unless @disordercs[name]
+ @fineorder[name] = Array.new unless @fineorder[name]
+ files.each do |file|
+ unless @disorder[name].include?(file) || name == file then
+ unless @defined[cs].include?(file) then
+ if @order[name] > @order[file] then
+ @disorder[name] << file
+ @disordercs[name] << "#{file}:#{cs}"
+ end
+ end
+ end
+ @fineorder[name] << file unless @fineorder[name].include?(file) || name == file
+ end
+ end
+ end
+ end
+
+ def moreuse(filename,l,line)
+ line.scan(/\\if([a-zA-Z@\?\!]{3,})/) do |name, rest| # rest, else array
+ pushuse(filename,l,"if#{name}") unless name =~ /^(true|false)$/
+ end
+ line.scan(/\\([a-zA-Z@\?\!]{3,})/) do |name, rest| # rest, else array
+ if name =~ /(true|false)$/ then
+ pushuse(filename,l,"if#{name}") unless name =~ /^(if|set)$/
+ else
+ pushuse(filename,l,name)
+ end
+ end
+ end
+
+ def feedback
+ begin
+ # get max length
+ l = 0
+ list = @defined.keys.sort
+ list.each do |cs|
+ l = cs.length if cs.length > l
+ end
+ if ! @compact then
+ n = 0
+ report('')
+ report("defined: #{@defined.size}")
+ report('')
+ @defined.keys.sort.each do |cs|
+ next if @namefilter && ! @namefilter.include?(cs)
+ next if @filefilter && ! @defined[cs].include?(cs)
+ if @defined[cs].size > 1 then
+ dlist = @defined[cs].collect do |d|
+ if d == @definitive[cs] then d else "[#{d}]" end
+ end
+ else
+ dlist = @defined[cs]
+ end
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{dlist.join(' ')}")
+ end
+ end
+ if true then
+ n = 0
+ report('')
+ report("used before defined: #{@used_before.size}")
+ report('')
+ @used_before.keys.sort.each do |cs|
+ next if @namefilter && ! @namefilter.include?(cs)
+ next if @filefilter && (@used_before[cs] & @filefilter).size == 0
+ used = @used_before[cs] - (@defined[cs] || [])
+ defined = (@defined[cs] || []).join(' ')
+ defined = "[ ? ]" if defined.empty?
+ if used.size > 0 then
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined} -> #{used.join(' ')}")
+ else
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined}")
+ end
+ end
+ report(' none') if n == 0
+ end
+ if ! @compact then
+ n = 0
+ report('')
+ report("used after defined: #{@used_after.size}")
+ report('')
+ @used_after.keys.sort.each do |cs|
+ next if @namefilter && ! @namefilter.include?(cs)
+ next if @filefilter && (@used_after[cs] & @filefilter).size == 0
+ used = @used_after[cs] - (@defined[cs] || [])
+ defined = (@defined[cs] || []).join(' ')
+ if used.size > 0 then
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined} <- #{used.join(' ')}")
+ else
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined}")
+ end
+ end
+ report(' none') if n == 0
+ end
+ if ! @compact then
+ unless @filefilter || @namefilter then
+ [false,true].each do |mode|
+ n = 0
+ report("")
+ report("file dependecies #{if mode then '(critical)' end}")
+ [@dependencies].each do |dependencies|
+ report("")
+ dependencies.keys.sort.each do |f|
+ if dependencies[f].size > 0 then
+ dependencies[f].delete(f)
+ end
+ if mode then
+ dep = dependencies[f].delete_if do |d|
+ f[0..3] == d[0..3] # same xxxx- prefix
+ end
+ else
+ dep = dependencies[f]
+ end
+ if dep.size > 0 then
+ name = f.nosuffix('tex').ljust(8,' ')
+ list = dep.sort.collect do |k| k.nosuffix('tex') end
+ report("#{(n += 1).to_s.rjust(5,' ')} #{name} !! #{list.join(' ')}")
+ end
+ end
+ end
+ report(' none') if n == 0
+ end
+ end
+ end
+ if true then
+ unless @filefilter || @namefilter then
+ [false,true].each do |mode|
+ [@disorder,@disordercs].each do |disorder|
+ n = 0
+ report("")
+ report("file disorder #{if mode then '(critical)' end}")
+ report("")
+ disorder.keys.sort.each do |f|
+ if disorder[f].size > 0 then
+ disorder[f].delete(f)
+ end
+ if mode then
+ dis = disorder[f].delete_if do |d|
+ f[0..3] == d[0..3] # same xxxx- prefix
+ end
+ else
+ dis = disorder[f]
+ end
+ if dis.size > 0 then
+ name = f.nosuffix('tex').ljust(8,' ')
+ list = dis.sort.collect do |k| k.nosuffix('tex') end
+ report("#{(n += 1).to_s.rjust(3,' ')} #{name} ?? #{list.join(' ')}")
+ end
+ end
+ end
+ report(' none') if n == 0
+ end
+ end
+ end
+ rescue
+ puts("fatal error: #{$!} #{$@.join("\n")}")
+ end
+ end
+
+ private
+
+ def csdefined?(cs,filename)
+ @defined[cs] && @defined[cs].include?(filename)
+ end
+ def csbefore?(cs,filename)
+ @used_before[cs] && @used_before[cs].include?(filename)
+ end
+ def csafter?(cs,filename)
+ @used_after[cs] && @used_after[cs].include?(filename)
+ end
+
+ def csignored?(cs)
+ cs.to_s =~ @@skips
+ end
+
+ def pushdef(filename,n,cs,type)
+ if csignored?(cs) then
+ # nothing
+ elsif @defined[cs] then
+ case type
+ when 5 then
+ # if test, no definition done
+ else
+ @definitive[cs] = filename
+ unless @filefilter || @namefilter then
+ report("#{cs} is redefined") unless csdefined?(cs,filename) || @compact
+ end
+ end
+ @defined[cs] << filename unless @defined[cs].include?(filename)
+ else
+ @defined[cs] = Array.new
+ @defined[cs] << filename
+ @definitive[cs] = filename
+ @type[cs] = type
+ end
+ end
+
+ def pushuse(filename,n,cs)
+ if csignored?(cs) then
+ # nothing
+ elsif @defined[cs] then
+ @used_after[cs] = Array.new unless @used_after[cs]
+ @used_after[cs] << filename unless csafter?(cs,filename)
+ else
+ @used_before[cs] = Array.new unless @used_before[cs]
+ @used_before[cs] << filename unless csbefore?(cs,filename)
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def dependencies
+
+ filename = if @commandline.arguments.empty? then 'context.tex' else @commandline.arguments.first end
+ compact = @commandline.option('compact')
+
+ ['context',''].each do |progname|
+ unless FileTest.file?(filename) then
+ name = Kpse.found(filename, progname)
+ if FileTest.file?(name) then
+ filename = name
+ break
+ end
+ end
+ end
+
+ if FileTest.file?(filename) && deps = TexDeps.new(logger,compact) then
+ deps.setfilter(@commandline.option('filter'))
+ deps.load
+ deps.analyze
+ deps.feedback
+ deps.save if @commandline.option('save')
+ else
+ report("unknown file #{filename}")
+ end
+
+ end
+
+end
+
+class Commands
+
+ @@re_utf_bom = /^\357\273\277/o # just utf-8
+
+ def disarmutfbom
+
+ if @commandline.arguments.empty? then
+ report("provide filename")
+ else
+ @commandline.arguments.each do |filename|
+ report("checking '#{filename}'")
+ if FileTest.file?(filename) then
+ begin
+ data = IO.read(filename)
+ if data.sub!(@@re_utf_bom,'') then
+ if @commandline.option('force') then
+ if f = File.open(filename,'wb') then
+ f << data
+ f.close
+ report("bom found and removed")
+ else
+ report("bom found and removed, but saving file fails")
+ end
+ else
+ report("bom found, use '--force' to remove it")
+ end
+ else
+ report("no bom found")
+ end
+ rescue
+ report("bom found, but removing it fails")
+ end
+ else
+ report("provide valid filename")
+ end
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def updatecontext
+
+ def fetchfile(site, name, target=nil)
+ begin
+ proxy = @commandline.option('proxy')
+ if proxy && ! proxy.empty? then
+ address, port = proxy.split(":")
+ if address && port then
+ http = Net::HTTP::Proxy(address, port).new(site)
+ else
+ http = Net::HTTP::Proxy(proxy, 80).new(site)
+ end
+ else
+ http = Net::HTTP.new(site)
+ end
+ resp, data = http.get(name.gsub(/^\/*/, '/'))
+ rescue
+ return false
+ else
+ begin
+ if data then
+ name = File.basename(name)
+ File.open(target || name, 'wb') do |f|
+ f << data
+ end
+ else
+ return false
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+ end
+
+ def locatedlocaltree
+ tree = Kpse.used_path('TEXMFLOCAL')
+ unless tree && FileTest.directory?(tree) then
+ tree = Kpse.used_path('TEXMF')
+ end
+ return tree
+ end
+
+ def extractarchive(archive)
+ unless FileTest.file?(archive) then
+ report("fatal error, '#{archive}' has not been downloaded")
+ return false
+ end
+ # unless system("unzip -uo #{archive}") then
+ unless system("unzip -o #{archive}") then
+ report("fatal error, make sure that you have 'unzip' in your path")
+ return false
+ end
+ stubs = "scripts/context/stubs/unix/*"
+ if System.unix? and not system("chmod +x #{stubs}") then
+ report("change x-permissions of '#{stubs}' manually")
+ end
+ return true
+ end
+
+ def remakeformats
+ system("mktexlsr")
+ system("luatools --selfupdate")
+ system("mtxrun --selfupdate")
+ system("luatools --generate")
+ system("texmfstart texexec --make --all --fast --pdftex")
+ system("texmfstart texexec --make --all --fast --luatex")
+ system("texmfstart texexec --make --all --fast --xetex")
+ return true
+ end
+
+ if localtree = locatedlocaltree then
+ report("updating #{localtree}")
+ begin
+ Dir.chdir(localtree)
+ rescue
+ report("unable to change to #{localtree}")
+ else
+ archive = 'cont-tmf.zip'
+ report("fetching #{archive}")
+ unless fetchfile("www.pragma-ade.com","/context/latest/#{archive}") then
+ report("unable to fetch #{archive}")
+ return
+ end
+ report("extracting #{archive}")
+ unless extractarchive(archive) then
+ report("unable to extract #{archive}")
+ return
+ end
+ report("remaking formats")
+ unless remakeformats then
+ report("unable to remak formats")
+ end
+ end
+ else
+ report("unable to locate local tree")
+ end
+
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('touchcontextfile' , 'update context version')
+commandline.registeraction('contextversion' , 'report context version')
+commandline.registeraction('jeditinterface' , 'generate jedit syntax files [--pipe]')
+commandline.registeraction('bbeditinterface' , 'generate bbedit syntax files [--pipe]')
+commandline.registeraction('sciteinterface' , 'generate scite syntax files [--pipe]')
+commandline.registeraction('rawinterface' , 'generate raw syntax files [--pipe]')
+# commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]')
+commandline.registeraction('purgefiles' , 'remove temporary files [--all --recurse] [basename]')
+commandline.registeraction('documentation' , 'generate documentation [--type=] [filename]')
+commandline.registeraction('filterpages' ) # no help, hidden temporary feature
+commandline.registeraction('patternfiles' , 'generate pattern files [--all --xml --utf8] [languagecode]')
+commandline.registeraction('dpxmapfiles' , 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]')
+commandline.registeraction('listentities' , 'create doctype entity definition from enco-uc.tex')
+commandline.registeraction('brandfiles' , 'add context copyright notice [--force]')
+commandline.registeraction('platformize' , 'replace line-endings [--recurse --force] [pattern]')
+commandline.registeraction('dependencies' , 'analyze depedencies within context [--save --compact --filter=[macros|filenames]] [filename]')
+commandline.registeraction('updatecontext' , 'download latest version and remake formats [--proxy]')
+commandline.registeraction('disarmutfbom' , 'remove utf bom [--force]')
+
+commandline.registervalue('type','')
+commandline.registervalue('filter','')
+commandline.registervalue('maproot','')
+commandline.registervalue('proxy','')
+
+commandline.registerflag('recurse')
+commandline.registerflag('force')
+commandline.registerflag('compact')
+commandline.registerflag('pipe')
+commandline.registerflag('save')
+commandline.registerflag('all')
+commandline.registerflag('xml')
+commandline.registerflag('log')
+commandline.registerflag('utf8')
+commandline.registerflag('doctype')
+
+# general
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/scripts/context/ruby/fcd_start.rb b/scripts/context/ruby/fcd_start.rb
new file mode 100644
index 000000000..b1fa42a2a
--- /dev/null
+++ b/scripts/context/ruby/fcd_start.rb
@@ -0,0 +1,472 @@
+# Hans Hagen / PRAGMA ADE / 2005 / www.pragma-ade.com
+#
+# Fast Change Dir
+#
+# This is a kind of variant of the good old ncd
+# program. This script uses the same indirect cmd
+# trick as Erwin Waterlander's wcd program.
+#
+# === windows: fcd.cmd ===
+#
+# @echo off
+# ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9
+# if exist "%HOME%/fcd_stage.cmd" call %HOME%/fcd_stage.cmd
+#
+# === linux: fcd (fcd.sh) ===
+#
+# !/usr/bin/env sh
+# ruby -S fcd_start.rb $1 $2 $3 $4 $5 $6 $7 $8 $9
+# if test -f "$HOME/fcd_stage.sh" ; then
+# . $HOME/fcd_stage.sh ;
+# fi;
+#
+# ===
+#
+# On linux, one should source the file: ". fcd args" in order
+# to make the chdir persistent.
+#
+# You can create a stub with:
+#
+# ruby fcd_start.rb --stub --verbose
+#
+# usage:
+#
+# fcd --make t:\
+# fcd --add f:\project
+# fcd [--find] whatever
+# fcd [--find] whatever c (c being a list entry)
+# fcd [--find] whatever . (last choice with this pattern)
+# fcd --list
+
+# todo: HOMEDRIVE\HOMEPATH
+
+require 'rbconfig'
+
+class FastCD
+
+ @@rootpath = nil
+
+ ['HOME','TEMP','TMP','TMPDIR'].each do |key|
+ if ENV[key] then
+ if FileTest.directory?(ENV[key]) then
+ @@rootpath = ENV[key]
+ break
+ end
+ end
+ end
+
+ exit unless @@rootpath
+
+ @@mswindows = Config::CONFIG['host_os'] =~ /mswin/
+ @@maxlength = 26
+
+ require 'Win32API' if @@mswindows
+
+ if @@mswindows then
+ @@stubcode = [
+ '@echo off',
+ '',
+ 'if not exist "%HOME%" goto temp',
+ '',
+ ':home',
+ '',
+ 'ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9',
+ '',
+ 'if exist "%HOME%\fcd_stage.cmd" call %HOME%\fcd_stage.cmd',
+ 'goto end',
+ '',
+ ':temp',
+ '',
+ 'ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9',
+ '',
+ 'if exist "%TEMP%\fcd_stage.cmd" call %TEMP%\fcd_stage.cmd',
+ 'goto end',
+ '',
+ ':end'
+ ].join("\n")
+ else
+ @@stubcode = [
+ '#!/usr/bin/env sh',
+ '',
+ 'ruby -S fcd_start.rb $1 $2 $3 $4 $5 $6 $7 $8 $9',
+ '',
+ 'if test -f "$HOME/fcd_stage.sh" ; then',
+ ' . $HOME/fcd_stage.sh ;',
+ 'fi;'
+ ].join("\n")
+ end
+
+ @@selfpath = File.dirname($0)
+ @@datafile = File.join(@@rootpath,'fcd_state.dat')
+ @@histfile = File.join(@@rootpath,'fcd_state.his')
+ @@cdirfile = File.join(@@rootpath,if @@mswindows then 'fcd_stage.cmd' else 'fcd_stage.sh' end)
+ @@stubfile = File.join(@@selfpath,if @@mswindows then 'fcd.cmd' else 'fcd' end)
+
+ def initialize(verbose=false)
+ @list = Array.new
+ @hist = Hash.new
+ @result = Array.new
+ @pattern = ''
+ @result = ''
+ @verbose = verbose
+ if f = File.open(@@cdirfile,'w') then
+ f << "#{if @@mswindows then 'rem' else '#' end} no dir to change to"
+ f.close
+ else
+ report("unable to create stub #{@@cdirfile}")
+ end
+ end
+
+ def filename(name)
+ File.join(@@root,name)
+ end
+
+ def report(str,verbose=@verbose)
+ puts(">> #{str}") if verbose
+ end
+
+ def flush(str,verbose=@verbose)
+ print(str) if verbose
+ end
+
+ def clear
+ if FileTest.file?(@@histfile)
+ begin
+ File.delete(@@histfile)
+ rescue
+ report("error in deleting history file '#{@histfile}'")
+ else
+ report("history file '#{@histfile}' is deleted")
+ end
+ else
+ report("no history file '#{@histfile}'")
+ end
+ end
+
+ def scan(dir='.')
+ begin
+ [dir].flatten.sort.uniq.each do |dir|
+ begin
+ Dir.chdir(dir)
+ report("scanning '#{dir}'")
+ # flush(">> ")
+ Dir.glob("**/*").each do |d|
+ if FileTest.directory?(d) then
+ @list << File.expand_path(d)
+ # flush(".")
+ end
+ end
+ # flush("\n")
+ @list = @list.sort.uniq
+ report("#{@list.size} entries found")
+ rescue
+ report("unknown directory '#{dir}'")
+ end
+ end
+ rescue
+ report("invalid dir specification ")
+ end
+ end
+
+ def save
+ begin
+ if f = File.open(@@datafile,'w') then
+ @list.each do |l|
+ f.puts(l)
+ end
+ f.close
+ report("#{@list.size} status bytes saved in #{@@datafile}")
+ else
+ report("unable to save status in #{@@datafile}")
+ end
+ rescue
+ report("error in saving status in #{@@datafile}")
+ end
+ end
+
+ def remember
+ if @hist[@pattern] == @result then
+ # no need to save result
+ else
+ begin
+ if f = File.open(@@histfile,'w') then
+ @hist[@pattern] = @result
+ @hist.keys.each do |k|
+ f.puts("#{k} #{@hist[k]}")
+ end
+ f.close
+ report("#{@hist.size} history entries saved in #{@@histfile}")
+ else
+ report("unable to save history in #{@@histfile}")
+ end
+ rescue
+ report("error in saving history in #{@@histfile}")
+ end
+ end
+ end
+
+ def load
+ begin
+ @list = IO.read(@@datafile).split("\n")
+ report("#{@list.length} status bytes loaded from #{@@datafile}")
+ rescue
+ report("error in loading status from #{@@datafile}")
+ end
+ begin
+ IO.readlines(@@histfile).each do |line|
+ if line =~ /^(.*?)\s+(.*)$/i then
+ @hist[$1] = $2
+ end
+ end
+ report("#{@hist.length} history entries loaded from #{@@histfile}")
+ rescue
+ report("error in loading history from #{@@histfile}")
+ end
+ end
+
+ def show
+ begin
+ puts("directories:")
+ puts("\n")
+ if @list.length > 0 then
+ @list.each do |l|
+ puts(l)
+ end
+ else
+ puts("no entries")
+ end
+ puts("\n")
+ puts("history:")
+ puts("\n")
+ if @hist.length > 0 then
+ @hist.keys.sort.each do |h|
+ puts("#{h} >> #{@hist[h]}")
+ end
+ else
+ puts("no entries")
+ end
+ rescue
+ end
+ end
+
+ def find(pattern=nil)
+ begin
+ if pattern = [pattern].flatten.first then
+ if pattern.length > 0 and @pattern = pattern then
+ @result = @list.grep(/\/#{@pattern}$/i)
+ if @result.length == 0 then
+ @result = @list.grep(/\/#{@pattern}[^\/]*$/i)
+ end
+ end
+ else
+ puts(Dir.pwd.gsub(/\\/o, '/'))
+ end
+ rescue
+ puts("some error")
+ end
+ end
+
+ def chdir(dir)
+ begin
+ if dir then
+ if f = File.open(@@cdirfile,'w') then
+ if @@mswindows then
+ f.puts("cd /d #{dir.gsub('/','\\')}")
+ else
+ f.puts("cd #{dir.gsub("\\",'/')}")
+ end
+ f.close
+ end
+ @result = dir
+ report("changing to #{dir}",true)
+ else
+ report("not changing dir")
+ end
+ rescue
+ end
+ end
+
+ def choose(args=[])
+ offset = 97
+ unless @pattern.empty? then
+ begin
+ case @result.size
+ when 0 then
+ report("dir '#{@pattern}' not found",true)
+ when 1 then
+ chdir(@result[0])
+ else
+ list = @result.dup
+ begin
+ if answer = args[1] then # assignment & test
+ if answer == '.' and @hist.key?(@pattern) then
+ if FileTest.directory?(@hist[@pattern]) then
+ print("last choice ")
+ chdir(@hist[@pattern])
+ return
+ end
+ else
+ index = answer[0] - offset
+ if dir = list[index] then
+ chdir(dir)
+ return
+ end
+ end
+ end
+ rescue
+ puts("some error")
+ end
+ loop do
+ print("\n")
+ list.each_index do |i|
+begin
+ if i < @@maxlength then
+ # puts("#{(i+?a).chr} #{list[i]}")
+ puts("#{(i+offset).chr} #{list[i]}")
+ else
+ puts("\n there are #{list.length-@@maxlength} entries more")
+ break
+ end
+rescue
+ puts("some error")
+end
+ end
+ print("\n>> ")
+ if answer = wait then
+ if answer >= offset and answer <= offset+25 then
+ index = answer - offset
+ if dir = list[index] then
+ print("#{answer.chr} ")
+ chdir(dir)
+ elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then
+ print("last choice ")
+ chdir(@hist[@pattern])
+ else
+ print("quit\n")
+ end
+ break
+ elsif list.length >= @@maxlength then
+ @@maxlength.times do |i| list.shift end
+ print("next set")
+ print("\n")
+ elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then
+ print("last choice ")
+ chdir(@hist[@pattern])
+ break
+ else
+ print("quit\n")
+ break
+ end
+ end
+ end
+ end
+ rescue
+ report($!)
+ end
+ end
+ end
+
+ def wait
+ begin
+ $stdout.flush
+ return getc
+ rescue
+ return nil
+ end
+ end
+
+ def getc
+ begin
+ if @@mswindows then
+ ch = Win32API.new('crtdll','_getch',[],'L').call
+ else
+ system('stty raw -echo')
+ ch = $stdin.getc
+ system('stty -raw echo')
+ end
+ rescue
+ ch = nil
+ end
+ return ch
+ end
+
+ def check
+ unless FileTest.file?(@@stubfile) then
+ report("creating stub #{@@stubfile}")
+ begin
+ if f = File.open(@@stubfile,'w') then
+ f.puts(@@stubcode)
+ f.close
+ end
+ rescue
+ report("unable to create stub #{@@stubfile}")
+ else
+ unless @mswindows then
+ begin
+ File.chmod(0755,@@stubfile)
+ rescue
+ report("unable to change protections on #{@@stubfile}")
+ end
+ end
+ end
+ else
+ report("stub #{@@stubfile} already present")
+ end
+ end
+
+end
+
+$stdout.sync = true
+
+verbose, action, args = false, :find, Array.new
+
+usage = "fcd [--add|clear|find|list|make|show|stub] [--verbose] [pattern]"
+version = "1.0.2"
+
+def quit(message)
+ puts(message)
+ exit
+end
+
+ARGV.each do |a|
+ case a
+ when '-a', '--add' then action = :add
+ when '-c', '--clear' then action = :clear
+ when '-f', '--find' then action = :find
+ when '-l', '--list' then action = :show
+ when '-m', '--make' then action = :make
+ when '-s', '--show' then action = :show
+ when '--stub' then action = :stub
+ when '-v', '--verbose' then verbose = true
+ when '--version' then quit("version: #{version}")
+ when '-h', '--help' then quit("usage: #{usage}")
+ when /^\-\-.*/ then quit("error: unknown switch #{a}, try --help")
+ else args << a
+ end
+end
+
+fcd = FastCD.new(verbose)
+fcd.report("Fast Change Dir / version #{version}")
+
+case action
+ when :make then
+ fcd.clear
+ fcd.scan(args)
+ fcd.save
+ when :clear then
+ fcd.clear
+ when :add then
+ fcd.load
+ fcd.scan(args)
+ fcd.save
+ when :show then
+ fcd.load
+ fcd.show
+ when :find then
+ fcd.load
+ fcd.find(args)
+ fcd.choose(args)
+ fcd.remember
+ when :stub
+ fcd.check
+end
diff --git a/scripts/context/ruby/graphics/gs.rb b/scripts/context/ruby/graphics/gs.rb
new file mode 100644
index 000000000..6143c8812
--- /dev/null
+++ b/scripts/context/ruby/graphics/gs.rb
@@ -0,0 +1,684 @@
+# module : graphics/gs
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/variables','../variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+# ['base/system', '../system', 'system' ].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/variables'
+require 'base/system'
+require 'fileutils'
+# Require 'ftools'
+
+class GhostScript
+
+ include Variables
+
+ @@pdftrimwhite = 'pdftrimwhite.pl'
+
+ @@pstopdfoptions = [
+ 'AntiAliasColorImages',
+ 'AntiAliasGrayImages',
+ 'AntiAliasMonoImages',
+ 'ASCII85EncodePages',
+ 'AutoFilterColorImages',
+ 'AutoFilterGrayImages',
+ 'AutoPositionEPSFiles',
+ 'AutoRotatePages',
+ 'Binding',
+ 'ColorConversionStrategy',
+ 'ColorImageDepth',
+ 'ColorImageDownsampleThreshold',
+ 'ColorImageDownsampleType',
+ 'ColorImageFilter',
+ 'ColorImageResolution',
+ 'CompatibilityLevel',
+ 'CompressPages',
+ #'ConvertCMYKImagesToRGB', # buggy
+ #'ConvertImagesToIndexed', # buggy
+ 'CreateJobTicket',
+ 'DetectBlends',
+ 'DoThumbnails',
+ 'DownsampleColorImages',
+ 'DownsampleGrayImages',
+ 'DownsampleMonoImages',
+ 'EmbedAllFonts',
+ 'EncodeColorImages',
+ 'EncodeGrayImages',
+ 'EncodeMonoImages',
+ 'EndPage',
+ 'FirstPage',
+ 'GrayImageDepth',
+ 'GrayImageDownsampleThreshold',
+ 'GrayImageDownsampleType',
+ 'GrayImageFilter',
+ 'GrayImageResolution',
+ 'MaxSubsetPct',
+ 'MonoImageDepth',
+ 'MonoImageDownsampleThreshold',
+ 'MonoImageDownsampleType',
+ 'MonoImageFilter',
+ 'MonoImageResolution',
+ 'Optimize',
+ 'ParseDCSComments',
+ 'ParseDCSCommentsForDocInfo',
+ 'PreserveCopyPage',
+ 'PreserveEPSInfo',
+ 'PreserveHalftoneInfo',
+ 'PreserveOPIComments',
+ 'PreserveOverprintSettings',
+ 'SubsetFonts',
+ 'UseFlateCompression'
+ ]
+
+ @@methods = Hash.new
+
+ @@methods['raw'] = '1'
+ @@methods['bound'] = '2'
+ @@methods['bounded'] = '2'
+ @@methods['crop'] = '3'
+ @@methods['cropped'] = '3'
+ @@methods['down'] = '4'
+ @@methods['downsample'] = '4'
+ @@methods['downsampled'] = '4'
+ @@methods['simplify'] = '5'
+ @@methods['simplified'] = '5'
+
+ @@tempfile = 'gstemp'
+ @@pstempfile = @@tempfile + '.ps'
+ @@pdftempfile = @@tempfile + '.pdf'
+
+ @@bboxspec = '\s*([\-\d\.]+)' + '\s+([\-\d\.]+)'*3
+
+ def initialize(logger=nil)
+
+ unless logger then
+ puts('gs class needs a logger')
+ exit
+ end
+
+ @variables = Hash.new
+ @psoptions = Hash.new
+ @logger = logger
+
+ setvariable('profile', 'gsprofile.ini')
+ setvariable('pipe', true)
+ setvariable('method', 2)
+ setvariable('force', false)
+ setvariable('colormodel', 'cmyk')
+ setvariable('inputfile', '')
+ setvariable('outputfile', '')
+
+ @@pstopdfoptions.each do |key|
+ @psoptions[key] = ''
+ end
+
+ reset
+
+ end
+
+ def reset
+ @llx = @lly = @ulx = @uly = 0
+ @oldbbox = [@llx,@lly,@urx,@ury]
+ @width = @height = @xoffset = @yoffset = @offset = 0
+ @rs = Tool.default_line_separator
+ end
+
+ def supported?(filename)
+ psfile?(filename) || pdffile?(filename)
+ end
+
+ def psfile?(filename)
+ filename =~ /\.(eps|epsf|ps|ai\d*)$/io
+ end
+
+ def pdffile?(filename)
+ filename =~ /\.(pdf)$/io
+ end
+
+ def setpsoption(key,value)
+ @psoptions[key] = value unless value.empty?
+ end
+
+ def setdimensions (llx,lly,urx,ury)
+ @oldbbox = [llx,lly,urx,ury]
+ @llx, @lly = llx.to_f-@offset, lly.to_f-@offset
+ @urx, @ury = urx.to_f+@offset, ury.to_f+@offset
+ @width, @height = @urx - @llx, @ury - @lly
+ @xoffset, @yoffset = 0 - @llx, 0 - @lly
+ end
+
+ def setoffset (offset=0)
+ @offset = offset.to_f
+ setdimensions(@llx,@lly,@urx,@ury) if dimensions?
+ end
+
+ def resetdimensions
+ setdimensions(0,0,0,0)
+ end
+
+ def dimensions?
+ (@width>0) && (@height>0)
+ end
+
+ def convert
+
+ inpfile = getvariable('inputfile')
+
+ if inpfile.empty? then
+ report('no inputfile specified')
+ return false
+ end
+
+ unless FileTest.file?(inpfile) then
+ report("unknown input file #{inpfile}")
+ return false
+ end
+
+ outfile = getvariable('outputfile')
+
+ if outfile.empty? then
+ outfile = inpfile
+ outfile = outfile.sub(/^.*[\\\/]/,'')
+ end
+
+ outfile = outfile.sub(/\.(pdf|eps|ps|ai)/i, "")
+ resultfile = outfile + '.pdf'
+ setvariable('outputfile', resultfile)
+
+ # flags
+
+ saveprofile(getvariable('profile'))
+
+ begin
+ gsmethod = method(getvariable('method')).to_i
+ report("conversion method #{gsmethod}")
+ rescue
+ gsmethod = 1
+ report("fallback conversion method #{gsmethod}")
+ end
+
+ debug('piping data') if getvariable('pipe')
+
+ ok = false
+ begin
+ case gsmethod
+ when 0, 1 then ok = convertasis(inpfile,resultfile)
+ when 2 then ok = convertbounded(inpfile,resultfile)
+ when 3 then ok = convertcropped(inpfile,resultfile)
+ when 4 then ok = downsample(inpfile,resultfile,'screen')
+ when 5 then ok = downsample(inpfile,resultfile,'prepress')
+ else report("invalid conversion method #{gsmethod}")
+ end
+ rescue
+ report("job aborted due to some error: #{$!}")
+ begin
+ File.delete(resultfile) if FileTest.file?(resultfile)
+ rescue
+ report("unable to delete faulty #{resultfile}")
+ end
+ ok = false
+ ensure
+ deleteprofile(getvariable('profile'))
+ File.delete(@@pstempfile) if FileTest.file?(@@pstempfile)
+ File.delete(@@pdftempfile) if FileTest.file?(@@pdftempfile)
+ end
+ return ok
+ end
+
+ # private
+
+ def method (str)
+ if @@methods.key?(str) then
+ @@methods[str]
+ else
+ str
+ end
+ end
+
+ def pdfmethod? (str)
+ case method(str).to_i
+ when 1, 3, 4, 5 then return true
+ end
+ return false
+ end
+
+ def pdfprefix (str)
+ case method(str).to_i
+ when 1 then return 'raw-'
+ when 4 then return 'lowres-'
+ when 5 then return 'normal-'
+ end
+ return ''
+ end
+
+ def psmethod? (str)
+ ! pdfmethod?(str)
+ end
+
+ def insertprofile (flags)
+ for key in flags.keys do
+ replacevariable("flag.#{key}", flags[key])
+ end
+ end
+
+ def deleteprofile (filename)
+ begin
+ File.delete(filename) if FileTest.file?(filename)
+ rescue
+ end
+ end
+
+ def saveprofile (filename)
+ return if filename.empty? || ! (ini = open(filename,"w"))
+ @@pstopdfoptions.each do |k|
+ str = @psoptions[k]
+ # beware, booleans are translated, but so are yes/no which is dangerous
+ if str.class == String then
+ if ! str.empty? && (str != 'empty') then
+ str.sub!(/(.+)\-/io, '')
+ str = "/" + str unless str =~ /^(true|false|none|[\d\.\-\+]+)$/
+ ini.puts("-d#{k}=#{str}\n")
+ end
+ end
+ end
+ ini.close
+ debug("gs profile #{filename} saved")
+ end
+
+ def gsstream # private
+ if getvariable('pipe') then '-' else @@pstempfile end
+ end
+
+ def gscolorswitch
+ case getvariable('colormodel')
+ when 'cmyk' then '-dProcessColorModel=/DeviceCMYK -dColorConversionStrategy=/CMYK '
+ when 'rgb' then '-dProcessColorModel=/DeviceRGB -dColorConversionStrategy=/RGB '
+ when 'gray' then '-dProcessColorModel=/DeviceGRAY -dColorConversionStrategy=/GRAY '
+ else
+ ''
+ end
+ end
+
+ def gsdefaults
+ defaults = ''
+ begin
+ defaults << '-dAutoRotatePages=/None ' if @psoptions['AutoRotatePages'].empty?
+ rescue
+ defaults << '-dAutoRotatePages=/None '
+ end
+ return defaults
+ end
+
+ def convertasis (inpfile, outfile)
+
+ report("converting #{inpfile} as-is")
+
+ @rs = Tool.line_separator(inpfile)
+ debug("platform mac") if @rs == "\r"
+
+ arguments = ''
+ arguments << "\@gsprofile.ini "
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH "
+ arguments << "#{gsdefaults} "
+ arguments << "#{gscolorswitch} "
+ arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit "
+
+ debug("ghostscript: #{arguments}")
+ unless ok = System.run('ghostscript',arguments) then
+ begin
+ report("removing file #{outfile}")
+ File.delete(outfile) if FileTest.file?(outfile)
+ rescue
+ debug("file #{outfile} may be invalid")
+ end
+ end
+ return ok
+
+ end
+
+ def convertbounded(inpfile, outfile)
+ report("converting #{inpfile} bounded")
+ do_convertbounded(inpfile, outfile)
+ end
+
+ def do_convertbounded(inpfile, outfile)
+
+ begin
+ return false if FileTest.file?(outfile) && (! File.delete(outfile))
+ rescue
+ return false
+ end
+
+ arguments = ''
+ arguments << "\@gsprofile.ini "
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER "
+ arguments << "#{gscolorswitch} "
+ arguments << "#{gsdefaults} "
+ arguments << "-sOutputFile=#{outfile} #{gsstream} -c quit "
+
+ debug("ghostscript: #{arguments}")
+ debug('opening input file')
+
+ @rs = Tool.line_separator(inpfile)
+ debug("platform mac") if @rs == "\r"
+
+ if FileTest.file?(outfile) and not File.writable?(outfile) then
+ report("output file cannot be written")
+ return false
+ elsif not tmp = open(inpfile, 'rb') then
+ report("input file cannot be opened")
+ return false
+ end
+
+ debug('opening pipe/file')
+
+ if getvariable('pipe') then
+
+ return false unless eps = IO.popen(System.command('ghostscript',arguments),'wb')
+ debug('piping data')
+ unless pipebounded(tmp,eps) then
+ debug('something went wrong in the pipe')
+ File.delete(outfile) if FileTest.file?(outfile)
+ end
+ debug('closing pipe')
+ eps.close_write
+
+ else
+
+ return false unless eps = File.open(@@pstempfile, 'wb')
+
+ debug('copying data')
+
+ if pipebounded(tmp,eps) then
+ eps.close
+ debug('processing temp file')
+ begin
+ ok = System.run('ghostscript',arguments)
+ rescue
+ ok = false
+ # debug("fatal error: #{$!}")
+ ensure
+ end
+ else
+ eps.close
+ ok = false
+ end
+
+ unless ok then
+ begin
+ report('no output file due to error')
+ File.delete(outfile) if FileTest.file?(outfile)
+ rescue
+ # debug("fatal error: #{$!}")
+ debug('file',outfile,'may be invalid')
+ end
+ end
+
+ debug('deleting temp file')
+ begin
+ File.delete(@@pstempfile) if FileTest.file?(@@pstempfile)
+ rescue
+ end
+
+ end
+
+ tmp.close
+ return FileTest.file?(outfile)
+
+ end
+
+ # hm, strange, no execute here, todo ! ! !
+
+ def getdimensions (inpfile)
+
+ # -dEPSFitPage and -dEPSCrop behave weird (don't work)
+
+ arguments = "-sDEVICE=bbox -dSAFER -dNOPAUSE -dBATCH #{inpfile} "
+
+ debug("ghostscript: #{arguments}")
+
+ begin
+ bbox = System.run('ghostscript',arguments,true,true)
+ rescue
+ bbox = ''
+ end
+
+ resetdimensions
+
+ debug('bbox spec', bbox)
+
+ if bbox =~ /(Exact|HiRes)BoundingBox:#{@@bboxspec}/moi then
+ debug("high res bbox #{$2} #{$3} #{$4} #{$5}")
+ setdimensions($2,$3,$4,$5)
+ elsif bbox =~ /BoundingBox:#{@@bboxspec}/moi
+ debug("low res bbox #{$1} #{$2} #{$3} #{$4}")
+ setdimensions($1,$2,$3,$4)
+ end
+
+ return dimensions?
+
+ end
+
+ # def convertcropped (inpfile, outfile)
+ # report("converting #{inpfile} cropped")
+ # do_convertbounded(inpfile, @@pdftempfile)
+ # return unless FileTest.file?(@@pdftempfile)
+ # arguments = " --offset=#{@offset} #{@@pdftempfile} #{outfile}"
+ # report("calling #{@@pdftrimwhite}")
+ # unless ok = System.run(@@pdftrimwhite,arguments) then
+ # report('cropping failed')
+ # begin
+ # File.delete(outfile)
+ # rescue
+ # end
+ # begin
+ # File.move(@@pdftempfile,outfile)
+ # rescue
+ # File.copy(@@pdftempfile,outfile)
+ # File.delete(@@pdftempfile)
+ # end
+ # end
+ # return ok
+ # end
+
+ def convertcropped (inpfile, outfile)
+ report("converting #{inpfile} cropped")
+ if File.expand_path(inpfile) == File.expand_path(outfile) then
+ report("output filename must be different")
+ elsif inpfile =~ /\.pdf$/io then
+ System.run("pdftops -eps #{inpfile} #{@@pstempfile}")
+ if getdimensions(@@pstempfile) then
+ report("tight boundingbox found")
+ end
+ do_convertbounded(@@pstempfile, outfile)
+ File.delete(@@pstempfile) if FileTest.file?(@@pstempfile)
+ else
+ if getdimensions(inpfile) then
+ report("tight boundingbox found")
+ end
+ do_convertbounded(inpfile, outfile)
+ end
+ resetdimensions
+ return true
+ end
+
+
+ def pipebounded (eps, out)
+
+ epsbbox, skip, buffer = false, false, ''
+
+ while str = eps.gets(rs=@rs) do
+ if str =~ /^%!PS/oi then
+ debug("looks like a valid ps file")
+ break
+ elsif str =~ /%PDF\-\d+\.\d+/oi then
+ debug("looks like a pdf file, so let\'s quit")
+ return false
+ end
+ end
+
+ # why no BeginData check
+
+ eps.rewind
+
+if dimensions? then
+
+ debug('using found boundingbox')
+
+else
+
+ debug('locating boundingbox')
+ while str = eps.gets(rs=@rs) do
+ case str
+ when /^%%Page:/io then
+ break
+ when /^%%(Crop|HiResBounding|ExactBounding)Box:#{@@bboxspec}/moi then
+ debug('high res boundingbox found')
+ setdimensions($2,$3,$4,$5)
+ break
+ when /^%%BoundingBox:#{@@bboxspec}/moi then
+ debug('low res boundingbox found')
+ setdimensions($1,$2,$3,$4)
+ end
+ end
+ debug('no boundingbox found') if @width == 0
+
+end
+
+ eps.rewind
+
+ while str = eps.gets(rs=@rs) do
+ if str.sub!(/^(.*)%!PS/moi, "%!PS") then
+ debug("removing pre banner data")
+ out.puts(str)
+ break
+ end
+ end
+
+ while str = eps.gets(rs=@rs) do
+ if skip then
+ skip = false if str =~ /^%+(EndData|EndPhotoshop|BeginProlog).*$/o
+ out.puts(str) if $1 == "BeginProlog"
+ elsif str =~ /^%(BeginPhotoshop)\:\s*\d+.*$/o then
+ skip = true
+ elsif str =~ /^%%/mos then
+ if ! epsbbox && str =~ /^%%(Page:|EndProlog)/io then
+ out.puts(str) if $1 == "EndProlog"
+ debug('faking papersize')
+ # out.puts("<< /PageSize [#{@width} #{@height}] >> setpagedevice\n")
+ if ! dimensions? then
+ out.puts("<< /PageSize [1 1] >> setpagedevice\n")
+ else
+ out.puts("<< /PageSize [#{@width} #{@height}] >> setpagedevice\n")
+ end
+ out.puts("gsave #{@xoffset} #{@yoffset} translate\n")
+ epsbbox = true
+ elsif str =~ /^%%BeginBinary\:\s*\d+\s*$/o then
+ debug('copying binary data')
+ out.puts(str)
+ while str = eps.gets(rs=@rs)
+ if str =~ /^%%EndBinary\s*$/o then
+ out.puts(str)
+ else
+ out.write(str)
+ end
+ end
+ elsif str =~ /^%AI9\_PrivateDataBegin/o then
+ debug('ignore private ai crap')
+ break
+ elsif str =~ /^%%EOF/o then
+ debug('ignore post eof crap')
+ break
+ # elsif str =~ /^%%PageTrailer/o then
+ # debug('ignoring post page trailer crap')
+ # break
+ elsif str =~ /^%%Trailer/o then
+ debug('ignoring post trailer crap')
+ break
+ elsif str =~ /^%%Creator.*Illustrator.*$/io then
+ debug('getting rid of problematic creator spec')
+ str = "% Creator: Adobe Illustrator ..."
+ out.puts(str)
+ elsif str =~ /^%%AI.*(PaperRect|Margin)/io then
+ debug('removing AI paper crap')
+ elsif str =~ /^%%AI.*Version.*$/io then
+ debug('removing dangerous version info')
+ elsif str =~ /^(%+AI.*Thumbnail.*)$/o then
+ debug('skipping AI thumbnail')
+ skip = true
+ else
+ out.puts(str)
+ end
+ else
+ out.puts(str)
+ end
+ end
+
+ debug('done, sending EOF')
+
+ out.puts "grestore\n%%EOF\n"
+
+ # ok = $? == 0
+ # report('process aborted, broken pipe, fatal error') unless ok
+ # return ok
+
+resetdimensions
+
+ return true
+
+ end
+
+ def downsample (inpfile, outfile, method='screen')
+
+ # gs <= 8.50
+
+ report("downsampling #{inpfile}")
+
+ doit = true
+ unless getvariable('force') then
+ begin
+ if f = File.open(inpfile) then
+ f.binmode
+ while doit && (data = f.gets) do
+ if data =~ /\/ArtBox\s*\[\s*[\d\.]+\s+[\d\.]+\s+[\d\.]+\s+[\d\.]+\s*\]/io then
+ doit = false
+ end
+ end
+ f.close
+ end
+ rescue
+ end
+ end
+
+ if doit then
+ arguments = ''
+ arguments << "-dPDFSETTINGS=/#{method} -dEmbedAllFonts=true "
+ arguments << "#{gscolorswitch} "
+ arguments << "#{gsdefaults} "
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER "
+ arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit "
+ unless ok = System.run('ghostscript',arguments) then
+ begin
+ File.delete(outfile) if FileTest.file?(outfile)
+ report("removing file #{outfile}")
+ rescue
+ debug("file #{outfile} may be invalid")
+ end
+ end
+ return ok
+ else
+ report("crop problem, straight copying #{inpfile}")
+ File.copy(inpfile,outfile)
+ return false
+ end
+
+ end
+
+end
diff --git a/scripts/context/ruby/graphics/inkscape.rb b/scripts/context/ruby/graphics/inkscape.rb
new file mode 100644
index 000000000..8d3b26468
--- /dev/null
+++ b/scripts/context/ruby/graphics/inkscape.rb
@@ -0,0 +1,112 @@
+# module : graphics/inkscape
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+# ['graphics/gs','gs'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/variables'
+require 'base/system'
+require 'graphics/gs'
+
+class InkScape
+
+ include Variables
+
+ def initialize(logger=nil)
+
+ unless logger then
+ puts('inkscape class needs a logger')
+ exit
+ end
+
+ @variables = Hash.new
+ @logger = logger
+
+ reset
+
+ end
+
+ def reset
+ # nothing yet
+ end
+
+ def supported?(filename)
+ filename =~ /.*\.(svg|svgz)/io
+ end
+
+ def convert(logfile=System.null)
+
+ directpdf = false
+
+ logfile = logfile.gsub(/\/+$/,"")
+
+ inpfilename = getvariable('inputfile').dup
+ outfilename = getvariable('outputfile').dup
+ outfilename = inpfilename.dup if outfilename.empty?
+ outfilename.gsub!(/(\.[^\.]*?)$/, ".pdf")
+ tmpfilename = outfilename.gsub(/(\.[^\.]*?)$/, ".ps")
+
+ if inpfilename.empty? || outfilename.empty? then
+ report("no filenames given")
+ return false
+ end
+ if inpfilename == outfilename then
+ report("filenames must differ (#{inpfilename} #{outfilename})")
+ return false
+ end
+ unless FileTest.file?(inpfilename) then
+ report("unknown file #{inpfilename}")
+ return false
+ end
+
+ # we need to redirect the error info else we get a pop up console
+
+ if directpdf then
+ report("converting #{inpfilename} to #{outfilename}")
+ resultpipe = "--without-gui --export-pdf=\"#{outfilename}\" 2>#{logfile}"
+ else
+ report("converting #{inpfilename} to #{tmpfilename}")
+ resultpipe = "--without-gui --print=\">#{tmpfilename}\" 2>#{logfile}"
+ end
+
+ arguments = [resultpipe,inpfilename].join(' ').gsub(/\s+/,' ')
+
+ ok = true
+ begin
+ debug("inkscape: #{arguments}")
+ # should work
+ # ok = System.run('inkscape',arguments) # does not work here
+ # but 0.40 only works with this:
+ command = "inkscape #{arguments}"
+ report(command)
+ ok = system(command)
+ # and 0.41 fails with everything
+ # and 0.45 is better
+ rescue
+ report("aborted due to error")
+ return false
+ else
+ return false unless ok
+ end
+
+ if not directpdf then
+ ghostscript = GhostScript.new(@logger)
+ ghostscript.setvariable('inputfile',tmpfilename)
+ ghostscript.setvariable('outputfile',outfilename)
+ report("converting #{tmpfilename} to #{outfilename}")
+ ghostscript.convert
+ begin
+ File.delete(tmpfilename)
+ rescue
+ end
+ end
+ end
+
+end
diff --git a/scripts/context/ruby/graphics/magick.rb b/scripts/context/ruby/graphics/magick.rb
new file mode 100644
index 000000000..f59087bdf
--- /dev/null
+++ b/scripts/context/ruby/graphics/magick.rb
@@ -0,0 +1,161 @@
+# module : graphics/inkscape
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/variables'
+
+class ImageMagick
+
+ include Variables
+
+ def initialize(logger=nil)
+
+ unless logger then
+ puts('magick class needs a logger')
+ exit
+ end
+
+ @variables = Hash.new
+ @logger = logger
+
+ reset
+
+ end
+
+ def reset
+ ['compression','depth','colorspace','quality'].each do |key|
+ setvariable(key)
+ end
+ end
+
+ def supported?(filename) # ? pdf
+ filename =~ /.*\.(png|gif|tif|tiff|jpg|jpeg|eps|ai\d*)/io
+ end
+
+ def convert(suffix='pdf')
+
+ inpfilename = getvariable('inputfile').dup
+ outfilename = getvariable('outputfile').dup
+ outfilename = inpfilename.dup if outfilename.empty?
+ outfilename.gsub!(/(\.[^\.]*?)$/, ".#{suffix}")
+
+ if inpfilename.empty? || outfilename.empty? then
+ report("no filenames given")
+ return false
+ end
+ if inpfilename == outfilename then
+ report("filenames must differ (#{inpfilename} #{outfilename})")
+ return false
+ end
+ unless FileTest.file?(inpfilename) then
+ report("unknown file #{inpfilename}")
+ return false
+ end
+
+ if inpfilename =~ /\.tif+$/io then
+ tmpfilename = 'temp.png'
+ arguments = "#{inpfilename} #{tmpfilename}"
+ begin
+ debug("imagemagick: #{arguments}")
+ ok = System.run('imagemagick',arguments)
+ rescue
+ report("aborted due to error")
+ return false
+ else
+ return false unless ok
+ end
+ inpfilename = tmpfilename
+ end
+
+ compression = depth = colorspace = quality = ''
+
+ if getvariable('compression') =~ /(zip|jpeg)/o then
+ compression = " -compress #{$1}"
+ end
+ if getvariable('depth') =~ /(8|16)/o then
+ depth = "-depth #{$1}"
+ end
+ if getvariable('colorspace') =~ /(gray|rgb|cmyk)/o then
+ colorspace = "-colorspace #{$1}"
+ end
+ case getvariable('quality')
+ when 'low' then quality = '-quality 0'
+ when 'medium' then quality = '-quality 75'
+ when 'high' then quality = '-quality 100'
+ end
+
+ report("converting #{inpfilename} to #{outfilename}")
+
+ arguments = [compression,depth,colorspace,quality,inpfilename,outfilename].join(' ').gsub(/\s+/,' ')
+
+ begin
+ debug("imagemagick: #{arguments}")
+ ok = System.run('imagemagick',arguments)
+ rescue
+ report("aborted due to error")
+ return false
+ else
+ return ok
+ end
+
+ end
+
+ def autoconvert
+
+ inpfilename = getvariable('inputfile')
+ outfilename = getvariable('outputfile')
+
+ if inpfilename.empty? || ! FileTest.file?(inpfilename) then
+ report("missing file #{inpfilename}")
+ return
+ end
+
+ outfilename = inpfilename.dup if outfilename.empty?
+ tmpfilename = 'temp.jpg'
+
+ reset
+
+ megabyte = 1024*1024
+
+ ok = false
+
+ if FileTest.size(inpfilename)>2*megabyte
+ setvariable('compression','zip')
+ ok = convert
+ else
+ setvariable('compression','jpeg')
+ if FileTest.size(inpfilename)>10*megabyte then
+ setvariable('quality',85)
+ elsif FileTest.size(inpfilename)>5*megabyte then
+ setvariable('quality',90)
+ else
+ setvariable('quality',95)
+ end
+ report("auto quality #{getvariable('quality')}")
+ setvariable('outputfile', tmpfilename)
+ ok = convert('jpg')
+ setvariable('inputfile', tmpfilename)
+ setvariable('outputfile', outfilename)
+ ok = convert
+ begin
+ File.delete(tmpfilename)
+ rescue
+ report("#{tmpfilename} cannot be deleted")
+ end
+ end
+
+ reset
+
+ return ok
+
+ end
+
+end
diff --git a/scripts/context/ruby/imgtopdf.rb b/scripts/context/ruby/imgtopdf.rb
new file mode 100644
index 000000000..98a36c17f
--- /dev/null
+++ b/scripts/context/ruby/imgtopdf.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+# program : newimgtopdf
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+
+require 'graphics/magick'
+
+banner = ['ImgToPdf', 'version 1.1.2', '2002-2006', 'PRAGMA ADE/POD']
+
+class Commands
+
+ include CommandBase
+
+ # nowadays we would force a directive, but
+ # for old times sake we handle default usage
+
+ def main
+ filename = @commandline.argument('first')
+
+ if filename.empty? then
+ help
+ else
+ convert
+ end
+ end
+
+ # actions
+
+ def convert
+
+ magick = Magick.new(session)
+
+ ['compression','depth','colorspace','quality','inputpath','outputpath'].each do |v|
+ magick.setvariable(v,@commandline.option(v))
+ end
+
+ @commandline.arguments.each do |fullname|
+ magick.setvariable('inputfile',fullname)
+ magick.setvariable('outputfile',fullname.gsub(/(\..*?$)/io, '.pdf'))
+ if @commandline.option('auto') then
+ magick.autoconvert
+ else
+ magick.convert
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('auto')
+
+commandline.registervalue('compression')
+commandline.registervalue('depth')
+commandline.registervalue('colorspace')
+commandline.registervalue('quality')
+
+commandline.registervalue('inputpath')
+commandline.registervalue('outputpath')
+
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registeraction('convert', 'convert image into pdf')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/scripts/context/ruby/mpstools.rb b/scripts/context/ruby/mpstools.rb
new file mode 100644
index 000000000..534bfb95b
--- /dev/null
+++ b/scripts/context/ruby/mpstools.rb
@@ -0,0 +1,7 @@
+# todo
+#
+# this script will replace mptopdf and makempy
+
+puts("This program is yet unfinished, for the moment it just calls 'mptopdf'.\n\n")
+
+system("texmfstart mptopdf #{ARGV.join(' ')}")
diff --git a/scripts/context/ruby/mtxtools.rb b/scripts/context/ruby/mtxtools.rb
new file mode 100644
index 000000000..41d0f7085
--- /dev/null
+++ b/scripts/context/ruby/mtxtools.rb
@@ -0,0 +1,475 @@
+#!/usr/bin/env ruby
+
+# program : mtxtools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2004-2005
+# author : Hans Hagen
+#
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script hosts MetaTeX related features.
+
+banner = ['MtxTools', 'version 1.0.0', '2006', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+require 'base/system'
+require 'base/kpse'
+
+class Reporter
+ def report(str)
+ puts(str)
+ end
+end
+
+module ConTeXt
+
+ def ConTeXt::banner(filename,companionname,compact=false)
+ "-- filename : #{File.basename(filename)}\n" +
+ "-- comment : companion to #{File.basename(companionname)} (in ConTeXt)\n" +
+ "-- author : Hans Hagen, PRAGMA-ADE, Hasselt NL\n" +
+ "-- copyright: PRAGMA ADE / ConTeXt Development Team\n" +
+ "-- license : see context related readme files\n" +
+ if compact then "\n-- remark : compact version\n" else "" end
+ end
+
+end
+
+class UnicodeTables
+
+ @@version = "1.001"
+
+ @@shape_a = /^((GREEK|LATIN|HEBREW)\s*(SMALL|CAPITAL|)\s*LETTER\s*[A-Z]+)$/
+ @@shape_b = /^((GREEK|LATIN|HEBREW)\s*(SMALL|CAPITAL|)\s*LETTER\s*[A-Z]+)\s*(.+)$/
+
+ @@shape_a = /^(.*\s*LETTER\s*[A-Z]+)$/
+ @@shape_b = /^(.*\s*LETTER\s*[A-Z]+)\s+WITH\s+(.+)$/
+
+ attr_accessor :context, :comment
+
+ def initialize(logger=Reporter.new)
+ @data = Array.new
+ @logger = logger
+ @error = false
+ @context = true
+ @comment = true
+ @shapes = Hash.new
+ end
+
+ def load_unicode_data(filename='unicodedata.txt')
+ # beware, the unicodedata table is bugged, sometimes ending
+ @logger.report("reading base data from #{filename}") if @logger
+ begin
+ IO.readlines(filename).each do |line|
+ if line =~ /^[0-9A-F]{4,4}/ then
+ d = line.chomp.sub(/\;$/, '').split(';')
+ if d then
+ while d.size < 15 do d << '' end
+ n = d[0].hex
+ @data[n] = d
+ if d[1] =~ @@shape_a then
+ @shapes[$1] = d[0]
+ end
+ end
+ end
+ end
+ rescue
+ @error = true
+ @logger.report("error while reading base data from #{filename}") if @logger
+ end
+ end
+
+ def load_context_data(filename='contextnames.txt')
+ @logger.report("reading data from #{filename}") if @logger
+ begin
+ IO.readlines(filename).each do |line|
+ if line =~ /^[0-9A-F]{4,4}/ then
+ d = line.chomp.split(';')
+ if d then
+ n = d[0].hex
+ if @data[n] then
+ @data[d[0].hex] << d[1] # adobename == 15
+ @data[d[0].hex] << d[2] # contextname == 16
+ else
+ @logger.report("missing information about #{d} in #{filename}") if @logger
+ end
+ end
+ end
+ end
+ rescue
+ @error = true
+ @logger.report("error while reading context data from #{filename}") if @logger
+ end
+ end
+
+ def save_metatex_data(filename='char-def.lua',compact=false)
+ if not @error then
+ begin
+ File.open(filename,'w') do |f|
+ @logger.report("saving data in #{filename}") if @logger
+ f << ConTeXt::banner(filename,'char-def.tex',compact)
+ f << "\n"
+ f << "\nif not versions then versions = { } end versions['#{filename.gsub(/\..*?$/,'')}'] = #{@@version}\n"
+ f << "\n"
+ f << "if not characters then characters = { } end\n"
+ f << "if not characters.data then characters.data = { } end\n"
+ f << "\n"
+ f << "characters.data = {\n" if compact
+ @data.each do |d|
+ if d then
+ r = metatex_data(d)
+ if compact then
+ f << "\t" << "[0x#{d[0]}]".rjust(8,' ') << " = { #{r.join(", ").gsub(/\t/,'')} }, \n"
+ else
+ f << "characters.define { -- #{d[0].hex}" << "\n"
+ f << r.join(",\n") << "\n"
+ f << "}" << "\n"
+ end
+ end
+ end
+ f << "}\n" if compact
+ end
+ rescue
+ @logger.report("error while saving data in #{filename}") if @logger
+ else
+ @logger.report("#{@data.size} (#{sprintf('%X',@data.size)}) entries saved in #{filename}") if @logger
+ end
+ else
+ @logger.report("not saving data in #{filename} due to previous error") if @logger
+ end
+ end
+
+ def metatex_data(d)
+ r = Array.new
+ r << "\tunicodeslot=0x#{d[0]}"
+ if d[2] && ! d[2].empty? then
+ r << "\tcategory='#{d[2].downcase}'"
+ end
+ if @context then
+ if d[15] && ! d[15].empty? then
+ r << "\tadobename='#{d[15]}'"
+ end
+ if d[16] && ! d[16].empty? then
+ r << "\tcontextname='#{d[16]}'"
+ end
+ end
+ if @comment then
+ if d[1] == "<control>" then
+ r << "\tdescription='#{d[10]}'" unless d[10].empty?
+ else
+ r << "\tdescription='#{d[1]}'" unless d[1].empty?
+ end
+ end
+ if d[1] =~ @@shape_b then
+ r << "\tshcode=0x#{@shapes[$1]}" if @shapes[$1]
+ end
+ if d[12] && ! d[12].empty? then
+ r << "\tuccode=0x#{d[12]}"
+ elsif d[14] && ! d[14].empty? then
+ r << "\tuccode=0x#{d[14]}"
+ end
+ if d[13] && ! d[13].empty? then
+ r << "\tlccode=0x#{d[13]}"
+ end
+ if d[5] && ! d[5].empty? then
+ special, specials = '', Array.new
+ c = d[5].split(/\s+/).collect do |cc|
+ if cc =~ /^\<(.*)\>$/io then
+ special = $1.downcase
+ else
+ specials << "0x#{cc}"
+ end
+ end
+ if specials.size > 0 then
+ special = 'char' if special.empty?
+ r << "\tspecials={'#{special}',#{specials.join(',')}}"
+ end
+ end
+ return r
+ end
+
+ def save_xetex_data(filename='enco-utf.tex')
+ if not @error then
+ begin
+ minnumber, maxnumber, n = 0x001F, 0xFFFF, 0
+ File.open(filename,'w') do |f|
+ @logger.report("saving data in #{filename}") if @logger
+ f << "% filename : #{filename}\n"
+ f << "% comment : poor man's alternative for a proper enco file\n"
+ f << "% this file is generated by mtxtools and can be\n"
+ f << "% used in xetex and luatex mkii mode\n"
+ f << "% author : Hans Hagen, PRAGMA-ADE, Hasselt NL\n"
+ f << "% copyright: PRAGMA ADE / ConTeXt Development Team\n"
+ f << "% license : see context related readme files\n"
+ f << "\n"
+ f << "\\ifx\\setcclcucx\\undefined\n"
+ f << "\n"
+ f << " \\def\\setcclcucx #1 #2 #3 %\n"
+ f << " {\\global\\catcode\"#1=11 \n"
+ f << " \\global\\lccode \"#1=\"#2 \n"
+ f << " \\global\\uccode \"#1=\"#3 }\n"
+ f << "\n"
+ f << "\\fi\n"
+ f << "\n"
+ @data.each do |d|
+ if d then
+ number, type = d[0], d[2].downcase
+ if number.hex >= minnumber && number.hex <= maxnumber && type =~ /^l(l|u|t)$/o then
+ if d[13] && ! d[13].empty? then
+ lc = d[13]
+ else
+ lc = number
+ end
+ if d[12] && ! d[12].empty? then
+ uc = d[12]
+ elsif d[14] && ! d[14].empty? then
+ uc = d[14]
+ else
+ uc = number
+ end
+ if @comment then
+ f << "\\setcclcuc #{number} #{lc} #{uc} % #{d[1]}\n"
+ else
+ f << "\\setcclcuc #{number} #{lc} #{uc} \n"
+ end
+ n += 1
+ end
+ end
+ end
+ f << "\n"
+ f << "\\endinput\n"
+ end
+ rescue
+ @logger.report("error while saving data in #{filename}") if @logger
+ else
+ @logger.report("#{n} entries saved in #{filename}") if @logger
+ end
+ else
+ @logger.report("not saving data in #{filename} due to previous error") if @logger
+ end
+ end
+
+end
+
+class RegimeTables
+
+ @@version = "1.001"
+
+ def initialize(logger=Reporter.new)
+ @logger = logger
+ reset
+ end
+
+ def reset
+ @code, @regime, @filename, @loaded = Array.new(256), '', '', false
+ (32..127).each do |i|
+ @code[i] = [sprintf('%04X',i), i.chr]
+ end
+ end
+
+ def load(filename)
+ begin
+ reset
+ if filename =~ /regi\-(ini|run|uni|utf|syn)/ then
+ report("skipping #{filename}")
+ else
+ report("loading file #{filename}")
+ @regime, unicodeset = File.basename(filename).sub(/\..*?$/,''), false
+ IO.readlines(filename).each do |line|
+ case line
+ when /^\#/ then
+ # skip
+ when /^(0x[0-9A-F]+)\s+(0x[0-9A-F]+)\s+\#\s+(.*)$/ then
+ @code[$1.hex], unicodeset = [$2, $3], true
+ when /^(0x[0-9A-F]+)\s+(0x[0-9A-F]+)\s+/ then
+ @code[$1.hex], unicodeset = [$2, ''], true
+ end
+ end
+ reset if not unicodeset
+ end
+ rescue
+ report("problem in loading file #{filename}")
+ reset
+ else
+ if ! @regime.empty? then
+ @loaded = true
+ else
+ reset
+ end
+ end
+ end
+
+ def save(filename,compact=false)
+ begin
+ if @loaded && ! @regime.empty? then
+ if File.expand_path(filename) == File.expand_path(@filename) then
+ report("saving in #{filename} is blocked")
+ else
+ report("saving file #{filename}")
+ File.open(filename,'w') do |f|
+ f << ConTeXt::banner(filename,'regi-ini.tex',compact)
+ f << "\n"
+ f << "\nif not versions then versions = { } end versions['#{filename.gsub(/\..*?$/,'')}'] = #{@@version}\n"
+ f << "\n"
+ f << "if not regimes then regimes = { } end\n"
+ f << "if not regimes.data then regimes.data = { } end\n"
+ f << "\n"
+ if compact then
+ f << "regimes.data[\"#{@regime}\"] = { [0] = \n\t"
+ i = 17
+ @code.each_index do |c|
+ if (i-=1) == 0 then
+ i = 16
+ f << "\n\t"
+ end
+ if @code[c] then
+ f << @code[c][0].rjust(6,' ')
+ else
+ f << "0x0000".rjust(6,' ')
+ end
+ f << ', ' if c<@code.length-1
+ end
+ f << "\n}\n"
+ else
+ @code.each_index do |c|
+ if @code[c] then
+ f << someregimeslot(@regime,c,@code[c][0],@code[c][1])
+ else
+ f << someregimeslot(@regime,c,'','')
+ end
+ end
+ end
+ end
+ end
+ end
+ rescue
+ report("problem in saving file #{filename} #{$!}")
+ end
+ end
+
+ def report(str)
+ @logger.report(str)
+ end
+
+ private
+
+ def someregimeslot(regime,slot,unicodeslot,comment)
+ "regimes.define { #{if comment.empty? then '' else '-- ' end} #{comment}\n" +
+ "\tregime='#{regime}',\n" +
+ "\tslot='#{sprintf('0x%02X',slot)}',\n" +
+ "\tunicodeslot='#{if unicodeslot.empty? then '0x0000' else unicodeslot end}'\n" +
+ "}\n"
+ end
+
+ public
+
+ def RegimeTables::convert(filenames,compact=false)
+ filenames.each do |filename|
+ txtfile = File.expand_path(filename)
+ luafile = File.join(File.dirname(txtfile),'regi-'+File.basename(txtfile.sub(/\..*?$/, '.lua')))
+ unless txtfile == luafile then
+ regime = RegimeTables.new
+ regime.load(txtfile)
+ regime.save(luafile,compact)
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def unicodetable
+ unicode = UnicodeTables.new(logger)
+ unicode.load_unicode_data
+ unicode.load_context_data
+ unicode.save_metatex_data('char-def.lua',@commandline.option('compact'))
+ end
+
+ def xetextable
+ unicode = UnicodeTables.new(logger)
+ unicode.load_unicode_data
+ unicode.load_context_data
+ # unicode.comment = false
+ unicode.save_xetex_data
+ end
+
+ def regimetable
+ if @commandline.arguments.length > 0 then
+ RegimeTables::convert(@commandline.arguments, @commandline.option('compact'))
+ else
+ RegimeTables::convert(Dir.glob("cp*.txt") , @commandline.option('compact'))
+ RegimeTables::convert(Dir.glob("8859*.txt") , @commandline.option('compact'))
+ end
+ end
+
+ def pdftextable
+ # instead of directly saving the data, we use luatex (kind of test)
+ pdfrdef = 'pdfr-def.tex'
+ tmpfile = 'mtxtools.tmp'
+ File.delete(pdfrdef) rescue false
+ if f = File.open(tmpfile,'w') then
+ f << "\\starttext\n"
+ f << "\\ctxlua{characters.pdftex.make_pdf_to_unicodetable('#{pdfrdef}')}\n"
+ f << "\\stoptext\n"
+ f.close()
+ system("texmfstart texexec --luatex --once --purge mtxtools.tmp")
+ report("vecor saved in #{pdfrdef}")
+ end
+ File.delete(tmpfile) rescue false
+ end
+
+ def xmlmapfile
+ # instead of directly saving the data, we use luatex (kind of test)
+ tmpfile = 'mtxtools.tmp'
+ xmlsuffix = 'frx'
+ @commandline.arguments.each do |mapname|
+ if f = File.open(tmpfile,'w') then
+ xmlname = mapname.gsub(/\.map$/,".#{xmlsuffix}")
+ File.delete(xmlname) rescue false
+ f << "\\starttext\n"
+ f << "\\ctxlua{\n"
+ f << " mapname = input.find_file(texmf.instance,'#{mapname}') or ''\n"
+ f << " xmlname = '#{xmlname}'\n"
+ f << " if mapname and not mapname:is_empty() then\n"
+ f << " ctx.fonts.map.convert_file(mapname,xmlname)\n"
+ f << " end\n"
+ f << "}\n"
+ f << "\\stoptext\n"
+ f.close()
+ system("texmfstart texexec --luatex --once --purge mtxtools.tmp")
+ if FileTest.file?(xmlname) then
+ report("map file #{mapname} converted to #{xmlname}")
+ else
+ report("no valid map file #{mapname}")
+ end
+ end
+ end
+ File.delete(tmpfile) rescue false
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('unicodetable', 'create unicode table for metatex/luatex')
+commandline.registeraction('regimetable' , 'create regime table(s) for metatex/luatex [--compact]')
+commandline.registeraction('xetextable' , 'create unicode table for xetex')
+commandline.registeraction('pdftextable' , 'create unicode table for xetex')
+commandline.registeraction('xmlmapfile' , 'convert traditional mapfile to xml font resourse')
+
+# general
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+commandline.registerflag('compact')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/scripts/context/ruby/pdftools.rb b/scripts/context/ruby/pdftools.rb
new file mode 100644
index 000000000..8ad74ec4f
--- /dev/null
+++ b/scripts/context/ruby/pdftools.rb
@@ -0,0 +1,861 @@
+#!/usr/bin/env ruby
+
+# program : pdftools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2003-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on tex
+# related files.
+
+banner = ['PDFTools', 'version 1.2.1', '2003/2005', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+
+require 'fileutils'
+# require 'ftools'
+
+class File
+
+ def File.deletefiles(*filenames)
+ filenames.flatten.each do |filename|
+ begin
+ delete(filename) if FileTest.file?(filename)
+ rescue
+ end
+ end
+ end
+
+ def File.needsupdate(oldname,newname)
+ begin
+ return File.stat(oldname).mtime != File.stat(newname).mtime
+ rescue
+ return true
+ end
+ end
+
+ def File.syncmtimes(oldname,newname)
+ begin
+ t = Time.now # i'm not sure if the time is frozen, so we do it here
+ File.utime(0,t,oldname,newname)
+ rescue
+ end
+ end
+
+ def File.replacesuffix(oldname,subpath='')
+ newname = File.expand_path(oldname.sub(/\.\w+?$/,'.pdf'))
+ File.join(File.dirname(newname),subpath,File.basename(newname))
+ end
+
+end
+
+class ImageMagick
+
+ def initialize
+
+ begin
+ version = `convert -version`
+ rescue
+ @binary = nil
+ ensure
+ if (version) && (! version.empty?) && (version =~ /ImageMagick/mo) && (version =~ /version/mio) then
+ @binary = 'convert'
+ else
+ @binary = 'imagemagick'
+ end
+ end
+
+ end
+
+ def process(arguments)
+ begin
+ @binary && system("#{@binary} #{arguments}")
+ rescue
+ false
+ end
+ end
+
+end
+
+class TexExec
+
+ def initialize
+ @binary = 'texmfstart texexec.pl --pdf --batch --silent --purge'
+ end
+
+ def process(arguments,once=true)
+ begin
+ if once then
+ @binary && system("#{@binary} --once #{arguments}")
+ else
+ @binary && system("#{@binary} #{arguments}")
+ end
+ rescue
+ false
+ end
+ end
+
+end
+
+class PdfImages
+
+ def initialize
+ @binary = "pdfimages"
+ end
+
+ def process(arguments)
+ begin
+ @binary && system("#{@binary} #{arguments}")
+ rescue
+ false
+ end
+ end
+
+end
+
+class ConvertImage
+
+ def initialize(command=nil)
+ @command = command
+ end
+
+ def convertimage(filename)
+
+ return if filename =~ /\.(pdf)$/io
+
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ if filename =~ /\s/ then
+ @command.report("skipping strange filename '#{filename}'")
+ else
+ newname = File.replacesuffix(filename,subpath)
+ # newname.gsub!(s/[^a-zA-Z0-9\_-\.]/o, '-')
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ imagemagick = ImageMagick.new
+ if imagemagick then
+ ok = imagemagick.process("-compress zip -quality 99 #{filename} #{newname}")
+ File.syncmtimes(oldname,newname) if retain
+ end
+ end
+ end
+ end
+
+end
+
+class DownsampleImage
+
+ def initialize(command=nil)
+ @command = command
+ end
+
+ def convertimage(filename)
+
+ return if filename =~ /\.(pdf)$/io
+
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ if @command.option('lowres') then
+ method = '4'
+ elsif @command.option('medres') || @command.option('normal') then
+ method = '5'
+ else
+ method = '4'
+ end
+
+ if filename =~ /\s/ then
+ @command.report("skipping strange filename '#{filename}'")
+ else
+ newname = File.replacesuffix(filename,subpath)
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ ok = system("texmfstart pstopdf.rb --method=#{method} #{filename} #{newname}")
+ File.syncmtimes(oldname,newname) if retain
+ end
+ end
+ end
+
+end
+
+class ColorImage
+
+ def initialize(command=nil,tmpname='pdftools')
+ @command = command
+ @tmpname = tmpname
+ @colorname = nil
+ @colorspec = nil
+ @colorspace = nil
+ end
+
+ def registercolor(spec='.5',name='c')
+ name = name || 'c'
+ spec = spec.split(',')
+ case spec.length
+ when 4
+ @colorname, @colorspec, @colorspace = name, spec.join('/'), 'cmyk'
+ when 3
+ @colorname, @colorspec, @colorspace = name, spec.join('/'), 'rgb'
+ when 1
+ @colorname, @colorspec, @colorspace = name, spec.join('/'), 'gray'
+ else
+ @colorname, @colorspec, @colorspace = nil, nil, nil
+ end
+ end
+
+ def convertimage(filename)
+
+ invert = @command.option('invert')
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ subpath += '/' unless subpath.empty?
+
+ if @colorname && ! @colorname.empty? && @colorspec && ! @colorspec.empty? then
+ basename = filename.sub(/\.\w+?$/,'')
+ oldname = filename
+ ppmname = @tmpname + '-000.ppm'
+ jpgname = @tmpname + '-000.jpg'
+ newname = File.expand_path(oldname)
+ newname = File.dirname(newname) + '/' + subpath + @colorname + '-' + File.basename(newname)
+ newname.sub!(/\.\w+?$/, '.pdf')
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ pdfimages = PdfImages.new
+ imagemagick = ImageMagick.new
+ if pdfimages && imagemagick then
+ File.deletefiles(ppmname,jpgname,newname)
+ if filename =~ /\.(pdf)$/io then
+ ok = pdfimages.process("-j -f 1 -l 1 #{filename} #{@tmpname}")
+ if ok then
+ if FileTest.file?(ppmname) then
+ inpname = ppmname
+ elsif FileTest.file?(jpgname) then
+ inpname = jpgname
+ else
+ ok = false
+ end
+ if ok then
+ switch = if ! invert then '-negate' else '' end
+ # make sure that we keep the format
+ tmpname = File.basename(inpname)
+ tmpname = tmpname.sub(/(.*)\..*?$/,@tmpname) # somehow sub! fails here
+ ok = imagemagick.process("-colorspace gray #{switch} #{inpname} #{tmpname}")
+ if ! ok || ! FileTest.file?(tmpname) then
+ # problems
+ else
+ ok = imagemagick.process("-colorspace #{switch} #{@colorspace} -colorize #{@colorspec} -compress zip #{tmpname} #{newname}")
+ if ! ok || ! FileTest.file?(newname) then
+ # unable to colorize image
+ else
+ # conversion done
+ end
+ end
+ end
+ end
+ else
+ # make sure that we keep the format
+ tmpname = File.basename(basename)
+ tmpname = tmpname.sub(/(.*)\..*?$/,@tmpname) # somehow sub! fails here
+ ok = imagemagick.process("-colorspace gray #{oldname} #{tmpname}")
+ if ! ok || ! FileTest.file?(tmpname) then
+ # unable to convert color to gray
+ else
+ ok = imagemagick.process("-colorspace #{@colorspace} -colorize #{@colorspec} -compress zip #{tmpname} #{newname}")
+ if ! ok || ! FileTest.file?(newname) then
+ # unable to colorize image
+ else
+ # conversion done
+ end
+ end
+ end
+ File.deletefiles(ppmname,jpgname,tmpname)
+ File.syncmtimes(filename,newname) if retain
+ end
+ end
+ end
+ end
+
+end
+
+class SpotColorImage
+
+ def initialize(command=nil, tmpname='pdftools')
+ @command = command
+ @tmpname = tmpname
+ @colorname = nil
+ @colorspec = nil
+ @colorspace = nil
+ @colorfile = nil
+ end
+
+ def registercolor(spec='.5',name='unknown')
+ name = name || 'unknown'
+ if spec =~ /^[\d\.\,]+$/ then
+ spec = spec.split(',')
+ case spec.length
+ when 4
+ @colorname, @colorspec, @colorspace = name, ["c=#{spec[0]}","m=#{spec[1]}","y=#{spec[2]}","k=#{spec[3]}"].join(','), 'cmyk'
+ when 3
+ @colorname, @colorspec, @colorspace = name, ["r=#{spec[0]}","g=#{spec[1]}","b=#{spec[2]}"].join(','), 'rgb'
+ when 1
+ @colorname, @colorspec, @colorspace = name, ["s=#{spec[0]}"].join(','), 'gray'
+ else
+ @colorname, @colorspec, @colorspace = nil, nil, nil
+ end
+ else
+ @colorname, @colorfile = name, spec
+ end
+ end
+
+ def convertgrayimage(filename)
+
+ invert = @command.option('invert')
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ subpath += '/' unless subpath.empty?
+
+ if @colorname && ! @colorname.empty? && ((@colorspec && ! @colorspec.empty?) || (@colorfile && ! @colorfile.empty?)) then
+ basename = filename.sub(/\.\w+?$/,'')
+ oldname = filename # png jpg pdf
+ newname = File.expand_path(oldname)
+ ppmname = @tmpname + '-000.ppm'
+ jpgname = @tmpname + '-000.jpg'
+ outname = @tmpname + '-000.pdf'
+ texname = @tmpname + '-temp.tex'
+ pdfname = @tmpname + '-temp.pdf'
+ newname = File.dirname(newname) + '/' + subpath + @colorname + '-' + File.basename(newname)
+ newname.sub!(/\.\w+?$/, '.pdf')
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ pdfimages = PdfImages.new
+ imagemagick = ImageMagick.new
+ texexec = TexExec.new
+ if pdfimages && imagemagick && texexec then
+ if filename =~ /\.(jpg|png|pdf)$/io then
+ @command.report("processing #{basename}")
+ File.deletefiles(ppmname,jpgname,newname)
+ switch = if ! invert then '-negate' else '' end
+ if filename =~ /\.(pdf)$/io then
+ ok = pdfimages.process("-j -f 1 -l 1 #{oldname} #{@tmpname}")
+ if ok then
+ if FileTest.file?(ppmname) then
+ inpname = ppmname
+ elsif FileTest.file?(jpgname) then
+ inpname = jpgname
+ else
+ ok = false
+ end
+ if ok then
+ ok = imagemagick.process("-colorspace gray #{switch} -compress zip #{inpname} #{outname}")
+ end
+ end
+ else
+ ok = imagemagick.process("-colorspace gray #{switch} -compress zip #{oldname} #{outname}")
+ end
+ if ok then
+ ok = false unless FileTest.file?(outname)
+ end
+ if ok then
+ if f = File.open(texname, 'w') then
+ f.puts(conversionfile(filename,outname,newname))
+ f.close
+ ok = texexec.process(texname)
+ else
+ ok = false
+ end
+ @command.report("error in processing #{newname}") unless ok
+ if FileTest.file?(pdfname) then
+ if f = File.open(pdfname,'r') then
+ f.binmode
+ begin
+ if g = File.open(newname,'w') then
+ g.binmode
+ data = f.read
+ # pdftex (direct) & imagemagick (indirect)
+ if data =~ /(\d+)\s+0\s+obj\s+\[\/Separation\s+\/#{@colorname}/mos then
+ @command.report("replacing separation color")
+ object = $1
+ data.gsub!(/(\/Type\s+\/XObject.*?)(\/ColorSpace\s*(\/DeviceGray|\/DeviceCMYK|\/DeviceRGB|\d+\s+\d+\s+R))/moi) do
+ $1 + "/ColorSpace #{object} 0 R".ljust($2.length)
+ end
+ elsif data =~ /(\d+)\s+0\s+obj\s+\[\/Indexed\s*\[/mos then
+ @command.report("replacing indexed color")
+ # todo: more precise check on color
+ object = $1
+ data.gsub!(/(\/Type\s+\/XObject.*?)(\/ColorSpace\s*(\/DeviceGray|\/DeviceCMYK|\/DeviceRGB|\d+\s+\d+\s+R))/moi) do
+ $1 + "/ColorSpace #{object} 0 R".ljust($2.length)
+ end
+ elsif data =~ /(\d+)\s+0\s+obj\s+\[\/Separation/mos then
+ @command.report("replacing separation color")
+ object = $1
+ data.gsub!(/(\/Type\s+\/XObject.*?)(\/ColorSpace\s*(\/DeviceGray|\/DeviceCMYK|\/DeviceRGB|\d+\s+\d+\s+R))/moi) do
+ $1 + "/ColorSpace #{object} 0 R".ljust($2.length)
+ end
+ end
+ g.write(data)
+ g.close
+ end
+ rescue
+ @command.report("error in converting #{newname}")
+ else
+ @command.report("#{newname} is converted")
+ end
+ f.close
+ end
+ else
+ @command.report("error in writing #{newname}")
+ end
+ else
+ @command.report("error in producing #{newname}")
+ end
+ File.deletefiles(ppmname,jpgname,outname)
+ # File.deletefiles(texname,pdfname)
+ File.syncmtimes(filename,newname) if retain
+ end
+ else
+ @command.report("error in locating binaries")
+ end
+ else
+ @command.report("#{newname} is not changed")
+ end
+ end
+ end
+
+ private
+
+ # % example colorfile:
+ #
+ # \definecolor [darkblue] [c=1,m=.38,y=0,k=.64] % pantone pms 2965 uncoated m
+ # \definecolor [darkyellow] [c=0,m=.28,y=1,k=.06] % pantone pms 124 uncoated m
+ #
+ # % \definecolor [darkblue-100] [darkblue] [p=1]
+ # % \definecolor [darkyellow-100] [darkyellow] [p=1]
+ #
+ # \definecolorcombination [pdftoolscolor] [darkblue=.12,darkyellow=.28] [c=.1,m=.1,y=.3,k=.1]
+
+ def conversionfile(originalname,filename,finalname)
+ tex = "\\setupcolors[state=start]\n"
+ if @colorfile then
+ tex += "\\readfile{#{@colorfile}}{}{}\n"
+ tex += "\\starttext\n"
+ # tex += "\\predefineindexcolor[pdftoolscolor]\n"
+ tex += "\\startTEXpage\n"
+ tex += "\\pdfimage{#{filename}}\n"
+ tex += "\\stopTEXpage\n"
+ tex += "\\stoptext\n"
+ else
+ tex += "\\definecolor[#{@colorname}][#{@colorspec}]\n"
+ tex += "\\definecolor[pdftoolscolor][#{@colorname}][p=1]\n"
+ tex += "\\starttext\n"
+ tex += "\\startTEXpage\n"
+ tex += "\\hbox{\\color[pdftoolscolor]{\\pdfimage{#{filename}}}}\n"
+ tex += "\\stopTEXpage\n"
+ tex += "\\stoptext\n"
+ end
+ tex += "\n"
+ tex += "% old: #{originalname}\n"
+ tex += "% new: #{finalname}\n"
+ return tex
+ end
+
+end
+
+module XML
+
+ def XML::version
+ "<?xml version='1.0'?>"
+ end
+
+ def XML::start(element, attributes='')
+ if attributes.empty? then
+ "<#{element}>"
+ else
+ "<#{element} #{attributes}>"
+ end
+ end
+
+ def XML::end(element)
+ "</#{element}>"
+ end
+
+ def XML::empty(element, attributes='')
+ if attributes && attributes.empty? then
+ "<#{element}/>"
+ else
+ "<#{element} #{attributes}/>"
+ end
+ end
+
+ def XML::element(element, attributes='', content='')
+ if content && ! content.empty? then
+ XML::start(element,attributes) + content + XML::end(element)
+ else
+ XML::empty(element,attributes)
+ end
+ end
+
+ def XML::box(tag, rect, type=1)
+ case type
+ when 1
+ if rect && ! rect.empty? then
+ rect = rect.split(' ')
+ XML::element("#{tag}box", '',
+ XML::element("llx", '', rect[0]) +
+ XML::element("lly", '', rect[1]) +
+ XML::element("ulx", '', rect[2]) +
+ XML::element("uly", '', rect[3]) )
+ else
+ XML::empty("#{tag}box")
+ end
+ when 2
+ if rect && ! rect.empty? then
+ rect = rect.split(' ')
+ XML::element("box", "type='#{tag}'",
+ XML::element("llx", '', rect[0]) +
+ XML::element("lly", '', rect[1]) +
+ XML::element("ulx", '', rect[2]) +
+ XML::element("uly", '', rect[3]) )
+ else
+ XML::empty("box", "type='#{tag}'")
+ end
+ when 3
+ if rect && ! rect.empty? then
+ rect = rect.split(' ')
+ XML::element("box", "type='#{tag}' llx='#{rect[0]}' lly='#{rect[1]}' ulx='#{rect[2]}' uly='#{rect[3]}'")
+ else
+ XML::empty("box", "type='#{tag}'")
+ end
+ else
+ ''
+ end
+ end
+
+ def XML::crlf
+ "\n"
+ end
+
+ def XML::skip(n=1)
+ ' '*n
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ # alias savedhelp :help
+
+ # def help
+ # savedhelp
+ # report("under construction (still separate tools)")
+ # end
+
+ # filename.pdf --spotimage --colorname=darkblue --colorspec=1,0.38,0,0.64
+
+ def spotimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ colorname = @commandline.option('colorname')
+ colorspec = @commandline.option('colorspec')
+ if colorname && ! colorname.empty? && colorspec && ! colorspec.empty? then
+ files.each do |filename|
+ s = SpotColorImage.new(self)
+ s.registercolor(colorspec,colorname)
+ s.convertgrayimage(filename)
+ end
+ else
+ report("provide --colorname=somename --colorspec=c,m,y,k")
+ end
+ else
+ report("provide filename (png, jpg, pdf)")
+ end
+
+ end
+
+ def colorimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ colorname = @commandline.option('colorname')
+ colorspec = @commandline.option('colorspec')
+ if colorspec && ! colorspec.empty? then
+ files.each do |filename|
+ s = ColorImage.new(self)
+ s.registercolor(colorspec,colorname) # name optional
+ s.convertimage(filename)
+ end
+ else
+ report("provide --colorspec=c,m,y,k")
+ end
+ else
+ report("provide filename")
+ end
+
+ end
+
+ def convertimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ files.each do |filename|
+ s = ConvertImage.new(self)
+ s.convertimage(filename)
+ end
+ else
+ report("provide filename")
+ end
+
+ end
+
+ def downsampleimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ files.each do |filename|
+ s = DownsampleImage.new(self)
+ s.convertimage(filename)
+ end
+ else
+ report("provide filename")
+ end
+
+ end
+
+ def info
+
+ if files = findfiles() then
+
+ print(XML.version + XML.crlf)
+ print(XML.start('pdfinfo', "xmlns='http://www.pragma-ade.com/schemas/pdfinfo.rng'") + XML.crlf)
+
+ files.each do |filename|
+
+ if filename =~ /\.pdf$/io then
+
+ begin
+ data = `pdfinfo -box #{filename}`.chomp.split("\n")
+ rescue
+ data = nil
+ end
+
+ if data then
+
+ pairs = Hash.new
+
+ data.each do |d|
+ if (d =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/moi) then
+ key, val = $1, $2
+ pairs[key.downcase.sub(/ /,'')] = val
+ end
+ end
+
+ print(XML.skip(1) + XML.start('pdffile', "filename='#{filename}'") + XML.crlf)
+
+ print(XML.skip(2) + XML.element('path', '', File.expand_path(filename)) + XML.crlf)
+
+ if pairs.key?('error') then
+
+ print(XML.skip(2) + XML.element('comment', '', pairs['error']) + XML.crlf)
+
+ else
+
+ print(XML.skip(2) + XML.element('version', '', pairs['pdfversion']) + XML.crlf)
+ print(XML.skip(2) + XML.element('pages', '', pairs['pages' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('title', '', pairs['title' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('subject', '', pairs['subject' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('author', '', pairs['author' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('producer', '', pairs['producer' ]) + XML.crlf)
+
+ if pairs.key?('creationdate') then
+ pairs['creationdate'].sub!(/(\d\d)\/(\d\d)\/(\d\d)/) do
+ '20' + $3 + '-' + $1 + '-' +$2
+ end
+ pairs['creationdate'].sub!(/(\d\d)\/(\d\d)\/(\d\d\d\d)/) do
+ $3 + '-' + $1 + '-' + $2
+ end
+ print(XML.skip(2) + XML.element('creationdate', '', pairs['creationdate']) + XML.crlf)
+ end
+
+ if pairs.key?('moddate') then
+ if pairs['moddate'] =~ /(\d\d\d\d)(\d\d)(\d\d)/ then
+ pairs['moddate'] = "#{$1}-#{$2}-#{$3}"
+ end
+ print(XML.skip(2) + XML.element('modificationdate', '', pairs['moddate']) + XML.crlf)
+ end
+
+ print(XML.skip(2) + XML.element('tagged', '', pairs['tagged' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('encrypted', '', pairs['encrypted']) + XML.crlf)
+ print(XML.skip(2) + XML.element('optimized', '', pairs['optimized']) + XML.crlf)
+
+ if pairs.key?('PageSize') then
+ print(XML.skip(2) + XML.element('width', '', pairs['pagesize'].sub(/\s*(.*?)\s+(.*?)\s+.*/, $1)) + XML.crlf)
+ print(XML.skip(2) + XML.element('height', '', pairs['pagesize'].sub(/\s*(.*?)\s+(.*?)\s+.*/, $2)) + XML.crlf)
+ end
+
+ if pairs.key?('FileSize') then
+ print(XML.skip(2) + XML.element('size', '', pairs['filesize'].sub(/\s*(.*?)\s+.*/, $1)) + XML.crlf)
+ end
+
+ print(XML.skip(2) + XML.box('media', pairs['mediabox']) + XML.crlf)
+ print(XML.skip(2) + XML.box('crop' , pairs['cropbox' ]) + XML.crlf)
+ print(XML.skip(2) + XML.box('bleed', pairs['bleedbox']) + XML.crlf)
+ print(XML.skip(2) + XML.box('trim' , pairs['trimBox' ]) + XML.crlf)
+ print(XML.skip(2) + XML.box('art' , pairs['artbox' ]) + XML.crlf)
+
+ end
+
+ print(XML.skip(1) + XML.end('pdffile') + XML.crlf)
+
+ end
+
+ end
+
+ end
+
+ print(XML.end('pdfinfo') + XML.crlf)
+
+ end
+
+ end
+
+ # name type emb sub uni object ID
+ # ------------------------------------ ------------ --- --- --- ---------
+ # EOPLBP+TimesNewRomanPSMT TrueType yes yes no 167 0
+ # Times-Roman TrueType no no no 95 0
+ # EPBAAB+Helvetica Type 1C yes yes yes 108 0
+ # EPBMLE+Helvetica-Oblique Type 1C yes yes yes 111 0
+ # Helvetica TrueType no no no 112 0
+
+ def checkembedded
+ $stderr = $stdout
+ $stdout.flush
+ if @commandline.option('pattern') then
+ # **/*.pdf
+ filenames, n = globfiles(@commandline.option('pattern'),'pdf'), 0
+ else
+ filenames, n = findfiles('pdf'), 0
+ end
+ filenames.sort.each do |file|
+ report("= checking #{File.expand_path(file)}")
+ result = `pdffonts #{file}`.chomp
+ lines = result.split(/\n/)
+ if result =~ /emb\s+sub\s+uni/io then
+ lines.each do |line|
+ report("! #{line}") if line =~ /no\s+(no|yes)\s+(no|yes)/io
+ end
+ else
+ lines.each do |line|
+ report("? #{line}")
+ end
+ end
+ report("")
+ end
+ end
+
+ def countpages
+ if @commandline.option('pattern') then
+ filenames, n = globfiles(@commandline.option('pattern'),'pdf'), 0
+ else
+ filenames, n = findfiles('pdf'), 0
+ end
+ threshold = @commandline.option('threshold').to_i rescue 0
+ filenames.each do |filename|
+ if `pdfinfo #{filename}`.chomp =~ /^pages\s*\:\s*(\d+)/moi then
+ p = $1
+ m = p.to_i rescue 0
+ if threshold == 0 or m > threshold then
+ report("#{p.rjust(4)} pages found in #{filename}")
+ n += m
+ end
+ end
+ end
+ report("")
+ report("#{n.to_s.rjust(4)} pages in total")
+ end
+
+ def analyzefile
+ # needs an update
+ filenames = @commandline.arguments
+ filenames.each do |filename|
+ if filename && FileTest.file?(filename) && filename =~ /\.pdf/io then
+ filesize = FileTest.size(filename)
+ report("analyzing file : #{filename}")
+ report("file size : #{filesize}")
+ if pdf = File.open(filename) then
+ pdf.binmode
+ nofobject, nofxform, nofannot, noflink, nofwidget, nofnamed, nofscript, nofcross = 0, 0, 0, 0, 0, 0, 0, 0
+ while data = pdf.gets do
+ data.scan(/\d+\s+\d+\s+obj/o) do nofobject += 1 end
+ data.scan(/\/Type\s*\/XObject/o) do nofxform += 1 end
+ data.scan(/\/Type\s*\/Annot/o) do nofannot += 1 end
+ data.scan(/\/GoToR\s*\/F/o) do nofcross += 1 end
+ data.scan(/\/Subtype\s*\/Link/o) do noflink += 1 end
+ data.scan(/\/Subtype\s*\/Widget/o) do nofwidget += 1 end
+ data.scan(/\/S\s*\/Named/o) do nofnamed += 1 end
+ data.scan(/\/S\s*\/JavaScript/o) do nofscript += 1 end
+ end
+ pdf.close
+ report("objects : #{nofobject}")
+ report("xforms : #{nofxform}")
+ report("annotations : #{nofannot}")
+ report("links : #{noflink} (#{nofnamed} named / #{nofscript} scripts / #{nofcross} files)")
+ report("widgets : #{nofwidget}")
+ end
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('spotimage' , 'filename --colorspec= --colorname= [--retain --invert --subpath=]')
+commandline.registeraction('colorimage', 'filename --colorspec= [--retain --invert --colorname= ]')
+commandline.registeraction('convertimage', 'filename [--retain --subpath]')
+commandline.registeraction('downsampleimage', 'filename [--retain --subpath --lowres --normal]')
+commandline.registeraction('info', 'filename')
+commandline.registeraction('countpages', '[--pattern --threshold]')
+commandline.registeraction('checkembedded', '[--pattern]')
+
+commandline.registeraction('analyzefile' , 'filename')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registervalue('colorname')
+commandline.registervalue('colorspec')
+commandline.registervalue('subpath')
+commandline.registervalue('pattern')
+commandline.registervalue('threshold',0)
+
+commandline.registerflag('lowres')
+commandline.registerflag('medres')
+commandline.registerflag('normal')
+commandline.registerflag('invert')
+commandline.registerflag('retain')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/scripts/context/ruby/pstopdf.rb b/scripts/context/ruby/pstopdf.rb
new file mode 100644
index 000000000..73e628df2
--- /dev/null
+++ b/scripts/context/ruby/pstopdf.rb
@@ -0,0 +1,533 @@
+#!/usr/bin/env ruby
+
+# program : pstopdf
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+banner = ['PsToPdf', 'version 2.0.1', '2002-2006', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+# todo: paden/prefix in magick and inkscape
+# todo: clean up method handling (pass strings, no numbers)
+# --method=crop|bounded|raw|...
+# --resolution=low|normal|medium|high|printer|print|screen|ebook|default
+# + downward compatible flag handling
+
+require 'base/switch'
+require 'base/tool'
+require 'base/logger'
+
+require 'graphics/gs'
+require 'graphics/magick'
+require 'graphics/inkscape'
+
+require 'rexml/document'
+
+exit if defined?(REQUIRE2LIB)
+
+class Commands
+
+ include CommandBase
+
+ # nowadays we would force a directive, but
+ # for old times sake we handle default usage
+
+ def main
+ filename = @commandline.argument('first')
+ pattern = @commandline.option('pattern')
+ if filename.empty? && ! pattern.empty? then
+ pattern = "**/#{pattern}" if @commandline.option('recurse')
+ globfiles(pattern)
+ end
+ filename = @commandline.argument('first')
+ if filename.empty? then
+ help
+ elsif filename =~ /\.exa$/ then
+ request
+ else
+ convert
+ end
+ end
+
+ # actions
+
+ def convert
+
+ ghostscript = GhostScript.new(logger)
+ magick = ImageMagick.new(logger)
+ inkscape = InkScape.new(logger)
+
+ outpath = @commandline.option('outputpath')
+ unless outpath.empty? then
+ begin
+ File.expand_path(outpath)
+ outpath = File.makedirs(outpath) unless FileTest.directory?(outpath)
+ rescue
+ # sorry
+ end
+ end
+
+ @commandline.arguments.each do |filename|
+
+ filename = Tool.cleanfilename(filename,@commandline) # brrrr
+ inppath = @commandline.option('inputpath')
+ if inppath.empty? then
+ inppath = '.'
+ fullname = filename # avoid duplicate './'
+ else
+ fullname = File.join(inppath,filename)
+ end
+ if FileTest.file?(fullname) then
+ handle_whatever(ghostscript,inkscape,magick,filename)
+ else
+ report("file #{fullname} does not exist")
+ end
+
+ end
+
+ end
+
+ def request
+
+ # <exa:request>
+ # <exa:application>
+ # <exa:command>pstopdf</exa:command>
+ # <exa:filename>E:/tmp/demo.ps</exa:filename>
+ # </exa:application>
+ # <exa:data>
+ # <exa:variable label='gs:DoThumbnails'>false</exa:variable>
+ # <exa:variable label='gs:ColorImageDepth'>-1</exa:variable>
+ # </exa:data>
+ # </exa:request>
+
+ ghostscript = GhostScript.new(logger)
+ magick = ImageMagick.new(logger)
+ inkscape = InkScape.new(logger)
+
+ dataname = @commandline.argument('first') || ''
+ filename = @commandline.argument('second') || ''
+
+ if dataname.empty? || ! FileTest.file?(dataname) then
+ report('provide valid exa file')
+ return
+ else
+ begin
+ request = REXML::Document.new(File.new(dataname))
+ rescue
+ report('provide valid exa file (xml error)')
+ return
+ end
+ end
+ if filename.empty? then
+ begin
+ if filename = REXML::XPath.first(request.root,"exa:request/exa:application/exa:filename/text()") then
+ filename = filename.to_s
+ else
+ report('no filename found in exa file')
+ return
+ end
+ rescue
+ filename = ''
+ end
+ end
+ if filename.empty? then
+ report('provide valid filename')
+ return
+ elsif ! FileTest.file?(filename) then
+ report("invalid filename #{filename}")
+ return
+ end
+
+ [ghostscript,inkscape,magick].each do |i|
+ i.setvariable('inputfile',filename)
+ end
+
+ # set ghostscript variables
+ REXML::XPath.each(request.root,"/exa:request/exa:data/exa:variable") do |v|
+ begin
+ if (key = v.attributes['label']) and (value = v.text.to_s) then
+ case key
+ when /gs[\:\.](var[\:\.])*(offset)/io then ghostscript.setoffset(value)
+ when /gs[\:\.](var[\:\.])*(method)/io then ghostscript.setvariable('method',value)
+ when /gs[\:\.](var[\:\.])*(.*)/io then ghostscript.setpsoption($2,value)
+ end
+ end
+ rescue
+ end
+ end
+
+ # no inkscape and magick variables (yet)
+
+ handle_whatever(ghostscript,inkscape,magick,filename)
+
+ end
+
+ def watch
+
+ ghostscript = GhostScript.new(logger)
+ magick = ImageMagick.new(logger)
+ inkscape = InkScape.new(logger)
+
+ pathname = commandline.option('watch')
+
+ unless pathname and not pathname.empty? then
+ report('empty watchpath is not supported')
+ exit
+ end
+
+ if pathname == '.' then
+ report("watchpath #{pathname} is not supported")
+ exit
+ end
+
+ if FileTest.directory?(pathname) then
+ if Dir.chdir(pathname) then
+ report("watching path #{pathname}")
+ else
+ report("unable to change to path #{pathname}")
+ exit
+ end
+ else
+ report("invalid path #{pathname}")
+ exit
+ end
+
+ waiting = false
+
+ loop do
+
+ if waiting then
+ report("waiting #{getvariable('delay')}")
+ waiting = false
+ sleep(getvariable('delay').to_i)
+ end
+
+ files = Dir.glob("**/*.*")
+
+ if files and files.length > 0 then
+
+ files.each do |fullname|
+
+ next unless fullname
+
+ if FileTest.directory?(fullname) then
+ debug('skipping path', fullname)
+ next
+ end
+
+ unless magick.supported(fullname) then
+ debug('not supported', fullname)
+ next
+ end
+
+ if (! FileTest.file?(fullname)) || (FileTest.size(fullname) < 100) then
+ debug("skipping small crap file #{fullname}")
+ next
+ end
+
+ debug("handling file #{fullname}")
+
+ begin
+ next unless File.rename(fullname,fullname) # access trick
+ rescue
+ next # being written
+ end
+
+ fullname = Tool.cleanfilename(fullname,@commandline)
+
+ fullname.gsub!(/\\/io, '/')
+
+ filename = File.basename(fullname)
+ filepath = File.dirname(fullname)
+
+ next if filename =~ /gstemp.*/io
+
+ if filepath !~ /(result|done|raw|crop|bound|bitmap)/io then
+ begin
+ File.makedirs(filepath+'/raw')
+ File.makedirs(filepath+'/bound')
+ File.makedirs(filepath+'/crop')
+ File.makedirs(filepath+'/bitmap')
+ debug("creating prefered input paths on #{filepath}")
+ rescue
+ debug("creating input paths on #{filepath} failed")
+ end
+ end
+
+ if filepath =~ /^(.*\/|)(done|result)$/io then
+ debug("skipping file #{fullname}")
+ else
+ report("start processing file #{fullname}")
+ if filepath =~ /^(.*\/*)(raw|crop|bound)$/io then
+ donepath = $1 + 'done'
+ resultpath = $1 + 'result'
+ case $2
+ when 'raw' then method = 1
+ when 'bound' then method = 2
+ when 'crop' then method = 3
+ else method = 2
+ end
+ report("forcing method #{method}")
+ else
+ method = 2
+ donepath = filepath + '/done'
+ resultpath = filepath + '/result'
+ report("default method #{method}")
+ end
+
+ begin
+ File.makedirs(donepath)
+ File.makedirs(resultpath)
+ rescue
+ report('result path creation fails')
+ end
+
+ if FileTest.directory?(donepath) && FileTest.directory?(resultpath) then
+
+ resultname = resultpath + '/' + filename.sub(/\.[^\.]*$/,'') + '.pdf'
+
+ @commandline.setoption('inputpath', filepath)
+ @commandline.setoption('outputpath', resultpath)
+ @commandline.setoption('method', method)
+
+ if ghostscript.psfile?(fullname) then
+ handle_ghostscript(ghostscript,filename)
+ else
+ handle_magick(magick,filename)
+ end
+
+ sleep(1) # calm down
+
+ if FileTest.file?(fullname) then
+ begin
+ File.copy(fullname,donepath + '/' + filename)
+ File.delete(fullname)
+ rescue
+ report('cleanup fails')
+ end
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+ waiting = true
+ end
+
+ end
+
+ private
+
+ def handle_whatever(ghostscript,inkscape,magick,filename)
+ if ghostscript.psfile?(filename) then
+ # report("processing ps file #{filename}")
+ ghostscript.setvariable('pipe',false) if @commandline.option('nopipe')
+ # ghostscript.setvariable('pipe',not @commandline.option('nopipe'))
+ ghostscript.setvariable('colormodel',@commandline.option('colormodel'))
+ ghostscript.setvariable('offset',@commandline.option('offset'))
+ handle_ghostscript(ghostscript,filename)
+ elsif ghostscript.pdffile?(filename) && ghostscript.pdfmethod?(@commandline.option('method')) then
+ # report("processing pdf file #{filename}")
+ handle_ghostscript(ghostscript,filename)
+ elsif inkscape.supported?(filename) then
+ # report("processing non ps/pdf file #{filename}")
+ handle_inkscape(inkscape,filename)
+ elsif magick.supported?(filename) then
+ # report("processing non ps/pdf file #{filename}")
+ handle_magick(magick,filename)
+ else
+ report("option not supported for #{filename}")
+ end
+ end
+
+ def handle_magick(magick,filename)
+
+ report("converting non-ps file #{filename} into pdf")
+
+ inppath = @commandline.option('inputpath')
+ outpath = @commandline.option('outputpath')
+
+ inppath = inppath + '/' if not inppath.empty?
+ outpath = outpath + '/' if not outpath.empty?
+
+ prefix = @commandline.option('prefix')
+ suffix = @commandline.option('suffix')
+
+ inpfilename = "#{inppath}#{filename}"
+ outfilename = "#{outpath}#{prefix}#{filename.sub(/\.([^\.]*?)$/, '')}#{suffix}.pdf"
+
+ magick.setvariable('inputfile' , inpfilename)
+ magick.setvariable('outputfile', outfilename)
+
+ magick.autoconvert
+
+ end
+
+ def handle_inkscape(inkscape,filename)
+
+ report("converting svg(z) file #{filename} into pdf")
+
+ inppath = @commandline.option('inputpath')
+ outpath = @commandline.option('outputpath')
+
+ inppath = inppath + '/' if not inppath.empty?
+ outpath = outpath + '/' if not outpath.empty?
+
+ prefix = @commandline.option('prefix')
+ suffix = @commandline.option('suffix')
+
+ inpfilename = "#{inppath}#{filename}"
+ outfilename = "#{outpath}#{prefix}#{filename.sub(/\.([^\.]*?)$/, '')}#{suffix}.pdf"
+
+ inkscape.setvariable('inputfile' , inpfilename)
+ inkscape.setvariable('outputfile', outfilename)
+
+ if @commandline.option('verbose') || @commandline.option('debug') then
+ logname = filename.gsub(/\.[^\.]*?$/, '.log')
+ report("log info saved in #{logname}")
+ inkscape.convert(logname) # logname ook doorgeven
+ else
+ inkscape.convert
+ end
+
+ end
+
+ def handle_ghostscript(ghostscript,filename)
+
+ ghostscript.reset
+
+ method = ghostscript.method(@commandline.option('method'))
+ force = ghostscript.method(@commandline.option('force'))
+
+ ghostscript.setvariable('method', method)
+ ghostscript.setvariable('force', force)
+
+ # report("conversion method #{method}")
+
+ inppath = @commandline.option('inputpath')
+ outpath = @commandline.option('outputpath')
+
+ inppath = inppath + '/' if not inppath.empty?
+ outpath = outpath + '/' if not outpath.empty?
+
+ prefix = @commandline.option('prefix')
+ suffix = @commandline.option('suffix')
+
+ ok = false
+
+ if ghostscript.pdfmethod?(method) then
+
+ report("converting pdf file #{filename} into pdf")
+
+ if prefix.empty? && suffix.empty? && inppath.empty? && outpath.empty? then
+ prefix = ghostscript.pdfprefix(method)
+ end
+
+ if ghostscript.pdffile?(filename) then
+
+ filename = filename.sub(/\.pdf$/, '')
+
+ inpfilename = "#{inppath}#{filename}.pdf"
+ outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf"
+
+ ghostscript.setvariable('inputfile' ,inpfilename)
+ ghostscript.setvariable('outputfile',outfilename)
+
+ if FileTest.file?(inpfilename) then
+ ok = ghostscript.convert
+ else
+ report("no file found #{filename}")
+ end
+
+ else
+ report("no pdf file #{filename}")
+ end
+
+ elsif ghostscript.psfile?(filename) then
+
+ if filename =~ /(.*)\.([^\.]*?)$/io then
+ filename, filesuffix = $1, $2
+ else
+ filesuffix = 'eps'
+ end
+
+ report("converting #{filesuffix} (ps) into pdf")
+
+ inpfilename = "#{inppath}#{filename}.#{filesuffix}"
+ outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf"
+
+ ghostscript.setvariable('inputfile' , inpfilename)
+ ghostscript.setvariable('outputfile', outfilename)
+
+ if FileTest.file?(inpfilename) then
+ ok = ghostscript.convert
+ if ! ok && FileTest.file?(outfilename) then
+ begin
+ File.delete(outfilename)
+ rescue
+ end
+ end
+ else
+ report("no file with name #{filename} found")
+ end
+
+ else
+ report('file must be of type eps/ps/ai/pdf')
+ end
+
+ return ok
+
+ end
+
+end
+
+# ook pdf -> pdf onder optie 0, andere kleurruimte
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('debug')
+commandline.registerflag('verbose')
+commandline.registerflag('nopipe')
+
+commandline.registervalue('method',2)
+commandline.registervalue('offset',0)
+
+commandline.registervalue('prefix')
+commandline.registervalue('suffix')
+
+commandline.registervalue('inputpath')
+commandline.registervalue('outputpath')
+
+commandline.registerflag('watch')
+commandline.registerflag('force')
+commandline.registerflag('recurse')
+
+commandline.registervalue('delay',2)
+
+commandline.registervalue('colormodel','cmyk')
+commandline.registervalue('pattern','')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registeraction('convert', 'convert ps into pdf')
+commandline.registeraction('request', 'handles exa request file')
+commandline.registeraction('watch', 'watch folders for conversions (untested)')
+
+commandline.expand
+
+logger.verbose if (commandline.option('verbose') || commandline.option('debug'))
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/scripts/context/ruby/rlxtools.rb b/scripts/context/ruby/rlxtools.rb
new file mode 100644
index 000000000..6a5c5ca20
--- /dev/null
+++ b/scripts/context/ruby/rlxtools.rb
@@ -0,0 +1,368 @@
+#!/usr/bin/env ruby
+
+# program : rlxtools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2004-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+banner = ['RlxTools', 'version 1.0.1', '2004/2005', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+require 'base/system'
+require 'base/kpse'
+
+require 'fileutils'
+# require 'ftools'
+require 'rexml/document'
+
+class Commands
+
+ include CommandBase
+
+ # <?xml version='1.0 standalone='yes'?>
+ # <rl:manipulators>
+ # <rl:manipulator name='lowres' suffix='pdf'>
+ # <rl:step>
+ # texmfstart
+ # --verbose
+ # --iftouched=<rl:value name='path'/>/<rl:value name='file'/>,<rl:value name='path'/>/<rl:value name='prefix'/><rl:value name='file'/>
+ # pstopdf
+ # --method=5
+ # --inputpath=<rl:value name='path'/>
+ # --outputpath=<rl:value name='path'/>/<rl:value name='prefix'/>
+ # <rl:value name='file'/>
+ # </rl:step>
+ # </rl:manipulator>
+ # </rl:manipulators>
+ #
+ # <?xml version='1.0' standalone='yes'?>
+ # <rl:library>
+ # <rl:usage>
+ # <rl:type>figure</rl:type>
+ # <rl:state>found</rl:state>
+ # <rl:file>cow.pdf</rl:file>
+ # <rl:suffix>pdf</rl:suffix>
+ # <rl:path>.</rl:path>
+ # <rl:conversion>lowres</rl:conversion>
+ # <rl:prefix>lowres/</rl:prefix>
+ # <rl:width>276.03125pt</rl:width>
+ # <rl:height>200.75pt</rl:height>
+ # </rl:usage>
+ # </r:library>
+
+ def manipulate
+
+ procname = @commandline.argument('first') || ''
+ filename = @commandline.argument('second') || ''
+
+ procname = Kpse.found(procname)
+
+ if procname.empty? || ! FileTest.file?(procname) then
+ report('provide valid manipulator file')
+ elsif filename.empty? || ! FileTest.file?(filename) then
+ report('provide valid resource log file')
+ else
+ begin
+ data = REXML::Document.new(File.new(filename))
+ rescue
+ report('provide valid resource log file (xml error)')
+ return
+ end
+ begin
+ proc = REXML::Document.new(File.new(procname))
+ rescue
+ report('provide valid manipulator file (xml error)')
+ return
+ end
+ report("manipulator file: #{procname}")
+ report("resourcelog file: #{filename}")
+ begin
+ nofrecords, nofdone = 0, 0
+ REXML::XPath.each(data.root,"/rl:library/rl:usage") do |usage|
+ nofrecords += 1
+ variables = Hash.new
+ usage.elements.each do |e|
+ variables[e.name] = e.text.to_s
+ end
+ report("processing record #{nofrecords} (#{variables['file'] || 'noname'}: #{variables.size} entries)")
+ if conversion = variables['conversion'] then
+ report("testing for conversion #{conversion}")
+ if suffix = variables['suffix'] then
+ suffix.downcase!
+ if ! suffix.empty? && variables['file'] && variables['file'] !~ /\.([a-z]+)$/i then
+ variables['file'] += ".#{suffix}"
+ end
+ if file = variables['file'] then
+ report("conversion #{conversion} for suffix #{suffix} for file #{file}")
+ else
+ report("conversion #{conversion} for suffix #{suffix}")
+ end
+ pattern = "@name='#{conversion}' and @suffix='#{suffix}'"
+ if steps = REXML::XPath.first(proc.root,"/rl:manipulators/rl:manipulator[#{pattern}]") then
+ localsteps = steps.deep_clone
+ ['rl:old','rl:new'].each do |tag|
+ REXML::XPath.each(localsteps,tag) do |extras|
+ REXML::XPath.each(extras,"rl:value") do |value|
+ if name = value.attributes['name'] then
+ substitute(value,variables[name.to_s] || '')
+ end
+ end
+ end
+ end
+ old = REXML::XPath.first(localsteps,"rl:old")
+ new = REXML::XPath.first(localsteps,"rl:new")
+ if old && new then
+ old, new = justtext(old.to_s), justtext(new.to_s)
+ variables['old'], variables['new'] = old, new
+ begin
+ [old,new].each do |d|
+ File.makedirs(File.dirname(d))
+ end
+ rescue
+ report("error during path creation")
+ end
+ report("old file #{old}")
+ report("new file #{new}")
+ level = if File.needsupdate(old,new) then 2 else 0 end
+ else
+ level = 1
+ end
+ if level>0 then
+ REXML::XPath.each(localsteps,"rl:step") do |command|
+ REXML::XPath.each(command,"rl:old") do |value|
+ replace(value,old)
+ end
+ REXML::XPath.each(command,"rl:new") do |value|
+ replace(value,new)
+ end
+ REXML::XPath.each(command,"rl:value") do |value|
+ if name = value.attributes['name'] then
+ substitute(value,variables[name.to_s])
+ end
+ end
+ str = justtext(command.to_s)
+ # str.gsub!(/(\.\/)+/io, '')
+ report("command #{str}")
+ System.run(str) unless @commandline.option('test')
+ report("synchronizing #{old} and #{new}")
+ File.syncmtimes(old,new) if level > 1
+ nofdone += 1
+ end
+ else
+ report("no need for a manipulation")
+ end
+ else
+ report("no manipulator found")
+ end
+ else
+ report("no suffix specified")
+ end
+ else
+ report("no conversion needed")
+ end
+ end
+ if nofdone > 0 then
+ jobname = filename.gsub(/\.(.*?)$/,'') # not 'tuo' here
+ tuoname = jobname + '.tuo'
+ if FileTest.file?(tuoname) && (f = File.open(tuoname,'a')) then
+ f.puts("%\n% number of rlx manipulations: #{nofdone}\n")
+ f.close
+ end
+ end
+ rescue
+ report("error in manipulating files: #{$!}")
+ end
+ begin
+ logname = "#{filename}.log"
+ File.delete(logname) if FileTest.file?(logname)
+ File.copy(filename,logname)
+ rescue
+ end
+ end
+
+ end
+
+ private
+
+ def justtext(str)
+ str = str.to_s
+ str.gsub!(/<[^>]*?>/o, '')
+ str.gsub!(/\s+/o, ' ')
+ str.gsub!(/&lt;/o, '<')
+ str.gsub!(/&gt;/o, '>')
+ str.gsub!(/&amp;/o, '&')
+ str.gsub!(/&quot;/o, '"')
+ str.gsub!(/[\/\\]+/o, '/')
+ return str.strip
+ end
+
+ def substitute(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
+ else str
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ @@xmlbanner = "<?xml version='1.0' standalone='yes'?>"
+
+ def identify(resultfile='rlxtools.rli')
+ if @commandline.option('collect') then
+ begin
+ File.open(resultfile,'w') do |f|
+ f << "#{@@xmlbanner}\n"
+ f << "<rl:identification>\n"
+ @commandline.arguments.each do |filename|
+ if state = do_identify(filename) then
+ report("#{filename} is identified")
+ f << state
+ else
+ report("unable to identify #{filename}")
+ end
+ end
+ f << "</rl:identification>\n"
+ report("result saved in #{resultfile}")
+ end
+ rescue
+ report("error in writing result")
+ end
+ else
+ @commandline.arguments.each do |filename|
+ if state = do_identify(filename) then
+ begin
+ File.open(filename+'.rli','w') do |f|
+ f << "#{@@xmlbanner}\n"
+ f << state
+ end
+ rescue
+ report("error in identifying #{filename}")
+ else
+ report("#{filename} is identified")
+ end
+ else
+ report("unable to identify #{filename}")
+ end
+ end
+ end
+ end
+
+ private
+
+ def do_identify(filename,centimeters=false)
+ begin
+ str = nil
+ if FileTest.file?(filename) then
+ # todo: use pdfinto for pdf files, identify is bugged
+ if centimeters then
+ result = `identify -units PixelsPerCentimeter -format \"x=%x,y=%y,w=%w,h=%h,b=%b\" #{filename}`.chomp.split(',')
+ else
+ result = `identify -units PixelsPerInch -format \"x=%x,y=%y,w=%w,h=%h,b=%b\" #{filename}`.chomp.split(',')
+ end
+ tags = Hash.new
+ result.each do |r|
+ if rr = r.split("=") then
+ tags[rr[0]] = rr[1]
+ end
+ end
+ size = (tags['b']||0).to_i
+ width = unified(tags['w']||0,tags['x']||'1')
+ height = unified(tags['h']||0,tags['y']||'1')
+ if size > 0 then
+ str = ''
+ str << "<rl:identify name='#{File.basename(filename)}'>\n"
+ str << " <rl:size>#{size}</rl:size>\n"
+ str << " <rl:path>#{File.dirname(filename).sub(/\\/o,'/')}</rl:path>\n"
+ str << " <rl:width>#{width}</rl:width>\n"
+ str << " <rl:height>#{height}</rl:height>\n"
+ str << "</rl:identify>\n"
+ end
+ else
+ str = nil
+ end
+ rescue
+ str = nil
+ end
+ return str
+ end
+
+ def unified(dim,res)
+ case res
+ when /([\d\.]+)\s*PixelsPerInch/io then
+ sprintf("%.4fin",dim.to_f/$1.to_f)
+ when /([\d\.]+)\s*PixelsPerCentimeter/io then
+ sprintf("%.4fcm",dim.to_f/$1.to_f)
+ when /([\d\.]+)\s*PixelsPerMillimeter/io then
+ sprintf("%.4fmm",dim.to_f/$1.to_f)
+ when /([\d\.]+)\s*PixelsPerPoint/io then
+ sprintf("%.4fbp",dim.to_f/$1.to_f)
+ else
+ sprintf("%.4fbp",dim.to_f)
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('manipulate', '[--test] manipulatorfile resourselog')
+commandline.registeraction('identify' , '[--collect] filename')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('test')
+commandline.registerflag('collect')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/scripts/context/ruby/rscortool.rb b/scripts/context/ruby/rscortool.rb
new file mode 100644
index 000000000..c656fed85
--- /dev/null
+++ b/scripts/context/ruby/rscortool.rb
@@ -0,0 +1,63 @@
+# program : rscortool
+# copyright : PRAGMA Publishing On Demand
+# version : 1.00 - 2002
+# author : Hans Hagen
+#
+# project : eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+require 'rexml/document.rb'
+
+class Array
+
+ def downcase
+ self.collect { |l| l.to_s.downcase }
+ end
+
+end
+
+class SortedXML
+
+ def initialize (filename)
+ return nil if not filename or filename.empty? or not test(?e,filename)
+ @data = REXML::Document.new(File.new(filename),
+ {:ignore_whitespace_nodes => :all,
+ :compress_whitespace => :all})
+ end
+
+ def save (filename)
+ # filename += '.xml' unless filename.match(/\..*?$/)
+ filename += '.xml' unless filename =~ /\..*?$/
+ if not filename.empty? and f = open(filename,'w')
+ @data.write(f,0)
+ f.close
+ end
+ end
+
+ def sort
+ keys = REXML::XPath.match(@data.root,"/contacts/contact/@label")
+ return unless keys
+ keys = keys.downcase
+ records = @data.elements.to_a("/contacts/contact")
+ @data.elements.delete_all("/contacts/contact")
+ keys = keys.collect do |l| # prepare numbers
+ l.gsub(/(\d+)/) do |d| sprintf('%05d', d) end
+ end
+ keys.sort.each do |s|
+ @data.root.add_element(records[keys.index(s)])
+ end
+ end
+
+end
+
+def sortfile (filename)
+ c = SortedXML.new(filename)
+ c.sort
+ c.save('test.xml')
+end
+
+exit if ARGV[0] == nil or ARGV[0].empty?
+
+sortfile(ARGV[0])
diff --git a/scripts/context/ruby/rsfiltool.rb b/scripts/context/ruby/rsfiltool.rb
new file mode 100644
index 000000000..6d7c7aba0
--- /dev/null
+++ b/scripts/context/ruby/rsfiltool.rb
@@ -0,0 +1,341 @@
+# program : rsfiltool
+# copyright : PRAGMA Publishing On Demand
+# version : 1.01 - 2002
+# author : Hans Hagen
+#
+# project : eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+# --name=a,b,c.xml wordt names [a.xml, b.xml, c.xml]
+# --path=x/y/z/a,b,c.xml wordt [x/y/z/a.xml, x/y/z/b.xml, x/y/z/c.xml]
+
+# todo : split session stuff from xmpl/base into an xmpl/session module and "include xmpl/session" into base and here and ...
+
+require 'fileutils'
+# require 'ftools'
+require 'xmpl/base'
+require 'xmpl/switch'
+require 'xmpl/request'
+
+session = Example.new('rsfiltool', '1.01', 'PRAGMA POD')
+
+filterprefix = 'rsfil-'
+
+commandline = CommandLine.new
+
+commandline.registerflag('submit')
+commandline.registerflag('fetch')
+commandline.registerflag('report')
+#commandline.registerflag('split')
+commandline.registerflag('stamp')
+commandline.registerflag('silent')
+commandline.registerflag('request')
+commandline.registerflag('nobackup')
+
+commandline.registervalue('filter')
+
+commandline.registervalue('root')
+commandline.registervalue('path')
+commandline.registervalue('name')
+
+commandline.expand
+
+session.set('log.silent',true) if commandline.option('silent')
+
+session.inherit(commandline)
+
+session.identify
+
+# session.exit unless session.loadenvironment
+
+def prepare (session)
+
+ # Normally the system provides the file, but a user can provide the rest; in
+ # order to prevent problems with keying in names, we force lowercase names.
+
+ session.set('option.file',session.get('argument.first')) if session.get('option.file').empty?
+
+ root = session.get('option.root').downcase
+ path = session.get('option.path').downcase
+ name = session.get('option.name').downcase
+ file = session.get('option.file').downcase
+
+ session.error('provide file') if file.empty?
+ session.error('provide root') if root.empty?
+
+ filter = session.get('option.filter').downcase
+ trash = session.get('option.trash').downcase
+
+ trash = '' unless FileTest.directory?(trash)
+
+ if not filter.empty? then
+ begin
+ require filter
+ rescue Exception
+ begin
+ require filterprefix + filter
+ rescue Exception
+ session.error('invalid filter')
+ end
+ end
+ begin
+ if RSFIL::valid?(file) then
+ split = RSFIL::split(file,name)
+ path = if split[0].downcase then split[0] else '' end
+ file = if split[1].downcase then split[1] else '' end
+ name = if split[2].downcase then split[2] else '' end
+ session.report('split result',split.inspect)
+ session.error('unable to split off path') if path.empty?
+ session.error('unable to split off file') if file.empty?
+ session.error('unable to split off name') if name.empty?
+ session.set('option.path',path) if path
+ session.set('option.file',file) if file
+ session.set('option.name',name) if name
+ else
+ session.error('invalid filename', file)
+ unless trash.empty? then
+ File.copy(file,trash + '/' + file)
+ end
+ end
+ rescue
+ session.error('unable to split',file,'with filter',filter)
+ end
+ end
+
+ session.error('provide path') if path.empty?
+
+ session.error('invalid root') unless test(?d,root)
+
+ exit if session.error?
+
+ session.set('fb.filename',file)
+
+ path.gsub!(/\\/o, '/')
+ path.gsub!(/\s/o, '')
+
+ path = root + '/' + path
+
+ # multiple paths
+
+ if path =~ /^(.*)\/(.*?)$/o then
+ prepath = $1
+ postpath = $2
+ paths = postpath.split(/\,/)
+ paths.collect! do |p|
+ prepath + '/' + p
+ end
+ else
+ paths = Array.new
+ paths.push(path)
+ end
+
+ paths.collect! do |p|
+ p.gsub(/[^a-zA-Z0-9\s\-\_\/\.\:]/o, '-')
+ end
+
+ file.gsub!(/\\/o, '/')
+ file.gsub!(/[^a-zA-Z0-9\s\-\_\/\.\:]/o, '-')
+
+# if session.get('option.split')
+# if file =~ /(.*)\.(.*?)$/o
+# path = path + '/' + $1
+# else
+# session.error('nothing to split in filename')
+# end
+# end
+
+ paths.each do |p|
+ begin
+ session.report('creating path', p)
+ File.makedirs(p)
+ rescue
+ session.error('unable to create path', p)
+ end
+ end
+
+ name.gsub!(/\s+/,'')
+
+ # can be a,b,c.exa.saved => a.exa.saved,b.exa.saved,c.exa.saved
+
+ if name =~ /(.*?)\.(.*)$/
+ name = $1
+ suffix = $2
+ names = name.split(/\,/)
+ names.collect! do |n|
+ n + '.' + suffix
+ end
+ name = names.join(',')
+ else
+ names = name.split(/\,/)
+ end
+
+ session.set('fb.path',path)
+ session.set('fb.paths',paths)
+ session.set('fb.name',name)
+ session.set('fb.names',names)
+
+end
+
+def thefullname(path,file,name='')
+
+ filename = file.gsub(/.*?\//, '')
+
+ if name.empty?
+ path + '/' + filename
+ else
+ unless name =~ /\..+$/o # unless name.match(/\..+$/o)
+ if filename =~ /(\..+)$/o # if file.match(/(\..+)$/o)
+ name = name + $1
+ end
+ end
+ path + '/' + name
+ end
+
+end
+
+def submitfile (session)
+
+ filename = session.get('fb.filename')
+ paths = session.get('fb.paths')
+ names = session.get('fb.names')
+
+ paths.each do |path|
+ session.report('submitting path',path)
+ names.each do |name|
+ session.report('submitting file',filename,'to',name)
+ submit(session,path,filename,name)
+ end
+ end
+
+end
+
+def submitlist (session)
+
+ requestname = session.get('fb.filename')
+ paths = session.get('fb.paths')
+
+ if test(?e,requestname)
+ session.report('loading request file', requestname)
+ if request = ExaRequest.new(requestname)
+ filelist = request.files
+ if filelist && (filelist.size > 0)
+ filelist.each do |filename|
+ paths.each do |path|
+ session.report('submitting file from list', filename)
+ submit(session,path,filename,request.naturalname(filename))
+ end
+ end
+ else
+ session.warning('no filelist in', requestname)
+ end
+ else
+ session.warning('unable to load', requestname)
+ end
+ else
+ session.warning('no file', requestname)
+ end
+
+end
+
+def submit (session, path, filename, newname)
+
+ fullname = thefullname(path,newname)
+
+ unless test(?e,filename)
+ session.warning('no file to submit', filename)
+ return
+ end
+
+ begin
+ File.copy(fullname,fullname+'.old') if ! session.get('nobackup') && test(?e,fullname)
+ if test(?e,filename)
+ File.copy(filename,fullname)
+ session.report('submit', filename, 'in', fullname)
+ if session.get('option.stamp')
+ f = open(fullname+'.tim','w')
+ f.puts(Time.now.gmtime.strftime("%a %b %d %H:%M:%S %Y"))
+ f.close
+ end
+ else
+ session.error('unable to locate', filename)
+ end
+ rescue
+ session.error('unable to move', filename, 'to', fullname)
+ end
+
+end
+
+def fetch (session)
+
+ filename = session.get('fb.filename')
+ paths = session.get('fb.paths')
+ name = session.get('fb.name')
+
+ begin
+ File.copy(filename,filename+'.old') if ! session.get('nobackup') && test(?e,filename)
+ paths.each do |path|
+ # fullname = thefullname(path,request.naturalname(filename))
+ # fullname = thefullname(path,filename)
+ fullname = thefullname(path,name)
+ if test(?e,fullname)
+ File.copy(fullname,filename)
+ session.report('fetch', filename, 'from', fullname)
+ return
+ else
+ session.report('file',fullname, 'is not present')
+ end
+ end
+ rescue
+ session.error('unable to fetch file from path')
+ end
+ session.error('no file',filename, 'fetched') unless test(?e,filename)
+
+end
+
+def report (session)
+
+ filename = session.get('fb.filename')
+ paths = session.get('fb.paths')
+
+ paths.each do |path|
+ fullname = thefullname(path,request.naturalname(filename))
+ if test(?e,fullname)
+ begin
+ session.report('file', fullname)
+ session.report('size', test(?s,fullname))
+ if test(?e,fullname+'.tim')
+ str = IO.readlines(fullname+'.tim')
+ # str = IO.read(fullname+'.tim')
+ session.report('time', str)
+ end
+ rescue
+ session.error('unable to report about', fullname)
+ end
+ end
+ end
+
+end
+
+if session.get('option.submit')
+ prepare(session)
+ if session.get('option.request')
+ submitlist(session)
+ else
+ submitfile(session)
+ end
+elsif session.get('option.fetch')
+ prepare(session)
+ fetch(session)
+elsif session.get('option.report')
+ prepare(session)
+ report(session)
+else
+ session.report('provide action')
+end
diff --git a/scripts/context/ruby/rslibtool.rb b/scripts/context/ruby/rslibtool.rb
new file mode 100644
index 000000000..7ae5efb64
--- /dev/null
+++ b/scripts/context/ruby/rslibtool.rb
@@ -0,0 +1,114 @@
+# program : rslibtool
+# copyright : PRAGMA Publishing On Demand
+# version : 1.00 - 2002
+# author : Hans Hagen
+#
+# project : eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+# --add --base=filename --path=directory pattern
+# --remove --base=filename --path=directory label
+# --sort --base=filename --path=directory
+# --purge --base=filename --path=directory
+# --dummy --base=filename
+# --namespace
+
+# rewrite
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'rslb/base'
+require 'xmpl/base'
+require 'xmpl/switch'
+
+session = Example.new('rslbtool', '1.0', 'PRAGMA POD')
+
+session.identify
+
+commandline = CommandLine.new
+
+commandline.registerflag('add')
+commandline.registerflag('remove')
+commandline.registerflag('delete')
+commandline.registerflag('sort')
+commandline.registerflag('purge')
+commandline.registerflag('dummy')
+commandline.registerflag('process')
+commandline.registerflag('namespace')
+
+commandline.registervalue('prefix')
+commandline.registervalue('base')
+commandline.registervalue('path')
+commandline.registervalue('result')
+commandline.registervalue('texexec')
+commandline.registervalue('zipalso')
+
+commandline.expand
+
+session.inherit(commandline)
+
+base = session.get('option.base')
+path = session.get('option.path')
+
+base = 'rslbtool.xml' if base.empty?
+
+# when path is given, assume that arg list is list of
+# suffixes, else assume it is a list of globbed filespec
+
+if path.empty?
+ base += '.xml' unless base =~ /\..+$/
+ list = commandline.arguments
+else
+ Dir.chdir(File.dirname(path))
+ list = Dir.glob("*.{#{commandline.arguments.join(',')}}")
+end
+
+begin
+ reslib = Resource.new(base,session.get('option.namespace'))
+ reslib.load(base)
+rescue
+ session.error('problems with loading base')
+ exit
+end
+
+unless session.get('option.texexec').empty?
+ reslib.set_texexec(session.get('option.texexec'))
+end
+
+if session.get('option.add')
+
+ session.report('adding records', list)
+ reslib.add_figures(list,session.get('option.prefix'))
+
+elsif session.get('option.remove') or session.get('option.delete')
+
+ session.report('removing records')
+ reslib.delete_figures(list)
+
+elsif session.get('option.sort')
+
+ session.report('sorting records')
+ reslib.sort_figures()
+
+elsif session.get('option.purge')
+
+ session.report('purging records')
+ reslib.purge_figures()
+
+elsif session.get('option.dummy')
+
+ session.report('creating dummy records')
+ reslib.create_dummies(session.get('option.process'),session.get('option.result'),session.get('option.zipalso'))
+
+else
+
+ session.warning('provide action')
+
+end
+
+reslib.save(base)
diff --git a/scripts/context/ruby/runtools.rb b/scripts/context/ruby/runtools.rb
new file mode 100644
index 000000000..5565748e2
--- /dev/null
+++ b/scripts/context/ruby/runtools.rb
@@ -0,0 +1,506 @@
+require 'timeout'
+require 'fileutils'
+# require 'ftools'
+require 'rbconfig'
+
+class File
+
+ # we don't want a/b//c
+ #
+ # puts File.join('a','b','c')
+ # puts File.join('/a','b','c')
+ # puts File.join('a:','b','c')
+ # puts File.join('a/','/b/','c')
+ # puts File.join('/a','/b/','c')
+ # puts File.join('//a/','/b/','c')
+
+ def File.join(*list)
+ path, prefix = [*list].flatten.join(File::SEPARATOR), ''
+ path.sub!(/^([\/]+)/) do
+ prefix = $1
+ ''
+ end
+ path.gsub!(/([\\\/]+)/) do
+ File::SEPARATOR
+ end
+ prefix + path
+ end
+
+end
+
+
+class Job
+
+ $ownfile, $ownpath = '', ''
+
+ def Job::set_own_path(file)
+ $ownfile, $ownpath = File.basename(file), File.expand_path(File.dirname(file))
+ $: << $ownpath
+ end
+
+ def Job::ownfile
+ $ownfile
+ end
+
+ def Job::ownpath
+ $ownpath
+ end
+
+end
+
+class Job
+
+ def initialize
+ @startuppath = Dir.getwd
+ @log = Array.new
+ @testmode = false
+ @ownpath = $ownpath
+ @paths = Array.new
+ end
+
+ def exit(showlog=false)
+ Dir.chdir(@startuppath)
+ show_log if showlog
+ Kernel::exit
+ end
+
+ def platform
+ case Config::CONFIG['host_os']
+ when /mswin/ then :windows
+ else :unix
+ end
+ end
+
+ def path(*components)
+ File.join([*components].flatten)
+ end
+
+ def found(name)
+ FileTest.file?(path(name)) || FileTest.directory?(path(name))
+ end
+
+ def binary(name)
+ if platform == :windows then
+ name.sub(/\.[^\/]+$/o,'') + '.exe'
+ else
+ name
+ end
+ end
+
+ def suffixed(name,suffix)
+ if name =~ /\.[^\/]+$/o then
+ name
+ else
+ name + '.' + suffix
+ end
+ end
+
+ def expanded(*name)
+ File.expand_path(File.join(*name))
+ end
+
+ def argument(n,default=nil)
+ ARGV[n] || default
+ end
+
+ def variable(name,default='')
+ ENV[name] || default
+ end
+
+ def change_dir(*dir)
+ dir, old = expanded(path(*dir)), expanded(Dir.getwd)
+ unless old == dir then
+ begin
+ Dir.chdir(dir)
+ rescue
+ error("unable to change to path #{dir}")
+ else
+ if old == dir then
+ error("error in changing to path #{dir}")
+ else
+ message("changed to path #{dir}")
+ end
+ end
+ end
+ # return File.expand_path(Dir.getwd)
+ end
+
+ def delete_dir(*dir)
+ begin
+ dir = path(*dir)
+ pattern = "#{dir}/**/*"
+ puts("analyzing dir #{pattern}")
+ files = Dir.glob(pattern).sort.reverse
+ files.each do |f|
+ begin
+ # FileTest.file?(f) fails on .whatever files
+ File.delete(f)
+ rescue
+ # probably directory
+ else
+ puts("deleting file #{f}")
+ end
+ end
+ files.each do |f|
+ begin
+ Dir.rmdir(f)
+ rescue
+ # strange
+ else
+ message("deleting path #{f}")
+ end
+ end
+ begin
+ Dir.rmdir(dir)
+ rescue
+ # strange
+ else
+ message("deleting parent #{dir}")
+ end
+ Dir.glob(pattern).sort.each do |f|
+ warning("unable to delete #{f}")
+ end
+ rescue
+ warning("unable to delete path #{File.expand_path(dir)} (#{$!})")
+ else
+ message("path #{File.expand_path(dir)} removed")
+ end
+ end
+
+
+ def create_dir(*dir)
+ begin
+ dir = path(*dir)
+ unless FileTest.directory?(dir) then
+ File.makedirs(dir)
+ else
+ return
+ end
+ rescue
+ error("unable to create path #{File.expand_path(dir)}")
+ else
+ message("path #{File.expand_path(dir)} created")
+ end
+ end
+
+ def show_dir(delay=0)
+ _puts_("\n")
+ print Dir.getwd + ' '
+ begin
+ timeout(delay) do
+ loop do
+ print '.'
+ sleep(1)
+ end
+ end
+ rescue TimeoutError
+ # ok
+ end
+ _puts_("\n\n")
+ end
+
+ def copy_file(from,to='.',exclude=[])
+ to, ex = path(to), [exclude].flatten
+ Dir.glob(path(from)).each do |file|
+ tofile = to.sub(/[\.\*]$/o) do File.basename(file) end
+ _do_copy_(file,tofile) unless ex.include?(File.extname(file))
+ end
+ end
+
+ def clone_file(from,to)
+ if from and to then
+ to = File.join(File.basename(from),to) if File.basename(to).empty?
+ _do_copy_(from,to)
+ end
+ end
+
+ def copy_dir(from,to,pattern='*',exclude=[]) # recursive
+ pattern = '*' if ! pattern or pattern.empty?
+ if from and to and File.expand_path(from) != File.expand_path(to) then
+ ex = [exclude].flatten
+ Dir.glob("#{from}/**/#{pattern}").each do |file|
+ unless ex.include?(File.extname(file)) then
+ _do_copy_(file,File.join(to,file.sub(/^#{from}/, '')))
+ end
+ end
+ end
+ end
+
+ def copy_path(from,to,pattern='*',exclude=[]) # non-recursive
+ pattern = '*' if ! pattern or pattern.empty?
+ if from and to and File.expand_path(from) != File.expand_path(to) then
+ ex = [exclude].flatten
+ Dir.glob("#{from}/#{pattern}").each do |file|
+ unless ex.include?(File.extname(file)) then
+ _do_copy_(file,File.join(to,file.sub(/^#{from}/, '')))
+ end
+ end
+ end
+ end
+
+ def _do_copy_(file,tofile)
+ if FileTest.file?(file) and File.expand_path(file) != File.expand_path(tofile) then
+ begin
+ create_dir(File.dirname(tofile))
+ File.copy(file,tofile)
+ rescue
+ error("unable to copy #{file} to #{tofile}")
+ else
+ message("file #{file} copied to #{tofile}")
+ end
+ else
+ puts("file #{file} is not copied")
+ end
+ end
+
+ def rename_file(from,to)
+ from, to = path(from), path(to)
+ begin
+ File.move(from,to)
+ rescue
+ error("unable to rename #{from} to #{to}")
+ else
+ message("#{from} renamed to #{to}")
+ end
+ end
+
+ def delete_file(pattern)
+ Dir.glob(path(pattern)).each do |file|
+ _do_delete_(file)
+ end
+ end
+
+ def delete_files(*files)
+ [*files].flatten.each do |file|
+ _do_delete_(file)
+ end
+ end
+
+ def _do_delete_(file)
+ if FileTest.file?(file) then
+ begin
+ File.delete(file)
+ rescue
+ error("unable to delete file #{file}")
+ else
+ message("file #{file} deleted")
+ end
+ else
+ message("no file #{File.expand_path(file)}")
+ end
+ end
+
+ def show_log(filename=nil)
+ if filename then
+ begin
+ if f = File.open(filename,'w') then
+ @log.each do |line|
+ f.puts(line)
+ end
+ f.close
+ end
+ message("log data written to #{filename}")
+ rescue
+ error("unable to write log to #{filename}")
+ end
+ else
+ @log.each do |line|
+ _puts_(line)
+ end
+ end
+ end
+
+ def _puts_(str)
+ begin
+ STDOUT.puts( str)
+ rescue
+ STDERR.puts("error while writing '#{str}' to terminal")
+ end
+ end
+
+ def puts(message)
+ @log << message
+ _puts_(message)
+ end
+
+ def error(message)
+ puts("! #{message}")
+ exit
+ end
+
+ def warning(message)
+ puts("- #{message}")
+ end
+
+ def message(message)
+ puts("+ #{message}")
+ end
+
+ def export_variable(variable,value)
+ value = path(value) if value.class == Array
+ ENV[variable] = value
+ message("environment variable #{variable} set to #{value}")
+ return value
+ end
+
+ def execute_command(*command)
+ begin
+ command = [*command].flatten.join(' ')
+ message("running '#{command}'")
+ _puts_("\n")
+ ok = system(command)
+ _puts_("\n")
+ if true then # ok then
+ message("finished '#{command}'")
+ else
+ error("error in running #{command}")
+ end
+ rescue
+ error("unable to run #{command}")
+ end
+ end
+
+ def pipe_command(*command)
+ begin
+ command = [*command].flatten.join(' ')
+ message("running '#{command}'")
+ result = `#{command}`
+ _puts_("\n")
+ _puts_(result)
+ _puts_("\n")
+ rescue
+ error("unable to run #{command}")
+ end
+ end
+
+ def execute_script(script)
+ script = suffixed(script,'rb')
+ script = path(script_path,File.basename(script)) unless found(script)
+ if found(script) then
+ begin
+ message("loading script #{script}")
+ load(script)
+ rescue
+ error("error in loading script #{script} (#{$!})")
+ else
+ message("script #{script} finished")
+ end
+ else
+ warning("no script #{script}")
+ end
+ end
+
+ def execute_binary(*command)
+ command = [*command].flatten.join(' ').split(' ')
+ command[0] = binary(command[0])
+ execute_command(command)
+ end
+
+ def extend_path(pth)
+ export_variable('PATH',"#{path(pth)}#{File::PATH_SEPARATOR}#{ENV['PATH']}")
+ end
+
+ def startup_path
+ @startuppath
+ end
+
+ def current_path
+ Dir.getwd
+ end
+
+ def script_path
+ @ownpath
+ end
+
+ def push_path(newpath)
+ newpath = File.expand_path(newpath)
+ @paths.push(newpath)
+ change_dir(newpath)
+ end
+
+ def pop_path
+ change_dir(if @paths.length > 0 then @paths.pop else @startuppath end)
+ end
+
+ # runner = Runner.new
+ # runner.texmfstart('texexec','--help')
+
+ def texmfstart(name,args,verbose=false)
+ command = ['texmfstart',"#{'--verbose' if verbose}",name,args].flatten.join(' ')
+ system(command)
+ end
+
+end
+
+class Job
+
+ # copied from texmfstart and patched (message/error), different name
+
+ def use_tree(tree)
+ unless tree.empty? then
+ begin
+ setuptex = File.join(tree,'setuptex.tmf')
+ if FileTest.file?(setuptex) then
+ message("tex tree : #{setuptex}")
+ ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/'
+ ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME']
+ case RUBY_PLATFORM
+ when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin'
+ when /(linux)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux'
+ when /(darwin|rhapsody|nextstep)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx'
+ # when /(netbsd|unix)/i then # todo
+ else # todo
+ end
+ ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}"
+ message("preset : TEXPATH => #{ENV['TEXPATH']}")
+ message("preset : TEXOS => #{ENV['TEXOS']}")
+ message("preset : TEXMFOS => #{ENV['TEXMFOS']}")
+ message("preset : TMP => #{ENV['TMP']}")
+ IO.readlines(File.join(tree,'setuptex.tmf')).each do |line|
+ case line
+ when /^[\#\%]/ then
+ # comment
+ when /^(.*?)\s+\=\s+(.*)\s*$/ then
+ k, v = $1, $2
+ ENV[k] = v.gsub(/\%(.*?)\%/) do
+ ENV[$1] || ''
+ end
+ message("user set : #{k} => #{ENV[k]}")
+ end
+ end
+ else
+ warning("no setup file '#{setuptex}', tree not initialized") # no error
+ end
+ rescue
+ warning("error in setup: #{$!}")
+ end
+ end
+ end
+
+end
+
+Job::set_own_path($0)
+
+if Job::ownfile == 'runtools.rb' then
+
+ begin
+ script = ARGV.shift
+ if script then
+ script += '.rb' if File.extname(script).empty?
+ fullname = File.expand_path(script)
+ fullname = File.join(Job::ownpath,script) unless FileTest.file?(fullname)
+ if FileTest.file?(fullname) then
+ puts("loading script #{fullname}")
+ Job::set_own_path(fullname)
+ load(fullname)
+ else
+ puts("unknown script #{fullname}")
+ end
+ else
+ puts("provide script name")
+ end
+ rescue
+ puts("fatal error: #{$!}")
+ end
+
+end
diff --git a/scripts/context/ruby/texexec.rb b/scripts/context/ruby/texexec.rb
new file mode 100644
index 000000000..747d76b68
--- /dev/null
+++ b/scripts/context/ruby/texexec.rb
@@ -0,0 +1,792 @@
+#!/usr/bin/env ruby
+#encoding: ASCII-8BIT
+
+banner = ['TeXExec', 'version 6.2.1', '1997-2009', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'fileutils'
+# require 'ftools' # needed ?
+
+require 'base/switch'
+require 'base/logger'
+require 'base/variables'
+require 'base/system'
+
+require 'base/state' # needed ?
+require 'base/file' # needed ?
+
+require 'base/tex'
+require 'base/texutil'
+require 'base/kpse'
+
+class Commands
+
+ include CommandBase
+
+ def make
+ if job = TEX.new(logger) then
+ prepare(job)
+ # bonus, overloads language switch !
+ job.setvariable('language','all') if @commandline.option('all')
+ if @commandline.arguments.length > 0 then
+ if @commandline.arguments.first == 'all' then
+ job.setvariable('texformats',job.defaulttexformats)
+ job.setvariable('mpsformats',job.defaultmpsformats)
+ else
+ job.setvariable('texformats',@commandline.arguments)
+ job.setvariable('mpsformats',@commandline.arguments)
+ end
+ end
+ job.makeformats
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ seterror if job.error?
+ end
+ end
+
+ def check
+ if job = TEX.new(logger) then
+ job.checkcontext
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ end
+ end
+
+ def main
+ if @commandline.arguments.length>0 then
+ process
+ else
+ help
+ end
+ end
+
+ def process
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processtex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ seterror if job.error?
+ end
+ end
+
+ def mptex
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmptex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ seterror if job.error?
+ end
+ end
+
+ def mpxtex
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmpxtex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ seterror if job.error?
+ end
+ end
+
+ def mpgraphic
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmpgraphic
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ seterror if job.error?
+ end
+ end
+
+ def mpstatic
+ if job = TEX.new(logger) then
+ job.setvariable('filename',@commandline.arguments.first)
+ prepare(job)
+ job.processmpstatic
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ seterror if job.error?
+ end
+ end
+
+ # hard coded goodies # to be redone as s-ctx-.. with vars passed as such
+
+ def listing
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ backspace = @commandline.checkedoption('backspace', '1.5cm')
+ topspace = @commandline.checkedoption('topspace', '1.5cm')
+ pretty = @commandline.option('pretty')
+ f << "% interface=english\n"
+ f << "\\setupbodyfont[11pt,tt]\n"
+ f << "\\setuplayout\n"
+ f << " [topspace=#{topspace},backspace=#{backspace},\n"
+ f << " header=0cm,footer=1.5cm,\n"
+ f << " width=middle,height=middle]\n"
+ f << "\\setuptyping[lines=yes]\n"
+ f << "\\setuptyping[option=color]\n" if pretty
+ f << "\\starttext\n";
+ files.each do |filename|
+ report("list file: #{filename}")
+ cleanname = cleantexfilename(filename).downcase
+ f << "\\page\n"
+ f << "\\setupfootertexts[\\tttf #{cleanname}][\\tttf \\pagenumber]\n"
+ f << "\\typefile{#{filename}}\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to list')
+ end
+ else
+ report('no files to list')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def figures
+ # we replaced "texutil --figures ..."
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ fast = @commandline.option('fast')
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ if fast or (files.length > 0) then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ files.delete("texexec.pdf")
+ # Kpse.runscript('rlxtools', ['--identify','--collect'], files.join(' ')) unless fast
+ system("texmfstart rlxtools --identify --collect #{files.join(' ')}")
+ figures = @commandline.checkedoption('method', 'a').downcase
+ paperoffset = @commandline.checkedoption('paperoffset', '0pt')
+ backspace = @commandline.checkedoption('backspace', '1.5cm')
+ topspace = @commandline.checkedoption('topspace', '1.5cm')
+ boxtype = @commandline.checkedoption('boxtype','')
+ f << "% format=english\n";
+ f << "\\usemodule[res-20]\n"
+ f << "\\setuplayout\n";
+ f << " [topspace=#{topspace},backspace=#{backspace},\n"
+ f << " header=1.5cm,footer=0pt,\n";
+ f << " width=middle,height=middle]\n";
+ if @commandline.option('fullscreen') then
+ f << "\\setupinteraction\n";
+ f << " [state=start]\n";
+ f << "\\setupinteractionscreen\n";
+ f << " [option=max]\n";
+ end
+ boxtype += "box" unless boxtype.empty? || (boxtype =~ /box$/io)
+ f << "\\starttext\n";
+ f << "\\showexternalfigures[alternative=#{figures},offset=#{paperoffset},size=#{boxtype}]\n";
+ f << "\\stoptext\n";
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ # File.silentdelete('rlxtools.rli') unless job.getvariable('keep')
+ else
+ report('no figures to show')
+ end
+ else
+ report('no figures to show')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def modules
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ msuffixes = ['tex','mkii','mkiv','mp','pl','pm','rb']
+ if files.length > 0 then
+ files.each do |fname|
+ fnames = Array.new
+ if FileTest.file?(fname) then
+ fnames << fname
+ else
+ msuffixes.each do |fsuffix|
+ fnames << File.suffixed(fname,fsuffix)
+ end
+ end
+ fnames.each do |ffname|
+ if msuffixes.include?(File.splitname(ffname)[1]) && FileTest.file?(ffname) then
+ if mod = File.open(job.tempfilename('tex'),'w') then
+ if File.suffix(ffname) =~ /^(mkii|mkiv)$/o then
+ markfile = $1
+ else
+ markfile = nil
+ end
+ # Kpse.runscript('ctxtools',['--document'],ffname)
+ system("texmfstart ctxtools --document #{ffname}")
+ if ted = File.silentopen(File.suffixed(ffname,'ted')) then
+ firstline = ted.gets
+ if firstline =~ /interface=/o then
+ mod << firstline
+ else
+ mod << "% interface=en\n"
+ end
+ ted.close
+ else
+ mod << "% interface=en\n"
+ end
+ mod << "\\usemodule[mod-01]\n"
+ mod << "\\def\\ModuleNumber{1}\n"
+ mod << "\\starttext\n"
+ # todo: global file too
+ mod << "\\readlocfile{#{File.suffixed(ffname,'ted')}}{}{}\n"
+ mod << "\\stoptext\n"
+ mod.close
+ job.setvariable('interface','english') # redundant
+ # job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ result = File.unsuffixed(File.basename(ffname))
+ if markfile then
+ result = result+'-'+markfile
+ end
+ job.setvariable('result',result)
+ job.processtex
+ # ["dvi", "pdf","ps"].each do |s|
+ # File.silentrename(job.tempfilename(s),File.suffixed(ffname,s));
+ # end
+ end
+ end
+ end
+ end
+ else
+ report('no modules to process')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def pdfsplit
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ filename = File.expand_path(@commandline.arguments.first)
+ if FileTest.file?(filename) then
+ basename = filename.sub(/\..*?$/,'')
+ tempfile = File.suffixed(job.tempfilename,'tex')
+ if basename != filename then
+ info = `pdfinfo #{filename}`
+ if info =~ /Pages:\s*(\d+)/ then
+ nofpages = $1.to_i
+ result = @commandline.checkedoption('result','texexec')
+ nofpages.times do |i|
+ if f = File.open(tempfile,"w") then
+ n = i + 1
+ report("extracting page #{n}")
+ f << "\\starttext\\startTEXpage\n"
+ f << "\\externalfigure[#{filename}][object=no,page=#{n}]\n"
+ f << "\\stopTEXpage\\stoptext\n"
+ f.close
+ job.setvariable('result',"#{result}-#{n}")
+ job.setvariable('interface','english') # redundant
+ job.setvariable('simplerun',true)
+ job.setvariable('purge',true)
+ job.setvariable('files',[tempfile])
+ job.processtex
+ end
+ end
+ end
+ end
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def arrangeoutput
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ emptypages = @commandline.checkedoption('addempty', '')
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ textwidth = @commandline.checkedoption('textwidth', '0cm')
+ backspace = @commandline.checkedoption('backspace', '0cm')
+ topspace = @commandline.checkedoption('topspace', '0cm')
+ f << "\\definepapersize\n"
+ f << " [offset=#{paperoffset}]\n"
+ f << "\\setuplayout\n"
+ f << " [backspace=#{backspace},\n"
+ f << " topspace=#{topspace},\n"
+ f << " marking=on,\n" if @commandline.option('marking')
+ f << " width=middle,\n"
+ f << " height=middle,\n"
+ f << " location=middle,\n"
+ f << " header=0pt,\n"
+ f << " footer=0pt]\n"
+ unless @commandline.option('noduplex') then
+ f << "\\setuppagenumbering\n"
+ f << " [alternative=doublesided]\n"
+ end
+ f << "\\starttext\n"
+ files.each do |filename|
+ report("arranging file #{filename}")
+ f << "\\insertpages\n"
+ f << " [#{filename}]\n"
+ f << " [#{emptypages}]\n" unless emptypages.empty?
+ f << " [width=#{textwidth}]\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ job.setvariable('arrange',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to arrange')
+ end
+ else
+ report('no files to arrange')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def selectoutput
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ selection = @commandline.checkedoption('selection', '')
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ textwidth = @commandline.checkedoption('textwidth', '0cm')
+ backspace = @commandline.checkedoption('backspace', '0cm')
+ topspace = @commandline.checkedoption('topspace', '0cm')
+ paperformat = @commandline.checkedoption('paperformat', 'A4*A4').split(/[\*x]/o)
+ from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4'
+ if from == 'fit' or to == 'fit' then
+ f << "\\getfiguredimensions[#{files.first}]\n"
+ if from == 'fit' then
+ f << "\\expanded{\\definepapersize[from-fit][width=\\figurewidth,height=\\figureheight]}\n"
+ from = 'from-fit'
+ end
+ if to == 'fit' then
+ f << "\\expanded{\\definepapersize[to-fit][width=\\figurewidth,height=\\figureheight]}\n"
+ to = 'to-fit'
+ end
+ end
+ job.setvariable('paperformat','') # else overloaded later on
+ f << "\\setuppapersize[#{from}][#{to}]\n"
+ f << "\\definepapersize\n";
+ f << " [offset=#{paperoffset}]\n";
+ f << "\\setuplayout\n";
+ f << " [backspace=#{backspace},\n";
+ f << " topspace=#{topspace},\n";
+ f << " marking=on,\n" if @commandline.option('marking')
+ f << " width=middle,\n";
+ f << " height=middle,\n";
+ f << " location=middle,\n";
+ f << " header=0pt,\n";
+ f << " footer=0pt]\n";
+ f << "\\setupexternalfigures\n";
+ f << " [directory=]\n";
+ f << "\\starttext\n";
+ unless selection.empty? then
+ f << "\\filterpages\n"
+ f << " [#{files.first}][#{selection}][width=#{textwidth}]\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to selectt')
+ end
+ else
+ report('no files to select')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def copyoutput
+ copyortrim(false,'copy')
+ end
+
+ def trimoutput
+ copyortrim(true,'trim')
+ end
+
+ def copyortrim(trim=false,what='unknown')
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ scale = @commandline.checkedoption('scale')
+ begin
+ scale = (scale.to_f * 1000.0).to_i if scale.to_i < 10
+ rescue
+ scale = 1000
+ end
+ scale = scale.to_i
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ f << "\\starttext\n"
+ files.each do |filename|
+ result = @commandline.checkedoption('result','texexec')
+ begin
+ if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then
+ report("copying file: #{filename}")
+ f << "\\getfiguredimensions\n"
+ f << " [#{filename}]\n"
+ f << " [scale=#{scale},\n"
+ f << " page=1,\n"
+ f << " size=trimbox\n" if trim
+ f << "]\n"
+ f << "\\definepapersize\n"
+ f << " [copy]\n"
+ f << " [width=\\figurewidth,\n"
+ f << " height=\\figureheight]\n"
+ f << "\\setuppapersize\n"
+ f << " [copy][copy]\n"
+ f << "\\setuplayout\n"
+ f << " [page]\n"
+ f << "\\setupexternalfigures\n"
+ f << " [directory=]\n"
+ f << "\\copypages\n"
+ f << " [#{filename}]\n"
+ f << " [scale=#{scale},\n"
+ f << " marking=on,\n" if @commandline.option('markings')
+ f << " size=trimbox,\n" if trim
+ f << " offset=#{paperoffset}]\n"
+ end
+ rescue
+ report("wrong specification")
+ end
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report("no files to #{what}")
+ end
+ else
+ report("no files to #{what}")
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ # todo: make this styles
+
+ def combineoutput
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = if @commandline.option('sort') then @commandline.arguments.sort else @commandline.arguments end
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ combination = @commandline.checkedoption('combination','2*2').split(/[\*x]/o)
+ paperformat = @commandline.checkedoption('paperformat', 'A4*A4').split(/[\*x]/o)
+ bannerheight = @commandline.checkedoption('bannerheight', '')
+ nx, ny = combination[0] || '2', combination[1] || combination[0] || '2'
+ from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4'
+ f << "\\setuppapersize[#{from}][#{to}]\n"
+ f << "\\setuplayout\n"
+ f << " [topspace=#{paperoffset},backspace=#{paperoffset},\n"
+ f << " header=0pt,footer=0pt,\n"
+ f << " width=middle,height=middle]\n"
+ if bannerheight.empty? then
+ f << "\\setuplayout[footer=1cm]\n"
+ else
+ f << "\\definelayer[page][width=\\paperwidth,height=\\paperheight]\n"
+ f << "\\setupbackgrounds[page][background=page]\n"
+ end
+ if @commandline.option('nobanner') then
+ f << "\\setuplayout[footer=0cm]\n"
+ f << "\\setupbackgrounds[page][background=]\n"
+ end
+ f << "\\setupexternalfigures\n"
+ f << " [directory=]\n"
+ f << "\\starttext\n"
+ files.each do |filename|
+ result = @commandline.checkedoption('result','texexec')
+ if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then
+ report("combination file: #{filename}")
+ cleanname = cleantexfilename(filename).downcase
+ bannerstring = "\\tttf #{cleanname}\\quad\\quad\\currentdate\\quad\\quad\\pagenumber"
+ if bannerheight.empty? then
+ f << "\\setupfootertexts\n"
+ f << " [#{bannerstring}]\n"
+ else
+ # for the moment we lack a better hook
+ f << "\\setuptexttexts\n"
+ f << " [{\\setlayerframed[page][preset=middlebottom][frame=off,height=#{bannerheight}]{#{bannerstring}}}]\n"
+ end
+ f << "\\combinepages[#{filename}][nx=#{nx},ny=#{ny}]\n"
+ f << "\\page\n"
+ end
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to list')
+ end
+ else
+ report('no files to list')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ private
+
+ def prepare(job)
+
+ job.booleanvars.each do |k|
+ job.setvariable(k,@commandline.option(k))
+ end
+ job.stringvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+ job.standardvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+ job.knownvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+
+job.setvariable('given.backend',job.getvariable('backend'))
+
+ if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then
+ job.setvariable('texengine',str)
+ elsif @commandline.oneof('luatex') then
+ job.setvariable('texengine','luatex')
+ elsif @commandline.oneof('pdfetex','pdftex','pdf') then
+ job.setvariable('texengine','pdftex')
+ elsif @commandline.oneof('xetex','xtx') then
+ job.setvariable('texengine','xetex')
+ elsif @commandline.oneof('aleph') then
+ job.setvariable('texengine','aleph')
+ elsif @commandline.oneof('petex') then
+ job.setvariable('texengine','petex')
+ else
+ job.setvariable('texengine','standard')
+ end
+
+ if (str = @commandline.option('backend')) && ! str.standard? && ! str.empty? then
+ job.setvariable('backend',str)
+ elsif @commandline.oneof('pdfetex','pdftex','pdf','luatex') then
+ job.setvariable('backend','pdftex')
+ elsif @commandline.oneof('dvipdfmx','dvipdfm','dpx','dpm') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('xetex','xtx') then
+ job.setvariable('backend','xetex')
+ elsif @commandline.oneof('aleph') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('petex') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('dvips','ps') then
+ job.setvariable('backend','dvips')
+ elsif @commandline.oneof('xdv') then
+ job.setvariable('backend','xdv')
+ else
+ case job.getvariable('texengine')
+ when 'pdfetex' then job.setvariable('backend','pdftex')
+ when 'pdftex' then job.setvariable('backend','pdftex')
+ when 'luatex' then job.setvariable('backend','pdftex')
+ when 'xetex' then job.setvariable('backend','xetex')
+ when 'petex' then job.setvariable('backend','dvipdfmx')
+ when 'aleph' then job.setvariable('backend','dvipdfmx')
+ else
+ job.setvariable('backend','standard')
+ end
+ end
+
+ if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then
+ job.setvariable('mpsengine',@commandline.option('engine'))
+ else
+ job.setvariable('mpsengine','standard')
+ end
+
+ end
+
+ def cleantexfilename(filename)
+ filename.gsub(/([\$\_\#])/) do "\\$1" end.gsub(/([\~])/) do "\\string$1" end
+ end
+
+end
+
+# we will make this pluggable, i.e. load plugins from base/tex that
+# extend the class and may even add switches
+#
+# commandline.load_plugins('base/tex')
+#
+# maybe it's too slow so for a while keep the --pdf* in here
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('make', 'make formats')
+commandline.registeraction('check', 'check versions')
+commandline.registeraction('process', 'process file')
+commandline.registeraction('mptex', 'process mp file')
+commandline.registeraction('mpxtex', 'process mpx file')
+commandline.registeraction('mpgraphic', 'process mp file to stand-alone graphics')
+commandline.registeraction('mpstatic', 'process mp/ctx file to stand-alone graphics')
+
+commandline.registeraction('listing', 'list of file content')
+commandline.registeraction('figures', 'generate overview of figures')
+commandline.registeraction('modules', 'generate module documentation')
+commandline.registeraction('pdfarrange', 'impose pages (booklets)')
+commandline.registeraction('pdfselect', 'select pages from file(s)')
+commandline.registeraction('pdfcopy', 'copy pages from file(s)')
+commandline.registeraction('pdftrim', 'trim pages from file(s)')
+commandline.registeraction('pdfcombine', 'combine multiple pages')
+commandline.registeraction('pdfsplit', 'split file in pages')
+
+# compatibility switch
+
+class Commands
+
+ include CommandBase
+
+ alias pdfarrange :arrangeoutput
+ alias pdfselect :selectoutput
+ alias pdfcopy :copyoutput
+ alias pdftrim :trimoutput
+ alias pdfcombine :combineoutput
+
+end
+
+# so far for compatibility, will move to tex
+
+@@extrastringvars = [
+ 'pages', 'background', 'backspace', 'topspace', 'boxtype', 'tempdir','bannerheight',
+ 'printformat', 'method', 'scale', 'selection',
+ 'combination', 'textwidth', 'addempty', 'logfile',
+ 'startline', 'endline', 'startcolumn', 'endcolumn', 'scale'
+]
+
+@@extrabooleanvars = [
+ 'centerpage', 'noduplex', 'color', 'pretty',
+ 'fullscreen', 'screensaver', 'markings'
+]
+
+if job = TEX.new(logger) then
+
+ job.setextrastringvars(@@extrastringvars)
+ job.setextrabooleanvars(@@extrabooleanvars)
+
+ job.booleanvars.each do |k|
+ commandline.registerflag(k)
+ end
+ job.stringvars.each do |k|
+ commandline.registervalue(k,'')
+ end
+ job.standardvars.each do |k|
+ commandline.registervalue(k,'standard')
+ end
+ job.knownvars.each do |k|
+ commandline.registervalue(k,'')
+ end
+
+end
+
+class Commands
+
+ alias saved_help help
+
+ def wrap_help(title, vars)
+ report("")
+ report(title)
+ report("")
+ r, n = '', 0
+ vars.sort.each do |s|
+ if n == 5 then
+ report(r)
+ r, n = '', 1
+ else
+ n += 1
+ end
+ r << ' ' + s
+ end
+ report(r) unless r.empty?
+ end
+
+ def help
+ saved_help
+ if @commandline.option('all') then
+ if job = TEX.new(logger) then
+ wrap_help("boolean switches:", job.allbooleanvars)
+ wrap_help("string switches:", job.allstringvars)
+ end
+ else
+ report('')
+ report('--help --all shows all switches')
+ end
+ end
+
+end
+
+# todo: register flags -> first one true
+
+commandline.registerflag('pdf')
+commandline.registerflag('pdftex')
+commandline.registerflag('pdfetex')
+commandline.registerflag('luatex')
+
+commandline.registerflag('dvipdfmx')
+commandline.registerflag('dvipdfm')
+commandline.registerflag('dpx')
+commandline.registerflag('dpm')
+
+commandline.registerflag('dvips')
+commandline.registerflag('ps')
+
+commandline.registerflag('xetex')
+commandline.registerflag('xtx')
+commandline.registerflag('xdv')
+
+commandline.registerflag('aleph')
+
+commandline.registerflag('petex')
+
+commandline.registerflag('all')
+commandline.registerflag('fast')
+commandline.registerflag('sort')
+
+# generic
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('verbose')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).execute(commandline.action || 'main') # or just execute()
diff --git a/scripts/context/ruby/texmfstart.rb b/scripts/context/ruby/texmfstart.rb
new file mode 100644
index 000000000..97087c3ae
--- /dev/null
+++ b/scripts/context/ruby/texmfstart.rb
@@ -0,0 +1,1277 @@
+#!/usr/bin/env ruby
+#encoding: ASCII-8BIT
+
+# We have removed the fast, server and client variants and no longer
+# provide the distributed 'serve trees' option. After all, we're now
+# using luatex.
+
+# program : texmfstart
+# copyright : PRAGMA Advanced Document Engineering
+# version : 1.9.0 - 2003/2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+# no special requirements, i.e. no exa modules/classes used
+
+# texmfstart [switches] filename [optional arguments]
+#
+# ruby2exe texmfstart --help -> avoids stub test
+#
+# Of couse I can make this into a nice class, which i'll undoubtely will
+# do when I feel the need. In that case it will be part of a bigger game.
+
+# --locate => provides location
+# --exec => exec instead of system
+# --iftouched=a,b => only if timestamp a<>b
+# --ifchanged=a,b => only if checksum changed
+#
+# file: path: bin:
+
+# texmfstart --exec bin:scite *.tex
+
+# we don't depend on other libs
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require "rbconfig"
+require "fileutils"
+
+require "digest/md5"
+
+# kpse_merge_start
+
+class File
+ def File::makedirs(*x)
+ FileUtils.makedirs(x)
+ end
+end
+
+class String
+
+ def split_path
+ if self =~ /\;/o || self =~ /^[a-z]\:/io then
+ self.split(";")
+ else
+ self.split(":")
+ end
+ end
+
+end
+
+class Array
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+end
+
+class File
+
+ def File.locate_file(path,name)
+ begin
+ files = Dir.entries(path)
+ if files.include?(name) then
+ fullname = File.join(path,name)
+ return fullname if FileTest.file?(fullname)
+ end
+ files.each do |p|
+ fullname = File.join(path,p)
+ if p != '.' and p != '..' and FileTest.directory?(fullname) and result = locate_file(fullname,name) then
+ return result
+ end
+ end
+ rescue
+ # bad path
+ end
+ return nil
+ end
+
+ def File.glob_file(pattern)
+ return Dir.glob(pattern).first
+ end
+
+end
+
+# kpse_merge_file: 't:/ruby/base/kpsedirect.rb'
+
+class KpseDirect
+
+ attr_accessor :progname, :format, :engine
+
+ def initialize
+ @progname, @format, @engine = '', '', ''
+ end
+
+ def expand_path(str)
+ clean_name(`kpsewhich -expand-path=#{str}`.chomp)
+ end
+
+ def expand_var(str)
+ clean_name(`kpsewhich -expand-var=#{str}`.chomp)
+ end
+
+ def find_file(str)
+ clean_name(`kpsewhich #{_progname_} #{_format_} #{str}`.chomp)
+ end
+
+ def _progname_
+ if @progname.empty? then '' else "-progname=#{@progname}" end
+ end
+ def _format_
+ if @format.empty? then '' else "-format=\"#{@format}\"" end
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
+
+# kpse_merge_stop
+
+$mswindows = Config::CONFIG['host_os'] =~ /mswin/
+$separator = File::PATH_SEPARATOR
+$version = "2.1.0"
+$ownpath = File.dirname($0)
+
+if $mswindows then
+ require "win32ole"
+ require "Win32API"
+end
+
+# exit if defined?(REQUIRE2LIB)
+
+$stdout.sync = true
+$stderr.sync = true
+
+$applications = Hash.new
+$suffixinputs = Hash.new
+$predefined = Hash.new
+$runners = Hash.new
+
+$suffixinputs['pl'] = 'PERLINPUTS'
+$suffixinputs['rb'] = 'RUBYINPUTS'
+$suffixinputs['py'] = 'PYTHONINPUTS'
+$suffixinputs['lua'] = 'LUAINPUTS'
+$suffixinputs['jar'] = 'JAVAINPUTS'
+$suffixinputs['pdf'] = 'PDFINPUTS'
+
+$predefined['texexec'] = 'texexec.rb'
+$predefined['texutil'] = 'texutil.rb'
+$predefined['texfont'] = 'texfont.pl'
+$predefined['texshow'] = 'texshow.pl'
+
+$predefined['makempy'] = 'makempy.pl'
+$predefined['mptopdf'] = 'mptopdf.pl'
+$predefined['pstopdf'] = 'pstopdf.rb'
+
+$predefined['examplex'] = 'examplex.rb'
+$predefined['concheck'] = 'concheck.rb'
+
+$predefined['runtools'] = 'runtools.rb'
+$predefined['textools'] = 'textools.rb'
+$predefined['tmftools'] = 'tmftools.rb'
+$predefined['ctxtools'] = 'ctxtools.rb'
+$predefined['rlxtools'] = 'rlxtools.rb'
+$predefined['pdftools'] = 'pdftools.rb'
+$predefined['mpstools'] = 'mpstools.rb'
+# $predefined['exatools'] = 'exatools.rb'
+$predefined['xmltools'] = 'xmltools.rb'
+# $predefined['luatools'] = 'luatools.lua'
+# $predefined['mtxtools'] = 'mtxtools.rb'
+
+$predefined['newpstopdf'] = 'pstopdf.rb'
+$predefined['newtexexec'] = 'texexec.rb'
+$predefined['pdftrimwhite'] = 'pdftrimwhite.pl'
+
+$makelist = [
+ # context
+ 'texexec',
+ 'texutil',
+ 'texfont',
+ # mp/ps
+ 'pstopdf',
+ 'mptopdf',
+ 'makempy',
+ # misc
+ 'ctxtools',
+ 'pdftools',
+ 'xmltools',
+ 'textools',
+ 'mpstools',
+ 'tmftools',
+ 'exatools',
+ 'runtools',
+ 'rlxtools',
+ 'pdftrimwhite',
+ 'texfind',
+ 'texshow'
+ #
+ # no 'luatools',
+ # no 'mtxtools',
+ # no, 'texmfstart'
+]
+
+$scriptlist = 'rb|pl|py|lua|jar'
+$documentlist = 'pdf|ps|eps|htm|html'
+
+$editor = ENV['TEXMFSTART_EDITOR'] || ENV['EDITOR'] || ENV['editor'] || 'scite'
+
+$crossover = true # to other tex tools, else only local
+$kpse = nil
+
+def set_applications(page=1)
+
+ $applications['unknown'] = ''
+ $applications['ruby'] = $applications['rb'] = 'ruby'
+ $applications['lua'] = $applications['lua'] = 'lua'
+ $applications['perl'] = $applications['pl'] = 'perl'
+ $applications['python'] = $applications['py'] = 'python'
+ $applications['java'] = $applications['jar'] = 'java'
+
+ if $mswindows then
+ $applications['pdf'] = ['',"pdfopen --page #{page} --file",'acroread']
+ $applications['html'] = ['','netscape','mozilla','opera','iexplore']
+ $applications['ps'] = ['','gview32','gv','gswin32','gs']
+ else
+ $applications['pdf'] = ["pdfopen --page #{page} --file",'acroread']
+ $applications['html'] = ['netscape','mozilla','opera']
+ $applications['ps'] = ['gview','gv','gs']
+ end
+
+ $applications['htm'] = $applications['html']
+ $applications['eps'] = $applications['ps']
+
+ $runners['lua'] = "texlua"
+
+end
+
+set_applications()
+
+def check_kpse
+ if $kpse then
+ # already done
+ else
+ $kpse = KpseDirect.new
+ end
+end
+
+if $mswindows then
+
+ GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', ['P','P','N'], 'N')
+ GetLongPathName = Win32API.new('kernel32', 'GetLongPathName', ['P','P','N'], 'N')
+
+ def dowith_pathname (filename,filemethod)
+ filename = filename.gsub(/\\/o,'/') # no gsub! because filename can be frozen
+ case filename
+ when /\;/o then
+ # could be a path spec
+ return filename
+ when /\s+/o then
+ # danger lurking
+ buffer = ' ' * 260
+ length = filemethod.call(filename,buffer,buffer.size)
+ if length>0 then
+ return buffer.slice(0..length-1)
+ else
+ # when the path or file does not exist, nothing is returned
+ # so we try to handle the path separately from the basename
+ basename = File.basename(filename)
+ pathname = File.dirname(filename)
+ length = filemethod.call(pathname,buffer,260)
+ if length>0 then
+ return buffer.slice(0..length-1) + '/' + basename
+ else
+ return filename
+ end
+ end
+ else
+ # no danger
+ return filename
+ end
+ end
+
+ def longpathname (filename)
+ dowith_pathname(filename,GetLongPathName)
+ end
+
+ def shortpathname (filename)
+ dowith_pathname(filename,GetShortPathName)
+ end
+
+else
+
+ def longpathname (filename)
+ filename
+ end
+
+ def shortpathname (filename)
+ filename
+ end
+
+end
+
+class File
+
+ @@update_eps = 1
+
+ def File.needsupdate(oldname,newname)
+ begin
+ oldtime = File.stat(oldname).mtime.to_i
+ newtime = File.stat(newname).mtime.to_i
+ if newtime >= oldtime then
+ return false
+ elsif oldtime-newtime < @@update_eps then
+ return false
+ else
+ return true
+ end
+ rescue
+ return true
+ end
+ end
+
+ def File.syncmtimes(oldname,newname)
+ return
+ begin
+ if $mswindows then
+ # does not work (yet) / gives future timestamp
+ # t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
+ # File.utime(0,t,oldname,newname)
+ else
+ t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
+ File.utime(0,t,oldname,newname)
+ end
+ rescue
+ end
+ end
+
+ def File.timestamp(name)
+ begin
+ "#{File.stat(name).mtime}"
+ rescue
+ return 'unknown'
+ end
+ end
+
+end
+
+def hashed (arr=[])
+ arg = if arr.class == String then arr.split(' ') else arr.dup end
+ hsh = Hash.new
+ if arg.length > 0
+ hsh['arguments'] = ''
+ done = false
+ arg.each do |s|
+ if done then
+ if s =~ /\s/ then
+ kvl = s.split('=')
+ if kvl[1] and kvl[1] !~ /^[\"\']/ then
+ hsh['arguments'] += ' ' + kvl[0] + "=" + '"' + kvl[1] + '"'
+ elsif s =~ /\s/ then
+ hsh['arguments'] += ' "' + s + '"'
+ else
+ hsh['arguments'] += ' ' + s
+ end
+ else
+ hsh['arguments'] += ' ' + s
+ end
+ else
+ kvl = s.split('=')
+ if kvl[0].sub!(/^\-+/,'') then
+ hsh[kvl[0]] = if kvl.length > 1 then kvl[1] else true end
+ else
+ hsh['file'] = s
+ done = true
+ end
+ end
+ end
+ end
+ return hsh
+end
+
+
+def launch(filename)
+ if $browser && $mswindows then
+ filename = filename.gsub(/\.[\/\\]/) do
+ Dir.getwd + '/'
+ end
+ report("launching #{filename}")
+ ie = WIN32OLE.new("InternetExplorer.Application")
+ ie.visible = true
+ ie.navigate(filename)
+ return true
+ else
+ return false
+ end
+end
+
+# env|environment
+# rel|relative
+# loc|locate|kpse|path|file
+
+def quoted(str)
+ if str =~ /^\"/ then
+ return str
+ elsif str =~ / / then
+ return "\"#{str}\""
+ else
+ return str
+ end
+end
+
+def expanded(arg) # no "other text files", too restricted
+ arg.gsub(/(env|environment)\:([a-zA-Z\-\_\.0-9]+)/o) do
+ method, original, resolved = $1, $2, ''
+ if resolved = ENV[original] then
+ report("environment variable #{original} expands to #{resolved}") unless $report
+ quoted(resolved)
+ else
+ report("environment variable #{original} cannot be resolved") unless $report
+ quoted(original)
+ end
+ end . gsub(/(rel|relative)\:([a-zA-Z\-\_\.0-9]+)/o) do
+ method, original, resolved = $1, $2, ''
+ ['.','..','../..'].each do |r|
+ if FileTest.file?(File.join(r,original)) then
+ resolved = File.join(r,original)
+ break
+ end
+ end
+ if resolved.empty? then
+ quoted(original)
+ else
+ quoted(resolved)
+ end
+ end . gsub(/(kpse|loc|locate|file|path)\:([a-zA-Z\-\_\.0-9]+)/o) do
+ method, original, resolved = $1, $2, ''
+ if $program && ! $program.empty? then
+ # pstrings = ["-progname=#{$program}"]
+ pstrings = [$program]
+ else
+ # pstrings = ['','-progname=context']
+ pstrings = ['','context']
+ end
+ # auto suffix with texinputs as fall back
+ if ENV["_CTX_K_V_#{original}_"] then
+ resolved = ENV["_CTX_K_V_#{original}_"]
+ report("environment provides #{original} as #{resolved}") unless $report
+ quoted(resolved)
+ else
+ check_kpse
+ pstrings.each do |pstr|
+ if resolved.empty? then
+ # command = "kpsewhich #{pstr} #{original}"
+ # report("running #{command}")
+ report("locating '#{original}' in program space '#{pstr}'")
+ begin
+ # resolved = `#{command}`.chomp
+ $kpse.progname = pstr
+ $kpse.format = ''
+ resolved = $kpse.find_file(original).gsub(/\\/,'/')
+ rescue
+ resolved = ''
+ end
+ end
+ # elsewhere in the tree
+ if resolved.empty? then
+ # command = "kpsewhich #{pstr} -format=\"other text files\" #{original}"
+ # report("running #{command}")
+ report("locating '#{original}' in program space '#{pstr}' using format 'other text files'")
+ begin
+ # resolved = `#{command}`.chomp
+ $kpse.progname = pstr
+ $kpse.format = 'other text files'
+ resolved = $kpse.find_file(original).gsub(/\\/,'/')
+ rescue
+ resolved = ''
+ end
+ end
+ end
+ if resolved.empty? then
+ original = File.dirname(original) if method =~ /path/
+ report("#{original} is not resolved") unless $report
+ ENV["_CTX_K_V_#{original}_"] = original if $crossover
+ quoted(original)
+ else
+ resolved = File.dirname(resolved) if method =~ /path/
+ report("#{original} is resolved to #{resolved}") unless $report
+ ENV["_CTX_K_V_#{original}_"] = resolved if $crossover
+ quoted(resolved)
+ end
+ end
+ end
+end
+
+def changeddir?(path)
+ if path.empty? then
+ return true
+ else
+ oldpath = File.expand_path(path)
+ begin
+ Dir.chdir(path) if not path.empty?
+ rescue
+ report("unable to change to directory: #{path}")
+ else
+ report("changed to directory: #{path}")
+ end
+ newpath = File.expand_path(Dir.getwd)
+ return oldpath == newpath
+ end
+end
+
+def runcommand(command)
+ if $locate then
+ command = command.split(' ').collect do |c|
+ if c =~ /\//o then
+ begin
+ cc = File.expand_path(c)
+ c = cc if FileTest.file?(cc)
+ rescue
+ end
+ end
+ c
+ end . join(' ')
+ print command # to stdout and no newline
+ elsif $execute then
+ report("using 'exec' instead of 'system' call: #{command}")
+ exec(command) if changeddir?($path)
+ else
+ report("using 'system' call: #{command}")
+ system(command) if changeddir?($path)
+ end
+end
+
+def join_command(args)
+ args[0] = $runners[args[0]] || args[0]
+ [args].join(' ')
+end
+
+def runoneof(application,fullname,browserpermitted)
+ if browserpermitted && launch(fullname) then
+ return true
+ else
+ fullname = quoted(fullname) # added because MM ran into problems
+ report("starting #{$filename}") unless $report
+ output("\n") if $report && $verbose
+ applications = $applications[application.downcase]
+ if ! applications then
+ output("problems with determining application type")
+ return true
+ elsif applications.class == Array then
+ if $report then
+ output(join_command([fullname,expanded($arguments)]))
+ return true
+ else
+ applications.each do |a|
+ return true if runcommand(join_command([a,fullname,expanded($arguments)]))
+ end
+ end
+ elsif applications.empty? then
+ if $report then
+ output(join_command([fullname,expanded($arguments)]))
+ return true
+ else
+ return runcommand(join_command([fullname,expanded($arguments)]))
+ end
+ else
+ if $report then
+ output(join_command([applications,fullname,expanded($arguments)]))
+ return true
+ else
+ return runcommand(join_command([applications,fullname,expanded($arguments)]))
+ end
+ end
+ return false
+ end
+end
+
+def report(str)
+ $stdout.puts(str) if $verbose
+end
+
+def output(str)
+ $stdout.puts(str)
+end
+
+def usage
+ print "version : #{$version} - 2003/2006 - www.pragma-ade.com\n"
+ print("\n")
+ print("usage : texmfstart [switches] filename [optional arguments]\n")
+ print("\n")
+ print("switches : --verbose --report --browser --direct --execute --locate --iftouched --ifchanged\n")
+ print(" --program --file --page --arguments --batch --edit --report --clear\n")
+ print(" --make --lmake --wmake --path --stubpath --indirect --before --after\n")
+ print(" --tree --autotree --environment --showenv\n")
+ print("\n")
+ print("example : texmfstart pstopdf.rb cow.eps\n")
+ print(" texmfstart --locate examplex.rb\n")
+ print(" texmfstart --execute examplex.rb\n")
+ print(" texmfstart --browser examplap.pdf\n")
+ print(" texmfstart showcase.pdf\n")
+ print(" texmfstart --page=2 --file=showcase.pdf\n")
+ print(" texmfstart --program=yourtex yourscript.rb arg-1 arg-2\n")
+ print(" texmfstart --direct xsltproc kpse:somefile.xsl somefile.xml\n")
+ print(" texmfstart --direct ruby rel:wn-cleanup-1.rb oldfile.xml newfile.xml\n")
+ print(" texmfstart bin:xsltproc env:somepreset path:somefile.xsl somefile.xml\n")
+ print(" texmfstart --iftouched=normal,lowres downsample.rb normal lowres\n")
+ print(" texmfstart --ifchanged=somefile.dat --direct processit somefile.dat\n")
+ print(" texmfstart bin:scite kpse:texmf.cnf\n")
+ print(" texmfstart --exec bin:scite *.tex\n")
+ print(" texmfstart --edit texmf.cnf\n")
+ print(" texmfstart --edit kpse:texmf.cnf\n")
+ print(" texmfstart --serve\n")
+ print("\n")
+ print(" texmfstart --stubpath=/usr/local/bin [--make --remove] --verbose all\n")
+ print(" texmfstart --stubpath=auto [--make --remove] all\n")
+ print("\n")
+ check_kpse
+end
+
+# somehow registration does not work out (at least not under windows)
+# the . is also not accepted by unix as seperator
+
+def tag(name)
+ if $crossover then "_CTX_K_S_#{name}_" else "TEXMFSTART.#{name}" end
+end
+
+def registered?(filename)
+ return ENV[tag(filename)] != nil
+end
+
+def registered(filename)
+ return ENV[tag(filename)] || 'unknown'
+end
+
+def register(filename,fullname)
+ if fullname && ! fullname.empty? then # && FileTest.file?(fullname)
+ ENV[tag(filename)] = fullname
+ report("registering '#{filename}' as '#{fullname}'")
+ return true
+ else
+ return false
+ end
+end
+
+def find(filename,program)
+ begin
+ filename = filename.sub(/script:/o, '') # so we have bin: and script: and nothing
+ if $predefined.key?(filename) then
+ report("expanding '#{filename}' to '#{$predefined[filename]}'")
+ filename = $predefined[filename]
+ end
+ if registered?(filename) then
+ report("already located '#{filename}'")
+ return registered(filename)
+ end
+ # create suffix list
+ if filename =~ /^(.*)\.(.+)$/ then
+ filename = $1
+ suffixlist = [$2]
+ else
+ suffixlist = [$scriptlist.split('|'),$documentlist.split('|')].flatten
+ end
+ # first we honor a given path
+ if filename =~ /[\\\/]/ then
+ report("trying to honor '#{filename}'")
+ suffixlist.each do |suffix|
+ fullname = filename+'.'+suffix
+ if FileTest.file?(fullname) && register(filename,fullname)
+ return shortpathname(fullname)
+ end
+ end
+ end
+ filename.sub!(/^.*[\\\/]/, '')
+ # next we look at the current path and the callerpath
+ pathlist = [ ]
+ progpath = $applications[suffixlist[0]]
+ threadok = registered("THREAD") !~ /unknown/
+ pathlist << ['.','current']
+ pathlist << [$ownpath,'caller'] if $ownpath != '.'
+ pathlist << ["#{$ownpath}/../#{progpath}",'caller'] if progpath
+ pathlist << [registered("THREAD"),'thread'] if threadok
+ pathlist << ["#{registered("THREAD")}/../#{progpath}",'thread'] if progpath && threadok
+ pathlist.each do |p|
+ if p && ! p.empty? && ! (p[0] == 'unknown') then
+ suffixlist.each do |suffix|
+ fname = "#{filename}.#{suffix}"
+ fullname = File.expand_path(File.join(p[0],fname))
+ report("locating '#{fname}' in #{p[1]} path '#{p[0]}'")
+ if FileTest.file?(fullname) && register(filename,fullname) then
+ report("'#{fname}' located in #{p[1]} path")
+ return shortpathname(fullname)
+ end
+ end
+ end
+ end
+ # now we consult environment settings
+ fullname = nil
+ check_kpse
+ $kpse.progname = program
+ suffixlist.each do |suffix|
+ begin
+ break unless $suffixinputs[suffix]
+ environment = ENV[$suffixinputs[suffix]] || ENV[$suffixinputs[suffix]+".#{$program}"]
+ if ! environment || environment.empty? then
+ begin
+ # environment = `kpsewhich -expand-path=\$#{$suffixinputs[suffix]}`.chomp
+ environment = $kpse.expand_path("\$#{$suffixinputs[suffix]}")
+ rescue
+ environment = nil
+ else
+ if environment && ! environment.empty? then
+ report("using kpsewhich variable #{$suffixinputs[suffix]}")
+ end
+ end
+ elsif environment && ! environment.empty? then
+ report("using environment variable #{$suffixinputs[suffix]}")
+ end
+ if environment && ! environment.empty? then
+ environment.split($separator).each do |e|
+ e.strip!
+ e = '.' if e == '\.' # somehow . gets escaped
+ e += '/' unless e =~ /[\\\/]$/
+ fullname = e + filename + '.' + suffix
+ report("testing '#{fullname}'")
+ if FileTest.file?(fullname) then
+ break
+ else
+ fullname = nil
+ end
+ end
+ end
+ rescue
+ report("environment string '#{$suffixinputs[suffix]}' cannot be used to locate '#{filename}'")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ return shortpathname(fullname) if register(filename,fullname)
+ # then we fall back on kpsewhich
+ suffixlist.each do |suffix|
+ # TDS script scripts location as per 2004
+ if suffix =~ /(#{$scriptlist})/ then
+ begin
+ report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (1)")
+ # fullname = `kpsewhich -progname=#{program} -format=texmfscripts #{filename}.#{suffix}`.chomp
+ $kpse.format = 'texmfscripts'
+ fullname = $kpse.find_file("#{filename}.#{suffix}").gsub(/\\/,'/')
+ rescue
+ report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (1)")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ # old TDS location: .../texmf/context/...
+ begin
+ report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (2)")
+ # fullname = `kpsewhich -progname=#{program} -format="other text files" #{filename}.#{suffix}`.chomp
+ $kpse.format = 'other text files'
+ fullname = $kpse.find_file("#{filename}.#{suffix}").gsub(/\\/,'/')
+ rescue
+ report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (2)")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ return shortpathname(fullname) if register(filename,fullname)
+ # let's take a look at the path
+ paths = ENV['PATH'].split($separator)
+ suffixlist.each do |s|
+ paths.each do |p|
+ suffixedname = "#{filename}.#{s}"
+ report("checking #{p} for #{filename}")
+ if FileTest.file?(File.join(p,suffixedname)) then
+ fullname = File.join(p,suffixedname)
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ end
+ # bad luck, we need to search the tree ourselves
+ if (suffixlist.length == 1) && (suffixlist.first =~ /(#{$documentlist})/) then
+ report("aggressively locating '#{filename}' in document trees")
+ begin
+ # texroot = `kpsewhich -expand-var=$SELFAUTOPARENT`.chomp
+ texroot = $kpse.expand_var("$SELFAUTOPARENT")
+ rescue
+ texroot = ''
+ else
+ texroot.sub!(/[\\\/][^\\\/]*?$/, '')
+ end
+ if not texroot.empty? then
+ sffxlst = suffixlist.join(',')
+ begin
+ report("locating '#{filename}' in document tree '#{texroot}/doc*'")
+ if (result = Dir.glob("#{texroot}/doc*/**/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then
+ fullname = result[0]
+ end
+ rescue
+ report("locating '#{filename}.#{suffixlist.join('|')}' in tree '#{texroot}' aborted")
+ end
+ end
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ report("aggressively locating '#{filename}' in tex trees")
+ begin
+ # textrees = `kpsewhich -expand-var=$TEXMF`.chomp
+ textrees = $kpse.expand_var("$TEXMF")
+ rescue
+ textrees = ''
+ end
+ if not textrees.empty? then
+ textrees.gsub!(/[\{\}\!]/, '')
+ textrees = textrees.split(',')
+ if (suffixlist.length == 1) && (suffixlist.first =~ /(#{$documentlist})/) then
+ speedup = ['doc**','**']
+ else
+ speedup = ['**']
+ end
+ sffxlst = suffixlist.join(',')
+ speedup.each do |speed|
+ textrees.each do |tt|
+ tt.gsub!(/[\\\/]$/, '')
+ if FileTest.directory?(tt) then
+ begin
+ report("locating '#{filename}' in tree '#{tt}/#{speed}/#{filename}.{#{sffxlst}}'")
+ if (result = Dir.glob("#{tt}/#{speed}/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then
+ fullname = result[0]
+ break
+ end
+ rescue
+ report("locating '#{filename}' in tree '#{tt}' aborted")
+ next
+ end
+ end
+ end
+ break if fullname && ! fullname.empty?
+ end
+ end
+ if register(filename,fullname) then
+ return shortpathname(fullname)
+ else
+ return ''
+ end
+ rescue
+ error, trace = $!, $@.join("\n")
+ report("fatal error: #{error}\n#{trace}")
+ # report("fatal error")
+ end
+end
+
+def run(fullname)
+ if ! fullname || fullname.empty? then
+ output("the file '#{$filename}' is not found")
+ elsif FileTest.file?(fullname) then
+ begin
+ case fullname
+ when /\.(#{$scriptlist})$/i then
+ return runoneof($1,fullname,false)
+ when /\.(#{$documentlist})$/i then
+ return runoneof($1,fullname,true)
+ else
+ return runoneof('unknown',fullname,false)
+ end
+ rescue
+ report("starting '#{$filename}' in program space '#{$program}' fails (#{$!})")
+ end
+ else
+ report("the file '#{$filename}' in program space '#{$program}' is not accessible")
+ end
+ return false
+end
+
+def direct(fullname)
+ begin
+ return runcommand([fullname.sub(/^(bin|binary)\:/, ''),expanded($arguments)].join(' '))
+ rescue
+ return false
+ end
+end
+
+def edit(filename)
+ begin
+ return runcommand([$editor,expanded(filename),expanded($arguments)].join(' '))
+ rescue
+ return false
+ end
+end
+
+def make(filename,windows=false,linux=false,remove=false)
+ basename = File.basename(filename).gsub(/\.[^.]+?$/, '')
+ if $stubpath == 'auto' then
+ basename = File.dirname($0) + '/' + basename
+ else
+ basename = $stubpath + '/' + basename unless $stubpath.empty?
+ end
+ if filename == 'texmfstart' then
+ program = 'ruby'
+ command = 'kpsewhich --format=texmfscripts --progname=context texmfstart.rb'
+ filename = `#{command}`.chomp.gsub(/\\/, '/')
+ if filename.empty? then
+ report("failure: #{command}")
+ return
+ elsif not remove then
+ if windows then
+ ['bat','cmd','exe'].each do |suffix|
+ if FileTest.file?("#{basename}.#{suffix}") then
+ report("windows stub '#{basename}.#{suffix}' skipped (already present)")
+ return
+ end
+ end
+ elsif linux && FileTest.file?(basename) then
+ report("unix stub '#{basename}' skipped (already present)")
+ return
+ end
+ end
+ else
+ program = nil
+ if filename =~ /[\\\/]/ && filename =~ /\.(#{$scriptlist})$/ then
+ program = $applications[$1]
+ end
+ filename = "\"#{filename}\"" if filename =~ /\s/
+ program = 'texmfstart' if $indirect || ! program || program.empty?
+ end
+ begin
+ callname = $predefined[filename.sub(/\.*?$/,'')] || filename
+ if remove then
+ if windows && (File.delete(basename+'.bat') rescue false) then
+ report("windows stub '#{basename}.bat' removed (calls #{callname})")
+ elsif linux && (File.delete(basename) rescue false) then
+ report("unix stub '#{basename}' removed (calls #{callname})")
+ end
+ else
+ if windows && f = open(basename+'.bat','w') then
+ f.binmode
+ f.write("@echo off\015\012")
+ f.write("#{program} #{callname} %*\015\012")
+ f.close
+ report("windows stub '#{basename}.bat' made (calls #{callname})")
+ elsif linux && f = open(basename,'w') then
+ f.binmode
+ f.write("#!/bin/sh\012")
+ f.write("#{program} #{callname} \"$@\"\012")
+ f.close
+ report("unix stub '#{basename}' made (calls #{callname})")
+ end
+ end
+ rescue
+ report("failed to make stub '#{basename}' #{$!}")
+ return false
+ else
+ return true
+ end
+end
+
+def process(&block)
+ if $iftouched then
+ files = $directives['iftouched'].split(',')
+ oldname, newname = files[0], files[1]
+ if oldname && newname && File.needsupdate(oldname,newname) then
+ report("file #{oldname}: #{File.timestamp(oldname)}")
+ report("file #{newname}: #{File.timestamp(newname)}")
+ report("file is touched, processing started")
+ yield
+ File.syncmtimes(oldname,newname)
+ else
+ report("file #{oldname} is untouched")
+ end
+ elsif $ifchanged then
+ filename = $directives['ifchanged']
+ checkname = filename + ".md5"
+ oldchecksum, newchecksum = "old", "new"
+ begin
+ newchecksum = Digest::MD5.hexdigest(IO.read(filename)).upcase
+ rescue
+ newchecksum = "new"
+ else
+ begin
+ oldchecksum = IO.read(checkname).chomp
+ rescue
+ oldchecksum = "old"
+ end
+ end
+ if $verbose then
+ report("old checksum #{filename}: #{oldchecksum}")
+ report("new checksum #{filename}: #{newchecksum}")
+ end
+ if oldchecksum != newchecksum then
+ report("file is changed, processing started")
+ begin
+ File.open(checkname,'w') do |f|
+ f << newchecksum
+ end
+ rescue
+ end
+ yield
+ else
+ report("file #{filename} is unchanged")
+ end
+ else
+ yield
+ end
+end
+
+def checkenvironment(tree)
+ report('')
+ ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME']
+ case RUBY_PLATFORM
+ when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin'
+ when /(linux)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux'
+ when /(darwin|rhapsody|nextstep)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx'
+ # when /(netbsd|unix)/i then # todo
+ else # todo
+ end
+ ENV['TEXOS'] = "#{ENV['TEXOS'].sub(/^[\\\/]*/, '').sub(/[\\\/]*$/, '')}"
+ ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/'
+ ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}"
+ report('')
+ report("preset : TEXPATH => #{ENV['TEXPATH']}")
+ report("preset : TEXOS => #{ENV['TEXOS']}")
+ report("preset : TEXMFOS => #{ENV['TEXMFOS']}")
+ report("preset : TMP => #{ENV['TMP']}")
+ report('')
+end
+
+def loadfile(filename)
+ begin
+ IO.readlines(filename).each do |line|
+ case line.chomp
+ when /^[\#\%]/ then
+ # comment
+ when /^(.*?)\s*(\>|\=|\<)\s*(.*)\s*$/ then
+ # = assign | > prepend | < append
+ key, how, value = $1, $2, $3
+ begin
+ # $SAFE = 0
+ value.gsub!(/\%(.*?)\%/) do
+ ENV[$1] || ''
+ end
+ # value.gsub!(/\;/,$separator) if key =~ /PATH/i then
+ case how
+ when '=', '<<' then ENV[key] = value
+ when '?', '??' then ENV[key] = ENV[key] || value
+ when '<', '+=' then ENV[key] = (ENV[key] || '') + $separator + value
+ when '>', '=+' then ENV[key] = value + $separator + (ENV[key] ||'')
+ end
+ rescue
+ report("user set failed : #{key} (#{$!})")
+ else
+ report("user set : #{key} => #{ENV[key]}")
+ end
+ end
+ end
+ rescue
+ report("error in reading file '#{filename}'")
+ end
+end
+
+def loadtree(tree)
+ begin
+ unless tree.empty? then
+ if File.directory?(tree) then
+ setuptex = File.join(tree,'setuptex.tmf')
+ else
+ setuptex = tree.dup
+ end
+ if FileTest.file?(setuptex) then
+ report("tex tree definition: #{setuptex}")
+ checkenvironment(File.dirname(setuptex))
+ loadfile(setuptex)
+ else
+ report("no setup file '#{setuptex}'")
+ end
+ end
+ rescue
+ # maybe tree is empty or boolean (no arg given)
+ end
+end
+
+def loadenvironment(environment)
+ begin
+ unless environment.empty? then
+ filename = if $path.empty? then environment else File.expand_path(File.join($path,environment)) end
+ if FileTest.file?(filename) then
+ report("environment : #{environment}")
+ loadfile(filename)
+ else
+ report("no environment file '#{environment}'")
+ end
+ end
+ rescue
+ report("problem while loading '#{environment}'")
+ end
+end
+
+def show_environment
+ if $showenv then
+ keys = ENV.keys.sort
+ size = 0
+ keys.each do |k|
+ size = k.size if k.size > size
+ end
+ report('')
+ keys.each do |k|
+ report("#{k.rjust(size)} => #{ENV[k]}")
+ end
+ report('')
+ end
+end
+
+def execute(arguments) # br global
+
+ arguments = arguments.split(/\s+/) if arguments.class == String
+ $directives = hashed(arguments)
+
+ $help = $directives['help'] || false
+ $batch = $directives['batch'] || false
+ $filename = $directives['file'] || ''
+ $program = $directives['program'] || 'context'
+ $direct = $directives['direct'] || false
+ $edit = $directives['edit'] || false
+ $page = $directives['page'] || 1
+ $browser = $directives['browser'] || false
+ $report = $directives['report'] || false
+ $verbose = $directives['verbose'] || false
+ $arguments = $directives['arguments'] || ''
+ $execute = $directives['execute'] || $directives['exec'] || false
+ $locate = $directives['locate'] || false
+
+ $autotree = if $directives['autotree'] then (ENV['TEXMFSTART_TREE'] || ENV['TEXMFSTARTTREE'] || '') else '' end
+
+ $path = $directives['path'] || ''
+ $tree = $directives['tree'] || $autotree || ''
+ $environment = $directives['environment'] || ''
+
+ $make = $directives['make'] || false
+ $remove = $directives['remove'] || $directives['delete'] || false
+ $unix = $directives['unix'] || false
+ $windows = $directives['windows'] || $directives['mswin'] || false
+ $stubpath = $directives['stubpath'] || ''
+ $indirect = $directives['indirect'] || false
+
+ $before = $directives['before'] || ''
+ $after = $directives['after'] || ''
+
+ $iftouched = $directives['iftouched'] || false
+ $ifchanged = $directives['ifchanged'] || false
+
+ $openoffice = $directives['oo'] || false
+
+ $crossover = false if $directives['clear']
+
+ $showenv = $directives['showenv'] || false
+ $verbose = true if $showenv
+
+ $serve = $directives['serve'] || false
+
+ $verbose = true if (ENV['_CTX_VERBOSE_'] =~ /(y|yes|t|true|on)/io) && ! $locate && ! $report
+
+ set_applications($page)
+
+ # private:
+
+ $selfmerge = $directives['selfmerge'] || false
+ $selfcleanup = $directives['selfclean'] || $directives['selfcleanup'] || false
+
+ ENV['_CTX_VERBOSE_'] = 'yes' if $verbose
+
+ if $openoffice then
+ if ENV['OOPATH'] then
+ if FileTest.directory?(ENV['OOPATH']) then
+ report("using open office python")
+ if $mswindows then
+ $applications['python'] = $applications['py'] = "\"#{File.join(ENV['OOPATH'],'program','python.bat')}\""
+ else
+ $applications['python'] = $applications['py'] = File.join(ENV['OOPATH'],'python')
+ end
+ report("python path #{$applications['python']}")
+ else
+ report("environment variable 'OOPATH' does not exist")
+ end
+ else
+ report("environment variable 'OOPATH' is not set")
+ end
+ end
+
+ if $selfmerge then
+ output("ruby libraries are cleaned up") if SelfMerge::cleanup
+ output("ruby libraries are merged") if SelfMerge::merge
+ return true
+ elsif $selfcleanup then
+ output("ruby libraries are cleaned up") if SelfMerge::cleanup
+ return true
+ elsif $help || ! $filename || $filename.empty? then
+ usage
+ loadtree($tree)
+ loadenvironment($environment)
+ show_environment()
+ return true
+ elsif $batch && $filename && ! $filename.empty? then
+ # todo, take commands from file and avoid multiple starts and checks
+ return false
+ else
+ report("texmfstart version #{$version}")
+ loadtree($tree)
+ loadenvironment($environment)
+ show_environment()
+ if $make || $remove then
+ if $filename == 'all' then
+ makelist = $makelist
+ else
+ makelist = [$filename]
+ end
+ makelist.each do |filename|
+ if $windows then
+ make(filename,true,false,$remove)
+ elsif $unix then
+ make(filename,false,true,$remove)
+ else
+ make(filename,$mswindows,!$mswindows,$remove)
+ end
+ end
+ return true # guess
+ elsif $browser && $filename =~ /^http\:\/\// then
+ return launch($filename)
+ else
+ begin
+ process do
+ if $direct || $filename =~ /^bin\:/ then
+ return direct($filename)
+ elsif $edit && ! $editor.empty? then
+ return edit($filename)
+ else # script: or no prefix
+ command = find(shortpathname($filename),$program)
+ if command then
+ register("THREAD",File.dirname(File.expand_path(command)))
+ return run(command)
+ else
+ report('unable to locate program')
+ return false
+ end
+ end
+ end
+ rescue
+ report('fatal error in starting process')
+ return false
+ end
+ end
+ end
+
+end
+
+if execute(ARGV) then
+ report("\nexecution was successful") if $verbose
+ exit(0)
+else
+ report("\nexecution failed") if $verbose
+ exit(1)
+end
diff --git a/scripts/context/ruby/texsync.rb b/scripts/context/ruby/texsync.rb
new file mode 100644
index 000000000..004610399
--- /dev/null
+++ b/scripts/context/ruby/texsync.rb
@@ -0,0 +1,206 @@
+#!/usr/bin/env ruby
+
+# program : texsync
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2003-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# For the moment this script only handles the 'minimal' context
+# distribution. In due time I will add a few more options, like
+# synchronization of the iso image.
+
+# taco's sync: rsync -au -v rsync://www.pragma-ade.com/all ./htdocs
+
+banner = ['TeXSync', 'version 1.1.1', '2002/2004', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+# require 'base/tool'
+
+require 'rbconfig'
+
+class Commands
+
+ include CommandBase
+
+ @@formats = ['en','nl','de','cz','it','ro', 'fr']
+ @@always = ['metafun','mptopdf','en','nl']
+ @@rsync = 'rsync -r -z -c --progress --stats "--exclude=*.fmt" "--exclude=*.efmt" "--exclude=*.mem"'
+
+ @@kpsewhich = Hash.new
+
+ @@kpsewhich['minimal'] = 'SELFAUTOPARENT'
+ @@kpsewhich['context'] = 'TEXMFLOCAL'
+ @@kpsewhich['documentation'] = 'TEXMFLOCAL'
+ @@kpsewhich['unknown'] = 'SELFAUTOPARENT'
+
+ def update
+
+ report
+
+ return unless destination = getdestination
+
+ texpaths = gettexpaths
+ address = option('address')
+ user = option('user')
+ tree = option('tree')
+ force = option('force')
+
+ ok = true
+ begin
+ report("synchronizing '#{tree}' from '#{address}' to '#{destination}'")
+ report
+ if texpaths then
+ texpaths.each do |path|
+ report("synchronizing path '#{path}' of '#{tree}' from '#{address}' to '#{destination}'")
+ command = "#{rsync} #{user}@#{address}::#{tree}/#{path} #{destination}/{path}"
+ ok = ok && system(command) if force
+ end
+ else
+ command = "#{@@rsync} #{user}@#{address}::#{tree} #{destination}"
+ ok = system(command) if force
+ end
+ rescue
+ report("error in running rsync")
+ ok = false
+ ensure
+ if force then
+ if ok then
+ if option('make') then
+ report("generating tex and metapost formats")
+ report
+ @@formats.delete_if do |f|
+ begin
+ `kpsewhich cont-#{f}`.chomp.empty?
+ rescue
+ end
+ end
+ str = [@@formats,@@always].flatten.uniq.join(' ')
+ begin
+ system("texexec --make --alone #{str}")
+ rescue
+ report("unable to generate formats '#{str}'")
+ else
+ report
+ end
+ else
+ report("regenerate the formats files if needed")
+ end
+ else
+ report("error in synchronizing '#{tree}'")
+ end
+ else
+ report("provide --force to execute '#{command}'") unless force
+ end
+ end
+
+ end
+
+ def list
+
+ report
+
+ address = option('address')
+ user = option('user')
+ result = nil
+
+ begin
+ report("fetching list of trees from '#{address}'")
+ command = "#{@@rsync} #{user}@#{address}::"
+ if option('force') then
+ result = `#{command}`.chomp
+ else
+ report("provide --force to execute '#{command}'")
+ end
+ rescue
+ result = nil
+ else
+ if result then
+ report("available trees:")
+ report
+ reportlines(result)
+ end
+ ensure
+ report("unable to fetch list") unless result
+ end
+
+ end
+
+ private
+
+ def gettexpaths
+ if option('full') then
+ texpaths = ['texmf','texmf-local','texmf-fonts','texmf-mswin','texmf-linux','texmf-macos']
+ elsif option('terse') then
+ texpaths = ['texmf','texmf-local','texmf-fonts']
+ case Config::CONFIG['host_os'] # or: Tool.ruby_platform
+ when /mswin/ then texpaths.push('texmf-mswin')
+ when /linux/ then texpaths.push('texmf-linux')
+ when /darwin/ then texpaths.push('texmf-macosx')
+ end
+ else
+ texpaths = nil
+ end
+ texpaths
+ end
+
+ def getdestination
+ if (destination = option('destination')) && ! destination.empty? then
+ begin
+ if @@kpsewhich.key?(destination) then
+ destination = @@kpsewhich[option('tree')] || @@kpsewhich['unknown']
+ destination = `kpsewhich --expand-var=$#{destination}`.chomp
+ elsif ! FileTest.directory?(destination) then
+ destination = nil
+ end
+ rescue
+ report("unable to determine destination tex root")
+ else
+ if ! destination || destination.empty? then
+ report("no destination is specified")
+ elsif not FileTest.directory?(destination) then
+ report("invalid destination '#{destination}'")
+ elsif not FileTest.writable?(destination) then
+ report("destination '#{destination}' is not writable")
+ else
+ report("using destination '#{destination}'")
+ return destination
+ end
+ end
+ else
+ report("unknown destination")
+ end
+ return nil
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('update', 'update installed tree')
+commandline.registeraction('list', 'list available trees')
+
+commandline.registerflag('terse', 'download as less as possible (esp binaries)')
+commandline.registerflag('full', 'download everything (all binaries)')
+commandline.registerflag('force', 'confirm action')
+commandline.registerflag('make', 'remake formats')
+
+commandline.registervalue('address', 'www.pragma-ade.com', 'adress of repository (www.pragma-ade)')
+commandline.registervalue('user', 'guest', 'user account (guest)')
+commandline.registervalue('tree', 'tex', 'tree to synchronize (tex)')
+commandline.registervalue('destination', nil, 'destination of tree (kpsewhich)')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/scripts/context/ruby/textools.rb b/scripts/context/ruby/textools.rb
new file mode 100644
index 000000000..a5858c5ca
--- /dev/null
+++ b/scripts/context/ruby/textools.rb
@@ -0,0 +1,1033 @@
+#!/usr/bin/env ruby
+
+# program : textools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on tex
+# related files.
+
+banner = ['TeXTools', 'version 1.3.1', '2002/2006', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+
+require 'fileutils'
+# require 'ftools'
+
+# Remark
+#
+# The fixtexmftrees feature does not realy belong in textools, but
+# since it looks like no measures will be taken to make texlive (and
+# tetex) downward compatible with respect to fonts installed by
+# users, we provide this fixer. This option also moves script files
+# to their new location (only for context) in the TDS. Beware: when
+# locating scripts, the --format switch in kpsewhich should now use
+# 'texmfscripts' instead of 'other text files' (texmfstart is already
+# aware of this). Files will only be moved when --force is given. Let
+# me know if more fixes need to be made.
+
+class Commands
+
+ include CommandBase
+
+ def tpmmake
+ if filename = @commandline.argument('first') then
+ filename = File.join('tpm',filename) unless filename =~ /^tpm[\/\\]/
+ filename += '.tpm' unless filename =~ /\.tpm$/
+ if FileTest.file?(filename) then
+ data = IO.read(filename) rescue ''
+ data, fn, n = calculate_tpm(data,"TPM:RunFiles")
+ data, fm, m = calculate_tpm(data,"TPM:DocFiles")
+ data = replace_tpm(data,"TPM:Size",n+m)
+ report("total size #{n+m}")
+ begin
+ File.open(filename, 'w') do |f|
+ f << data
+ end
+ rescue
+ report("unable to save '#{filename}'")
+ else
+ report("file '#{filename}' is updated")
+ filename = File.basename(filename).sub(/\..*$/,'')
+ zipname = sprintf("%s-%04i.%02i.%02i%s",filename,Time.now.year,Time.now.month,Time.now.day,'.zip')
+ File.delete(zipname) rescue true
+ report("zipping file '#{zipname}'")
+ system("zip -r -9 -q #{zipname} #{[fn,fm].flatten.join(' ')}")
+ end
+ else
+ report("no file '#{filename}'")
+ end
+ end
+ end
+
+ def calculate_tpm(data, tag='')
+ size, ok = 0, Array.new
+ data.gsub!(/<#{tag}.*>(.*?)<\/#{tag}>/m) do
+ content = $1
+ files = content.split(/\s+/)
+ files.each do |file|
+ unless file =~ /^\s*$/ then
+ if FileTest.file?(file) then
+ report("found file #{file}")
+ size += FileTest.size(file) rescue 0
+ ok << file
+ else
+ report("missing file #{file}")
+ end
+ end
+ end
+ "<#{tag} size=\"#{size}\">#{content}</#{tag}>"
+ end
+ [data, ok, size]
+ end
+
+ def replace_tpm(data, tag='', txt='')
+ data.gsub(/(<#{tag}.*>)(.*?)(<\/#{tag}>)/m) do
+ $1 + txt.to_s + $3
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def hidemapnames
+ report('hiding FontNames in map files')
+ xidemapnames(true)
+ end
+
+ def videmapnames
+ report('unhiding FontNames in map files')
+ xidemapnames(false)
+ end
+
+ def removemapnames
+
+ report('removing FontNames from map files')
+
+ if files = findfiles('map') then
+ report
+ files.sort.each do |fn|
+ gn = fn # + '.nonames'
+ hn = fn + '.original'
+ begin
+ if FileTest.file?(fn) && ! FileTest.file?(hn) then
+ if File.rename(fn,hn) then
+ if (fh = File.open(hn,'r')) && (gh = File.open(gn,'w')) then
+ report("processing #{fn}")
+ while str = fh.gets do
+ str.sub!(/^([^\%]+?)(\s+)([^\"\<\s]*?)(\s)/) do
+ $1 + $2 + " "*$3.length + $4
+ end
+ gh.puts(str)
+ end
+ fh.close
+ gh.close
+ else
+ report("no permissions to handle #{fn}")
+ end
+ else
+ report("unable to rename #{fn} to #{hn}")
+ end
+ else
+ report("not processing #{fn} due to presence of #{hn}")
+ end
+ rescue
+ report("error in handling #{fn}")
+ end
+ end
+ end
+
+ end
+
+ def restoremapnames
+
+ report('restoring FontNames in map files')
+
+ if files = findfiles('map') then
+ report
+ files.sort.each do |fn|
+ hn = fn + '.original'
+ begin
+ if FileTest.file?(hn) then
+ File.delete(fn) if FileTest.file?(fn)
+ report("#{fn} restored") if File.rename(hn,fn)
+ else
+ report("no original found for #{fn}")
+ end
+ rescue
+ report("error in restoring #{fn}")
+ end
+ end
+ end
+
+ end
+
+ def findfile
+
+ report('locating file in texmf tree')
+
+ # ! not in tree
+ # ? fuzzy
+ # . in tree
+ # > in tree and used
+
+ if filename = @commandline.argument('first') then
+ if filename && ! filename.empty? then
+ report
+ used = kpsefile(filename) || pathfile(filename)
+ if paths = texmfroots then
+ found, prefered = false, false
+ paths.each do |p|
+ if files = texmffiles(p,filename) then
+ found = true
+ files.each do |f|
+ # unreadable: report("#{if f == used then '>' else '.' end} #{f}")
+ if f == used then
+ prefered = true
+ report("> #{f}")
+ else
+ report(". #{f}")
+ end
+ end
+ end
+ end
+ if prefered then
+ report("! #{used}") unless found
+ else
+ report("> #{used}")
+ end
+ elsif used then
+ report("? #{used}")
+ else
+ report('no file found')
+ end
+ else
+ report('no file specified')
+ end
+ else
+ report('no file specified')
+ end
+
+ end
+
+ def unzipfiles
+
+ report('g-unzipping files')
+
+ if files = findfiles('gz') then
+ report
+ files.each do |f|
+ begin
+ system("gunzip -d #{f}")
+ rescue
+ report("unable to unzip file #{f}")
+ else
+ report("file #{f} is unzipped")
+ end
+ end
+ end
+
+ end
+
+ def fixafmfiles
+
+ report('fixing afm files')
+
+ if files = findfiles('afm') then
+ report
+ ok = false
+ files.each do |filename|
+ if filename =~ /\.afm$/io then
+ if f = File.open(filename) then
+ result = ''
+ done = false
+ while str = f.gets do
+ str.chomp!
+ str.strip!
+ if str.empty? then
+ # skip
+ elsif (str.length > 200) && (str =~ /^(comment|notice)\s(.*)\s*$/io) then
+ done = true
+ tag, words, len = $1, $2.split(' '), 0
+ result += tag
+ while words.size > 0 do
+ str = words.shift
+ len += str.length + 1
+ result += ' ' + str
+ if len > (70 - tag.length) then
+ result += "\n"
+ result += tag if words.size > 0
+ len = 0
+ end
+ end
+ result += "\n" if len>0
+ else
+ result += str + "\n"
+ end
+ end
+ f.close
+ if done then
+ ok = true
+ begin
+ if File.rename(filename,filename+'.original') then
+ if FileTest.file?(filename) then
+ report("something to fix in #{filename} but error in renaming (3)")
+ elsif f = File.open(filename,'w') then
+ f.puts(result)
+ f.close
+ report('file', filename, 'has been fixed')
+ else
+ report("something to fix in #{filename} but error in opening (4)")
+ File.rename(filename+'.original',filename) # gamble
+ end
+ else
+ report("something to fix in #{filename} but error in renaming (2)")
+ end
+ rescue
+ report("something to fix in #{filename} but error in renaming (1)")
+ end
+ else
+ report("nothing to fix in #{filename}")
+ end
+ else
+ report("error in opening #{filename}")
+ end
+ end
+ end
+ report('no files match the pattern') unless ok
+ end
+
+ end
+
+ def mactodos
+
+ report('fixing mac newlines')
+
+ if files = findfiles('tex') then
+ report
+ files.each do |filename|
+ begin
+ report("converting file #{filename}")
+ tmpfilename = filename + '.tmp'
+ if f = File.open(filename) then
+ if g = File.open(tmpfilename, 'w')
+ while str = f.gets do
+ g.puts(str.gsub(/\r/,"\n"))
+ end
+ if f.close && g.close && FileTest.file?(tmpfilename) then
+ File.delete(filename)
+ File.rename(tmpfilename,filename)
+ end
+ else
+ report("unable to open temporary file #{tmpfilename}")
+ end
+ else
+ report("unable to open #{filename}")
+ end
+ rescue
+ report("problems with fixing #{filename}")
+ end
+ end
+ end
+
+ end
+
+ def fixtexmftrees
+
+ if paths = @commandline.argument('first') then
+ paths = [paths] if ! paths.empty?
+ end
+ paths = texmfroots if paths.empty?
+
+ if paths then
+
+ moved = 0
+ force = @commandline.option('force')
+
+ report
+ report("checking TDS 2003 => TDS 2004 : map files")
+ # report
+
+ # move [map,enc] files from /texmf/[dvips,pdftex,dvipdfmx] -> /texmf/fonts/[*]
+
+ ['map','enc'].each do |suffix|
+ paths.each do |path|
+ ['dvips','pdftex','dvipdfmx'].each do |program|
+ report
+ report("checking #{suffix} files for #{program} on #{path}")
+ report
+ moved += movefiles("#{path}/#{program}","#{path}/fonts/#{suffix}/#{program}",suffix) do
+ # nothing
+ end
+ end
+ end
+ end
+
+ report
+ report("checking TDS 2003 => TDS 2004 : scripts")
+ # report
+
+ # move [rb,pl,py] files from /texmf/someplace -> /texmf/scripts/someplace
+
+ ['rb','pl','py'].each do |suffix|
+ paths.each do |path|
+ ['context'].each do |program|
+ report
+ report("checking #{suffix} files for #{program} on #{path}")
+ report
+ moved += movefiles("#{path}/#{program}","#{path}/scripts/#{program}",suffix) do |f|
+ f.gsub!(/\/(perl|ruby|python)tk\//o) do
+ "/#{$1}/"
+ end
+ end
+ end
+ end
+ end
+
+ begin
+ if moved>0 then
+ report
+ if force then
+ system('mktexlsr')
+ report
+ report("#{moved} files moved")
+ else
+ report("#{moved} files will be moved")
+ end
+ else
+ report('no files need to be moved')
+ end
+ rescue
+ report('you need to run mktexlsr')
+ end
+
+ end
+
+ end
+
+ def replacefile
+
+ report('replace file')
+
+ if newname = @commandline.argument('first') then
+ if newname && ! newname.empty? then
+ report
+ report("replacing #{newname}")
+ report
+ oldname = kpsefile(File.basename(newname))
+ force = @commandline.option('force')
+ if oldname && ! oldname.empty? then
+ oldname = File.expand_path(oldname)
+ newname = File.expand_path(newname)
+ report("old: #{oldname}")
+ report("new: #{newname}")
+ report
+ if newname == oldname then
+ report('unable to replace itself')
+ elsif force then
+ begin
+ File.copy(newname,oldname)
+ rescue
+ report('error in replacing the old file')
+ end
+ else
+ report('the old file will be replaced (use --force)')
+ end
+ else
+ report('nothing to replace')
+ end
+ else
+ report('no file specified')
+ end
+ else
+ report('no file specified')
+ end
+
+ end
+
+ private # general
+
+ def texmfroots
+ begin
+ paths = `kpsewhich -expand-path=\$TEXMF`.chomp
+ rescue
+ else
+ return paths.split(/#{File::PATH_SEPARATOR}/) if paths && ! paths.empty?
+ end
+ return nil
+ end
+
+ def texmffiles(root, filename)
+ begin
+ files = Dir.glob("#{root}/**/#{filename}")
+ rescue
+ else
+ return files if files && files.length>0
+ end
+ return nil
+ end
+
+ def pathfile(filename)
+ used = nil
+ begin
+ if ! filename || filename.empty? then
+ return nil
+ else
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ if FileTest.file?(File.join(path,filename)) then
+ used = File.join(path,filename)
+ break
+ end
+ end
+ end
+ rescue
+ used = nil
+ else
+ used = nil if used && used.empty?
+ end
+ return used
+ end
+
+ def kpsefile(filename)
+ used = nil
+ begin
+ if ! filename || filename.empty? then
+ return nil
+ else
+ used = `kpsewhich #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -progname=context #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -format=texmfscripts #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -progname=context -format=texmfscripts #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -format="other text files" #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -progname=context -format="other text files" #{filename}`.chomp
+ end
+ rescue
+ used = nil
+ else
+ used = nil if used && used.empty?
+ end
+ return used
+ end
+
+ def downcasefilenames
+
+ report('downcase filenames')
+
+ force = @commandline.option('force')
+
+ # if @commandline.option('recurse') then
+ # files = Dir.glob('**/*')
+ # else
+ # files = Dir.glob('*')
+ # end
+ # if files && files.length>0 then
+
+ if files = findfiles() then
+ files.each do |oldname|
+ if FileTest.file?(oldname) then
+ newname = oldname.downcase
+ if oldname != newname then
+ if force then
+ begin
+ File.rename(oldname,newname)
+ rescue
+ report("#{oldname} == #{oldname}\n")
+ else
+ report("#{oldname} => #{newname}\n")
+ end
+ else
+ report("(#{oldname} => #{newname})\n")
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def stripformfeeds
+
+ report('strip formfeeds')
+
+ force = @commandline.option('force')
+
+ if files = findfiles() then
+ files.each do |filename|
+ if FileTest.file?(filename) then
+ begin
+ data = IO.readlines(filename).join('')
+ rescue
+ else
+ if data.gsub!(/\n*\f\n*/io,"\n\n") then
+ if force then
+ if f = open(filename,'w') then
+ report("#{filename} is stripped\n")
+ f.puts(data)
+ f.close
+ else
+ report("#{filename} cannot be stripped\n")
+ end
+ else
+ report("#{filename} will be stripped\n")
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ public
+
+ def showfont
+
+ file = @commandline.argument('first')
+
+ if file.empty? then
+ report('provide filename')
+ else
+ file.sub!(/\.afm$/,'')
+ begin
+ report("analyzing afm file #{file}.afm")
+ file = `kpsewhich #{file}.afm`.chomp
+ rescue
+ report('unable to run kpsewhich')
+ return
+ end
+
+ names = Array.new
+
+ if FileTest.file?(file) then
+ File.new(file).each do |line|
+ if line.match(/^C\s*([\-\d]+)\s*\;.*?\s*N\s*(.+?)\s*\;/o) then
+ names.push($2)
+ end
+ end
+ ranges = names.size
+ report("number of glyphs: #{ranges}")
+ ranges = ranges/256 + 1
+ report("number of subsets: #{ranges}")
+ file = File.basename(file).sub(/\.afm$/,'')
+ tex = File.open("textools.tex",'w')
+ map = File.open("textools.map",'w')
+ tex.puts("\\starttext\n")
+ tex.puts("\\loadmapfile[textools.map]\n")
+ for i in 1..ranges do
+ rfile = "#{file}-range-#{i}"
+ report("generating enc file #{rfile}.enc")
+ flushencoding("#{rfile}", (i-1)*256, i*256-1, names)
+ # catch console output
+ report("generating tfm file #{rfile}.tfm")
+ mapline = `afm2tfm #{file}.afm -T #{rfile}.enc #{rfile}.tfm`
+ # more robust replacement
+ mapline = "#{rfile} <#{rfile}.enc <#{file}.pfb"
+ # final entry in map file
+ mapline = "#{mapline} <#{file}.pfb"
+ map.puts("#{mapline}\n")
+ tex.puts("\\showfont[#{rfile}][unknown]\n")
+ end
+ tex.puts("\\stoptext\n")
+ report("generating map file textools.map")
+ report("generating tex file textools.tex")
+ map.close
+ tex.close
+ else
+ report("invalid file #{file}")
+ end
+ end
+
+ end
+
+ @@knownchars = Hash.new
+
+ @@knownchars['ae'] = 'aeligature' ; @@knownchars['oe'] = 'oeligature'
+ @@knownchars['AE'] = 'AEligature' ; @@knownchars['OE'] = 'OEligature'
+
+ @@knownchars['acute' ] = 'textacute'
+ @@knownchars['breve' ] = 'textbreve'
+ @@knownchars['caron' ] = 'textcaron'
+ @@knownchars['cedilla' ] = 'textcedilla'
+ @@knownchars['circumflex' ] = 'textcircumflex'
+ @@knownchars['diaeresis' ] = 'textdiaeresis'
+ @@knownchars['dotaccent' ] = 'textdotaccent'
+ @@knownchars['grave' ] = 'textgrave'
+ @@knownchars['hungarumlaut'] = 'texthungarumlaut'
+ @@knownchars['macron' ] = 'textmacron'
+ @@knownchars['ogonek' ] = 'textogonek'
+ @@knownchars['ring' ] = 'textring'
+ @@knownchars['tilde' ] = 'texttilde'
+
+ @@knownchars['cent' ] = 'textcent'
+ @@knownchars['currency'] = 'textcurrency'
+ @@knownchars['euro' ] = 'texteuro'
+ @@knownchars['florin' ] = 'textflorin'
+ @@knownchars['sterling'] = 'textsterling'
+ @@knownchars['yen' ] = 'textyen'
+
+ @@knownchars['brokenbar'] = 'textbrokenbar'
+ @@knownchars['bullet' ] = 'textbullet'
+ @@knownchars['dag' ] = 'textdag'
+ @@knownchars['ddag' ] = 'textddag'
+ @@knownchars['degree' ] = 'textdegree'
+ @@knownchars['div' ] = 'textdiv'
+ @@knownchars['ellipsis' ] = 'textellipsis'
+ @@knownchars['fraction' ] = 'textfraction'
+ @@knownchars['lognot' ] = 'textlognot'
+ @@knownchars['minus' ] = 'textminus'
+ @@knownchars['mu' ] = 'textmu'
+ @@knownchars['multiply' ] = 'textmultiply'
+ @@knownchars['pm' ] = 'textpm'
+
+ def encmake
+ afmfile = @commandline.argument('first')
+ encoding = @commandline.argument('second') || 'dummy'
+ if afmfile && FileTest.file?(afmfile) then
+ chars = Array.new
+ IO.readlines(afmfile).each do |line|
+ if line =~ /C\s+(\d+).*?N\s+([a-zA-Z\-\.]+?)\s*;/ then
+ chars[$1.to_i] = $2
+ end
+ end
+ if f = File.open(encoding+'.enc','w') then
+ f << "% Encoding file, generated by textools.rb from #{afmfile}\n"
+ f << "\n"
+ f << "/#{encoding.gsub(/[^a-zA-Z]/,'')}encoding [\n"
+ 256.times do |i|
+ f << " /#{chars[i] || '.notdef'} % #{i}\n"
+ end
+ f << "] def\n"
+ f.close
+ end
+ if f = File.open('enco-'+encoding+'.tex','w') then
+ f << "% ConTeXt file, generated by textools.rb from #{afmfile}\n"
+ f << "\n"
+ f << "\\startencoding[#{encoding}]\n\n"
+ 256.times do |i|
+ if str = chars[i] then
+ tmp = str.gsub(/dieresis/,'diaeresis')
+ if chr = @@knownchars[tmp] then
+ f << " \\definecharacter #{chr} #{i}\n"
+ elsif tmp.length > 5 then
+ f << " \\definecharacter #{tmp} #{i}\n"
+ end
+ end
+ end
+ f << "\n\\stopencoding\n"
+ f << "\n\\endinput\n"
+ f.close
+ end
+ end
+ end
+
+ private
+
+ def flushencoding (file, from, to, names)
+ n = 0
+ out = File.open("#{file}.enc",'w')
+ out.puts("/#{file.gsub(/\-/,'')} [\n")
+ for i in from..to do
+ if names[i] then
+ n += 1
+ out.puts("/#{names[i]}\n")
+ else
+ out.puts("/.notdef\n")
+ end
+ end
+ out.puts("] def\n")
+ out.close
+ return n
+ end
+
+ private # specific
+
+ def movefiles(from_path,to_path,suffix,&block)
+ obsolete = 'obsolete'
+ force = @commandline.option('force')
+ moved = 0
+ if files = texmffiles(from_path, "*.#{suffix}") then
+ files.each do |filename|
+ newfilename = filename.sub(/^#{from_path}/, to_path)
+ yield(newfilename) if block
+ if FileTest.file?(newfilename) then
+ begin
+ File.rename(filename,filename+'.obsolete') if force
+ rescue
+ report("#{filename} cannot be made obsolete") if force
+ else
+ if force then
+ report("#{filename} is made obsolete")
+ else
+ report("#{filename} will become obsolete")
+ end
+ end
+ else
+ begin
+ File.makedirs(File.dirname(newfilename)) if force
+ rescue
+ end
+ begin
+ File.copy(filename,newfilename) if force
+ rescue
+ report("#{filename} cannot be copied to #{newfilename}")
+ else
+ begin
+ File.delete(filename) if force
+ rescue
+ report("#{filename} cannot be deleted") if force
+ else
+ if force then
+ report("#{filename} is moved to #{newfilename}")
+ moved += 1
+ else
+ report("#{filename} will be moved to #{newfilename}")
+ end
+ end
+ end
+ end
+ end
+ else
+ report('no matches found')
+ end
+ return moved
+ end
+
+ def xidemapnames(hide)
+
+ filter = /^([^\%]+?)(\s+)([^\"\<\s]*?)(\s)/
+ banner = '% textools:nn '
+
+ if files = findfiles('map') then
+ report
+ files.sort.each do |fn|
+ if fn.has_suffix?('map') then
+ begin
+ lines = IO.read(fn)
+ report("processing #{fn}")
+ if f = File.open(fn,'w') then
+ skip = false
+ if hide then
+ lines.each do |str|
+ if skip then
+ skip = false
+ elsif str =~ /#{banner}/ then
+ skip = true
+ elsif str =~ filter then
+ f.puts(banner+str)
+ str.sub!(filter) do
+ $1 + $2 + " "*$3.length + $4
+ end
+ end
+ f.puts(str)
+ end
+ else
+ lines.each do |str|
+ if skip then
+ skip = false
+ elsif str.sub!(/#{banner}/, '') then
+ f.puts(str)
+ skip = true
+ else
+ f.puts(str)
+ end
+ end
+ end
+ f.close
+ end
+ rescue
+ report("error in handling #{fn}")
+ end
+ end
+ end
+ end
+
+ end
+
+ public
+
+ def updatetree
+
+ nocheck = @commandline.option('nocheck')
+ merge = @commandline.option('merge')
+ delete = @commandline.option('delete')
+ force = @commandline.option('force')
+ root = @commandline.argument('first').gsub(/\\/,'/')
+ path = @commandline.argument('second').gsub(/\\/,'/')
+
+ if FileTest.directory?(root) then
+ report("scanning #{root}")
+ rootfiles = Dir.glob("#{root}/**/*")
+ else
+ report("provide source root")
+ return
+ end
+ if rootfiles.size > 0 then
+ report("#{rootfiles.size} files")
+ else
+ report("no files")
+ return
+ end
+ rootfiles.collect! do |rf|
+ rf.gsub(/\\/o, '/').sub(/#{root}\//o, '')
+ end
+ rootfiles = rootfiles.delete_if do |rf|
+ FileTest.directory?(File.join(root,rf))
+ end
+
+ if FileTest.directory?(path) then
+ report("scanning #{path}")
+ pathfiles = Dir.glob("#{path}/**/*")
+ else
+ report("provide destination root")
+ return
+ end
+ if pathfiles.size > 0 then
+ report("#{pathfiles.size} files")
+ else
+ report("no files")
+ return
+ end
+ pathfiles.collect! do |pf|
+ pf.gsub(/\\/o, '/').sub(/#{path}\//o, '')
+ end
+ pathfiles = pathfiles.delete_if do |pf|
+ FileTest.directory?(File.join(path,pf))
+ end
+
+ root = File.expand_path(root)
+ path = File.expand_path(path)
+
+ donepaths = Hash.new
+ copiedfiles = Hash.new
+
+ # update existing files, assume similar paths
+
+ report("")
+ pathfiles.each do |f| # destination
+ p = File.join(path,f)
+ if rootfiles.include?(f) then
+ r = File.join(root,f)
+ if p != r then
+ if nocheck or File.mtime(p) < File.mtime(r) then
+ copiedfiles[File.expand_path(p)] = true
+ report("updating '#{r}' to '#{p}'")
+ begin
+ begin File.makedirs(File.dirname(p)) if force ; rescue ; end
+ File.copy(r,p) if force
+ rescue
+ report("updating failed")
+ end
+ else
+ report("not updating '#{r}'")
+ end
+ end
+ end
+ end
+
+ # merging non existing files
+
+ report("")
+ rootfiles.each do |f|
+ donepaths[File.dirname(f)] = true
+ r = File.join(root,f)
+ if not pathfiles.include?(f) then
+ p = File.join(path,f)
+ if p != r then
+ if merge then
+ copiedfiles[File.expand_path(p)] = true
+ report("merging '#{r}' to '#{p}'")
+ begin
+ begin File.makedirs(File.dirname(p)) if force ; rescue ; end
+ File.copy(r,p) if force
+ rescue
+ report("merging failed")
+ end
+ else
+ report("not merging '#{r}'")
+ end
+ end
+ end
+ end
+
+ # deleting obsolete files
+
+ report("")
+ donepaths.keys.sort.each do |d|
+ pathfiles = Dir.glob("#{path}/#{d}/**/*")
+ pathfiles.each do |p|
+# puts(File.dirname(p))
+# if donepaths[File.dirname(p)] then
+ r = File.join(root,d,File.basename(p))
+ if FileTest.file?(p) and not FileTest.file?(r) and not copiedfiles.key?(File.expand_path(p)) then
+ if delete then
+ report("deleting '#{p}'")
+ begin
+ File.delete(p) if force
+ rescue
+ report("deleting failed")
+ end
+ else
+ report("not deleting '#{p}'")
+ end
+ end
+ end
+# end
+ end
+
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('removemapnames' , '[pattern] [--recurse]')
+commandline.registeraction('restoremapnames' , '[pattern] [--recurse]')
+commandline.registeraction('hidemapnames' , '[pattern] [--recurse]')
+commandline.registeraction('videmapnames' , '[pattern] [--recurse]')
+commandline.registeraction('findfile' , 'filename [--recurse]')
+commandline.registeraction('unzipfiles' , '[pattern] [--recurse]')
+commandline.registeraction('fixafmfiles' , '[pattern] [--recurse]')
+commandline.registeraction('mactodos' , '[pattern] [--recurse]')
+commandline.registeraction('fixtexmftrees' , '[texmfroot] [--force]')
+commandline.registeraction('replacefile' , 'filename [--force]')
+commandline.registeraction('updatetree' , 'fromroot toroot [--force --nocheck --merge --delete]')
+commandline.registeraction('downcasefilenames', '[--recurse] [--force]') # not yet documented
+commandline.registeraction('stripformfeeds' , '[--recurse] [--force]') # not yet documented
+commandline.registeraction('showfont' , 'filename')
+commandline.registeraction('encmake' , 'afmfile encodingname')
+
+commandline.registeraction('tpmmake' , 'tpm file (run in texmf root)')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('recurse')
+commandline.registerflag('force')
+commandline.registerflag('merge')
+commandline.registerflag('delete')
+commandline.registerflag('nocheck')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/scripts/context/ruby/texutil.rb b/scripts/context/ruby/texutil.rb
new file mode 100644
index 000000000..ee0fc1e5e
--- /dev/null
+++ b/scripts/context/ruby/texutil.rb
@@ -0,0 +1,93 @@
+banner = ['TeXUtil ', 'version 9.1.0', '1997-2005', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+require 'base/file'
+require 'base/texutil'
+
+class Commands
+
+ include CommandBase
+
+ def references
+ filename = @commandline.argument('first')
+ if not filename.empty? and FileTest.file?(File.suffixed(filename,'tuo')) then
+ if tu = TeXUtil::Converter.new(logger) and tu.loaded(filename) then
+ tu.saved if tu.processed
+ end
+ end
+ end
+
+ def main
+ if @commandline.arguments.length>0 then
+ references
+ else
+ help
+ end
+ end
+
+ def purgefiles
+ system("texmfstart ctxtools --purge #{@commandline.arguments.join(' ')}")
+ end
+
+ def purgeallfiles
+ system("texmfstart ctxtools --purge --all #{@commandline.arguments.join(' ')}")
+ end
+
+ def documentation
+ system("texmfstart ctxtools --document #{@commandline.arguments.join(' ')}")
+ end
+
+ def analyzefile
+ system("texmfstart pdftools --analyze #{@commandline.arguments.join(' ')}")
+ end
+
+ def filterpages # obsolete
+ system("texmfstart ctxtools --purge #{@commandline.arguments.join(' ')}")
+ end
+
+ def figures
+ report("this code is not yet converted from perl to ruby")
+ end
+
+ def logfile
+ report("this code is not yet converted from perl to ruby")
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+# main feature
+
+commandline.registeraction('references', 'convert tui file into tuo file')
+
+# todo features
+
+commandline.registeraction('figures', 'generate figure dimensions file')
+commandline.registeraction('logfile', 'filter essential log messages')
+
+# backward compatibility features
+
+commandline.registeraction('purgefiles', 'remove most temporary files')
+commandline.registeraction('purgeallfiles', 'remove all temporary files')
+commandline.registeraction('documentation', 'generate documentation file from source')
+commandline.registeraction('analyzefile', 'analyze pdf file')
+
+# old feature, not needed any longer due to extension of pdftex
+
+commandline.registeraction('filterpages')
+
+# generic features
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('verbose')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/scripts/context/ruby/tmftools.rb b/scripts/context/ruby/tmftools.rb
new file mode 100644
index 000000000..626ef1f4a
--- /dev/null
+++ b/scripts/context/ruby/tmftools.rb
@@ -0,0 +1,165 @@
+#!/usr/bin/env ruby
+
+# program : tmftools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2005
+# author : Hans Hagen
+#
+# project : ConTeXt
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# The script based alternative is not slower than the kpse one.
+# Loading is a bit faster when the log file is used.
+
+# todo: create database
+
+# tmftools [some of the kpsewhich switches]
+
+# tmftools --analyze
+# tmftools --analyze > kpsewhat.log
+# tmftools --analyze --strict > kpsewhat.log
+# tmftools --analyze --delete --force "texmf-local/fonts/.*/somename"
+# tmftools --serve
+
+# the real thing
+
+banner = ['TMFTools', 'version 1.1.0 (experimental, no help yet)', '2005/2006', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+
+class Commands
+
+ include CommandBase
+
+ def init_kpse
+ # require 'base/kpseremote'
+ # if KpseRemote::available? then
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ require 'base/kpseremote'
+ k = KpseRemote.new
+ else
+ k = nil
+ end
+ if k && k.okay? then
+ k.progname = @commandline.option('progname')
+ k.engine = @commandline.option('engine')
+ k.format = @commandline.option('format')
+ else
+ require 'base/kpsefast'
+ k = KpseFast.new
+ k.rootpath = @commandline.option('rootpath')
+ k.treepath = @commandline.option('treepath')
+ k.progname = @commandline.option('progname')
+ k.engine = @commandline.option('engine')
+ k.format = @commandline.option('format')
+ k.diskcache = @commandline.option('diskcache')
+ k.renewcache = @commandline.option('renewcache')
+ k.load_cnf
+ k.expand_variables
+ k.load_lsr
+ end
+ return k
+ end
+
+ def serve
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ require 'base/kpseremote'
+ begin
+ KpseRemote::start_server
+ rescue
+ end
+ end
+ end
+
+ def reload
+ begin
+ init_kpse.load
+ rescue
+ end
+ end
+
+ def main
+ if option = @commandline.option('expand-braces') and not option.empty? then
+ puts init_kpse.expand_braces(option)
+ elsif option = @commandline.option('expand-path') and not option.empty? then
+ puts init_kpse.expand_path(option)
+ elsif option = @commandline.option('expand-var') and not option.empty? then
+ if option == '*' then
+ init_kpse.list_expansions()
+ else
+ puts init_kpse.expand_var(option)
+ end
+ elsif option = @commandline.option('show-path') and not option.empty? then
+ puts init_kpse.show_path(option)
+ elsif option = @commandline.option('var-value') and not option.empty? then
+ if option == '*' then
+ init_kpse.list_variables()
+ else
+ puts init_kpse.expand_var(option)
+ end
+ elsif @commandline.arguments.size > 0 then
+ kpse = init_kpse
+ @commandline.arguments.each do |option|
+ puts kpse.find_file(option)
+ end
+ else
+ help
+ end
+ end
+
+ def analyze
+ pattern = @commandline.argument('first')
+ strict = @commandline.option('strict')
+ sort = @commandline.option('sort')
+ delete = @commandline.option('delete') and @commandline.option('force')
+ init_kpse.analyze_files(pattern, strict, sort, delete)
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+# kpsewhich compatible options
+
+commandline.registervalue('expand-braces','')
+commandline.registervalue('expand-path','')
+commandline.registervalue('expand-var','')
+commandline.registervalue('show-path','')
+commandline.registervalue('var-value','')
+
+commandline.registervalue('engine','')
+commandline.registervalue('progname','')
+commandline.registervalue('format','')
+
+# additional goodies
+
+commandline.registervalue('rootpath','')
+commandline.registervalue('treepath','')
+commandline.registervalue('sort','')
+
+commandline.registerflag('diskcache')
+commandline.registerflag('renewcache')
+commandline.registerflag('strict')
+commandline.registerflag('delete')
+commandline.registerflag('force')
+
+commandline.registeraction('analyze', "[--strict --sort --rootpath --treepath]\n[--delete [--force]] [pattern]")
+
+# general purpose options
+
+commandline.registerflag('verbose')
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registeraction('reload', 'reload file database')
+commandline.registeraction('serve', 'act as kpse server')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/scripts/context/ruby/xmltools.rb b/scripts/context/ruby/xmltools.rb
new file mode 100644
index 000000000..c28df200d
--- /dev/null
+++ b/scripts/context/ruby/xmltools.rb
@@ -0,0 +1,656 @@
+#!/usr/bin/env ruby
+
+# program : xmltools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# todo : use kpse lib
+
+# This script will harbor some handy manipulations on tex
+# related files.
+
+banner = ['XMLTools', 'version 1.2.2', '2002/2007', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+
+class String
+
+ def astring(n=10)
+ gsub(/(\d+)/o) do $1.to_s.rjust(n) end.gsub(/ /o, '0')
+ end
+
+ def xstring
+ if self =~ /\'/o then
+ "\"#{self.gsub(/\"/, '&quot;')}\""
+ else
+ "\'#{self}\'"
+ end
+ end
+
+end
+
+class Array
+
+ def asort(n=10)
+ sort {|x,y| x.astring(n) <=> y.astring(n)}
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def dir
+
+ @xmlns = "xmlns='http://www.pragma-ade.com/rlg/xmldir.rng'"
+
+ pattern = @commandline.option('pattern')
+ recurse = @commandline.option('recurse')
+ stripname = @commandline.option('stripname')
+ longname = @commandline.option('longname')
+ url = @commandline.option('url')
+ outputfile = @commandline.option('output')
+ root = @commandline.option('root')
+
+ def generate(output,files,url,root,longname)
+
+ class << output
+ def xputs(str,n=0)
+ puts("#{' '*n}#{str}")
+ end
+ end
+
+ dirname = ''
+ output.xputs("<?xml version='1.0'?>\n\n")
+ if ! root || root.empty? then
+ rootatt = @xmlns
+ else
+ rootatt = " #{@xmlns} root='#{root}'"
+ end
+ rootatt += " timestamp='#{Time.now}'"
+ if url.empty? then
+ output.xputs("<files #{rootatt}>\n")
+ else
+ output.xputs("<files url='#{url}'#{rootatt}>\n")
+ end
+ files.each do |f|
+ bn, dn = File.basename(f), File.dirname(f)
+ if dirname != dn then
+ output.xputs("</directory>\n", 2) if dirname != ''
+ output.xputs("<directory name='#{dn}'>\n", 2)
+ dirname = dn
+ end
+ if longname && dn != '.' then
+ output.xputs("<file name='#{dn}/#{bn}'>\n", 4)
+ else
+ output.xputs("<file name='#{bn}'>\n", 4)
+ end
+ output.xputs("<base>#{bn.sub(/\..*$/,'')}</base>\n", 6)
+ if File.stat(f).file? then
+ bt = bn.sub(/^.*\./,'')
+ if bt != bn then
+ output.xputs("<type>#{bt}</type>\n", 6)
+ end
+ output.xputs("<size>#{File.stat(f).size}</size>\n", 6)
+ permissions = ''
+ permissions << 'r' if File.readable?(f)
+ permissions << 'w' if File.writable?(f)
+ permissions << 'x' if File.executable?(f)
+ output.xputs("<permissions>#{permissions}</permissions>\n", 6) unless permissions.empty?
+ end
+ output.xputs("<date>#{File.stat(f).mtime.strftime("%Y-%m-%d %H:%M")}</date>\n", 6)
+ output.xputs("</file>\n", 4)
+ end
+ output.xputs("</directory>\n", 2) if dirname != ''
+ output.xputs("</files>\n")
+
+ end
+
+ if pattern.empty? then
+ report('provide --pattern=')
+ return
+ end
+
+ unless outputfile.empty? then
+ begin
+ output = File.open(outputfile,'w')
+ rescue
+ report("unable to open #{outputfile}")
+ return
+ end
+ else
+ report('provide --output')
+ return
+ end
+
+ if stripname && pattern.class == String && ! pattern.empty? then
+ pattern = File.dirname(pattern)
+ end
+
+ pattern = '*' if pattern.empty?
+
+ unless root.empty? then
+ unless FileTest.directory?(root) then
+ report("unknown root #{root}")
+ return
+ end
+ begin
+ Dir.chdir(root)
+ rescue
+ report("unable to change to root #{root}")
+ return
+ end
+ end
+
+ generate(output, globbed(pattern, recurse), url, root, longname)
+
+ output.close if output
+
+ end
+
+ alias ls :dir
+
+ def mmlpages
+
+ file = @commandline.argument('first')
+ eps = @commandline.option('eps')
+ jpg = @commandline.option('jpg')
+ png = @commandline.option('png')
+ style = @commandline.option('style')
+ modes = @commandline.option('modes')
+
+ file = file.sub(/\.xml/io, '')
+ long = "#{file}-mmlpages"
+ if FileTest.file?(file+'.xml') then
+ style = "--arg=\"style=#{style}\"" unless style.empty?
+ modes = "--mode=#{modes}" unless modes.empty?
+ if system("texmfstart texexec --batch --pdf --once --result=#{long} --use=mmlpag #{style} #{modes} #{file}.xml") then
+ if eps then
+ if f = open("#{file}-mmlpages.txt") then
+ while line = f.gets do
+ data = Hash.new
+ if fields = line.split then
+ fields.each do |fld|
+ key, value = fld.split('=')
+ data[key] = value if key && value
+ end
+ if data.key?('p') then
+ page = data['p']
+ name = "#{long}-#{page.to_i-1}"
+ if eps then
+ report("generating eps file #{name}")
+ if system("pdftops -eps -f #{page} -l #{page} #{long}.pdf #{name}.eps") then
+ if data.key?('d') then
+ if epsfile = IO.read("#{name}.eps") then
+ epsfile.sub!(/^(\%\%BoundingBox:.*?$)/i) do
+ newline = $1 + "\n%%Baseline: #{data['d']}\n"
+ if data.key?('w') && data.key?('h') then
+ newline += "%%PositionWidth: #{data['w']}\n"
+ newline += "%%PositionHeight: #{data['h']}\n"
+ newline += "%%PositionDepth: #{data['d']}"
+ end
+ newline
+ end
+ if g = File.open("#{name}.eps",'wb') then
+ g.write(epsfile)
+ g.close
+ end
+ end
+ end
+ else
+ report("error in generating eps from #{name}")
+ end
+ end
+ end
+ end
+ end
+ f.close
+ else
+ report("missing data log file #{file}")
+ end
+ end
+ if png then
+ report("generating png file for #{long}")
+ system("imagemagick #{long}.pdf #{long}-%d.png")
+ end
+ if jpg then
+ report("generating jpg files for #{long}")
+ system("imagemagick #{long}.pdf #{long}-%d.jpg")
+ end
+ else
+ report("error in processing file #{file}")
+ end
+ system("texmfstart ctxtools --purge")
+ else
+ report("error in processing file #{file}")
+ end
+
+ end
+
+ def analyze
+
+ file = @commandline.argument('first')
+ result = @commandline.option('output')
+ utf = @commandline.option('utf')
+ process = @commandline.option('process')
+
+ if FileTest.file?(file) then
+ if data = IO.read(file) then
+ if data =~ /<?xml.*?version\=/ then
+ report("xml file #{file} loaded")
+ elements = Hash.new
+ attributes = Hash.new
+ entities = Hash.new
+ chars = Hash.new
+ unicodes = Hash.new
+ names = Hash.new
+ data.scan(/<([^>\s\/\!\?]+)([^>]*?)>/o) do
+ element, attributelist = $1, $2
+ if elements.key?(element) then
+ elements[element] += 1
+ else
+ elements[element] = 1
+ end
+ attributelist.scan(/\s*([^\=]+)\=([\"\'])(.*?)(\2)/) do
+ key, value = $1, $3
+ attributes[element] = Hash.new unless attributes.key?(element)
+ attributes[element][key] = Hash.new unless attributes[element].key?(key)
+ if attributes[element][key].key?(value) then
+ attributes[element][key][value] += 1
+ else
+ attributes[element][key][value] = 1
+ end
+ end
+ end
+ data.scan(/\&([^\;]+)\;/o) do
+ entity = $1
+ if entities.key?(entity) then
+ entities[entity] += 1
+ else
+ entities[entity] = 1
+ end
+ end
+ if utf then
+ data.scan(/(\w)/u) do
+ chars[$1] = (chars[$1] || 0) + 1
+ end
+ if chars.size > 0 then
+ begin
+ # todo : use kpse lib
+ filename, ownpath, foundpath = 'contextnames.txt', File.dirname($0), ''
+ begin
+ foundpath = File.dirname(`kpsewhich -progname=context -format=\"other text files\" #{filename}`.chomp)
+ rescue
+ foundpath = '.'
+ else
+ foundpath = '.' if foundpath.empty?
+ end
+ [foundpath,ownpath,File.join(ownpath,'../../../context/data')].each do |path|
+ fullname = File.join(path,filename)
+ if FileTest.file?(fullname) then
+ report("loading '#{fullname}'")
+ # rough scan, we assume no valid lines after comments
+ IO.read(fullname).scan(/^([0-9A-F][0-9A-F][0-9A-F][0-9A-F])\s*\;\s*(.*?)\s*\;\s*(.*?)\s*\;\s*(.*?)\s*$/) do
+ names[$1.hex.to_i.to_s] = [$2,$3,$4]
+ end
+ break
+ end
+ end
+ rescue
+ end
+ end
+ end
+ result = file.gsub(/\..*?$/, '') + '.xlg' if result.empty?
+ if f = File.open(result,'w') then
+ report("saving report in #{result}")
+ f.puts "<?xml version='1.0'?>\n"
+ f.puts "<document>\n"
+ if entities.length>0 then
+ total = 0
+ entities.each do |k,v|
+ total += v
+ end
+ f.puts " <entities n=#{total.to_s.xstring}>\n"
+ entities.keys.asort.each do |entity|
+ f.puts " <entity name=#{entity.xstring} n=#{entities[entity].to_s.xstring}/>\n"
+ end
+ f.puts " </entities>\n"
+ end
+ if utf && (chars.size > 0) then
+ total = 0
+ chars.each do |k,v|
+ total += v
+ end
+ f.puts " <characters n=#{total.to_s.xstring}>\n"
+ chars.each do |k,v|
+ if k.length > 1 then
+ begin
+ u = k.unpack('U')
+ unicodes[u] = (unicodes[u] || 0) + v
+ rescue
+ report("invalid utf codes")
+ end
+ end
+ end
+ unicodes.keys.sort.each do |u|
+ ustr = u.to_s
+ if names[ustr] then
+ f.puts " <character number=#{ustr.xstring} pname=#{names[ustr][0].xstring} cname=#{names[ustr][1].xstring} uname=#{names[ustr][2].xstring} n=#{unicodes[u].to_s.xstring}/>\n"
+ else
+ f.puts " <character number=#{ustr.xstring} n=#{unicodes[u].to_s.xstring}/>\n"
+ end
+ end
+ f.puts " </characters>\n"
+ end
+ if elements.length>0 then
+ f.puts " <elements>\n"
+ elements.keys.sort.each do |element|
+ if attributes.key?(element) then
+ f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}>\n"
+ if attributes.key?(element) then
+ attributes[element].keys.asort.each do |attribute|
+ f.puts " <attribute name=#{attribute.xstring}>\n"
+ if attribute =~ /id$/o then
+ nn = 0
+ attributes[element][attribute].keys.asort.each do |value|
+ nn += attributes[element][attribute][value].to_i
+ end
+ f.puts " <instance value=#{"*".xstring} n=#{nn.to_s.xstring}/>\n"
+ else
+ attributes[element][attribute].keys.asort.each do |value|
+ f.puts " <instance value=#{value.xstring} n=#{attributes[element][attribute][value].to_s.xstring}/>\n"
+ end
+ end
+ f.puts " </attribute>\n"
+ end
+ end
+ f.puts " </element>\n"
+ else
+ f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}/>\n"
+ end
+ end
+ f.puts " </elements>\n"
+ end
+ f.puts "</document>\n"
+ f.close
+ if process then
+ system("texmfstart texexec --purge --pdf --use=xml-analyze #{result}")
+ end
+ else
+ report("unable to open file '#{result}'")
+ end
+ else
+ report("invalid xml file '#{file}'")
+ end
+ else
+ report("unable to load file '#{file}'")
+ end
+ else
+ report("unknown file '#{file}'")
+ end
+ end
+
+ def filter
+
+ require "rexml/document"
+
+ element = @commandline.option('element')
+ files = @commandline.arguments
+ result = "xmltools.xlg"
+
+ if element.empty? then
+ report("provide element using --element")
+ elsif files.length == 0 then
+ report("provide filename(s)")
+ else
+ begin
+ File.open(result,'w') do |f|
+ f << "<?xml version='1.0'?>\n\n"
+ f << "<xlg:document>\n\n"
+ total = 0
+ files.sort.each do |file|
+ begin
+ report("loading: #{file}")
+ data = REXML::Document.new(IO.read(file))
+ rescue
+ report("error: invalid xml")
+ else
+ found = 0
+ report("filtering: #{element}")
+ REXML::XPath.each(data,"//#{element}") do |table|
+ str = table.to_s
+ if str.length > 0 then
+ total += 1
+ found += 1
+ report("found: #{total} / #{found} / #{str.length} bytes")
+ f << "<xlg:file name='#{file}'>\n\n" unless found > 1
+ f << "<xlg:filtered n='#{total}' m='#{found}'>"
+ f << "#{str.gsub(/^\s*/m,'').gsub(/\s*$/m,'')}"
+ f << "</xlg:filtered>\n\n"
+ end
+ end
+ f << "</xlg:file>\n\n" if found > 0
+ end
+ end
+ f << "</xlg:document>\n"
+ end
+ report("result: #{result}")
+ rescue
+ report("error in opening #{result}")
+ end
+ end
+
+ end
+
+ def enhance
+ oldname = @commandline.argument('first')
+ newname = @commandline.argument('second')
+ verbose = @commandline.option('verbose')
+ # todo: options, maybe a config file
+ if ! newname || newname.empty? then
+ newname = oldname + ".prep"
+ end
+ if FileTest.file?(oldname) then
+ report("") if verbose
+ data = IO.read(oldname)
+ elements = Array.new
+ preamble = ""
+ done = false
+ data.sub!(/^(.*?)\s*(<[a-z])/moi) do
+ preamble = $1
+ $2
+ end
+ # hide elements
+ data.gsub!(/<([^>]*?)>/moi) do
+ elements << $1
+ "<#{elements.length}>"
+ end
+ # abc[-/]def
+ data.gsub!(/([a-z]{3,})([\/\-\(\)]+)([a-z]{3,})/moi) do
+ done = true
+ report("compound: #{$1}#{$2}#{$3}") if verbose
+ "#{$1}<compound token='#{$2}'/>#{$3}"
+ end
+ # (abcd
+ # data.gsub!(/(\()([a-z]{4,})/moi) do
+ # done = true
+ # report("compound: #{$1}#{$2}") if verbose
+ # "<compound token='#{$1}'/>#{$2}"
+ # end
+ # abcd)
+ # data.gsub!(/(\()([a-z]{4,})/moi) do
+ # done = true
+ # report("compound: #{$1}#{$2}") if verbose
+ # "#{$2}<compound token='#{$2}'/>"
+ # end
+ # roll back elements
+ data.gsub!(/<(\d+)>/moi) do
+ "<#{elements.shift}>"
+ end
+ File.open(newname,'wb') do |f|
+ f << preamble
+ f << "\n"
+ f << data
+ end
+ if verbose then
+ if done then
+ report("")
+ report(oldname," converted to ",newname)
+ else
+ report(oldname," copied to ",newname)
+ end
+ end
+ end
+ end
+
+ def cleanup # todo, share loading/saving with previous
+
+ file = @commandline.argument('first')
+ force = @commandline.option('force')
+ verbose = @commandline.option('verbose')
+
+ if FileTest.file?(file) then
+ if data = IO.read(file) then
+ if data =~ /<?xml.*?version\=/ then
+ data = doxmlcleanup(data,verbose)
+ result = if force then file else file.gsub(/\..*?$/, '') + '.xlg' end
+ begin
+ if f = File.open(result,'w') then
+ f << data
+ f.close
+ end
+ rescue
+ report("unable to open file '#{result}'")
+ end
+ else
+ report("invalid xml file '#{file}'")
+ end
+ else
+ report("unable to load file '#{file}'")
+ end
+ else
+ report("unknown file '#{file}'")
+ end
+
+ end
+
+ def doxmlreport(str,verbose=false)
+ if verbose then
+ result = str
+ report(result)
+ return result
+ else
+ return str
+ end
+ end
+
+ def doxmlcleanup(data="",verbose=false)
+
+ # remove funny spaces (looks cleaner)
+ #
+ # data = "<whatever ></whatever ><whatever />"
+
+ data.gsub!(/\<(\/*\w+)\s*(\/*)>/o) do
+ "<#{$1}#{$2}>"
+ end
+
+ # remove funny ampersands
+ #
+ # data = "<x> B&W </x>"
+
+ data.gsub!(/\&([^\<\>\&]*?)\;/mo) do
+ "<entity name='#{$1}'/>"
+ end
+ data.gsub!(/\&/o) do
+ doxmlreport("&amp;",verbose)
+ end
+ data.gsub!(/\<entity name=\'(.*?)\'\/\>/o) do
+ doxmlreport("&#{$1};",verbose)
+ end
+
+ # remove funny < >
+ #
+ # data = "<x> < 5% </x>"
+
+ data.gsub!(/<([^>].*?)>/o) do
+ tag = $1
+ case tag
+ when /^\//o then
+ "<#{tag}>" # funny tag but ok
+ when /\/$/o then
+ "<#{tag}>" # funny tag but ok
+ when /</o then
+ doxmlreport("&lt;#{tag}>",verbose)
+ else
+ "<#{tag}>"
+ end
+ end
+
+ # remove funny < >
+ #
+ # data = "<x> > 5% </x>"
+
+ data.gsub!(/<([^>].*?)>([^\>\<]*?)>/o) do
+ doxmlreport("<#{$1}>#{$2}&gt;",verbose)
+ end
+
+ return data
+ end
+
+ # puts doxmlcleanup("<whatever ></whatever ><whatever />")
+ # puts doxmlcleanup("<x> B&W </x>")
+ # puts doxmlcleanup("<x> < 5% </x>")
+ # puts doxmlcleanup("<x> > 5% </x>")
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('dir', 'generate directory listing')
+commandline.registeraction('mmlpages','generate graphic from mathml')
+commandline.registeraction('analyze', 'report entities and elements [--utf --process]')
+commandline.registeraction('cleanup', 'cleanup xml file [--force]')
+commandline.registeraction('enhance', 'enhance xml file (partial)')
+commandline.registeraction('filter', 'filter elements from xml file [element=]')
+
+# commandline.registeraction('dir', 'filename --pattern= --output= [--recurse --stripname --longname --url --root]')
+# commandline.registeraction('mmlpages','filename [--eps --jpg --png --style= --mode=]')
+
+commandline.registeraction('ls')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('stripname')
+commandline.registerflag('longname')
+commandline.registerflag('recurse')
+commandline.registerflag('verbose')
+
+commandline.registervalue('pattern')
+commandline.registervalue('element')
+commandline.registervalue('url')
+commandline.registervalue('output')
+commandline.registervalue('root')
+
+commandline.registerflag('eps')
+commandline.registerflag('png')
+commandline.registerflag('jpg')
+commandline.registerflag('utf')
+commandline.registerflag('process')
+commandline.registervalue('style')
+commandline.registervalue('modes')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')