summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/context/ruby/base
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-01-09 01:54:09 +0000
committerKarl Berry <karl@freefriends.org>2006-01-09 01:54:09 +0000
commit50b347972956e0bfbe7029305e0f459e5ce3ac0c (patch)
treed1b824bbc33a30bf7fcf54b866a1cff949d2e0bf /Master/texmf-dist/scripts/context/ruby/base
parent52f01b2f769ac290674a469d46f149985042ee2e (diff)
trunk/Master/texmf-dist/scripts
git-svn-id: svn://tug.org/texlive/trunk@92 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/context/ruby/base')
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/ctx.rb285
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/file.rb147
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpse.rb305
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb881
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/logger.rb104
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/pdf.rb51
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/state.rb75
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/switch.rb605
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/system.rb102
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/tex.rb1495
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/texutil.rb872
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/tool.rb306
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/variables.rb45
13 files changed, 5273 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/context/ruby/base/ctx.rb b/Master/texmf-dist/scripts/context/ruby/base/ctx.rb
new file mode 100644
index 00000000000..852c3f7046e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/ctx.rb
@@ -0,0 +1,285 @@
+# 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
+
+ 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
+ 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('provide ctx file')
+ return
+ end
+
+ if not FileTest.file?(@ctxname) and defaultname and FileTest.file?(defaultname) then
+ @ctxname = defaultname
+ end
+
+ if not FileTest.file?(@ctxname) then
+ report('provide ctx file')
+ return
+ end
+
+ @xmldata = IO.read(@ctxname)
+
+ unless @xmldata =~ /^.*<\?xml.*?\?>/moi then
+ report("ctx file #{@ctxname} is no xml file, skipping")
+ return
+ else
+ report("loading ctx file #{@ctxname}")
+ 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
+ REXML::XPath.each(@xmldata.root,"//ctx:value[@name='job']") do |val|
+ substititute(val,variables['job'])
+ end
+ REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:message") do |mes|
+ report("preprocessing: #{justtext(mes)}")
+ end
+ REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:environment") do |sty|
+ @environments << justtext(sty)
+ end
+ REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:module") do |mod|
+ @modules << justtext(mod)
+ end
+ REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:filter") do |fil|
+ @filters << justtext(mod)
+ end
+ REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:files") do |files|
+ REXML::XPath.each(files,"ctx:file") do |pattern|
+ preprocessor = pattern.attributes['processor']
+ if preprocessor and not preprocessor.empty? then
+ pattern = justtext(pattern)
+ Dir.glob(pattern).each do |oldfile|
+ newfile = "#{oldfile}.prep"
+ if File.needsupdate(oldfile,newfile) then
+ begin
+ File.delete(newfile)
+ rescue
+ # hope for the best
+ end
+ # there can be a sequence of processors
+ preprocessor.split(',').each do |pp|
+ if command = REXML::XPath.first(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor[@name='#{pp}']") then
+ # a lie: no <?xml ...?>
+ command = REXML::Document.new(command.to_s) # don't infect original
+ command = command.elements["ctx:processor"]
+ report("preprocessing #{oldfile} using #{pp}")
+ REXML::XPath.each(command,"ctx:old") do |value| replace(value,oldfile) end
+ REXML::XPath.each(command,"ctx:new") do |value| replace(value,newfile) end
+ 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
+ end
+ end
+ if FileTest.file?(newfile) then
+ File.syncmtimes(oldfile,newfile)
+ else
+ report("preprocessing #{oldfile} gave no #{newfile}")
+ end
+ else
+ report("#{oldfile} needs no preprocessing")
+ 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"
+ log << "<ctx:preplist>\n"
+ @prepfiles.keys.sort.each do |prep|
+ log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{File.basename(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
+ if name and not name.empty? and FileTest.file?(name) then
+ if f = File.open(name,'r') and i = REXML::Document.new(f) then
+ report("including ctx file #{name}")
+ REXML::XPath.each(i.root,"*") do |ii|
+ xmldata.root.insert_after(e,ii)
+ more = true
+ end
+ end
+ else
+ report("no valid ctx inclusion file #{name}")
+ end
+ 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
+ else str
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/file.rb b/Master/texmf-dist/scripts/context/ruby/base/file.rb
new file mode 100644
index 00000000000..42fb346c409
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/file.rb
@@ -0,0 +1,147 @@
+# 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 '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)
+ begin File.delete(filename) ; rescue ; end
+ end
+
+ def File.silentcopy(oldname,newname)
+ return if File.expand_path(oldname) == File.expand_path(newname)
+ begin File.copy(oldname,newname) ; rescue ; end
+ 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)
+ begin File.delete(newname) ; rescue ; end
+ begin
+ File.rename(oldname,newname)
+ rescue
+ begin File.copy(oldname,newname) ; rescue ; end
+ 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/Master/texmf-dist/scripts/context/ruby/base/kpse.rb b/Master/texmf-dist/scripts/context/ruby/base/kpse.rb
new file mode 100644
index 00000000000..dc4898ffc58
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpse.rb
@@ -0,0 +1,305 @@
+# 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'
+
+# beware $engine is lowercase in kpse
+#
+# miktex has mem|fmt|base paths
+
+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
+
+ @@usekpserunner = false || ENV['KPSEFAST'] == 'yes'
+
+ require 'base/tool' if @@usekpserunner
+
+ 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.found(filename, progname=nil, format=nil)
+ begin
+ tag = Kpse.key(filename) # all
+ if @@located.key?(tag) then
+ return @@located[tag]
+ 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]
+ 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]
+ elsif p = Kpse.kpsewhich(filename,prg,fmt) then
+ setvariable(tag,p.chomp)
+ return @@located[tag]
+ end
+ rescue
+ end
+ end
+ end
+ setvariable(tag,filename)
+ return filename
+ end
+ rescue
+ filename
+ end
+ end
+
+ def Kpse.kpsewhich(filename,progname,format)
+ Kpse.run("-progname=#{progname} -format=\"#{format}\" #{filename}")
+ end
+
+ def Kpse.which
+ Kpse.kpsewhich
+ end
+
+ def Kpse.run(arguments)
+ puts arguments if @@tracing
+ begin
+ if @@problems then
+ results = ''
+ else
+ if @@usekpserunner then
+ results = KpseRunner.kpsewhich(arguments).chomp
+ else
+ results = `kpsewhich #{arguments}`.chomp
+ end
+ 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").gsub(/\\/,'/').split(File::PATH_SEPARATOR))
+ 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='pdfetex',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
+ formatpath.split(File::PATH_SEPARATOR).each do |fp|
+ fp.gsub!(/\\/,'/')
+ # remove funny patterns
+ fp.sub!(/^!!/,'')
+ fp.sub!(/\/+$/,'')
+ fp.sub!(/unsetengine/,if enginepath then engine else '' end)
+ if ! fp.empty? && (fp != '.') then
+ # strip (possible engine) and test for writeability
+ fpp = fp.sub(/#{engine}\/*$/,'')
+ if FileTest.directory?(fpp) && FileTest.writable?(fpp) then
+ # use this path
+ formatpath = fp.dup
+ break
+ end
+ end
+ end
+ end
+ # needed !
+ begin File.makedirs(formatpath) ; rescue ; end ;
+ # 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
+ begin File.makedirs(formatpath) ; rescue ; end ;
+ 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
+
+ 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/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb b/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb
new file mode 100644
index 00000000000..52ab28d0fe7
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb
@@ -0,0 +1,881 @@
+# 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
+
+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
+
+class KPSEFAST
+
+ # formats are an incredible inconsistent mess
+
+ @@suffixes = Hash.new
+ @@formats = Hash.new
+ @@suffixmap = Hash.new
+
+ @@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'] = ''
+ @@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 = 'pdfetex'
+ @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'
+ 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
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ ENV['SELFAUTOPARENT'] = ownpath
+ else
+ ENV['SELFAUTOPARENT'] = '.'
+ end
+ unless @treepath.empty? then
+ unless @rootpath.empty? then
+ @treepath = @treepath.split(',').collect do |p| File.join(@rootpath,p) end.join(',')
+ end
+ ENV['TEXMF'] = @treepath
+ ENV['TEXMFCNF'] = File.join(@treepath.split(',').first,'texmf/web2c')
+ end
+ unless @rootpath.empty? then
+ ENV['TEXMFCNF'] = File.join(@rootpath,'texmf/web2c')
+ ENV['SELFAUTOPARENT'] = @rootpath
+ @isolate = true
+ end
+ filenames = Array.new
+ if ENV['TEXMFCNF'] and not ENV['TEXMFCNF'].empty? then
+ filenames << File.join(ENV['TEXMFCNF'],'texmf.cnf')
+ elsif ENV['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.','texmf.cnf')
+ else
+ ['texmf-local','texmf'].each do |tree|
+ filenames << File.join(ENV['SELFAUTOPARENT'],tree,'web2c','texmf.cnf')
+ end
+ end
+ # <root>/texmf/web2c/texmf.cnf
+ @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 ENV[key] then
+ if FileTest.directory?(ENV[key]) then
+ @cachepath = ENV[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'] = ENV['TEXMFCNF'].dup
+ @variables['SELFAUTOPARENT'] = ENV['SELFAUTOPARENT'].dup
+ else
+ ENV.keys.each do |e|
+ if e =~ /^([a-zA-Z]+)\_(.*)\s*$/o then
+ @expansions["#{$1}.#{$2}"] = ENV[e].dup
+ else
+ @expansions[e] = ENV[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.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?(@format) then
+ @@formats[@format]
+ 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(File::PATH_SEPARATOR)
+ 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(";")).join(File::PATH_SEPARATOR)
+ end
+
+ def expand_path(str) # output complete path expansion of STRING.
+ _expanded_path_(expanded_variable(str).split(";")).join(File::PATH_SEPARATOR)
+ 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(var_of_format(str)).join(File::PATH_SEPARATOR)
+ end
+
+ def var_value(str) # output the value of variable $STRING.
+ original_variable(str)
+ end
+
+end
+
+class KPSEFAST
+
+ def find_file(filename)
+ find_files(filename,true)
+ end
+
+ def find_files(filename,first=false)
+ if @remember then
+ stamp = "#{filename}--#{@format}--#{@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
+ 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
+ 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
+ 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
+ "deleted | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 2
+ "present | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 3
+ "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
+
+module KpseRunner
+
+ @@kpse = nil
+
+ def KpseRunner.kpsewhich(arg='')
+ options, arguments = split_args(arg)
+ unless @@kpse then
+ @@kpse = KPSEFAST.new
+ @@kpse.load_cnf
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ @@kpse.expand_variables
+ @@kpse.load_lsr
+ 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
+
+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', 'pdfetex', '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/Master/texmf-dist/scripts/context/ruby/base/logger.rb b/Master/texmf-dist/scripts/context/ruby/base/logger.rb
new file mode 100644
index 00000000000..2526cdb0e25
--- /dev/null
+++ b/Master/texmf-dist/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/Master/texmf-dist/scripts/context/ruby/base/pdf.rb b/Master/texmf-dist/scripts/context/ruby/base/pdf.rb
new file mode 100644
index 00000000000..d8cbf9e0523
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/pdf.rb
@@ -0,0 +1,51 @@
+module PDFview
+
+ @files = Hash.new
+
+ def PDFview.open(*list)
+ begin
+ [*list].flatten.each do |file|
+ filename = fullname(file)
+ if FileTest.file?(filename) then
+ result = `pdfopen --file #{filename} 2>&1`
+ @files[filename] = true
+ end
+ end
+ rescue
+ end
+ end
+
+ def PDFview.close(*list)
+ [*list].flatten.each do |file|
+ filename = fullname(file)
+ begin
+ if @files.key?(filename) then
+ result = `pdfclose --file #{filename} 2>&1`
+ else
+ closeall
+ return
+ end
+ rescue
+ end
+ @files.delete(filename)
+ end
+ end
+
+ def PDFview.closeall
+ begin
+ result = `pdfclose --all 2>&1`
+ 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/Master/texmf-dist/scripts/context/ruby/base/state.rb b/Master/texmf-dist/scripts/context/ruby/base/state.rb
new file mode 100644
index 00000000000..f57231592a7
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/state.rb
@@ -0,0 +1,75 @@
+require "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/ms,"\n") if omit
+ sum = MD5.new(data).hexdigest.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/Master/texmf-dist/scripts/context/ruby/base/switch.rb b/Master/texmf-dist/scripts/context/ruby/base/switch.rb
new file mode 100644
index 00000000000..64d518bd42f
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/switch.rb
@@ -0,0 +1,605 @@
+# 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
+
+ def File.needsupdate(oldname,newname)
+ begin
+ if $mswindows then
+ return File.stat(oldname).mtime > File.stat(newname).mtime
+ else
+ return File.stat(oldname).mtime != File.stat(newname).mtime
+ end
+ rescue
+ return true
+ end
+ end
+
+ def File.syncmtimes(oldname,newname)
+ begin
+ if $mswindows then
+ # does not work (yet)
+ 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 = 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 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
+ if option =~ /^#{key}/i then
+ case n
+ when 0
+ foundkey, foundkind = option, kind
+ n = 1
+ when 1
+ # ambiguous matches, like --fix => --fixme --fixyou
+ foundkey, foundkind = nil, nil
+ break
+ 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/Master/texmf-dist/scripts/context/ruby/base/system.rb b/Master/texmf-dist/scripts/context/ruby/base/system.rb
new file mode 100644
index 00000000000..ed8c2756e2b
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/system.rb
@@ -0,0 +1,102 @@
+# 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.binnames(str)
+ if @@binnames.key?(str) then
+ @@binnames[str]
+ else
+ [str]
+ end
+ 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] = 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] = 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/Master/texmf-dist/scripts/context/ruby/base/tex.rb b/Master/texmf-dist/scripts/context/ruby/base/tex.rb
new file mode 100644
index 00000000000..2cc9d25422e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/tex.rb
@@ -0,0 +1,1495 @@
+# 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
+
+# report ?
+
+require 'base/variables'
+require 'base/kpse'
+require 'base/system'
+require 'base/state'
+require 'base/pdf'
+require 'base/file'
+require 'base/ctx'
+
+class String
+
+ def standard?
+ begin
+ self == 'standard'
+ rescue
+ false
+ end
+ end
+
+end
+
+class Array
+
+ def standard?
+ begin
+ self.include?('standard')
+ rescue
+ false
+ end
+ 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
+ @@runoptions = 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
+
+ ['tex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdfetex' end
+ ['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end
+ ['xetex'] .each do |e| @@texengines[e] = 'xetex' 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
+ ['dvips','ps'] .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
+
+ # 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-cz','cz','czech'] .each do |f| @@texformats[f] = 'cont-cz' end
+ ['cont-ro','ro','romanian'] .each do |f| @@texformats[f] = 'cont-ro' end
+ ['cont-uk','uk','brittish'] .each do |f| @@texformats[f] = 'cont-uk' 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] = 'plain' end
+ ['metafun','context','standard'] .each do |f| @@mpsformats[f] = 'metafun' end
+
+ ['pdfetex','aleph','omega'] .each do |p| @@prognames[p] = 'context' end
+ ['mpost'] .each do |p| @@prognames[p] = 'metafun' end
+
+ ['plain','default','standard','mptopdf'] .each do |f| @@texmethods[f] = 'plain' end
+ ['cont-en','cont-nl','cont-de','cont-it',
+ 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texmethods[f] = 'context' end
+ ['latex'] .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'] = "\\dump"
+ @@mpsmakestr['plain'] = "\\dump"
+
+ ['cont-en','cont-nl','cont-de','cont-it',
+ 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = "\\emergencyend" end
+
+ @@runoptions['xetex'] = ['--no-pdf']
+
+ @@booleanvars = [
+ 'batchmode', 'nonstopmode', 'fast', 'fastdisabled', 'silentmode', 'final',
+ 'paranoid', 'notparanoid', 'nobanner', 'once', 'allpatterns',
+ 'nompmode', 'nomprun', 'automprun',
+ 'nomapfiles', 'local',
+ 'arrange', 'noarrange',
+ 'forcexml', 'foxet',
+ 'mpyforce', 'forcempy',
+ 'forcetexutil', 'texutil',
+ 'globalfile', 'autopath',
+ 'purge', 'purgeall', 'autopdf', 'simplerun', 'verbose',
+ 'nooptionfile'
+ ]
+ @@stringvars = [
+ 'modefile', 'result', 'suffix', 'response', 'path',
+ 'filters', 'usemodules', 'environments', 'separation', 'setuppath',
+ 'arguments', 'input', 'output', 'randomseed', 'modes', 'filename',
+ 'modefile', 'ctxfile'
+ ]
+ @@standardvars = [
+ 'mainlanguage', 'bodyfont', 'language'
+ ]
+ @@knownvars = [
+ 'engine', 'distribution', 'texformats', 'mpsformats', 'progname', 'interface',
+ 'runs', 'backend'
+ ]
+
+ @@extrabooleanvars = []
+ @@extrastringvars = []
+
+ def booleanvars
+ [@@booleanvars,@@extrabooleanvars].flatten
+ end
+ def stringvars
+ [@@stringvars,@@extrastringvars].flatten
+ end
+ def standardvars
+ @@standardvars
+ end
+ def knownvars
+ @@knownvars
+ end
+
+ def setextrastringvars(vars)
+ @@extrastringvars << vars
+ end
+ def setextrabooleanvars(vars)
+ @@extrabooleanvars << vars
+ 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', 'context')
+ setvariable('interface', 'standard')
+ setvariable('engine', 'standard') # replaced by tex/mpsengine
+ setvariable('backend', 'pdftex')
+ setvariable('runs', '8')
+ setvariable('randomseed', rand(1440).to_s)
+ # files
+ setvariable('files', [])
+ # defaults
+ setvariable('texengine', 'standard')
+ setvariable('mpsengine', 'standard')
+ setvariable('backend', 'standard')
+ end
+
+ def runtime
+ Time.now - @startuptime
+ end
+
+ def reportruntime
+ report("runtime: #{runtime}")
+ end
+
+ def inspect(name=nil)
+ if ! name || name.empty? then
+ name = [booleanvars,stringvars,standardvars,knownvars]
+ end
+ [name].flatten.each do |n|
+ if str = getvariable(n) then
+ 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
+ begin File.delete(name) ; rescue ; end
+ end
+ end
+ rescue
+ 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)
+ if @@runoptions.key?(engine) then @@runoptions[engine].join(' ') else '' 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)
+ case engine
+ when /etex|eetex|pdfetex|pdfeetex|pdfxtex|xpdfetex|eomega|aleph|xetex/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) end
+ def validmpsformat(str) validsomething(str,@@mpsformats) end
+ def validtexengine(str) validsomething(str,@@texengines) end
+ def validmpsengine(str) validsomething(str,@@mpsengines) end
+
+ def validtexmethod(str) [validsomething(str,@@texmethods)].flatten.first end
+ def validmpsmethod(str) [validsomething(str,@@mpsmethods)].flatten.first end
+
+ def validsomething(str,something)
+ if str then
+ list = [str].flatten.collect do |s|
+ something[s]
+ 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,engine='standard')
+ if str && @@prognames.key?(str) then
+ @@prognames[str]
+ elsif (engine != 'standard') && @@prognames.key?(engine) then
+ @@prognames[engine]
+ else
+ str
+ 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
+ # if engine then
+ # "#{prefix}=#{engine}/#{format}"
+ # else
+ "#{prefix}=#{format}"
+ # end
+ 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 then
+ if progname = validprogname(progname) then
+ "#{prefix}=#{progname}"
+ else
+ ""
+ end
+ 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(file="natural.tcx")
+ if Kpse.miktex? then
+ "-tcx=#{file}"
+ else
+ "--translate-file=#{file}"
+ 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 makeformats
+ if getvariable('fast') then
+ report('using existing database')
+ else
+ report('updating file database')
+ Kpse.update
+ 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
+ if texformats && texengine && (progname = validprogname(getvariable('progname'),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}")
+ begin
+ Dir.chdir(texformatpath)
+ rescue
+ end
+ if texformats.length > 0 then
+ makeuserfile
+ makeresponsefile
+ end
+ texformats.each do |texformat|
+ report("generating tex format #{texformat}")
+ command = [quoted(texengine),prognameflag(progname),iniflag,tcxflag,prefixed(texformat,texengine),texmakeextras(texformat)].join(' ')
+ report(command) if getvariable('verbose')
+ system(command)
+ end
+ else
+ texformatpath = ''
+ end
+ # generate mps formats
+ if mpsformats && mpsengine && (progname = validprogname(getvariable('progname'),mpsengine)) then
+ report("using mp engine #{mpsengine}")
+ mpsformatpath = if getvariable('local') then '.' else Kpse.formatpath(mpsengine,false) end
+ report("using mps format path #{mpsformatpath}")
+ begin
+ Dir.chdir(mpsformatpath)
+ rescue
+ end
+ mpsformats.each do |mpsformat|
+ report("generating mps format #{mpsformat}")
+ command = [quoted(mpsengine),prognameflag(progname),iniflag,tcxflag,mpsformat,mpsmakeextras(mpsformat)].join(' ')
+ report(command) if getvariable('verbose')
+ system(command)
+ end
+ else
+ mpsformatpath = ''
+ end
+ # check for problems
+ report("tex engine path: #{texformatpath}") unless texformatpath.empty?
+ report("mps engine path: #{mpsformatpath}") unless mpsformatpath.empty?
+ [['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]}format: #{filestate(file)} > #{File.expand_path(file)}")
+ end
+ end
+ end
+ end
+ # to be sure, go back to current path
+ begin
+ Dir.chdir(savedpath)
+ rescue
+ end
+ # finalize
+ cleanup
+ 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|
+ 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 =~ /(pdfetex|aleph|xetex)[\/\\]#{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 = ['--process','--batch','--once',"--interface=#{interface}",engineflag]
+ # result = Kpse.pipescript('newtexexec',tempfilename,flags)
+ result = runtexexec([tempfilename], flags, 1)
+ if FileTest.file?("#{@@temprunfile}.log") then
+ logdata = IO.read("#{@@temprunfile}.log")
+ if logdata =~ /^\s*This is (.*?)[\s\,]+(.*?)$/mois 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*$/mois 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
+ language = getvariable('language')
+ mainlanguage = getvariable('mainlanguage')
+ bodyfont = getvariable('bodyfont')
+ if f = openedfile("cont-fmt.tex") then
+ f << "\\unprotect"
+ 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'],
+ ['program','texengine'],
+ ['translate','tcxfilter'],
+ ['tcx','tcxfilter'],
+ ['output','backend'],
+ ['mode','mode'],
+ ['ctx','ctxfile'],
+ ['version','contextversion'],
+ ['format','texformat'],
+ ['interface','texformat']
+ ]
+
+ def scantexpreamble(filename)
+ begin
+ if FileTest.file?(filename) and tex = File.open(filename) then
+ while str = tex.gets and str.chomp! do
+ if str =~ /^\%\s*(.*)/o then
+ vars = Hash.new
+ $1.split(/\s+/o).each do |s|
+ k, v = s.split('=')
+ vars[k] = v
+ end
+ @@preamblekeys.each do |v|
+ setvariable(v[1],vars[v[0]]) if vars.key?(v[0])
+ 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) then
+ while str = tex.gets do
+ case str.chomp
+ when /^\%/o then
+ # next
+ when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then
+ setvariable('texformat','nl') ; break
+ when /\\(stelle|verwende|umgebung|benutze)/o then
+ setvariable('texformat','de') ; break
+ when /\\(stel|gebruik|omgeving)/o then
+ setvariable('texformat','nl') ; break
+ when /\\(use|setup|environment)/o then
+ setvariable('texformat','en') ; break
+ when /\\(usa|imposta|ambiente)/o then
+ setvariable('texformat','it') ; break
+ when /(height|width|style)=/o then
+ setvariable('texformat','en') ; break
+ when /(hoehe|breite|schrift)=/o then
+ setvariable('texformat','de') ; break
+ when /(hoogte|breedte|letter)=/o then
+ setvariable('texformat','nl') ; break
+ when /(altezza|ampiezza|stile)=/o then
+ setvariable('texformat','it') ; break
+ when /externfiguur/o then
+ setvariable('texformat','nl') ; break
+ when /externalfigure/o then
+ setvariable('texformat','en') ; break
+ when /externeabbildung/o then
+ setvariable('texformat','de') ; break
+ when /figuraesterna/o then
+ setvariable('texformat','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','log','dvi','pdf'].each do |s|
+ File.silentrename(File.suffixed(fname,s),File.suffixed('texexec',s))
+ end
+ ['tuo'].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','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','log','dvi','pdf'].each do |s|
+ File.silentrename(File.suffixed('texexec',s),File.suffixed(fname,s))
+ end
+ end
+ end
+ end
+
+ def makestubfile(rawname,forcexml=false)
+ if tmp = File.open(File.suffixed(rawname,'run'),'w') then
+ tmp << "\\starttext\n"
+ if forcexml then
+ 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
+ end
+ end
+ when /<[a-z]+/io then # beware of order, first pi test
+ break
+ end
+ end
+ xml.close
+ end
+ tmp << "\\processXMLfilegrouped{#{rawname}}\n"
+ else
+ tmp << "\\processfile{#{rawname}}\n"
+ end
+ tmp << "\\stoptext\n"
+ tmp.close
+ return "run"
+ else
+ return File.splitname(rawname)[1]
+ end
+ end
+
+end
+
+class TEX
+
+ 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}'")
+ processfile
+ end
+ reportruntime
+ end
+
+ def processmptex
+ getarrayvariable('files').each do |filename|
+ setvariable('filename',filename)
+ report("processing graphic '#{filename}'")
+ runtexmp(filename)
+ end
+ reportruntime
+ end
+
+ def processmpxtex
+ getarrayvariable('files').each do |filename|
+ setvariable('filename',filename)
+ report("processing text of graphic '#{filename}'")
+ processmpx(filename,true)
+ end
+ reportruntime
+ end
+
+ def deleteoptionfile(rawname)
+ begin
+ File.delete(File.suffixed(rawname,'top'))
+ rescue
+ end
+ end
+
+ def makeoptionfile(rawname, jobname, jobsuffix, finalrun, fastdisabled, kindofrun)
+ # jobsuffix = orisuffix
+ if topname = File.suffixed(rawname,'top') and opt = File.open(topname,'w') then
+ # local handies
+ opt << "\% #{topname}\n"
+ opt << "\\unprotect\n"
+ opt << "\\setupsystem[\\c!n=#{kindofrun}]\n"
+ opt << "\\def\\MPOSTformatswitch\{#{prognameflag('metafun')} #{formatflag('mpost')}=\}\n"
+ 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 (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
+ 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 = validbackend(getvariable('backend')) then
+ opt << "\\setupoutput[#{str}]\n"
+ end
+ if getvariable('color') then
+ opt << "\\setupcolors[\\c!state=\\v!start]\n"
+ end
+ if getvariable('nompmode') || getvariable('nomprun') || getvariable('automprun') then
+ opt << "\\runMPgraphicsfalse\n"
+ end
+ if getvariable('fast') && ! getvariable('fastdisabled') then
+ opt << "\\fastmode\n"
+ end
+ if getvariable('silentmode') then
+ opt << "\\silentmode\n"
+ end
+ if (str = getvariable('separation')) && ! str.empty? then
+ opt << "\\setupcolors[\\c!split=#{str}]\n"
+ end
+ if (str = getvariable('setuppath')) && ! str.empty? then
+ opt << "\\setupsystem[\\c!directory=\{#{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 pd.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('nomapfiles') then
+ opt << "\\disablemapfiles\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 printformat
+ when '' then arrangement << "\\v!normal"
+ when /.*up/oi then arrangement << "\\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('modes')) && ! str.empty? then
+ opt << "\\enablemode[#{modes}]\n"
+ end
+ if (str = getvariable('arguments')) && ! str.empty? then
+ opt << "\\setupenv[#{str}]\n"
+ end
+ if (str = getvariable('randomseed')) && ! str.empty? then
+ 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
+ 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(/(\:|\.\.)/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 << "\\protect\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
+ # this will become:
+ # begin getvariable('environments').split(',').uniq.each do |e| opt << "\\useenvironment[#{e}]\n" end ; rescue ; end
+ opt << "\\endinput\n"
+ opt.close
+ 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
+ ['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
+ else
+ ENV[res] = pat
+ end
+ end
+ end
+ end
+ end
+
+ def runtex(filename)
+ texengine = validtexengine(getvariable('texengine'))
+ texformat = validtexformat(getarrayvariable('texformats').first)
+ progname = validprogname(getvariable('progname'))
+ report("tex engine: #{texengine}")
+ report("tex format: #{texformat}")
+ report("progname: #{progname}")
+ if texengine && texformat && progname then
+ command = [quoted(texengine),prognameflag(progname),formatflag(texengine,texformat),runoptions(texengine),filename,texprocextras(texformat)].join(' ')
+ report(command) if getvariable('verbose')
+ system(command)
+ else
+ false
+ end
+ end
+
+ def runmp(filename)
+ mpsengine = validmpsengine(getvariable('mpsengine'))
+ mpsformat = validmpsformat(getarrayvariable('mpsformats').first)
+ progname = validprogname(getvariable('progname'))
+ if mpsengine && mpsformat && progname then
+ command = [quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),runoptions(mpsengine),filename,mpsprocextras(mpsformat)].join(' ')
+ report(command) if getvariable('verbose')
+ system(command)
+ else
+ false
+ end
+ end
+
+ def runtexmp(filename,filetype='')
+ mpfile = File.suffixed(filename,filetype,'mp')
+ if File.atleast?(mpfile,25) then
+ # first run needed
+ File.silentdelete(File.suffixed(mpfile,'mpt'))
+ doruntexmp(mpfile,false)
+ mpgraphics = checkmpgraphics(mpfile)
+ mplabels = checkmplabels(mpfile)
+ if mpgraphics || mplabels then
+ # second run needed
+ doruntexmp(mpfile,mplabels)
+ end
+ end
+ end
+
+ def runtexmpjob(filename,filetype='')
+ mpfile = File.suffixed(filename,filetype,'mp')
+ if File.atleast?(mpfile,25) && (data = File.silentread(mpfile)) 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")
+ return runtexexec(mpfile,options,2)
+ end
+ end
+ return false
+ end
+
+ def runtexutil(filename=[], options=['--ref','--ij','--high'], old=false)
+ filename.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
+ tu.saved if tu.processed
+ end
+ rescue
+ Kpse.runscript('texutil',fname,options)
+ end
+ end
+ end
+ end
+
+ # 1=tex 2=mptex 3=mpxtex
+
+ def runtexexec(filename=[], options=[], mode=nil)
+ begin
+ if mode and job = TEX.new(@logger) then
+ options.each do |option|
+ if option=~ /^\-*(.*?)\=(.*)$/o then
+ job.setvariable($1,$2)
+ else
+ job.setvariable(option,true)
+ end
+ end
+ job.setvariable("files",filename)
+ case mode
+ when 1 then job.processtex
+ when 2 then job.processmptex
+ when 3 then job.processmpxtex
+ 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)
+ ENV['backend'] = backend ;
+ ENV['progname'] = backend unless validtexengine(backend)
+ ENV['TEXFONTMAPS'] = ".;\$TEXMF/fonts/map/{#{backend},pdftex,dvips,}//"
+ end
+
+ def runbackend(rawname)
+ case validbackend(getvariable('backend'))
+ when 'dvipdfmx' then
+ fixbackendvars('dvipdfm')
+ system("dvipdfmx -d 4 #{File.unsuffixed(rawname)}")
+ when 'xetex' then
+ fixbackendvars('xetex')
+ system("xdv2pdf #{File.suffixed(jrawname,'xdv')}")
+ 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
+ system("dvips #{mapfiles} #{File.unsuffixed(rawname)}")
+ when 'pdftex' then
+ # no need for postprocessing
+ else
+ report("no postprocessing needed")
+ end
+ end
+
+ def processfile
+
+ takeprecautions
+
+ rawname = getvariable('filename')
+
+ jobname = getvariable('filename')
+ suffix = getvariable('suffix')
+ result = getvariable('result')
+
+ runonce = getvariable('once')
+ finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange'))
+ globalfile = getvariable('globalfile')
+
+ 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)
+ result = File.unixfied(result)
+
+ orisuffix = jobsuffix # still needed ?
+
+ setvariable('nomprun',true) if orisuffix == 'mpx' # else cylic run
+
+ PDFview.closeall if getvariable('autopdf')
+
+ forcexml = jobsuffix.match(/^(xml|fo|fox|rlg|exa)$/io) # nil or match
+
+ 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
+
+ unless FileTest.file?(rawname) then
+ inppath.split(',').each do |ip|
+ break if dummyfile = FileTest.file?(File.join(ip,rawname))
+ end
+ end
+
+ # preprocess files
+
+ ctx = CtxRunner.new(rawname,@logger)
+ if getvariable('ctxfile').empty? then
+ ctx.manipulate(File.suffixed(rawname,'ctx'),'jobname.ctx')
+ else
+ ctx.manipulate(File.suffixed(getvariable('ctxfile'),'ctx'))
+ end
+ ctx.savelog(File.suffixed(rawname,'ctl'))
+
+ envs = ctx.environments
+ mods = ctx.modules
+
+ # merge environment and module specs
+
+ envs << getvariable('environments') unless getvariable('environments').empty?
+ mods << getvariable('modules') unless getvariable('modules') .empty?
+
+ envs = envs.uniq.join(',')
+ mods = mods.uniq.join(',')
+
+ report("using environments #{envs}") if envs.length > 0
+ report("using modules #{mods}") if mods.length > 0
+
+ setvariable('environments', envs)
+ setvariable('modules', mods)
+
+ # end of preprocessing and merging
+
+ jobsuffix = makestubfile(rawname,forcexml) if dummyfile || forcexml
+
+ if globalfile || FileTest.file?(rawname) then
+
+ if not dummyfile and not globalfile then
+ scantexpreamble(rawname)
+ scantexcontent(rawname) if getvariable('texformats').standard?
+ end
+
+ result = File.suffixed(rawname,suffix) unless suffix.empty?
+
+ pushresult(rawname,result)
+
+ method = validtexmethod(validtexformat(getvariable('texformats')))
+
+ report("tex processing method: #{method}")
+
+ case method
+
+ when 'context' then
+ if getvariable('simplerun') || runonce then
+ makeoptionfile(rawname,jobname,orisuffix,true,true,3) unless getvariable('nooptionfile')
+ ok = runtex(rawname)
+ if ok then
+ ok = runtexutil(rawname) if getvariable('texutil') || getvariable('forcetexutil')
+ runbackend(rawname)
+ popresult(rawname,result)
+ end
+ File.silentdelete(File.suffixed(rawname,'tmp'))
+ File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp'))
+ else
+ mprundone, ok, stoprunning = false, true, false
+ texruns, nofruns = 0, getvariable('runs').to_i
+ state = FileState.new
+ ['tub','tuo'].each do |s|
+ state.register(File.suffixed(rawname,s))
+ end
+ if getvariable('automprun') then # check this
+ ['mprun','mpgraph'].each do |s|
+ state.register(File.suffixed(rawname,s,'mp'),'randomseed')
+ end
+ end
+ while ! stoprunning && (texruns < nofruns) && ok do
+ texruns += 1
+ report("TeX run #{texruns}")
+ if texruns == 1 then
+ makeoptionfile(rawname,jobname,orisuffix,false,false,1) unless getvariable('nooptionfile')
+ else
+ makeoptionfile(rawname,jobname,orisuffix,false,false,2) unless getvariable('nooptionfile')
+ end
+ ok = runtex(File.suffixed(rawname,jobsuffix))
+ if ok && (nofruns > 1) then
+ unless getvariable('nompmode') then
+ mprundone = runtexmpjob(rawname, "mpgraph")
+ mprundone = runtexmpjob(rawname, "mprun")
+ end
+ ok = runtexutil(rawname)
+ state.update
+ stoprunning = state.stable?
+ end
+ end
+ ok = runtexutil(rawname) if (nofruns == 1) && getvariable('texutil')
+ if ok && finalrun && (nofruns > 1) then
+ makeoptionfile(rawname,jobname,orisuffix,true,finalrun,4) unless getvariable('nooptionfile')
+ report("final TeX run #{texruns}")
+ ok = runtex(File.suffixed(rawname,jobsuffix))
+ end
+ ['tmp','top'].each do |s| # previous tuo file / runtime option file
+ File.silentdelete(File.suffixed(rawname,s))
+ end
+ File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp'))
+ if ok then
+ runbackend(rawname)
+ popresult(rawname,result)
+ end
+ end
+
+ Kpse.runscript('ctxtools',rawname,'--purge') if getvariable('purge')
+ Kpse.runscript('ctxtools',rawname,'--purgeall') if getvariable('purgeall')
+
+ when 'latex' then
+
+ ok = runtex(rawname)
+
+ else
+
+ ok = runtex(rawname)
+
+ end
+
+ if (dummyfile or forcexml) and FileTest.file?(rawname) then
+ begin
+ File.delete(File.suffixed(rawname,'run'))
+ rescue
+ report("unable to delete stub file")
+ end
+ end
+
+ if ok and getvariable('autopdf') then
+ PDFview.open(File.suffixed(if result.empty? then rawname else result end,'pdf'))
+ end
+
+ end
+
+ end
+
+ # mp specific
+
+ def doruntexmp(mpname,mergebe=true,context=true)
+ texfound = false
+ mpbetex = Hash.new
+ mpfile = File.suffixed(mpname,'mp')
+ mpcopy = File.suffixed(mpname,'copy','mp')
+ setvariable('mp.file',mpfile)
+ setvariable('mp.line','')
+ setvariable('mp.error','')
+ if mpdata = File.silentread(mpfile) then
+ mpdata.gsub!(/^\#.*\n/o,'')
+ File.silentrename(mpfile,mpcopy)
+ texfound = mergebe || mpdata =~ /btex .*? etex/o
+ if mp = File.silentopen(mpfile,'w') then
+ mpdata.gsub!(/(btex.*?)\;(.*?etex)/o) do "#{$1}@@@#{$2}" end
+ mpdata.gsub!(/(\".*?)\;(.*?\")/o) do "#{$1}@@@#{$2}" end
+ mpdata.gsub!(/\;/o, "\;\n")
+ mpdata.gsub!(/\n+/o, "\n")
+ mpdata.gsub!(/(btex.*?)@@@(.*?etex)/o) do "#{$1}\;#{$2}" end
+ mpdata.gsub!(/(\".*?)@@@(.*?\")/o) do "#{$1};#{$2}" end
+ if mergebe then
+ mpdata.gsub!(/beginfig\s*\((\d+)\)\s*\;(.*?)endfig\s*\;/o) do
+ n, str = $1, $2
+ if str =~ /(.*?)(verbatimtex.*?etex)\s*\;(.*)/o then
+ "beginfig(#{n})\;\n$1$2\;\n#{mpbetex(n)}\n$3\;endfig\;\n"
+ else
+ "beginfig(#{n})\;\n#{mpbetex(n)}\n#{str}\;endfig\;\n"
+ end
+ end
+ end
+ unless mpdata =~ /beginfig\s*\(\s*0\s*\)/o then
+ mp << mpbetex[0] if mpbetex.key?(0)
+ end
+ mp << mpdata # ??
+ mp << "\n"
+ mp << "end"
+ mp << "\n"
+ mp.close
+ end
+ processmpx(mpname) if texfound
+ if getvariable('batchmode') then
+ options = ' --interaction=batch'
+ elsif getvariable('nonstopmode') then
+ options = ' --interaction=nonstop'
+ else
+ options = ''
+ end
+ # todo plain|mpost|metafun
+ ok = runmp(mpname)
+ if f = File.silentopen(File.suffixed(mpfile,'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(mpfile,"mptrace.tmp")
+ File.silentrename(mpcopy, mpfile)
+ end
+ end
+
+ def processmpx(mpname,context=true)
+ mpname = File.suffixed(mpname,'mp')
+ if File.atleast?(mpname,10) && (data = File.silentread(mpname)) then
+ begin
+ if data =~ /(btex|etex|verbatimtex)/o then
+ mptex = File.suffixed(mpname,'temp','tex')
+ mpdvi = File.suffixed(mpname,'temp','dvi')
+ mplog = File.suffixed(mpname,'temp','log')
+ mpmpx = File.suffixed(mpname,'temp','mpx')
+ ok = system("mpto #{mpname} > #{mptex}")
+ if ok && File.appended(mptex, "\\end\n") then
+ if context then
+ ok = RunConTeXtFile(mptex)
+ else
+ ok = RunSomeTeXFile(mptex)
+ end
+ ok = ok && FileTest.file?(mpdvi) && system("dvitomp #{mpdvi} #{mpmpx}")
+ [mptex,mpdvi,mplog].each do |mpfil|
+ File.silentdelete(mpfil)
+ end
+ end
+ end
+ rescue
+ # error in processing mpx file
+ end
+ end
+ end
+
+ def checkmpgraphics(mpname)
+ mpoptions = ''
+ if getvariable('makempy') then
+ mpoptions += " --makempy "
+ end
+ if getvariable('mpyforce') || getvariable('forcempy') then
+ mpoptions += " --force "
+ else
+ mponame = File.suffixed(mpname,'mpo')
+ mpyname = File.suffixed(mpname,'mpy')
+ return false unless File.atleast?(mponame,32)
+ mpochecksum = State.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
+ if f = File.open(mpyname) then
+ str = f.gets.chomp
+ f.close
+ if str =~ /^\%\s*mpochecksum\s*\:\s*(\d+)/o then
+ return false if mpochecksum == $1
+ end
+ end
+ end
+ return Kpse.runscript('makempy',mpname)
+ end
+
+ def checkmplabels(mpname)
+ mpname = File.suffixed(mpname,'mpt')
+ if File.atleast?(mpname,10) && (mp = File.open(mpname)) then
+ labels = Hash.new
+ while str = mp.gets do
+ if str =~ /%\s*setup\s*:\s*(.*)/o then
+ t = $1
+ else
+ t = ''
+ end
+ if str =~ /%\s*figure\s*(\d+)\s*:\s*(.*)/o then
+ unless t.empty? then
+ labels[$1] += "#{t}\n"
+ t = ''
+ end
+ labels[$1] += "$2\n"
+ end
+ end
+ mp.close
+ return labels if labels.size>0
+ end
+ return nil
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/texutil.rb b/Master/texmf-dist/scripts/context/ruby/base/texutil.rb
new file mode 100644
index 00000000000..c279bcc97a6
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/texutil.rb
@@ -0,0 +1,872 @@
+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 reset(name)
+ if @plugins.include?(name) then
+ begin
+ eval("#{name}").reset(@logger)
+ rescue Exception
+ @logger.report("fatal error in resetting plugin")
+ 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
+ @logger.report("fatal error in plugin reader #{name} (#{$!})")
+ 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
+ @logger.report("fatal error in plugin writer #{p} (#{$!})")
+ end
+ end
+ end
+
+ def processors
+ @plugins.each do |p|
+ begin
+ eval("#{p}").processor(@logger)
+ rescue Exception
+ @logger.report("fatal error in plugin processor #{p} (#{$!})")
+ end
+ end
+ end
+
+ end
+
+ class Sorter
+
+ 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)
+ from, to = converted(from), converted(to)
+ @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 shortcut(from,to)
+ 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
+ @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/ # o
+ else
+ @rexb = /(\\[a-zA-Z]+|.)\s*/o
+ end
+ end
+
+ def remap(str)
+ s = str.dup
+ s.gsub!(/(\d+)/o) do
+ $1.rjust(10,'a') # rest is b .. k
+ 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 @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=[])
+ # maybe we should move this to sort-def.tex
+ '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('-','-') ;
+ # end potential move
+ shortcuts.each do |s| shortcut(s[0],s[1]) end
+ expansions.each do |e| expander(e[0],e[1]) end
+ reductions.each do |r| reducer(r[0],r[1]) end
+ divisions.each do |d| division(d[0],d[1]) 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-z][a-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
+ 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 = Hash.new
+
+ def MyFiles::reset(logger)
+ @@files = Hash.new
+ end
+
+ def MyFiles::reader(logger,data)
+ case data[0]
+ when 'b', 'e' then
+ if @@files.key?(data[1]) then
+ @@files[data[1]] += 1
+ else
+ @@files[data[1]] = 1
+ end
+ 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
+ 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
+ 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
+
+ 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 do |p|
+ handle << "% #{p} (#{@@programs[p.to_i]})\n"
+ end
+ end
+
+ def MyExtras::processor(logger)
+ @@programs.each do |p|
+ cmd = "texmfstart #{@@programs[p.to_i]}"
+ logger.report("running #{cmd}")
+ system(cmd)
+ end
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MySynonyms
+
+ class Synonym
+
+ @@debug = false
+ @@debug = true
+
+ def initialize(t, c, k, d)
+ @type, @command, @key, @sortkey, @data = t, c, k, k, d
+ end
+
+ attr_reader :type, :command, :key, :data
+ attr_reader :sortkey
+ attr_writer :sortkey
+
+ def build(sorter)
+ @sortkey = sorter.remap(sorter.simplify(@key.downcase))
+ if @sortkey.empty? then
+ @sortkey = sorter.remap(@command.downcase)
+ 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
+
+ def MySynonyms::reset(logger)
+ @@synonyms = Hash.new
+ end
+
+ def MySynonyms::reader(logger,data)
+ if data[0] == '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]))
+ 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)
+ sorter = Sorter.new
+ sorter.preset(eval("MyKeys").shortcuts,eval("MyKeys").expansions,eval("MyKeys").reductions,eval("MyKeys").divisions)
+ sorter.prepare
+ @@synonyms.keys.each do |s|
+ @@synonyms[s].each_index do |i|
+ @@synonyms[s][i].build(sorter)
+ end
+ @@synonyms[s] = @@synonyms[s].sort
+ end
+ end
+
+ end
+
+ end
+
+ class Plugin
+
+ module MyRegisters
+
+ class Register
+
+ @@debug = false
+ @@debug = true
+
+ @@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
+ end
+
+ attr_reader :state, :type, :location, :key, :entry, :seetoo, :page, :realpage, :texthowto, :pagehowto
+ attr_reader :sortkey
+ attr_writer :sortkey
+
+ def build(sorter)
+ @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
+ @sortkey = sorter.simplify(@key)
+ @sortkey = @sortkey.split(@@split).collect do |c| sorter.remap(c) end.join(@@split)
+ # if ($Key eq "") { $Key = SanitizedString($Entry) }
+ # if ($ProcessHigh){ $Key = HighConverted($Key) }
+ @sortkey = [
+ @sortkey.downcase,
+ @sortkey,
+ @texthowto.ljust(10,' '),
+ @state,
+ @realpage.rjust(6,' '),
+ @pagehowto
+ ].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|
+ 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 or 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.length > 1 then
+ # character = "\\getvalue\{#{alpha}\}"
+ character = "\\#{alpha}"
+ else
+ character = "\\#{alpha}"
+ 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] = ''
+ end
+ if howto[1] == previous[1] then
+ current[1] = ''
+ else
+ previous[1] = howto[1].dup
+ previous[2] = ''
+ end
+ if howto[2] == previous[2] then
+ current[2] = ''
+ else
+ previous[2] = howto[2].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
+ @nofentries += 1 if copied
+ if entry.realpage.to_i == 0 then
+ Register.flushsavedline(handle)
+ handle << "\\registersee{#{entry.type}}{#{entry.pagehowto},#{entry.texthowto}}{#{entry.seetoo}}{#{entry.page}}\n" ;
+ lastpage, lastrealpage = entry.page, entry.realpage
+ elsif @@savedhowto != entry.pagehowto and ! entry.pagehowto.empty? then
+ @@savedhowto = entry.pagehowto
+ end
+ if copied || ! ((lastpage == entry.page) && (lastrealpage == entry.realpage)) then
+ nextentry = "{#{entry.type}}{#{previous[0]}}{#{previous[1]}}{#{previous[2]}}{#{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 = Sorter.new
+
+ def MyRegisters::reset(logger)
+ @@registers = Hash.new
+ @@sorter = Sorter.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])
+ @@registers[data[1]].push(Register.new(4,data[1],data[2],data[3],data[4],data[5],data[6],nil))
+ 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)
+ # report("register #{@@registers[s].class}: #{@@registers[s].@nofentries} entries and #{@@registers[s].@nofpages} pages")
+ end
+ end
+ end
+
+ def MyRegisters::processor(logger)
+ @@sorter.preset(eval("MyKeys").shortcuts,eval("MyKeys").expansions,eval("MyKeys").reductions,eval("MyKeys").divisions)
+ @@sorter.prepare
+ @@registers.keys.each do |s|
+ @@registers[s].each_index do |i|
+ @@registers[s][i].build(@@sorter)
+ end
+ @@registers[s] = @@registers[s].sort
+ end
+ 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
+
+ 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
+
+ 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
+ report("parsing file #{filename}")
+ if f = open(File.suffixed(filename,'tui')) 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)
+ else report("unknown entry #{line[0,1]} in line #{line.chomp}")
+ end
+ end
+ f.close
+ end
+ rescue
+ report("fatal error in parsing #{filename}")
+ @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")
+ else
+ begin
+ if f = File.open(File.suffixed(filename,'tuo'),'w') then
+ @plugins.writers(f)
+ f.close
+ end
+ rescue
+ report("fatal error when saving file (#{$!})")
+ else
+ report("tuo file saved")
+ end
+ end
+ @plugins.resets
+ end
+
+ def reset
+ @plugins.resets
+ end
+
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/tool.rb b/Master/texmf-dist/scripts/context/ruby/base/tool.rb
new file mode 100644
index 00000000000..7f40e773d0c
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/tool.rb
@@ -0,0 +1,306 @@
+# 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}")
+ if pth == $constructedtempdir
+ # sleep(0.01)
+ retry
+ end
+ 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 unless test(?f,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
+ 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 test(?f,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(.*?)$/io) do
+ '-' + $1 + '.eps'
+ end
+ new
+
+ end
+
+ def Tool.cleanfilename(old,logging=nil)
+
+ return old unless test(?f,old)
+
+ new = checksuffix(simplefilename(old))
+ unless new == old
+ begin
+ 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.preventduplicates(old,logging=nil)
+
+ return false unless test(?f,old)
+
+ if old =~ /\.(tif|jpg|png|tiff)$/io
+ suffix = $1
+ new = old
+ newn, news = new.split('.')
+ if test(?e,'newn.eps')
+ new = newn + '-' + suffix + '.' + suffix
+ begin
+ File.rename(old,new)
+ logging.report("renaming duplicate #{old} to #{new}") unless logging
+ return true
+ rescue
+ logging.report("unable to rename duplicate #{old} to #{new}") unless logging
+ end
+ end
+ end
+ return false
+
+ 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/Master/texmf-dist/scripts/context/ruby/base/variables.rb b/Master/texmf-dist/scripts/context/ruby/base/variables.rb
new file mode 100644
index 00000000000..5cbc5ba538e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/variables.rb
@@ -0,0 +1,45 @@
+# 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'
+
+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 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