summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts/context/ruby
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
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')
-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
-rw-r--r--Master/texmf-dist/scripts/context/ruby/concheck.rb461
-rw-r--r--Master/texmf-dist/scripts/context/ruby/ctxtools.rb1405
-rw-r--r--Master/texmf-dist/scripts/context/ruby/fcd_start.rb453
-rw-r--r--Master/texmf-dist/scripts/context/ruby/graphics/gs.rb641
-rw-r--r--Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb103
-rw-r--r--Master/texmf-dist/scripts/context/ruby/graphics/magick.rb161
-rw-r--r--Master/texmf-dist/scripts/context/ruby/mpstools.rb5
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb86
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newpstopdf.rb530
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newtexexec.rb609
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newtexutil.rb96
-rw-r--r--Master/texmf-dist/scripts/context/ruby/rlxtools.rb262
-rw-r--r--Master/texmf-dist/scripts/context/ruby/texmfstart.rb847
-rw-r--r--Master/texmf-dist/scripts/context/ruby/texsync.rb207
-rw-r--r--Master/texmf-dist/scripts/context/ruby/textools.rb885
-rw-r--r--Master/texmf-dist/scripts/context/ruby/tmftools.rb133
-rw-r--r--Master/texmf-dist/scripts/context/ruby/xmltools.rb352
30 files changed, 12509 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
diff --git a/Master/texmf-dist/scripts/context/ruby/concheck.rb b/Master/texmf-dist/scripts/context/ruby/concheck.rb
new file mode 100644
index 00000000000..bf09bbdc8da
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/concheck.rb
@@ -0,0 +1,461 @@
+# Program : concheck (tex & context syntax checker)
+# Copyright : PRAGMA ADE / Hasselt NL / www.pragma-ade.com
+# Author : Hans Hagen
+# Version : 1.1 / 2003.08.18
+
+# remarks:
+#
+# - the error messages are formatted like tex's messages so that scite can see them
+# - begin and end tags are only tested on a per line basis because we assume clean sources
+# - maybe i'll add begin{something} ... end{something} checking
+
+# # example validation file
+#
+# begin interface en
+#
+# 1 text
+# 4 Question
+# 0 endinput
+# 0 setupsomething
+# 0 chapter
+#
+# end interface en
+
+# nicer
+
+# class Interface
+
+ # def initialize (language = 'unknown')
+ # @valid = Array.new
+ # @language = language
+ # end
+
+ # def register (left, right)
+ # @valid.push([left,right])
+ # end
+
+# end
+
+# $interfaces = Hash.new
+
+# $interfaces['en'] = Interface.new('english')
+# $interfaces['nl'] = Interface.new('dutch')
+
+# $interfaces['en'].add('\\\\start','\\\\stop')
+# $interfaces['en'].add('\\\\begin','\\\\end')
+# $interfaces['en'].add('\\\\Start','\\\\Stop')
+# $interfaces['en'].add('\\\\Begin','\\\\End')
+
+# $interfaces['nl'].add('\\\\start','\\\\stop')
+# $interfaces['nl'].add('\\\\beginvan','\\\\eindvan')
+# $interfaces['nl'].add('\\\\Start','\\\\Stop')
+# $interfaces['nl'].add('\\\\BeginVan','\\\\Eindvan')
+
+# rest todo
+
+$valid = Hash.new
+
+$valid['en'] = Array.new
+$valid['nl'] = Array.new
+
+#$valid['en'].push(['',''])
+$valid['en'].push(['\\\\start','\\\\stop'])
+$valid['en'].push(['\\\\begin','\\\\end'])
+$valid['en'].push(['\\\\Start','\\\\Stop'])
+$valid['en'].push(['\\\\Begin','\\\\End'])
+
+#$valid['nl'].push(['',''])
+$valid['nl'].push(['\\\\start','\\\\stop'])
+$valid['nl'].push(['\\\\beginvan','\\\\eindvan'])
+$valid['nl'].push(['\\\\Start','\\\\Stop'])
+$valid['nl'].push(['\\\\BeginVan','\\\\Eindvan'])
+
+$valid_tex = "\\\\end\(input|insert|csname|linechar|graf|buffer|strut\)"
+$valid_mp = "(enddef||end||endinput)"
+
+$start_verbatim = Hash.new
+$stop_verbatim = Hash.new
+
+$start_verbatim['en'] = '\\\\starttyping'
+$start_verbatim['nl'] = '\\\\starttypen'
+
+$stop_verbatim['en'] = '\\\\stoptyping'
+$stop_verbatim['nl'] = '\\\\stoptypen'
+
+def message(str, filename=nil, line=nil, column=nil)
+ if filename then
+ if line then
+ if column then
+ puts("error in file #{filename} at line #{line} in column #{column}: #{str}\n")
+ else
+ puts("error in file #{filename} at line #{line}: #{str}\n")
+ end
+ else
+ puts("file #{filename}: #{str}\n")
+ end
+ else
+ puts(str+"\n")
+ end
+end
+
+def load_file (filename='')
+ begin
+ data = IO.readlines(filename)
+ data.collect! do |d|
+ if d =~ /^\s*%/o then
+ ''
+ elsif d =~ /(.*?[^\\])%.*$/o then
+ $1
+ else
+ d
+ end
+ end
+ rescue
+ message("provide proper filename")
+ return nil
+ end
+ # print data.to_s + "\n"
+ return data
+end
+
+def guess_interface(data)
+ if data.first =~ /^%.*interface\=(.*)\s*/ then
+ return $1
+ else
+ data.each do |line|
+ case line
+ when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then return 'nl'
+ when /\\(stelle|verwende|umgebung|benutze)/o then return 'de'
+ when /\\(stel|gebruik|omgeving)/ then return 'nl'
+ when /\\(use|setup|environment)/ then return 'en'
+ when /\\(usa|imposta|ambiente)/ then return 'it'
+ when /(height|width|style)=/ then return 'en'
+ when /(hoehe|breite|schrift)=/ then return 'de'
+ when /(hoogte|breedte|letter)=/ then return 'nl'
+ when /(altezza|ampiezza|stile)=/ then return 'it'
+ when /externfiguur/ then return 'nl'
+ when /externalfigure/ then return 'en'
+ when /externeabbildung/ then return 'de'
+ when /figuraesterna/ then return 'it'
+ end
+ end
+ return 'en'
+ end
+end
+
+def cleanup_data(data, interface='en')
+ verbatim = 0
+ re_start = /^\s*#{$start_verbatim[interface]}/
+ re_stop = /^\s*#{$stop_verbatim[interface]}/
+ data.collect! do |d|
+ if d =~ re_start then
+ verbatim += 1
+ if verbatim>1 then
+ ''
+ else
+ d
+ end
+ elsif d =~ re_stop then
+ verbatim -= 1
+ if verbatim>0 then
+ ''
+ else
+ d
+ end
+ elsif verbatim > 0 then
+ ''
+ else
+ d
+ end
+ end
+ return data
+end
+
+def load_valid(data, interface=nil)
+ if data && (data.first =~ /^%.*valid\=(.*)\s*/)
+ filename = $1
+ filename = '../' + filename unless test(?f,filename)
+ filename = '../' + filename unless test(?f,filename)
+ if test(?f,filename) then
+ interface = guess_interface(data) unless interface
+ if $valid.has_key?(interface) then
+ interface = $valid[interface]
+ else
+ interface = $valid['en']
+ end
+ begin
+ message("loading validation file",filename)
+ validkeys = Hash.new
+ line = 1
+ IO.readlines(filename).each do |l|
+ if l =~ /\s+[\#\%]/io then
+ # ignore line
+ elsif l =~ /^\s*(begin|end)\s+interface\s+([a-z][a-z])/o then
+ # not yet supported
+ elsif l =~ /^\s*(\d+)\s+([a-zA-Z]*)$/o then
+ type, key = $1.to_i, $2.strip
+ if interface[type] then
+ validkeys[interface[type].first+key] = true
+ validkeys[interface[type].last+key] = true
+ else
+ error_message(filename,line,nil,'wrong definition')
+ end
+ end
+ line += 1
+ end
+ if validkeys then
+ message("#{validkeys.length} validation keys loaded",filename)
+ end
+ return validkeys
+ rescue
+ message("invalid validation file",filename)
+ end
+ else
+ message("unknown validation file", filename)
+ end
+ else
+ message("no extra validation file specified")
+ end
+ return nil
+end
+
+def some_chr_error(data, filename, left, right)
+ levels = Array.new
+ for line in 0..data.length-1 do
+ str = data[line]
+ column = 0
+ while column<str.length do
+ case str[column].chr
+ when "\%" then
+ break
+ when "\\" then
+ column += 2
+ when left then
+ levels.push([line,column])
+ column += 1
+ when right then
+ if levels.pop
+ column += 1
+ else
+ message("missing #{left} for #{right}",filename,line+1,column+1)
+ return true
+ end
+ else
+ column += 1
+ end
+ end
+ end
+ if levels && levels.length>0 then
+ levels.each do |l|
+ column = l.pop
+ line = l.pop
+ message("missing #{right} for #{left}",filename,line+1,column+1)
+ end
+ return true
+ else
+ return false
+ end
+end
+
+def some_wrd_error(data, filename, start, stop, ignore)
+ levels = Array.new
+ len = 0
+ re_start = /[^\%]*(#{start})([a-zA-Z]*)/
+ re_stop = /[^\%]*(#{stop})([a-zA-Z]*)/
+ re_ignore = /#{ignore}.*/
+ str_start = start.gsub(/\\+/,'\\')
+ str_stop = stop.gsub(/\\+/,'\\')
+ line = 0
+ while line<data.length do
+ dataline = data[line].split(/[^\\A-Za-z]/)
+ if dataline.length>0 then
+ # todo: more on one line
+ dataline.each do |dataword|
+ case dataword
+ when re_ignore then
+ # just go on
+ when re_start then
+ levels.push([line,$2])
+ # print ' '*levels.length + '>' + $2 + "\n"
+ when re_stop then
+ # print ' '*levels.length + '<' + $2 + "\n"
+ if levels && levels.last && (levels.last[1] == $2) then
+ levels.pop
+ elsif levels && levels.last then
+ message("#{str_stop}#{levels.last[1]} expected instead of #{str_stop}#{$2}",filename,line+1)
+ return true
+ else
+ message("missing #{str_start}#{$2} for #{str_stop}#{$2}",filename,line+1)
+ return true
+ end
+ else
+ # just go on
+ end
+ end
+ end
+ line += 1
+ end
+ if levels && levels.length>0 then
+ levels.each do |l|
+ text = l.pop
+ line = l.pop
+ message("missing #{str_stop}#{text} for #{str_start}#{text}",filename,line+1)
+ end
+ return true
+ else
+ return false
+ end
+end
+
+def some_sym_error (data, filename, symbol,template=false)
+ saved = Array.new
+ inside = false
+ level = 0
+ for line in 0..data.length-1 do
+ str = data[line]
+ column = 0
+ while column<str.length do
+ case str[column].chr
+ when "[" then
+ level += 1 if template
+ when "]" then
+ level -= 1 if template && level > 0
+ when "\%" then
+ break
+ when "\\" then
+ column += 1
+ when symbol then
+ if level == 0 then
+ inside = ! inside
+ saved = [line,column]
+ else
+ # we're in some kind of template or so
+ end
+ else
+ # go on
+ end
+ column += 1
+ end
+ end
+ if inside && saved && level == 0 then
+ column = saved.pop
+ line = saved.pop
+ message("missing #{symbol} for #{symbol}",filename,line+1)
+ return true
+ else
+ return false
+ end
+end
+
+def some_key_error(data, filename, valid)
+ return if (! valid) || (valid.length == 0)
+ error = false
+ # data.foreach do |line| ... end
+ for line in 0..data.length-1 do
+ data[line].scan(/\\([a-zA-Z]+)/io) do
+ unless valid.has_key?($1) then
+ message("unknown command \\#{$1}",filename,line+1)
+ error = true
+ end
+ end
+ end
+ return error
+end
+
+# todo : language dependent
+
+def check_file_tex (filename)
+ if data = load_file(filename) then
+ message("checking tex file", filename)
+ interface = guess_interface(data)
+ valid = load_valid(data,interface)
+ data = cleanup_data(data,interface)
+ # data.each do |d| print d end
+ $valid[interface].each do |v|
+ return false if some_wrd_error(data, filename, v[0], v[1] ,$valid_tex)
+ end
+ # return false if some_wrd_error(data, filename, '\\\\start' , '\\\\stop' , $valid_tex)
+ # return false if some_wrd_error(data, filename, '\\\\Start' , '\\\\Stop' , $valid_tex)
+ # return false if some_wrd_error(data, filename, '\\\\beginvan', '\\\\eindvan', $valid_tex)
+ # return false if some_wrd_error(data, filename, '\\\\begin' , '\\\\end|\\\\eind', $valid_tex)
+ return false if some_sym_error(data, filename, '$', false)
+ return false if some_sym_error(data, filename, '|', true)
+ return false if some_chr_error(data, filename, '{', '}')
+ return false if some_chr_error(data, filename, '[', ']')
+ return false if some_chr_error(data, filename, '(', ')')
+ return false if some_key_error(data, filename, valid)
+ message("no errors in tex code", filename)
+ return true
+ else
+ return false
+ end
+end
+
+def check_file_mp (filename)
+ if data = load_file(filename) then
+ message("checking metapost file", filename)
+ interface = guess_interface(data)
+ valid = load_valid(data,interface)
+ $valid[interface].each do |v|
+ return false if some_wrd_error(data, filename, v[0], v[1] ,$valid_tex)
+ end
+ # return false if some_wrd_error(data, filename, '', 'begin', 'end', $valid_mp)
+ return false if some_chr_error(data, filename, '{', '}')
+ return false if some_chr_error(data, filename, '[', ']')
+ return false if some_chr_error(data, filename, '(', ')')
+ return false if some_key_error(data, filename, valid)
+ message("no errors in metapost code", filename)
+ return true
+ else
+ return true
+ end
+end
+
+def check_file_text(filename='')
+ if data = load_file(filename) then
+ for line in 0..data.length-1 do
+ # case data[line]
+ # when /\s([\:\;\,\.\?\!])/ then
+ # message("space before #{$1}",filename,line+1)
+ # when /\D([\:\;\,\.\?\!])\S/ then
+ # message("no space after #{$1}",filename,line+1)
+ # end
+ if data[line] =~ /\s([\:\;\,\.\?\!])/ then
+ message("space before #{$1}",filename,line+1)
+ else
+ data[line].gsub!(/\[.*?\]/o, '')
+ data[line].gsub!(/\(.*?\)/o, '')
+ data[line].gsub!(/\[.*?$/o, '')
+ data[line].gsub!(/^.*?\]/o, '')
+ if data[line] =~ /\D([\:\;\,\.\?\!])\S/ then
+ message("no space after #{$1}",filename,line+1)
+ end
+ end
+ end
+ end
+end
+
+def check_file(filename='')
+ case filename
+ when '' then
+ message("provide filename")
+ return false
+ when /\.tex$/i then
+ return check_file_tex(filename) # && check_file_text(filename)
+ when /\.mp$/i then
+ return check_file_mp(filename)
+ else
+ message("only tex and metapost files are checked")
+ return false
+ end
+end
+
+if filename = ARGV[0] then
+ if check_file(filename) then
+ exit 0
+ else
+ exit 1
+ end
+else
+ exit 1
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/ctxtools.rb b/Master/texmf-dist/scripts/context/ruby/ctxtools.rb
new file mode 100644
index 00000000000..94e6e735a20
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/ctxtools.rb
@@ -0,0 +1,1405 @@
+#!/usr/bin/env ruby
+
+# program : ctxtools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2004-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on context
+# related files.
+
+# todo: move scite here
+#
+# todo: move kpse call to kpse class/module
+
+banner = ['CtxTools', 'version 1.2.2', '2004/2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+require 'base/system'
+
+require 'rexml/document'
+require 'ftools'
+require 'kconv'
+
+exit if defined?(REQUIRE2LIB)
+
+class String
+
+ def i_translate(element, attribute, category)
+ self.gsub!(/(<#{element}.*?#{attribute}=)([\"\'])(.*?)\2/) do
+ if category.key?($3) then
+ # puts "#{element} #{$3} -> #{category[$3]}\n" if element == 'cd:inherit'
+ # puts "#{element} #{$3} => #{category[$3]}\n" if element == 'cd:command'
+ "#{$1}#{$2}#{category[$3]}#{$2}"
+ else
+ # puts "#{element} #{$3} -> ?\n" if element == 'cd:inherit'
+ # puts "#{element} #{$3} => ?\n" if element == 'cd:command'
+ "#{$1}#{$2}#{$3}#{$2}" # unchanged
+ end
+ end
+ end
+
+ def i_load(element, category)
+ self.scan(/<#{element}.*?name=([\"\'])(.*?)\1.*?value=\1(.*?)\1/) do
+ category[$2] = $3
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def touchcontextfile
+ dowithcontextfile(1)
+ end
+
+ def contextversion
+ dowithcontextfile(2)
+ end
+
+ private
+
+ def dowithcontextfile(action)
+ maincontextfile = 'context.tex'
+ unless FileTest.file?(maincontextfile) then
+ begin
+ maincontextfile = `kpsewhich -progname=context #{maincontextfile}`.chomp
+ rescue
+ maincontextfile = ''
+ end
+ end
+ unless maincontextfile.empty? then
+ case action
+ when 1 then touchfile(maincontextfile)
+ when 2 then reportversion(maincontextfile)
+ end
+ end
+
+ end
+
+ def touchfile(filename)
+
+ if FileTest.file?(filename) then
+ if data = IO.read(filename) then
+ timestamp = Time.now.strftime('%Y.%m.%d')
+ prevstamp = ''
+ begin
+ data.gsub!(/\\contextversion\{(\d+\.\d+\.\d+)\}/) do
+ prevstamp = $1
+ "\\contextversion{#{timestamp}}"
+ end
+ rescue
+ else
+ begin
+ File.delete(filename+'.old')
+ rescue
+ end
+ begin
+ File.copy(filename,filename+'.old')
+ rescue
+ end
+ begin
+ if f = File.open(filename,'w') then
+ f.puts(data)
+ f.close
+ end
+ rescue
+ end
+ end
+ if prevstamp.empty? then
+ report("#{filename} is not updated, no timestamp found")
+ else
+ report("#{filename} is updated from #{prevstamp} to #{timestamp}")
+ end
+ end
+ else
+ report("#{filename} is not found")
+ end
+
+ end
+
+ def reportversion(filename)
+
+ version = 'unknown'
+ begin
+ if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+)\}/) then
+ version = $1
+ end
+ rescue
+ end
+ if @commandline.option("pipe") then
+ print version
+ else
+ report("context version: #{version}")
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def jeditinterface
+ editinterface('jedit')
+ end
+
+ def bbeditinterface
+ editinterface('bbedit')
+ end
+
+ def sciteinterface
+ editinterface('scite')
+ end
+
+ def rawinterface
+ editinterface('raw')
+ end
+
+ private
+
+ def editinterface(type='raw')
+
+ return unless FileTest.file?("cont-en.xml")
+
+ interfaces = @commandline.arguments
+
+ if interfaces.empty? then
+ interfaces = ['en', 'cz','de','it','nl','ro']
+ end
+
+ interfaces.each do |interface|
+ begin
+ collection = Hash.new
+ mappings = Hash.new
+ if f = open("keys-#{interface}.xml") then
+ while str = f.gets do
+ if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+value=\"(.*?)\".*?\>/o then
+ mappings[$1] = $2
+ end
+ end
+ f.close
+ if f = open("cont-en.xml") then
+ while str = f.gets do
+ if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+type=\"environment\".*?\>/o then
+ collection["start#{mappings[$1]}"] = ''
+ collection["stop#{mappings[$1]}"] = ''
+ elsif str =~ /\<cd\:command\s+name=\"(.*?)\".*?\>/o then
+ collection["#{mappings[$1]}"] = ''
+ end
+ end
+ f.close
+ case type
+ when 'jedit' then
+ if f = open("context-jedit-#{interface}.xml", 'w') then
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<!DOCTYPE MODE SYSTEM 'xmode.dtd'>\n\n")
+ f.puts("<MODE>\n")
+ f.puts(" <RULES>\n")
+ f.puts(" <KEYWORDS>\n")
+ collection.keys.sort.each do |name|
+ f.puts(" <KEYWORD2>\\#{name}</KEYWORD2>\n") unless name.empty?
+ end
+ f.puts(" </KEYWORDS>\n")
+ f.puts(" </RULES>\n")
+ f.puts("</MODE>\n")
+ f.close
+ end
+ when 'bbedit' then
+ if f = open("context-bbedit-#{interface}.xml", 'w') then
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<key>BBLMKeywordList</key>\n")
+ f.puts("<array>\n")
+ collection.keys.sort.each do |name|
+ f.puts(" <string>\\#{name}</string>\n") unless name.empty?
+ end
+ f.puts("</array>\n")
+ f.close
+ end
+ when 'scite' then
+ if f = open("cont-#{interface}-scite.properties", 'w') then
+ i = 0
+ f.write("keywordclass.macros.context.#{interface}=")
+ collection.keys.sort.each do |name|
+ unless name.empty? then
+ if i==0 then
+ f.write("\\\n ")
+ i = 5
+ else
+ i = i - 1
+ end
+ f.write("#{name} ")
+ end
+ end
+ f.write("\n")
+ f.close
+ end
+ else # raw
+ collection.keys.sort.each do |name|
+ puts("\\#{name}\n") unless name.empty?
+ end
+ end
+ end
+ end
+ end
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def translateinterface
+
+ # since we know what kind of file we're dealing with,
+ # we do it quick and dirty instead of using rexml or
+ # xslt
+
+ interfaces = @commandline.arguments
+
+ if interfaces.empty? then
+ interfaces = ['cz','de','it','nl','ro']
+ else
+ interfaces.delete('en')
+ end
+
+ interfaces.flatten.each do |interface|
+
+ variables, constants, strings, list, data = Hash.new, Hash.new, Hash.new, '', ''
+
+ keyfile, intfile, outfile = "keys-#{interface}.xml", "cont-en.xml", "cont-#{interface}.xml"
+
+ report("generating #{keyfile}")
+
+ begin
+ one = "texexec --make --alone --all #{interface}"
+ two = "texexec --batch --silent --interface=#{interface} x-set-01"
+ if @commandline.option("force") then
+ system(one)
+ system(two)
+ elsif not system(two) then
+ system(one)
+ system(two)
+ end
+ rescue
+ end
+
+ unless File.file?(keyfile) then
+ report("no #{keyfile} generated")
+ next
+ end
+
+ report("loading #{keyfile}")
+
+ begin
+ list = IO.read(keyfile)
+ rescue
+ list = empty
+ end
+
+ if list.empty? then
+ report("error in loading #{keyfile}")
+ next
+ end
+
+ list.i_load('cd:variable', variables)
+ list.i_load('cd:constant', constants)
+ list.i_load('cd:command' , strings)
+ # list.i_load('cd:element' , strings)
+
+ report("loading #{intfile}")
+
+ begin
+ data = IO.read(intfile)
+ rescue
+ data = empty
+ end
+
+ if data.empty? then
+ report("error in loading #{intfile}")
+ next
+ end
+
+ report("translating interface en to #{interface}")
+
+ data.i_translate('cd:string' , 'value', strings)
+ data.i_translate('cd:variable' , 'value', variables)
+ data.i_translate('cd:parameter', 'name' , constants)
+ data.i_translate('cd:constant' , 'type' , variables)
+ data.i_translate('cd:variable' , 'type' , variables)
+ data.i_translate('cd:inherit' , 'name' , strings)
+ # data.i_translate('cd:command' , 'name' , strings)
+
+ report("saving #{outfile}")
+
+ begin
+ if f = File.open(outfile, 'w') then
+ f.write(data)
+ f.close
+ end
+ rescue
+ end
+
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def purgefiles(all=false)
+
+ pattern = @commandline.arguments
+ purgeall = @commandline.option("all") || all
+ recurse = @commandline.option("recurse")
+
+ $dontaskprefixes.push(Dir.glob("mpx-*"))
+ $dontaskprefixes.flatten!
+ $dontaskprefixes.sort!
+
+ if purgeall then
+ $forsuresuffixes.push($texnonesuffixes)
+ $texnonesuffixes = []
+ $forsuresuffixes.flatten!
+ end
+
+ if ! pattern || pattern.empty? then
+ globbed = if recurse then "**/*.*" else "*.*" end
+ files = Dir.glob(globbed)
+ report("purging files : #{globbed}")
+ else
+ pattern.each do |pat|
+ globbed = if recurse then "**/#{pat}-*.*" else "#{pat}-*.*" end
+ files = Dir.glob(globbed)
+ globbed = if recurse then "**/#{pat}.*" else "#{pat}.*" end
+ files.push(Dir.glob(globbed))
+ end
+ report("purging files : #{pattern.join(' ')}")
+ end
+ files.flatten!
+ files.sort!
+
+ $dontaskprefixes.each do |file|
+ removecontextfile(file)
+ end
+ $dontasksuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /#{suffix}$/i
+ end
+ end
+ $forsuresuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /\.#{suffix}$/i
+ end
+ end
+ files.each do |file|
+ if file =~ /(.*?)\.\d+$/o then
+ basename = $1
+ if file =~ /mp(graph|run)/o || FileTest.file?("#{basename}.mp") then
+ removecontextfile($file)
+ end
+ end
+ end
+ $texnonesuffixes.each do |suffix|
+ files.each do |file|
+ if file =~ /(.*)\.#{suffix}$/i then
+ if FileTest.file?("#{$1}.tex") || FileTest.file?("#{$1}.xml") || FileTest.file?("#{$1}.fo") then
+ keepcontextfile(file)
+ else
+ strippedname = $1.gsub(/\-[a-z]$/io, '')
+ if FileTest.file?("#{strippedname}.tex") || FileTest.file?("#{strippedname}.xml") then
+ keepcontextfile("#{file} (potential result file)")
+ else
+ removecontextfile(file)
+ end
+ end
+ end
+ end
+ end
+
+ files = Dir.glob("*.*")
+ $dontasksuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /^#{suffix}$/i
+ end
+ end
+
+ if $removedfiles || $keptfiles || $persistentfiles then
+ report("removed files : #{$removedfiles}")
+ report("kept files : #{$keptfiles}")
+ report("persistent files : #{$persistentfiles}")
+ report("reclaimed bytes : #{$reclaimedbytes}")
+ end
+
+ end
+
+ def purgeallfiles
+ purgefiles(true) # for old times sake
+ end
+
+ private
+
+ $removedfiles = 0
+ $keptfiles = 0
+ $persistentfiles = 0
+ $reclaimedbytes = 0
+
+ $dontaskprefixes = [
+ # "tex-form.tex", "tex-edit.tex", "tex-temp.tex",
+ "texexec.tex", "texexec.tui", "texexec.tuo",
+ "texexec.ps", "texexec.pdf", "texexec.dvi",
+ "cont-opt.tex", "cont-opt.bak"
+ ]
+ $dontasksuffixes = [
+ "mp(graph|run)\\.mp", "mp(graph|run)\\.mpd", "mp(graph|run)\\.mpo", "mp(graph|run)\\.mpy",
+ "mp(graph|run)\\.\\d+",
+ "xlscript\\.xsl"
+ ]
+ $forsuresuffixes = [
+ "tui", "tup", "ted", "tes", "top",
+ "log", "tmp", "run", "bck", "rlg",
+ "mpt", "mpx", "mpd", "mpo"
+ ]
+ $texonlysuffixes = [
+ "dvi", "ps", "pdf"
+ ]
+ $texnonesuffixes = [
+ "tuo", "tub", "top"
+ ]
+
+ def removecontextfile (filename)
+ if filename && FileTest.file?(filename) then
+ begin
+ filesize = FileTest.size(filename)
+ File.delete(filename)
+ rescue
+ report("problematic : #{filename}")
+ else
+ if FileTest.file?(filename) then
+ $persistentfiles += 1
+ report("persistent : #{filename}")
+ else
+ $removedfiles += 1
+ $reclaimedbytes += filesize
+ report("removed : #{filename}")
+ end
+ end
+ end
+ end
+
+ def keepcontextfile (filename)
+ if filename && FileTest.file?(filename) then
+ $keptfiles += 1
+ report("not removed : #{filename}")
+ end
+ end
+
+end
+
+#D Documentation can be woven into a source file. The next
+#D routine generates a new, \TEX\ ready file with the
+#D documentation and source fragments properly tagged. The
+#D documentation is included as comment:
+#D
+#D \starttypen
+#D %D ...... some kind of documentation
+#D %M ...... macros needed for documenation
+#D %S B begin skipping
+#D %S E end skipping
+#D \stoptypen
+#D
+#D The most important tag is \type {%D}. Both \TEX\ and \METAPOST\
+#D files use \type{%} as a comment chacacter, while \PERL, \RUBY\
+#D and alike use \type{#}. Therefore \type{#D} is also handled.
+#D
+#D The generated file gets the suffix \type{ted} and is
+#D structured as:
+#D
+#D \starttypen
+#D \startmodule[type=suffix]
+#D \startdocumentation
+#D \stopdocumentation
+#D \startdefinition
+#D \stopdefinition
+#D \stopmodule
+#D \stoptypen
+#D
+#D Macro definitions specific to the documentation are not
+#D surrounded by start||stop commands. The suffix specifaction
+#D can be overruled at runtime, but defaults to the file
+#D extension. This specification can be used for language
+#D depended verbatim typesetting.
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def documentation
+ files = @commandline.arguments
+ processtype = @commandline.option("type")
+ files.each do |fullname|
+ if fullname =~ /(.*)\.(.+?)$/o then
+ filename, filesuffix = $1, $2
+ else
+ filename, filesuffix = fullname, 'tex'
+ end
+ filesuffix = 'tex' if filesuffix.empty?
+ fullname, resultname = "#{filename}.#{filesuffix}", "#{filename}.ted"
+ if ! FileTest.file?(fullname)
+ report("empty input file #{fullname}")
+ elsif ! tex = File.open(fullname)
+ report("invalid input file #{fullname}")
+ elsif ! ted = File.open(resultname,'w') then
+ report("unable to openresult file #{resultname}")
+ else
+ report("input file : #{fullname}")
+ report("output file : #{resultname}")
+ nofdocuments, nofdefinitions, nofskips = 0, 0, 0
+ skiplevel, indocument, indefinition, skippingbang = 0, false, false, false
+ if processtype.empty? then
+ filetype = filesuffix.downcase
+ else
+ filetype = processtype.downcase
+ end
+ report("filetype : #{filetype}")
+ # we need to signal to texexec what interface to use
+ firstline = tex.gets
+ if firstline =~ /^\%.*interface\=/ then
+ ted.puts(firstline)
+ else
+ tex.rewind # seek(0)
+ end
+ ted.puts("\\startmodule[type=#{filetype}]\n")
+ while str = tex.gets do
+ if skippingbang then
+ skippingbang = false
+ else
+ str.chomp!
+ str.sub!(/\s*$/o, '')
+ case str
+ when /^[%\#]D/io then
+ if skiplevel == 0 then
+ someline = if str.length < 3 then "" else str[3,str.length-1] end
+ if indocument then
+ ted.puts("#{someline}\n")
+ else
+ if indefinition then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ end
+ unless indocument then
+ ted.puts("\n\\startdocumentation\n")
+ end
+ ted.puts("#{someline}\n")
+ indocument = true
+ nofdocuments += 1
+ end
+ end
+ when /^[%\#]M/io then
+ if skiplevel == 0 then
+ someline = if str.length < 3 then "" else str[3,str.length-1] end
+ ted.puts("#{someline}\n")
+ end
+ when /^[%\%]S B/io then
+ skiplevel += 1
+ nofskips += 1
+ when /^[%\%]S E/io then
+ skiplevel -= 1
+ when /^[%\#]/io then
+ #nothing
+ when /^eval \'\(exit \$\?0\)\' \&\& eval \'exec perl/o then
+ skippingbang = true
+ else
+ if skiplevel == 0 then
+ inlocaldocument = indocument
+ someline = str
+ if indocument then
+ ted.puts("\\stopdocumentation\n")
+ indocument = false
+ end
+ if someline.empty? && indefinition then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ elsif indefinition then
+ ted.puts("#{someline}\n")
+ elsif ! someline.empty? then
+ ted.puts("\n\\startdefinition\n")
+ indefinition = true
+ unless inlocaldocument then
+ nofdefinitions += 1
+ ted.puts("#{someline}\n")
+ end
+ end
+ end
+ end
+ end
+ end
+ if indocument then
+ ted.puts("\\stopdocumentation\n")
+ end
+ if indefinition then
+ ted.puts("\\stopdefinition\n")
+ end
+ ted.puts("\\stopmodule\n")
+ ted.close
+
+ if nofdocuments == 0 && nofdefinitions == 0 then
+ begin
+ File.delete(resultname)
+ rescue
+ end
+ end
+ report("documentation sections : #{nofdocuments}")
+ report("definition sections : #{nofdefinitions}")
+ report("skipped sections : #{nofskips}")
+ end
+ end
+ end
+
+end
+
+#D This feature was needed when \PDFTEX\ could not yet access page object
+#D numbers (versions prior to 1.11).
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def filterpages # temp feature / no reporting
+ filename = @commandline.argument('first')
+ filename.sub!(/\.([a-z]+?)$/io,'')
+ pdffile = "#{filename}.pdf"
+ tuofile = "#{filename}.tuo"
+ if FileTest.file?(pdffile) then
+ begin
+ prevline, n = '', 0
+ if (pdf = File.open(pdffile)) && (tuo = File.open(tuofile,'a')) then
+ report('filtering page object numbers')
+ pdf.binmode
+ while line = pdf.gets do
+ line.chomp
+ # typical pdftex search
+ if (line =~ /\/Type \/Page/o) && (prevline =~ /^(\d+)\s+0\s+obj/o) then
+ p = $1
+ n += 1
+ tuo.puts("\\objectreference{PDFP}{#{n}}{#{p}}{#{n}}\n")
+ else
+ prevline = line
+ end
+ end
+ end
+ pdf.close
+ tuo.close
+ report("number of pages : #{n}")
+ rescue
+ report("fatal error in filtering pages")
+ end
+ end
+ end
+
+end
+
+# This script is used to generate hyphenation pattern files
+# that suit ConTeXt. One reason for independent files is that
+# over the years too many uncommunicated changes took place
+# as well that inconsistency in content, naming, and location
+# in the texmf tree takes more time than I'm willing to spend
+# on it. Pattern files are normally shipped for LaTeX (and
+# partially plain). A side effect of independent files is that
+# we can make them encoding independent.
+#
+# Maybe I'll make this hyptools.tex
+
+class Language
+
+ include CommandBase
+
+ def initialize(commandline=nil, language='en', filenames=nil, encoding='ec')
+ @commandline= commandline
+ @language = language
+ @filenames = filenames
+ @remapping = Array.new
+ @unicode = Hash.new
+ @encoding = encoding
+ @data = ''
+ @read = ''
+ preload_accents()
+ preload_unicode() if @commandline.option('utf8')
+ case @encoding.downcase
+ when 't1', 'ec', 'cork' then preload_vector('ec')
+ when 'y', 'texnansi' then preload_vector('texnansi')
+ end
+ end
+
+ def report(str)
+ if @commandline then
+ @commandline.report(str)
+ else
+ puts("#{str}\n")
+ end
+ end
+
+ def remap(from, to)
+ @remapping.push([from,to])
+ end
+
+ def load(filenames=@filenames)
+ begin
+ if filenames then
+ @filenames.each do |fileset|
+ [fileset].flatten.each do |filename|
+ begin
+ if fname = located(filename) then
+ data = IO.read(fname)
+ @data += data.gsub(/\%.*$/, '')
+ data.gsub!(/(\\patterns|\\hyphenation)\s*\{.*/mo) do '' end
+ @read += "\n% preamble of file #{fname}\n\n#{data}\n"
+ report("file #{fname} is loaded")
+ break # next fileset
+ end
+ rescue
+ report("file #{filename} is not readable")
+ end
+ end
+ end
+ end
+ rescue
+ end
+ end
+
+ def valid?
+ ! @data.empty?
+ end
+
+ def convert
+ if @data then
+ n = 0
+ @remapping.each do |k|
+ @data.gsub!(k[0]) do
+ # report("#{k[0]} => #{k[1]}")
+ n += 1
+ k[1]
+ end
+ end
+ report("#{n} changes in patterns and exceptions")
+ if @commandline.option('utf8') then
+ n = 0
+ @data.gsub!(/\[(.*?)\]/o) do
+ n += 1
+ @unicode[$1] || $1
+ end
+ report("#{n} unicode utf8 entries")
+ end
+ return true
+ else
+ return false
+ end
+ end
+
+ def comment(str)
+ str.gsub!(/^\n/o, '')
+ str.chomp!
+ if @commandline.option('xml') then
+ "<!-- #{str.strip} -->\n\n"
+ else
+ "% #{str.strip}\n\n"
+ end
+ end
+
+ def content(tag, str)
+ lst = str.split(/\s+/)
+ lst.collect! do |l|
+ l.strip
+ end
+ if lst.length>0 then
+ lst = "\n#{lst.join("\n")}\n"
+ else
+ lst = ""
+ end
+ if @commandline.option('xml') then
+ lst.gsub!(/\[(.*?)\]/o) do
+ "&#{$1};"
+ end
+ "<#{tag}>#{lst}</#{tag}>\n\n"
+ else
+ "\\#{tag} \{#{lst}\}\n\n"
+ end
+ end
+
+ def banner
+ if @commandline.option('xml') then
+ "<?xml version='1.0' standalone='yes' ?>\n\n"
+ end
+ end
+
+ def triggerunicode
+ if @commandline.option('utf8') then
+ "% xetex needs utf8 encoded patterns and for patterns\n" +
+ "% coded as such we need to enable this regime when\n" +
+ "% not in xetex; this code will be moved into context\n" +
+ "% as soon as we've spread the generic patterns\n" +
+ "\n" +
+ "\\ifx\\XeTeXversion\\undefined \\else\n" +
+ " \\ifx\\enableregime\\undefined \\else\n" +
+ " \\enableregime[utf]\n" +
+ " \\fi\n" +
+ "\\fi\n" +
+ "\n"
+ end
+ end
+
+ def save
+ xml = @commandline.option("xml")
+
+ patname = "lang-#{@language}.pat"
+ hypname = "lang-#{@language}.hyp"
+ rmename = "lang-#{@language}.rme"
+ logname = "lang-#{@language}.log"
+
+ desname = "lang-all.xml"
+
+ @data.gsub!(/\\[nc]\{(.+?)\}/) do $1 end
+ @data.gsub!(/\{\}/) do '' end
+ @data.gsub!(/\n+/mo) do "\n" end
+ @read.gsub!(/\n+/mo) do "\n" end
+
+ description = ''
+ commentfile = rmename.dup
+
+ begin
+ desfile = `kpsewhich -progname=context #{desname}`.chomp
+ if f = File.new(desfile) then
+ if doc = REXML::Document.new(f) then
+ if e = REXML::XPath.first(doc.root,"/descriptions/description[@language='#{@language}']") then
+ description = e.to_s
+ end
+ end
+ end
+ rescue
+ description = ''
+ else
+ unless description.empty? then
+ commentfile = desname.dup
+ str = "<!-- copied from lang-all.xml\n\n"
+ str << "<?xml version='1.0' standalone='yes'?>\n\n"
+ str << description.chomp
+ str << "\n\nend of copy -->\n"
+ str.gsub!(/^/io, "% ") unless @commandline.option('xml')
+ description = comment("begin description data")
+ description << str + "\n"
+ description << comment("end description data")
+ report("description found for language #{@language}")
+ end
+ end
+
+ begin
+ if description.empty? || @commandline.option('log') then
+ if f = File.open(logname,'w') then
+ report("saving #{@remapping.length} remap patterns in #{logname}")
+ @remapping.each do |m|
+ f.puts("#{m[0].inspect} => #{m[1]}\n")
+ end
+ f.close
+ end
+ else
+ File.delete(logname) if FileTest.file?(logname)
+ end
+ rescue
+ end
+
+ begin
+ if description.empty? || @commandline.option('log') then
+ if f = File.open(rmename,'w') then
+ data = @read.dup
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+ f << comment("comment copied from public hyphenation files}")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << comment("begin original comment")
+ f << "#{data}\n"
+ f << comment("end original comment")
+ f.close
+ report("comment saved in file #{rmename}")
+ else
+ report("file #{rmename} is not writable")
+ end
+ else
+ File.delete(rmename) if FileTest.file?(rmename)
+ end
+ rescue
+ end
+
+ begin
+ if f = File.open(patname,'w') then
+ data = ''
+ @data.scan(/\\patterns\s*\{\s*(.*?)\s*\}/m) do
+ report("merging patterns")
+ data += $1 + "\n"
+ end
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+
+ f << banner
+ f << comment("context pattern file, see #{commentfile} for original comment")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << description
+ f << comment("begin pattern data")
+ f << triggerunicode
+ f << content('patterns', data)
+ f << comment("end pattern data")
+ f.close
+ report("patterns saved in file #{patname}")
+ else
+ report("file #{patname} is not writable")
+ end
+ rescue
+ report("problems with file #{patname}")
+ end
+
+ begin
+ if f = File.open(hypname,'w') then
+ data = ''
+ @data.scan(/\\hyphenation\s*\{\s*(.*?)\s*\}/m) do
+ report("merging exceptions")
+ data += $1 + "\n"
+ end
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+ f << banner
+ f << comment("context hyphenation file, see #{commentfile} for original comment")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << description
+ f << comment("begin hyphenation data")
+ f << triggerunicode
+ f << content('hyphenation', data)
+ f << comment("end hyphenation data")
+ f.close
+ report("exceptions saved in file #{hypname}")
+ else
+ report("file #{hypname} is not writable")
+ end
+ rescue
+ report("problems with file #{hypname}")
+ end
+ end
+
+ def process
+ load
+ if valid? then
+ convert
+ save
+ else
+ report("aborted due to missing files")
+ end
+ end
+
+ def Language::generate(commandline, language='', filenames='', encoding='ec')
+ if ! language.empty? && ! filenames.empty? then
+ commandline.report("processing language #{language}")
+ commandline.report("")
+ language = Language.new(commandline,language,filenames,encoding)
+ language.load
+ language.convert
+ language.save
+ commandline.report("")
+ end
+ end
+
+ private
+
+ def located(filename)
+ begin
+ fname = `kpsewhich -progname=context #{filename}`.chomp
+ if FileTest.file?(fname) then
+ report("using file #{fname}")
+ return fname
+ else
+ report("file #{filename} is not present")
+ return nil
+ end
+ rescue
+ report("file #{filename} cannot be located using kpsewhich")
+ return nil
+ end
+ end
+
+ def preload_accents
+
+ begin
+ if filename = located("enco-acc.tex") then
+ if data = IO.read(filename) then
+ report("preloading accent conversions")
+ data.scan(/\\defineaccent\s*\\*(.+?)\s*\{*(.+?)\}*\s*\{\\(.+?)\}/o) do
+ one, two, three = $1, $2, $3
+ one.gsub!(/[\`\~\!\^\*\_\-\+\=\:\;\"\'\,\.\?]/o) do
+ "\\#{one}"
+ end
+ remap(/\\#{one} #{two}/, "[#{three}]")
+ remap(/\\#{one}#{two}/, "[#{three}]") unless one =~ /[a-zA-Z]/o
+ remap(/\\#{one}\{#{two}\}/, "[#{three}]")
+ end
+ end
+ end
+ rescue
+ end
+
+ end
+
+ def preload_unicode
+
+ # \definecharacter Agrave {\uchar0{192}}
+
+ begin
+ if filename = located("enco-uc.tex") then
+ if data = IO.read(filename) then
+ report("preloading unicode conversions")
+ data.scan(/\\definecharacter\s*(.+?)\s*\{\\uchar\{*(\d+)\}*\s*\{(\d+)\}/o) do
+ one, two, three = $1, $2.to_i, $3.to_i
+ @unicode[one] = [(two*256 + three)].pack("U")
+ end
+ end
+ end
+ rescue
+ report("error in loading unicode mapping (#{$!})")
+ end
+
+ end
+
+ def preload_vector(encoding='')
+
+ # funny polish
+
+ case @language
+ when 'pl' then
+ remap(/\/a/, "[aogonek]") ; remap(/\/A/, "[Aogonek]")
+ remap(/\/c/, "[cacute]") ; remap(/\/C/, "[Cacute]")
+ remap(/\/e/, "[eogonek]") ; remap(/\/E/, "[Eogonek]")
+ remap(/\/l/, "[lstroke]") ; remap(/\/L/, "[Lstroke]")
+ remap(/\/n/, "[nacute]") ; remap(/\/N/, "[Nacute]")
+ remap(/\/o/, "[oacute]") ; remap(/\/O/, "[Oacute]")
+ remap(/\/s/, "[sacute]") ; remap(/\/S/, "[Sacute]")
+ remap(/\/x/, "[zacute]") ; remap(/\/X/, "[Zacute]")
+ remap(/\/z/, "[zdotaccent]") ; remap(/\/Z/, "[Zdotaccent]")
+ when 'sl' then
+ remap(/\"c/,"[ccaron]") ; remap(/\"C/,"[Ccaron]")
+ remap(/\"s/,"[scaron]") ; remap(/\"S/,"[Scaron]")
+ remap(/\"z/,"[zcaron]") ; remap(/\"Z/,"[Zcaron]")
+ when 'da' then
+ remap(/X/, "[aeligature]")
+ remap(/Y/, "[ostroke]")
+ remap(/Z/, "[aring]")
+ when 'ca' then
+ remap(/\\c\{.*?\}/, "")
+ when 'de', 'deo' then
+ remap(/\\c\{.*?\}/, "")
+ remap(/\\n\{\}/, "")
+ remap(/\\3/, "[ssharp]")
+ remap(/\\9/, "[ssharp]")
+ remap(/\"a/, "[adiaeresis]")
+ remap(/\"o/, "[odiaeresis]")
+ remap(/\"u/, "[udiaeresis]")
+ when 'fr' then
+ remap(/\\ae/, "[adiaeresis]")
+ remap(/\\oe/, "[odiaeresis]")
+ when 'la' then
+ # \lccode`'=`' somewhere else, todo
+ remap(/\\c\{.*?\}/, "")
+ remap(/\\a\s*/, "[aeligature]")
+ remap(/\\o\s*/, "[oeligature]")
+ else
+ end
+
+ if ! encoding.empty? then
+ begin
+ filename = `kpsewhich -progname=context enco-#{encoding}.tex`
+ if data = IO.read(filename.chomp) then
+ report("preloading #{encoding} character mappings")
+ data.scan(/\\definecharacter\s*([a-zA-Z]+)\s*(\d+)\s*/o) do
+ name, number = $1, $2
+ remap(/\^\^#{sprintf("%02x",number)}/, "[#{name}]")
+ end
+ end
+ rescue
+ end
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ @@languagedata = Hash.new
+
+ def patternfiles
+ language = @commandline.argument('first')
+ if (language == 'all') || language.empty? then
+ languages = @@languagedata.keys.sort
+ elsif @@languagedata.key?(language) then
+ languages = [language]
+ else
+ languages = []
+ end
+ languages.each do |language|
+ encoding = @@languagedata[language][0] || ''
+ files = @@languagedata[language][1] || []
+ Language::generate(self,language,files,encoding)
+ end
+ end
+
+ private
+
+ # todo: filter the fallback list from context
+
+ # The first entry in the array is the encoding which will be used
+ # when interpreting th eraw patterns. The second entry is a list of
+ # filesets (string|aray), each first match of a set is taken.
+
+ @@languagedata['ba' ] = [ 'ec' , ['bahyph.tex'] ]
+ @@languagedata['ca' ] = [ 'ec' , ['cahyph.tex'] ]
+ @@languagedata['cy' ] = [ 'ec' , ['cyhyph.tex'] ]
+ @@languagedata['cz' ] = [ 'ec' , ['czhyphen.tex','czhyphen.ex'] ]
+ @@languagedata['de' ] = [ 'ec' , ['dehyphn.tex'] ]
+ @@languagedata['deo'] = [ 'ec' , ['dehypht.tex'] ]
+ @@languagedata['da' ] = [ 'ec' , ['dkspecial.tex','dkcommon.tex'] ]
+ # elhyph.tex
+ @@languagedata['es' ] = [ 'ec' , ['eshyph.tex'] ]
+ @@languagedata['fi' ] = [ 'ec' , ['ethyph.tex'] ]
+ @@languagedata['fi' ] = [ 'ec' , ['fihyph.tex'] ]
+ @@languagedata['fr' ] = [ 'ec' , ['frhyph.tex'] ]
+ # ghyphen.readme ghyph31.readme grphyph
+ @@languagedata['hr' ] = [ 'ec' , ['hrhyph.tex'] ]
+ @@languagedata['hu' ] = [ 'ec' , ['huhyphn.tex'] ]
+ @@languagedata['en' ] = [ 'default' , [['ushyphmax.tex','ushyph.tex','hyphen.tex']] ]
+ # inhyph.tex
+ @@languagedata['is' ] = [ 'ec' , ['ishyph.tex'] ]
+ @@languagedata['it' ] = [ 'ec' , ['ithyph.tex'] ]
+ @@languagedata['la' ] = [ 'ec' , ['lahyph.tex'] ]
+ # mnhyph
+ @@languagedata['nl' ] = [ 'ec' , ['nehyph96.tex'] ]
+ @@languagedata['no' ] = [ 'ec' , ['nohyph.tex'] ]
+ # oldgrhyph.tex
+ @@languagedata['pl' ] = [ 'ec' , ['plhyph.tex'] ]
+ @@languagedata['pt' ] = [ 'ec' , ['pthyph.tex'] ]
+ @@languagedata['ro' ] = [ 'ec' , ['rohyph.tex'] ]
+ @@languagedata['sl' ] = [ 'ec' , ['sihyph.tex'] ]
+ @@languagedata['sk' ] = [ 'ec' , ['skhyphen.tex','skhyphen.ex'] ]
+ # sorhyph.tex / upper sorbian
+ # srhyphc.tex / cyrillic
+ @@languagedata['sv' ] = [ 'ec' , ['svhyph.tex'] ]
+ @@languagedata['tr' ] = [ 'ec' , ['tkhyph.tex'] ]
+ @@languagedata['uk' ] = [ 'default' , [['ukhyphen.tex','ukhyph.tex']] ]
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def dpxmapfiles
+
+ force = @commandline.option("force")
+
+ texmfroot = @commandline.argument('first')
+ texmfroot = '.' if texmfroot.empty?
+ maproot = "#{texmfroot}/fonts/map/pdftex/context"
+
+ if File.directory?(maproot) then
+ if files = Dir.glob("#{maproot}/*.map") and files.size > 0 then
+ files.each do |pdffile|
+ next if File.basename(pdffile) == 'pdftex.map'
+ pdffile = File.expand_path(pdffile)
+ dpxfile = File.expand_path(pdffile.sub(/pdftex/i,'dvipdfm'))
+ unless pdffile == dpxfile then
+ begin
+ if data = File.read(pdffile) then
+ report("< #{File.basename(pdffile)} - pdf(e)tex")
+ n = 0
+ data = data.collect do |line|
+ if line =~ /^[\%\#]+/mo then
+ ''
+ else
+ encoding = if line =~ /([a-z0-9\-]+)\.enc/io then $1 else '' end
+ fontfile = if line =~ /([a-z0-9\-]+)\.(pfb|ttf)/io then $1 else nil end
+ metrics = if line =~ /^([a-z0-9\-]+)[\s\<]+/io then $1 else nil end
+ slant = if line =~ /\"([\d\.]+)\s+SlantFont\"/io then "-s #{$1}" else '' end
+ if metrics && encoding && fontfile then
+ n += 1
+ "#{metrics} #{encoding} #{fontfile} #{slant}"
+ else
+ ''
+ end
+ end
+ end
+ data.delete_if do |line|
+ line.gsub(/\s+/,'').empty?
+ end
+ begin
+ if force then
+ if n > 0 then
+ File.makedirs(File.dirname(dpxfile))
+ if f = File.open(dpxfile,'w') then
+ report("> #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
+ f.puts(data)
+ f.close
+ else
+ report("? #{File.basename(dpxfile)} - dvipdfm(x)")
+ end
+ else
+ report("- #{File.basename(dpxfile)} - dvipdfm(x)")
+ begin File.delete(dpxname) ; rescue ; end
+ end
+ else
+ report(". #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
+ end
+ rescue
+ report("error in saving dvipdfm file")
+ end
+ else
+ report("error in loading pdftex file")
+ end
+ rescue
+ report("error in processing pdftex file")
+ end
+ end
+ end
+ if force then
+ begin
+ report("regenerating database for #{texmfroot}")
+ system("mktexlsr #{texmfroot}")
+ rescue
+ end
+ end
+ else
+ report("no mapfiles found in #{maproot}")
+ end
+ else
+ report("provide proper texmfroot")
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ # usage : ctxtools --listentities entities.xml
+ # document: <!DOCTYPE something SYSTEM "entities.xml">
+
+ def flushentities(handle,entities,doctype=nil) # 'stylesheet'
+ tab = if doctype then "\t" else "" end
+ handle.puts("<!DOCTYPE #{doctype} [") if doctype
+ entities.keys.sort.each do |k|
+ handle.puts("#{tab}<!ENTITY #{k} \"\&\##{entities[k]};\">")
+ end
+ handle.puts("]>") if doctype
+ end
+
+ def listentities
+
+ # filename = `texmfstart tmftools.rb --progname=context enco-uc.tex`.chomp
+ filename = `kpsewhich --progname=context enco-uc.tex`.chomp
+ outputname = @commandline.argument('first')
+
+ if filename and not filename.empty? and FileTest.file?(filename) then
+ entities = Hash.new
+ IO.readlines(filename).each do |line|
+ if line =~ /\\definecharacter\s+([a-zA-Z]+)\s+\{\\uchar\{*(\d+)\}*\{(\d+)\}\}/o then
+ name, low, high = $1, $2.to_i, $3.to_i
+ entities[name] = low*256 + high
+ end
+ end
+ if outputname and not outputname.empty? then
+ if f = File.open(outputname,'w') then
+ flushentities(f,entities)
+ f.close
+ else
+ flushentities($stdout,entities)
+ end
+ else
+ flushentities($stdout,entities)
+ end
+ end
+
+ end
+
+end
+
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('touchcontextfile', 'update context version')
+commandline.registeraction('contextversion', 'report context version')
+
+commandline.registeraction('jeditinterface', 'generate jedit syntax files [--pipe]')
+commandline.registeraction('bbeditinterface', 'generate bbedit syntax files [--pipe]')
+commandline.registeraction('sciteinterface', 'generate scite syntax files [--pipe]')
+commandline.registeraction('rawinterface', 'generate raw syntax files [--pipe]')
+
+commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]')
+commandline.registeraction('purgefiles', 'remove temporary files [--all --recurse] [basename]')
+
+commandline.registeraction('documentation', 'generate documentation [--type=] [filename]')
+
+commandline.registeraction('filterpages') # no help, hidden temporary feature
+commandline.registeraction('purgeallfiles') # no help, compatibility feature
+
+commandline.registeraction('patternfiles', 'generate pattern files [--all --xml --utf8] [languagecode]')
+
+commandline.registeraction('dpxmapfiles', 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]')
+commandline.registeraction('listentities', 'create doctype entity definition from enco-uc.tex')
+
+commandline.registervalue('type','')
+
+commandline.registerflag('recurse')
+commandline.registerflag('force')
+commandline.registerflag('pipe')
+commandline.registerflag('all')
+commandline.registerflag('xml')
+commandline.registerflag('log')
+commandline.registerflag('utf8')
+
+# general
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/Master/texmf-dist/scripts/context/ruby/fcd_start.rb b/Master/texmf-dist/scripts/context/ruby/fcd_start.rb
new file mode 100644
index 00000000000..8ac48f79e8c
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/fcd_start.rb
@@ -0,0 +1,453 @@
+# Hans Hagen / PRAGMA ADE / 2005 / www.pragma-ade.com
+#
+# Fast Change Dir
+#
+# This is a kind of variant of the good old ncd
+# program. This script uses the same indirect cmd
+# trick as Erwin Waterlander's wcd program.
+#
+# === windows: fcd.cmd ===
+#
+# @echo off
+# ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9
+# if exist "%HOME%/fcd_stage.cmd" call %HOME%/fcd_stage.cmd
+#
+# === linux: fcd (fcd.sh) ===
+#
+# !/usr/bin/env sh
+# ruby -S fcd_start.rb $1 $2 $3 $4 $5 $6 $7 $8 $9
+# if test -f "$HOME/fcd_stage.sh" ; then
+# . $HOME/fcd_stage.sh ;
+# fi;
+#
+# ===
+#
+# On linux, one should source the file: ". fcd args" in order
+# to make the chdir persistent.
+#
+# You can create a stub with:
+#
+# ruby fcd_start.rb --stub --verbose
+#
+# usage:
+#
+# fcd --make t:\
+# fcd --add f:\project
+# fcd [--find] whatever
+# fcd [--find] whatever c (c being a list entry)
+# fcd [--find] whatever . (last choice with this pattern)
+# fcd --list
+
+require 'rbconfig'
+
+class FastCD
+
+ @@rootpath = nil
+
+ ['HOME','TEMP','TMP','TMPDIR'].each do |key|
+ if ENV[key] then
+ if FileTest.directory?(ENV[key]) then
+ @@rootpath = ENV[key]
+ break
+ end
+ end
+ end
+
+ exit unless @@rootpath
+
+ @@mswindows = Config::CONFIG['host_os'] =~ /mswin/
+ @@maxlength = 26
+
+ require 'Win32API' if @@mswindows
+
+ if @@mswindows then
+ @@stubcode = [
+ '@echo off',
+ '',
+ 'if not exist "%HOME%" goto temp',
+ '',
+ ':home',
+ '',
+ 'ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9',
+ '',
+ 'if exist "%HOME%\fcd_stage.cmd" call %HOME%\fcd_stage.cmd',
+ 'goto end',
+ '',
+ ':temp',
+ '',
+ 'ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9',
+ '',
+ 'if exist "%TEMP%\fcd_stage.cmd" call %TEMP%\fcd_stage.cmd',
+ 'goto end',
+ '',
+ ':end'
+ ].join("\n")
+ else
+ @@stubcode = [
+ '#!/usr/bin/env sh',
+ '',
+ 'ruby -S fcd_start.rb $1 $2 $3 $4 $5 $6 $7 $8 $9',
+ '',
+ 'if test -f "$HOME/fcd_stage.sh" ; then',
+ ' . $HOME/fcd_stage.sh ;',
+ 'fi;'
+ ].join("\n")
+ end
+
+ @@selfpath = File.dirname($0)
+ @@datafile = File.join(@@rootpath,'fcd_state.dat')
+ @@histfile = File.join(@@rootpath,'fcd_state.his')
+ @@cdirfile = File.join(@@rootpath,if @@mswindows then 'fcd_stage.cmd' else 'fcd_stage.sh' end)
+ @@stubfile = File.join(@@selfpath,if @@mswindows then 'fcd.cmd' else 'fcd' end)
+
+ def initialize(verbose=false)
+ @list = Array.new
+ @hist = Hash.new
+ @result = Array.new
+ @pattern = ''
+ @result = ''
+ @verbose = verbose
+ if f = File.open(@@cdirfile,'w') then
+ f << "#{if @@mswindows then 'rem' else '#' end} no dir to change to"
+ f.close
+ else
+ report("unable to create stub #{@@cdirfile}")
+ end
+ end
+
+ def filename(name)
+ File.join(@@root,name)
+ end
+
+ def report(str,verbose=@verbose)
+ puts(">> #{str}") if verbose
+ end
+
+ def flush(str,verbose=@verbose)
+ print(str) if verbose
+ end
+
+ def clear
+ if FileTest.file?(@@histfile)
+ begin
+ File.delete(@@histfile)
+ rescue
+ report("error in deleting history file '#{@histfile}'")
+ else
+ report("history file '#{@histfile}' is deleted")
+ end
+ else
+ report("no history file '#{@histfile}'")
+ end
+ end
+
+ def scan(dir='.')
+ begin
+ [dir].flatten.sort.uniq.each do |dir|
+ begin
+ Dir.chdir(dir)
+ report("scanning '#{dir}'")
+ # flush(">> ")
+ Dir.glob("**/*").each do |d|
+ if FileTest.directory?(d) then
+ @list << File.expand_path(d)
+ # flush(".")
+ end
+ end
+ # flush("\n")
+ @list = @list.sort.uniq
+ report("#{@list.size} entries found")
+ rescue
+ report("unknown directory '#{dir}'")
+ end
+ end
+ rescue
+ report("invalid dir specification ")
+ end
+ end
+
+ def save
+ begin
+ if f = File.open(@@datafile,'w') then
+ @list.each do |l|
+ f.puts(l)
+ end
+ f.close
+ report("#{@list.size} status bytes saved in #{@@datafile}")
+ else
+ report("unable to save status in #{@@datafile}")
+ end
+ rescue
+ report("error in saving status in #{@@datafile}")
+ end
+ end
+
+ def remember
+ if @hist[@pattern] == @result then
+ # no need to save result
+ else
+ begin
+ if f = File.open(@@histfile,'w') then
+ @hist[@pattern] = @result
+ @hist.keys.each do |k|
+ f.puts("#{k} #{@hist[k]}")
+ end
+ f.close
+ report("#{@hist.size} history entries saved in #{@@histfile}")
+ else
+ report("unable to save history in #{@@histfile}")
+ end
+ rescue
+ report("error in saving history in #{@@histfile}")
+ end
+ end
+ end
+
+ def load
+ begin
+ @list = IO.read(@@datafile).split("\n")
+ report("#{@list.length} status bytes loaded from #{@@datafile}")
+ rescue
+ report("error in loading status from #{@@datafile}")
+ end
+ begin
+ IO.readlines(@@histfile).each do |line|
+ if line =~ /^(.*?)\s+(.*)$/i then
+ @hist[$1] = $2
+ end
+ end
+ report("#{@hist.length} history entries loaded from #{@@histfile}")
+ rescue
+ report("error in loading history from #{@@histfile}")
+ end
+ end
+
+ def show
+ begin
+ puts("directories:")
+ puts("\n")
+ if @list.length > 0 then
+ @list.each do |l|
+ puts(l)
+ end
+ else
+ puts("no entries")
+ end
+ puts("\n")
+ puts("history:")
+ puts("\n")
+ if @hist.length > 0 then
+ @hist.keys.sort.each do |h|
+ puts("#{h} >> #{@hist[h]}")
+ end
+ else
+ puts("no entries")
+ end
+ rescue
+ end
+ end
+
+ def find(pattern=nil)
+ begin
+ if pattern = [pattern].flatten.first then
+ if pattern.length > 0 and @pattern = pattern then
+ @result = @list.grep(/\/#{@pattern}$/i)
+ if @result.length == 0 then
+ @result = @list.grep(/\/#{@pattern}[^\/]*$/i)
+ end
+ end
+ end
+ rescue
+ end
+ end
+
+ def chdir(dir)
+ begin
+ if dir then
+ if f = File.open(@@cdirfile,'w') then
+ if @@mswindows then
+ f.puts("cd /d #{dir.gsub('/','\\')}")
+ else
+ f.puts("cd #{dir.gsub("\\",'/')}")
+ end
+ end
+ @result = dir
+ report("changing to #{dir}",true)
+ else
+ report("not changing dir")
+ end
+ rescue
+ end
+ end
+
+ def choose(args=[])
+ unless @pattern.empty? then
+ begin
+ case @result.size
+ when 0 then
+ report("dir '#{@pattern}' not found",true)
+ when 1 then
+ chdir(@result[0])
+ else
+ list = @result.dup
+ begin
+ if answer = args[1] then # assignment & test
+ if answer == '.' and @hist.key?(@pattern) then
+ if FileTest.directory?(@hist[@pattern]) then
+ print("last choice ")
+ chdir(@hist[@pattern])
+ return
+ end
+ else
+ index = answer[0] - ?a
+ if dir = list[index] then
+ chdir(dir)
+ return
+ end
+ end
+ end
+ rescue
+ end
+ loop do
+ print("\n")
+ list.each_index do |i|
+ if i < @@maxlength then
+ puts("#{(i+?a).chr} #{list[i]}")
+ else
+ puts("\n there are #{list.length-@@maxlength} entries more")
+ break
+ end
+ end
+ print("\n>> ")
+ if answer = wait then
+ if answer >= ?a and answer <= ?z then
+ index = answer - ?a
+ if dir = list[index] then
+ print("#{answer.chr} ")
+ chdir(dir)
+ elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then
+ print("last choice ")
+ chdir(@hist[@pattern])
+ else
+ print("quit\n")
+ end
+ break
+ elsif list.length >= @@maxlength then
+ @@maxlength.times do |i| list.shift end
+ print("next set")
+ print("\n")
+ elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then
+ print("last choice ")
+ chdir(@hist[@pattern])
+ break
+ else
+ print("quit\n")
+ break
+ end
+ end
+ end
+ end
+ rescue
+ # report($!)
+ end
+ end
+ end
+
+ def wait
+ begin
+ $stdout.flush
+ return getc
+ rescue
+ return nil
+ end
+ end
+
+ def getc
+ begin
+ if @@mswindows then
+ ch = Win32API.new('crtdll','_getch',[],'L').call
+ else
+ system('stty raw -echo')
+ ch = $stdin.getc
+ system('stty -raw echo')
+ end
+ rescue
+ ch = nil
+ end
+ return ch
+ end
+
+ def check
+ unless FileTest.file?(@@stubfile) then
+ report("creating stub #{@@stubfile}")
+ begin
+ if f = File.open(@@stubfile,'w') then
+ f.puts(@@stubcode)
+ f.close
+ end
+ rescue
+ report("unable to create stub #{@@stubfile}")
+ else
+ unless @mswindows then
+ begin
+ File.chmod(0755,@@stubfile)
+ rescue
+ report("unable to change protections on #{@@stubfile}")
+ end
+ end
+ end
+ else
+ report("stub #{@@stubfile} already present")
+ end
+ end
+
+end
+
+verbose, action, args = false, :find, Array.new
+
+usage = "fcd [--make|add|show|find] [--verbose] [pattern]"
+
+ARGV.each do |a|
+ case a
+ when '-v', '--verbose' then verbose = true
+ when '-m', '--make' then action = :make
+ when '-c', '--clear' then action = :clear
+ when '-a', '--add' then action = :add
+ when '-s', '--show' then action = :show
+ when '-l', '--list' then action = :show
+ when '-f', '--find' then action = :find
+ when '--stub' then action = :stub
+ when '-h', '--help' then puts "usage: #{usage}" ; exit
+ when /^\-\-.*/ then puts "unknown switch: #{a}" + "\n" + "usage: #{usage}" ; exit
+ else args << a
+ end
+end
+
+$stdout.sync = true
+
+fcd = FastCD.new(verbose)
+
+fcd.report("Fast Change Dir / version 1.0")
+
+case action
+ when :make then
+ fcd.clear
+ fcd.scan(args)
+ fcd.save
+ when :clear then
+ fcd.clear
+ when :add then
+ fcd.load
+ fcd.scan(args)
+ fcd.save
+ when :show then
+ fcd.load
+ fcd.show
+ when :find then
+ fcd.load
+ fcd.find(args)
+ fcd.choose(args)
+ fcd.remember
+ when :stub
+ fcd.check
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb
new file mode 100644
index 00000000000..807cad10ccd
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb
@@ -0,0 +1,641 @@
+# module : graphics/gs
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/variables','../variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+# ['base/system', '../system', 'system' ].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/variables'
+require 'base/system'
+
+class GhostScript
+
+ include Variables
+
+ @@pstopdfoptions = [
+ 'AntiAliasColorImages',
+ 'AntiAliasGrayImages',
+ 'AntiAliasMonoImages',
+ 'ASCII85EncodePages',
+ 'AutoFilterColorImages',
+ 'AutoFilterGrayImages',
+ 'AutoPositionEPSFiles',
+ 'AutoRotatePages',
+ 'Binding',
+ 'ColorConversionStrategy',
+ 'ColorImageDepth',
+ 'ColorImageDownsampleThreshold',
+ 'ColorImageDownsampleType',
+ 'ColorImageFilter',
+ 'ColorImageResolution',
+ 'CompatibilityLevel',
+ 'CompressPages',
+ #'ConvertCMYKImagesToRGB', # buggy
+ #'ConvertImagesToIndexed', # buggy
+ 'CreateJobTicket',
+ 'DetectBlends',
+ 'DoThumbnails',
+ 'DownsampleColorImages',
+ 'DownsampleGrayImages',
+ 'DownsampleMonoImages',
+ 'EmbedAllFonts',
+ 'EncodeColorImages',
+ 'EncodeGrayImages',
+ 'EncodeMonoImages',
+ 'EndPage',
+ 'FirstPage',
+ 'GrayImageDepth',
+ 'GrayImageDownsampleThreshold',
+ 'GrayImageDownsampleType',
+ 'GrayImageFilter',
+ 'GrayImageResolution',
+ 'MaxSubsetPct',
+ 'MonoImageDepth',
+ 'MonoImageDownsampleThreshold',
+ 'MonoImageDownsampleType',
+ 'MonoImageFilter',
+ 'MonoImageResolution',
+ 'Optimize',
+ 'ParseDCSComments',
+ 'ParseDCSCommentsForDocInfo',
+ 'PreserveCopyPage',
+ 'PreserveEPSInfo',
+ 'PreserveHalftoneInfo',
+ 'PreserveOPIComments',
+ 'PreserveOverprintSettings',
+ 'SubsetFonts',
+ 'UseFlateCompression'
+ ]
+
+ @@methods = Hash.new
+
+ @@methods['raw'] = '1'
+ @@methods['bound'] = '2'
+ @@methods['bounded'] = '2'
+ @@methods['crop'] = '3'
+ @@methods['cropped'] = '3'
+ @@methods['down'] = '4'
+ @@methods['downsample'] = '4'
+ @@methods['downsampled'] = '4'
+ @@methods['simplify'] = '5'
+ @@methods['simplified'] = '5'
+
+ @@tempfile = 'gstemp'
+ @@pstempfile = @@tempfile + '.ps'
+ @@pdftempfile = @@tempfile + '.pdf'
+
+ @@bboxspec = '\s*([\-\d\.]+)' + '\s+([\-\d\.]+)'*3
+
+ def initialize(logger=nil)
+
+ unless logger then
+ puts('gs class needs a logger')
+ exit
+ end
+
+ @variables = Hash.new
+ @psoptions = Hash.new
+ @logger = logger
+
+ setvariable('profile', 'gsprofile.ini')
+ setvariable('pipe', true)
+ setvariable('method', 2)
+ setvariable('force', false)
+ setvariable('colormodel', 'cmyk')
+ setvariable('inputfile', '')
+ setvariable('outputfile', '')
+
+ @@pstopdfoptions.each do |key|
+ @psoptions[key] = ''
+ end
+
+ reset
+
+ end
+
+ def reset
+ @llx = @lly = @ulx = @uly = 0
+ @oldbbox = [@llx,@lly,@urx,@ury]
+ @width = @height = @xoffset = @yoffset = @offset = 0
+ @rs = Tool.default_line_separator
+ end
+
+ def supported?(filename)
+ psfile?(filename) || pdffile?(filename)
+ end
+
+ def psfile?(filename)
+ filename =~ /\.(eps|epsf|ps|ai\d*)$/io
+ end
+
+ def pdffile?(filename)
+ filename =~ /\.(pdf)$/io
+ end
+
+ def setpsoption(key,value)
+ @psoptions[key] = value unless value.empty?
+ end
+
+ def setdimensions (llx,lly,urx,ury)
+ @oldbbox = [llx,lly,urx,ury]
+ @llx, @lly = llx.to_f-@offset, lly.to_f-@offset
+ @urx, @ury = urx.to_f+@offset, ury.to_f+@offset
+ @width, @height = @urx - @llx, @ury - @lly
+ @xoffset, @yoffset = 0 - @llx, 0 - @lly
+ end
+
+ def setoffset (offset=0)
+ @offset = offset.to_f
+ setdimensions(@llx,@lly,@urx,@ury) if dimensions?
+ end
+
+ def resetdimensions
+ setdimensions(0,0,0,0)
+ end
+
+ def dimensions?
+ (@width>0) && (@height>0)
+ end
+
+ def convert
+
+ inpfile = getvariable('inputfile')
+
+ if inpfile.empty? then
+ report('no inputfile specified')
+ return false
+ end
+
+ unless FileTest.file?(inpfile) then
+ report("unknown input file #{inpfile}")
+ return false
+ end
+
+ outfile = getvariable('outputfile')
+
+ if outfile.empty? then
+ outfile = inpfile
+ outfile = outfile.sub(/^.*[\\\/]/,'')
+ end
+
+ outfile = outfile.sub(/\.(pdf|eps|ps|ai)/i, "")
+ resultfile = outfile + '.pdf'
+ setvariable('outputfile', resultfile)
+
+ # flags
+
+ saveprofile(getvariable('profile'))
+
+ begin
+ gsmethod = method(getvariable('method')).to_i
+ report("conversion method #{gsmethod}")
+ rescue
+ gsmethod = 1
+ report("fallback conversion method #{gsmethod}")
+ end
+
+ debug('piping data') if getvariable('pipe')
+
+ ok = false
+ begin
+ case gsmethod
+ when 0, 1 then ok = convertasis(inpfile,resultfile)
+ when 2 then ok = convertbounded(inpfile,resultfile)
+ when 3 then ok = convertcropped(inpfile,resultfile)
+ when 4 then ok = downsample(inpfile,resultfile,'screen')
+ when 5 then ok = downsample(inpfile,resultfile,'prepress')
+ else report("invalid conversion method #{gsmethod}")
+ end
+ rescue
+ report("job aborted due to some error #{$!}")
+ begin
+ File.delete(resultfile) if test(?e,resultfile)
+ rescue
+ report("unable to delete faulty #{resultfile}")
+ end
+ ok = false
+ ensure
+ deleteprofile(getvariable('profile'))
+ File.delete(@@pstempfile) if test(?e,@@pstempfile)
+ File.delete(@@pdftempfile) if test(?e,@@pdftempfile)
+ end
+ return ok
+ end
+
+ # private
+
+ def method (str)
+ if @@methods.key?(str) then
+ @@methods[str]
+ else
+ str
+ end
+ end
+
+ def pdfmethod? (str)
+ case method(str).to_i
+ when 4, 5 then return true
+ end
+ return false
+ end
+
+ def pdfprefix (str)
+ case method(str).to_i
+ when 4 then return 'lowres-'
+ when 5 then return 'normal-'
+ end
+ return ''
+ end
+
+ def psmethod? (str)
+ ! pdfmethod?(str)
+ end
+
+ def insertprofile (flags)
+ for key in flags.keys do
+ replacevariable("flag.#{key}", flags[key])
+ end
+ end
+
+ def deleteprofile (filename)
+ begin
+ File.delete(filename) if FileTest.file?(filename)
+ rescue
+ end
+ end
+
+ def saveprofile (filename)
+ return if filename.empty? || ! (ini = open(filename,"w"))
+ @@pstopdfoptions.each do |k|
+ str = @psoptions[k]
+ # beware, booleans are translated, but so are yes/no which is dangerous
+ if str.class == String then
+ if ! str.empty? && (str != 'empty') then
+ str.sub!(/(.+)\-/io, '')
+ str = "/" + str unless str =~ /^(true|false|none|[\d\.\-\+]+)$/
+ ini.puts("-d#{k}=#{str}\n")
+ end
+ end
+ end
+ ini.close
+ debug("gs profile #{filename} saved")
+ end
+
+ def gsstream # private
+ if getvariable('pipe') then '-' else @@pstempfile end
+ end
+
+ def gscolorswitch
+ case getvariable('colormodel')
+ when 'cmyk' then '-dProcessColorModel=/DeviceCMYK '
+ when 'rgb' then '-dProcessColorModel=/DeviceRGB '
+ when 'gray' then '-dProcessColorModel=/DeviceGRAY '
+ else
+ ''
+ end
+ end
+
+ def gsdefaults
+ defaults = ''
+ begin
+ defaults << '-dAutoRotatePages=/None ' if @psoptions['AutoRotatePages'].empty?
+ rescue
+ defaults << '-dAutoRotatePages=/None '
+ end
+ return defaults
+ end
+
+ def convertasis (inpfile, outfile)
+
+ report("converting #{inpfile} as-is")
+
+ @rs = Tool.line_separator(inpfile)
+ debug("platform mac") if @rs == "\r"
+
+ arguments = ''
+ arguments << "\@gsprofile.ini "
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH "
+ arguments << "#{gsdefaults} "
+ arguments << "#{gscolorswitch} "
+ arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit "
+
+ debug("ghostscript: #{arguments}")
+ unless ok = System.run('ghostscript',arguments) then
+ begin
+ report("removing file #{outfile}")
+ File.delete(outfile) if FileTest.file?(outfile)
+ rescue
+ debug("file #{outfile} may be invalid")
+ end
+ end
+ return ok
+
+ end
+
+ def convertbounded (inpfile, outfile)
+
+ report("converting #{inpfile} bounded")
+
+ begin
+ return false if FileTest.file?(outfile) && (! File.delete(outfile))
+ rescue
+ return false
+ end
+
+ arguments = ''
+ arguments << "\@gsprofile.ini "
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER"
+ arguments << "#{gscolorswitch} "
+ arguments << "#{gsdefaults} "
+ arguments << "-sOutputFile=#{outfile} #{gsstream} -c quit "
+
+ debug("ghostscript: #{arguments}")
+ debug('opening input file')
+
+ @rs = Tool.line_separator(inpfile)
+ debug("platform mac") if @rs == "\r"
+
+ return false unless tmp = open(inpfile, 'rb')
+
+ debug('opening pipe/file')
+
+ if getvariable('pipe') then
+
+ return false unless eps = IO.popen(System.command('ghostscript',arguments),'wb')
+ debug('piping data')
+ unless pipebounded(tmp,eps) then
+ debug('something went wrong in the pipe')
+ File.delete(outfile) if test(?e,outfile)
+ end
+ debug('closing pipe')
+ eps.close_write
+
+ else
+
+ return false unless eps = File.open(@@pstempfile, 'wb')
+
+ debug('copying data')
+
+ if pipebounded(tmp,eps) then
+ eps.close
+ debug('processing temp file')
+ begin
+ ok = System.run('ghostscript',arguments)
+ rescue
+ ok = false
+ # debug("fatal error: #{$!}")
+ ensure
+ end
+ else
+ eps.close
+ ok = false
+ end
+
+ unless ok then
+ begin
+ report('no output file due to error')
+ File.delete(outfile) if test(?e,outfile)
+ rescue
+ # debug("fatal error: #{$!}")
+ debug('file',outfile,'may be invalid')
+ end
+ end
+
+ debug('deleting temp file')
+ begin
+ File.delete(@@pstempfile) if test(?e,@@pstempfile)
+ rescue
+ end
+
+ end
+
+ tmp.close
+ return FileTest.file?(outfile)
+
+ end
+
+ # hm, strange, no execute here, todo ! ! !
+
+ def getdimensions (inpfile)
+
+ # -dEPSFitPage and -dEPSCrop behave weird (don't work)
+
+ arguments = "-sDEVICE=bbox -dSAFER -dNOPAUSE -dBATCH #{inpfile}"
+
+ debug("ghostscript: #{arguments}")
+
+ begin
+ bbox = System.run('ghostscript',arguments,true,true)
+ rescue
+ bbox = ''
+ end
+
+ resetdimensions
+
+ debug('bbox spec', bbox)
+
+ if bbox =~ /(Exact|HiRes)BoundingBox:#{@@bboxspec}/mois then
+ debug("high res bbox #{$2} #{$3} #{$4} #{$5}")
+ setdimensions($2,$3,$4,$5)
+ elsif bbox =~ /BoundingBox:#{@@bboxspec}/mois
+ debug("low res bbox #{$1} #{$2} #{$3} #{$4}")
+ setdimensions($1,$2,$3,$4)
+ end
+
+ return dimensions?
+
+ end
+
+ def convertcropped (inpfile, outfile)
+
+ report("converting #{inpfile} cropped")
+
+ convertbounded(inpfile, @@pdftempfile)
+
+ return unless test(?e,@@pdftempfile)
+
+ arguments = " --offset=#{@offset} #{@@pdftempfile} #{outfile}"
+
+ unless ok = System.run('cropcrap',arguments) then
+ report('cropping failed')
+ begin
+ File.delete(outfile)
+ rescue
+ end
+ begin
+ File.move(@@pdftempfile,outfile)
+ rescue
+ File.copy(@@pdftempfile,outfile)
+ File.delete(@@pdftempfile)
+ end
+ end
+
+ return ok
+
+ end
+
+ def pipebounded (eps, out)
+
+ epsbbox, skip, buffer = false, false, ''
+
+ while str = eps.gets(rs=@rs) do
+ if str =~ /^%!PS/oi then
+ debug("looks like a valid ps file")
+ break
+ elsif str =~ /%PDF\-\d+\.\d+/oi then
+ debug("looks like a pdf file, so let\'s quit")
+ return false
+ end
+ end
+
+ debug('locating boundingbox')
+
+ # why no BeginData check
+
+ eps.rewind
+
+ while str = eps.gets(rs=@rs) do
+ case str
+ when /^%%Page:/io then
+ break
+ when /^%%(Crop|HiResBounding|ExactBounding)Box:#{@@bboxspec}/moi then
+ debug('high res boundingbox found')
+ setdimensions($2,$3,$4,$5)
+ break
+ when /^%%BoundingBox:#{@@bboxspec}/moi then
+ debug('low res boundingbox found')
+ setdimensions($1,$2,$3,$4)
+ end
+ end
+
+ debug('no boundingbox found') if @width == 0
+
+ eps.rewind
+
+ while str = eps.gets(rs=@rs) do
+ if str.sub!(/^(.*)%!PS/moi, "%!PS") then
+ debug("removing pre banner data")
+ out.puts(str)
+ break
+ end
+ end
+
+ while str = eps.gets(rs=@rs) do
+ if skip then
+ skip = false if str =~ /^%+(EndData|EndPhotoshop|BeginProlog).*$/o
+ out.puts(str) if $1 == "BeginProlog"
+ elsif str =~ /^%(BeginPhotoshop)\:\s*\d+.*$/o then
+ skip = true
+ elsif str =~ /^%%/mos then
+ if ! epsbbox && str =~ /^%%(Page:|EndProlog)/io then
+ out.puts(str) if $1 == "EndProlog"
+ debug('faking papersize')
+ out.puts("<< /PageSize [#{@width} #{@height}] >> setpagedevice\n")
+ out.puts("gsave #{@xoffset} #{@yoffset} translate\n")
+ epsbbox = true
+ elsif str =~ /^%%BeginBinary\:\s*\d+\s*$/o then
+ debug('copying binary data')
+ out.puts(str)
+ while str = eps.gets(rs=@rs)
+ if str =~ /^%%EndBinary\s*$/o then
+ out.puts(str)
+ else
+ out.write(str)
+ end
+ end
+ elsif str =~ /^%AI9\_PrivateDataBegin/o then
+ debug('ignore private ai crap')
+ break
+ elsif str =~ /^%%EOF/o then
+ debug('ignore post eof crap')
+ break
+ # elsif str =~ /^%%PageTrailer/o then
+ # debug('ignoring post page trailer crap')
+ # break
+ elsif str =~ /^%%Trailer/o then
+ debug('ignoring post trailer crap')
+ break
+ elsif str =~ /^%%Creator.*Illustrator.*$/io then
+ debug('getting rid of problematic creator spec')
+ str = "% Creator: Adobe Illustrator ..."
+ out.puts(str)
+ elsif str =~ /^%%AI.*(PaperRect|Margin)/io then
+ debug('removing AI paper crap')
+ elsif str =~ /^%%AI.*Version.*$/io then
+ debug('removing dangerous version info')
+ elsif str =~ /^(%+AI.*Thumbnail.*)$/o then
+ debug('skipping AI thumbnail')
+ skip = true
+ else
+ out.puts(str)
+ end
+ else
+ out.puts(str)
+ end
+ end
+
+ debug('done, sending EOF')
+
+ out.puts "grestore\n%%EOF\n"
+
+ # ok = $? == 0
+ # report('process aborted, broken pipe, fatal error') unless ok
+ # return ok
+
+ return true
+
+ end
+
+ def downsample (inpfile, outfile, method='screen')
+
+ # gs <= 8.50
+
+ report("downsampling #{inpfile}")
+
+ doit = true
+ unless getvariable('force') then
+ begin
+ if f = File.open(inpfile) then
+ f.binmode
+ while doit && (data = f.gets) do
+ if data =~ /\/ArtBox\s*\[\s*[\d\.]+\s+[\d\.]+\s+[\d\.]+\s+[\d\.]+\s*\]/io then
+ doit = false
+ end
+ end
+ f.close
+ end
+ rescue
+ end
+ end
+
+ if doit then
+ arguments = ''
+ arguments << "-dPDFSETTINGS=/#{method} -dEmbedAllFonts=true "
+ arguments << "#{gscolorswitch} "
+ arguments << "#{gsdefaults} "
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER "
+ arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit "
+ unless ok = System.run('ghostscript',arguments) then
+ begin
+ File.delete(outfile) if FileTest.file?(outfile)
+ report("removing file #{outfile}")
+ rescue
+ debug("file #{outfile} may be invalid")
+ end
+ end
+ return ok
+ else
+ report("crop problem, straight copying #{inpfile}")
+ File.copy(inpfile,outfile)
+ return false
+ end
+
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb b/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb
new file mode 100644
index 00000000000..4495c3070f4
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb
@@ -0,0 +1,103 @@
+# module : graphics/inkscape
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+# ['graphics/gs','gs'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/variables'
+require 'base/system'
+require 'graphics/gs'
+
+class InkScape
+
+ include Variables
+
+ def initialize(logger=nil)
+
+ unless logger then
+ puts('inkscape class needs a logger')
+ exit
+ end
+
+ @variables = Hash.new
+ @logger = logger
+
+ reset
+
+ end
+
+ def reset
+ # nothing yet
+ end
+
+ def supported?(filename)
+ filename =~ /.*\.(svg|svgz)/io
+ end
+
+ def convert(logfile=System.null)
+
+ inpfilename = getvariable('inputfile').dup
+ outfilename = getvariable('outputfile').dup
+ outfilename = inpfilename.dup if outfilename.empty?
+ outfilename.gsub!(/(\.[^\.]*?)$/, ".pdf")
+ tmpfilename = outfilename.gsub(/(\.[^\.]*?)$/, ".ps")
+
+ if inpfilename.empty? || outfilename.empty? then
+ report("no filenames given")
+ return false
+ end
+ if inpfilename == outfilename then
+ report("filenames must differ (#{inpfilename} #{outfilename})")
+ return false
+ end
+ unless FileTest.file?(inpfilename) then
+ report("unknown file #{inpfilename}")
+ return false
+ end
+
+ report("converting #{inpfilename} to #{tmpfilename}")
+
+ # we need to redirect the error info else we get a pop up console
+
+ resultpipe = "--without-gui --print=\">#{tmpfilename}\" 2>#{logfile}"
+
+ arguments = [resultpipe,inpfilename].join(' ').gsub(/\s+/,' ')
+
+ ok = true
+ begin
+ debug("inkscape: #{arguments}")
+ # should work
+ # ok = System.run('inkscape',arguments) # does not work here
+ # but 0.40 only works with this:
+ ok = system("inkscape #{arguments}")
+ # and 0.41 fails with everything
+ rescue
+ report("aborted due to error")
+ return false
+ else
+ return false unless ok
+ end
+
+ ghostscript = GhostScript.new(@logger)
+
+ ghostscript.setvariable('inputfile',tmpfilename)
+ ghostscript.setvariable('outputfile',outfilename)
+
+ report("converting #{tmpfilename} to #{outfilename}")
+
+ ghostscript.convert
+
+ begin
+ File.delete(tmpfilename)
+ rescue
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb b/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb
new file mode 100644
index 00000000000..f59087bdf44
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb
@@ -0,0 +1,161 @@
+# module : graphics/inkscape
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end
+
+require 'base/variables'
+
+class ImageMagick
+
+ include Variables
+
+ def initialize(logger=nil)
+
+ unless logger then
+ puts('magick class needs a logger')
+ exit
+ end
+
+ @variables = Hash.new
+ @logger = logger
+
+ reset
+
+ end
+
+ def reset
+ ['compression','depth','colorspace','quality'].each do |key|
+ setvariable(key)
+ end
+ end
+
+ def supported?(filename) # ? pdf
+ filename =~ /.*\.(png|gif|tif|tiff|jpg|jpeg|eps|ai\d*)/io
+ end
+
+ def convert(suffix='pdf')
+
+ inpfilename = getvariable('inputfile').dup
+ outfilename = getvariable('outputfile').dup
+ outfilename = inpfilename.dup if outfilename.empty?
+ outfilename.gsub!(/(\.[^\.]*?)$/, ".#{suffix}")
+
+ if inpfilename.empty? || outfilename.empty? then
+ report("no filenames given")
+ return false
+ end
+ if inpfilename == outfilename then
+ report("filenames must differ (#{inpfilename} #{outfilename})")
+ return false
+ end
+ unless FileTest.file?(inpfilename) then
+ report("unknown file #{inpfilename}")
+ return false
+ end
+
+ if inpfilename =~ /\.tif+$/io then
+ tmpfilename = 'temp.png'
+ arguments = "#{inpfilename} #{tmpfilename}"
+ begin
+ debug("imagemagick: #{arguments}")
+ ok = System.run('imagemagick',arguments)
+ rescue
+ report("aborted due to error")
+ return false
+ else
+ return false unless ok
+ end
+ inpfilename = tmpfilename
+ end
+
+ compression = depth = colorspace = quality = ''
+
+ if getvariable('compression') =~ /(zip|jpeg)/o then
+ compression = " -compress #{$1}"
+ end
+ if getvariable('depth') =~ /(8|16)/o then
+ depth = "-depth #{$1}"
+ end
+ if getvariable('colorspace') =~ /(gray|rgb|cmyk)/o then
+ colorspace = "-colorspace #{$1}"
+ end
+ case getvariable('quality')
+ when 'low' then quality = '-quality 0'
+ when 'medium' then quality = '-quality 75'
+ when 'high' then quality = '-quality 100'
+ end
+
+ report("converting #{inpfilename} to #{outfilename}")
+
+ arguments = [compression,depth,colorspace,quality,inpfilename,outfilename].join(' ').gsub(/\s+/,' ')
+
+ begin
+ debug("imagemagick: #{arguments}")
+ ok = System.run('imagemagick',arguments)
+ rescue
+ report("aborted due to error")
+ return false
+ else
+ return ok
+ end
+
+ end
+
+ def autoconvert
+
+ inpfilename = getvariable('inputfile')
+ outfilename = getvariable('outputfile')
+
+ if inpfilename.empty? || ! FileTest.file?(inpfilename) then
+ report("missing file #{inpfilename}")
+ return
+ end
+
+ outfilename = inpfilename.dup if outfilename.empty?
+ tmpfilename = 'temp.jpg'
+
+ reset
+
+ megabyte = 1024*1024
+
+ ok = false
+
+ if FileTest.size(inpfilename)>2*megabyte
+ setvariable('compression','zip')
+ ok = convert
+ else
+ setvariable('compression','jpeg')
+ if FileTest.size(inpfilename)>10*megabyte then
+ setvariable('quality',85)
+ elsif FileTest.size(inpfilename)>5*megabyte then
+ setvariable('quality',90)
+ else
+ setvariable('quality',95)
+ end
+ report("auto quality #{getvariable('quality')}")
+ setvariable('outputfile', tmpfilename)
+ ok = convert('jpg')
+ setvariable('inputfile', tmpfilename)
+ setvariable('outputfile', outfilename)
+ ok = convert
+ begin
+ File.delete(tmpfilename)
+ rescue
+ report("#{tmpfilename} cannot be deleted")
+ end
+ end
+
+ reset
+
+ return ok
+
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/mpstools.rb b/Master/texmf-dist/scripts/context/ruby/mpstools.rb
new file mode 100644
index 00000000000..6dc0c35ad0c
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/mpstools.rb
@@ -0,0 +1,5 @@
+# todo
+
+puts("This program is yet unfinished, for the moment it just calls 'mptopdf'.\n\n")
+
+system("texmfstart mptopdf #{ARGV.join(' ')}")
diff --git a/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb b/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb
new file mode 100644
index 00000000000..3c1636cf2bc
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+# program : newimgtopdf
+# 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
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+
+require 'graphics/magick'
+
+banner = ['ImgToPdf', 'version 1.1.1', '2002-2005', 'PRAGMA ADE/POD']
+
+class Commands
+
+ include CommandBase
+
+ # nowadays we would force a directive, but
+ # for old times sake we handle default usage
+
+ def main
+ filename = @commandline.argument('first')
+
+ if filename.empty? then
+ help
+ else
+ convert
+ end
+ end
+
+ # actions
+
+ def convert
+
+ magick = Magick.new(session)
+
+ ['compression','depth','colorspace','quality','inputpath','outputpath'].each do |v|
+ magick.setvariable(v,@commandline.option(v))
+ end
+
+ @commandline.arguments.each do |fullname|
+ magick.setvariable('inputfile',fullname)
+ magick.setvariable('outputfile',fullname.gsub(/(\..*?$)/io, '.pdf'))
+ if @commandline.option('auto') then
+ magick.autoconvert
+ else
+ magick.convert
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('auto')
+
+commandline.registervalue('compression')
+commandline.registervalue('depth')
+commandline.registervalue('colorspace')
+commandline.registervalue('quality')
+
+commandline.registervalue('inputpath')
+commandline.registervalue('outputpath')
+
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registeraction('convert', 'convert image into pdf')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb b/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb
new file mode 100644
index 00000000000..898f987cbce
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb
@@ -0,0 +1,530 @@
+#!/usr/bin/env ruby
+
+# program : pstopdf
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+banner = ['PsToPdf', 'version 2.0.0', '2002-2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+# todo: paden/prefix in magick and inkscape
+# todo: clean up method handling (pass strings, no numbers)
+# --method=crop|bounded|raw|...
+# --resolution=low|normal|medium|high|printer|print|screen|ebook|default
+# + downward compatible flag handling
+
+require 'base/switch'
+require 'base/tool'
+require 'base/logger'
+
+require 'graphics/gs'
+require 'graphics/magick'
+require 'graphics/inkscape'
+
+require 'rexml/document'
+
+exit if defined?(REQUIRE2LIB)
+
+class Commands
+
+ include CommandBase
+
+ # nowadays we would force a directive, but
+ # for old times sake we handle default usage
+
+ def main
+ filename = @commandline.argument('first')
+ pattern = @commandline.option('pattern')
+ globfiles(pattern) if filename.empty? && ! pattern.empty?
+ filename = @commandline.argument('first')
+ if filename.empty? then
+ help
+ elsif filename =~ /\.exa$/ then
+ request
+ else
+ convert
+ end
+ end
+
+ # actions
+
+ def convert
+
+ ghostscript = GhostScript.new(logger)
+ magick = ImageMagick.new(logger)
+ inkscape = InkScape.new(logger)
+
+ outpath = @commandline.option('outputpath')
+ unless outpath.empty? then
+ begin
+ File.expand_path(outpath)
+ outpath = File.makedirs(outpath) unless FileTest.directory?(outpath)
+ rescue
+ # sorry
+ end
+ end
+
+ @commandline.arguments.each do |filename|
+
+ filename = Tool.cleanfilename(filename,@commandline)
+ inppath = @commandline.option('inputpath')
+ if inppath.empty? then
+ inppath = '.'
+ fullname = filename # avoid duplicate './'
+ else
+ fullname = File.join(inppath,filename)
+ end
+ if FileTest.file?(fullname) then
+ handle_whatever(ghostscript,inkscape,magick,filename)
+ else
+ report("file #{fullname} does not exist")
+ end
+
+ end
+
+ end
+
+ def request
+
+ # <exa:request>
+ # <exa:application>
+ # <exa:command>pstopdf</exa:command>
+ # <exa:filename>E:/tmp/demo.ps</exa:filename>
+ # </exa:application>
+ # <exa:data>
+ # <exa:variable label='gs:DoThumbnails'>false</exa:variable>
+ # <exa:variable label='gs:ColorImageDepth'>-1</exa:variable>
+ # </exa:data>
+ # </exa:request>
+
+ ghostscript = GhostScript.new(logger)
+ magick = ImageMagick.new(logger)
+ inkscape = InkScape.new(logger)
+
+ dataname = @commandline.argument('first') || ''
+ filename = @commandline.argument('second') || ''
+
+ if dataname.empty? || ! FileTest.file?(dataname) then
+ report('provide valid exa file')
+ return
+ else
+ begin
+ request = REXML::Document.new(File.new(dataname))
+ rescue
+ report('provide valid exa file (xml error)')
+ return
+ end
+ end
+ if filename.empty? then
+ begin
+ if filename = REXML::XPath.first(request.root,"exa:request/exa:application/exa:filename/text()") then
+ filename = filename.to_s
+ else
+ report('no filename found in exa file')
+ return
+ end
+ rescue
+ filename = ''
+ end
+ end
+ if filename.empty? then
+ report('provide valid filename')
+ return
+ elsif ! FileTest.file?(filename) then
+ report("invalid filename #{filename}")
+ return
+ end
+
+ [ghostscript,inkscape,magick].each do |i|
+ i.setvariable('inputfile',filename)
+ end
+
+ # set ghostscript variables
+ REXML::XPath.each(request.root,"/exa:request/exa:data/exa:variable") do |v|
+ begin
+ if (key = v.attributes['label']) and (value = v.text.to_s) then
+ case key
+ when /gs[\:\.](var[\:\.])*(offset)/io then ghostscript.setoffset(value)
+ when /gs[\:\.](var[\:\.])*(method)/io then ghostscript.setvariable('method',value)
+ when /gs[\:\.](var[\:\.])*(.*)/io then ghostscript.setpsoption($2,value)
+ end
+ end
+ rescue
+ end
+ end
+
+ # no inkscape and magick variables (yet)
+
+ handle_whatever(ghostscript,inkscape,magick,filename)
+
+ end
+
+ def watch
+
+ ghostscript = GhostScript.new(logger)
+ magick = ImageMagick.new(logger)
+ inkscape = InkScape.new(logger)
+
+ pathname = commandline.option('watch')
+
+ unless pathname and not pathname.empty? then
+ report('empty watchpath is not supported')
+ exit
+ end
+
+ if pathname == '.' then
+ report("watchpath #{pathname} is not supported")
+ exit
+ end
+
+ if FileTest.directory?(pathname) then
+ if Dir.chdir(pathname) then
+ report("watching path #{pathname}")
+ else
+ report("unable to change to path #{pathname}")
+ exit
+ end
+ else
+ report("invalid path #{pathname}")
+ exit
+ end
+
+ waiting = false
+
+ loop do
+
+ if waiting then
+ report("waiting #{getvariable('delay')}")
+ waiting = false
+ sleep(getvariable('delay').to_i)
+ end
+
+ files = Dir.glob("**/*.*")
+
+ if files and files.length > 0 then
+
+ files.each do |fullname|
+
+ next unless fullname
+
+ if FileTest.directory?(fullname) then
+ debug('skipping path', fullname)
+ next
+ end
+
+ unless magick.supported(fullname) then
+ debug('not supported', fullname)
+ next
+ end
+
+ if (! FileTest.file?(fullname)) || (FileTest.size(fullname) < 100) then
+ debug("skipping small crap file #{fullname}")
+ next
+ end
+
+ debug("handling file #{fullname}")
+
+ begin
+ next unless File.rename(fullname,fullname) # access trick
+ rescue
+ next # being written
+ end
+
+ fullname = Tool.cleanfilename(fullname,@commandline)
+
+ fullname.gsub!(/\\/io, '/')
+
+ filename = File.basename(fullname)
+ filepath = File.dirname(fullname)
+
+ next if filename =~ /gstemp.*/io
+
+ if filepath !~ /(result|done|raw|crop|bound|bitmap)/io then
+ begin
+ File.makedirs(filepath+'/raw')
+ File.makedirs(filepath+'/bound')
+ File.makedirs(filepath+'/crop')
+ File.makedirs(filepath+'/bitmap')
+ debug("creating prefered input paths on #{filepath}")
+ rescue
+ debug("creating input paths on #{filepath} failed")
+ end
+ end
+
+ if filepath =~ /^(.*\/|)(done|result)$/io then
+ debug("skipping file #{fullname}")
+ else
+ report("start processing file #{fullname}")
+ if filepath =~ /^(.*\/*)(raw|crop|bound)$/io then
+ donepath = $1 + 'done'
+ resultpath = $1 + 'result'
+ case $2
+ when 'raw' then method = 1
+ when 'bound' then method = 2
+ when 'crop' then method = 3
+ else method = 2
+ end
+ report("forcing method #{method}")
+ else
+ method = 2
+ donepath = filepath + '/done'
+ resultpath = filepath + '/result'
+ report("default method #{method}")
+ end
+
+ begin
+ File.makedirs(donepath)
+ File.makedirs(resultpath)
+ rescue
+ report('result path creation fails')
+ end
+
+ if FileTest.directory?(donepath) && FileTest.directory?(resultpath) then
+
+ resultname = resultpath + '/' + filename.sub(/\..*$/,'') + '.pdf'
+
+ @commandline.setoption('inputpath', filepath)
+ @commandline.setoption('outputpath', resultpath)
+ @commandline.setoption('method', method)
+
+ if ghostscript.psfile?(fullname) then
+ handle_ghostscript(ghostscript,filename)
+ else
+ handle_magick(magick,filename)
+ end
+
+ sleep(1) # calm down
+
+ if FileTest.file?(fullname) then
+ begin
+ File.copy(fullname,donepath + '/' + filename)
+ File.delete(fullname)
+ rescue
+ report('cleanup fails')
+ end
+ end
+
+ end
+
+ end
+
+ end
+
+ end
+
+ waiting = true
+ end
+
+ end
+
+ private
+
+ def handle_whatever(ghostscript,inkscape,magick,filename)
+ if ghostscript.psfile?(filename) then
+ # report("processing ps file #{filename}")
+ ghostscript.setvariable('pipe',false) if @commandline.option('nopipe')
+ # ghostscript.setvariable('pipe',not @commandline.option('nopipe'))
+ ghostscript.setvariable('colormodel',@commandline.option('colormodel'))
+ ghostscript.setvariable('offset',@commandline.option('offset'))
+ handle_ghostscript(ghostscript,filename)
+ elsif ghostscript.pdffile?(filename) && ghostscript.pdfmethod?(@commandline.option('method')) then
+ # report("processing pdf file #{filename}")
+ handle_ghostscript(ghostscript,filename)
+ elsif inkscape.supported?(filename) then
+ # report("processing non ps/pdf file #{filename}")
+ handle_inkscape(inkscape,filename)
+ elsif magick.supported?(filename) then
+ # report("processing non ps/pdf file #{filename}")
+ handle_magick(magick,filename)
+ end
+ end
+
+ def handle_magick(magick,filename)
+
+ report("converting non-ps file #{filename} into pdf")
+
+ inppath = @commandline.option('inputpath')
+ outpath = @commandline.option('outputpath')
+
+ inppath = inppath + '/' if not inppath.empty?
+ outpath = outpath + '/' if not outpath.empty?
+
+ prefix = @commandline.option('prefix')
+ suffix = @commandline.option('suffix')
+
+ inpfilename = "#{inppath}#{filename}"
+ outfilename = "#{outpath}#{prefix}#{filename.sub(/\.(.*?)$/, '')}#{suffix}.pdf"
+
+ magick.setvariable('inputfile' , inpfilename)
+ magick.setvariable('outputfile', outfilename)
+
+ magick.autoconvert
+
+ end
+
+ def handle_inkscape(inkscape,filename)
+
+ report("converting svg(z) file #{filename} into pdf")
+
+ inppath = @commandline.option('inputpath')
+ outpath = @commandline.option('outputpath')
+
+ inppath = inppath + '/' if not inppath.empty?
+ outpath = outpath + '/' if not outpath.empty?
+
+ prefix = @commandline.option('prefix')
+ suffix = @commandline.option('suffix')
+
+ inpfilename = "#{inppath}#{filename}"
+ outfilename = "#{outpath}#{prefix}#{filename.sub(/\.(.*?)$/, '')}#{suffix}.pdf"
+
+ inkscape.setvariable('inputfile' , inpfilename)
+ inkscape.setvariable('outputfile', outfilename)
+
+ if @commandline.option('verbose') || @commandline.option('debug') then
+ logname = filename.gsub(/\.[^\.]*?$/, '.log')
+ report("log info saved in #{logname}")
+ inkscape.convert(logname) # logname ook doorgeven
+ else
+ inkscape.convert
+ end
+
+ end
+
+ def handle_ghostscript(ghostscript,filename)
+
+ ghostscript.reset
+
+ method = ghostscript.method(@commandline.option('method'))
+ force = ghostscript.method(@commandline.option('force'))
+
+ ghostscript.setvariable('method', method)
+ ghostscript.setvariable('force', force)
+
+ # report("conversion method #{method}")
+
+ inppath = @commandline.option('inputpath')
+ outpath = @commandline.option('outputpath')
+
+ inppath = inppath + '/' if not inppath.empty?
+ outpath = outpath + '/' if not outpath.empty?
+
+ prefix = @commandline.option('prefix')
+ suffix = @commandline.option('suffix')
+
+ ok = false
+
+ if ghostscript.pdfmethod?(method) then
+
+ report("converting pdf file #{filename} into pdf")
+
+ if prefix.empty? && suffix.empty? && inppath.empty? && outpath.empty? then
+ prefix = ghostscript.pdfprefix(method)
+ end
+
+ if ghostscript.pdffile?(filename) then
+
+ filename = filename.sub(/\.pdf$/, '')
+
+ inpfilename = "#{inppath}#{filename}.pdf"
+ outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf"
+
+ ghostscript.setvariable('inputfile' ,inpfilename)
+ ghostscript.setvariable('outputfile',outfilename)
+
+ if FileTest.file?(inpfilename) then
+ ok = ghostscript.convert
+ else
+ report("no file found #{filename}")
+ end
+
+ else
+ report("no pdf file #{filename}")
+ end
+
+ elsif ghostscript.psfile?(filename) then
+
+ if filename =~ /(.*)\.(.*?)$/io then
+ filename, filesuffix = $1, $2
+ else
+ filesuffix = 'eps'
+ end
+
+ report("converting #{filesuffix} (ps) into pdf")
+
+ inpfilename = "#{inppath}#{filename}.#{filesuffix}"
+ outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf"
+
+ ghostscript.setvariable('inputfile' , inpfilename)
+ ghostscript.setvariable('outputfile', outfilename)
+
+ if FileTest.file?(inpfilename) then
+ ok = ghostscript.convert
+ if ! ok && FileTest.file?(outfilename) then
+ begin
+ File.delete(outfilename)
+ rescue
+ end
+ end
+ else
+ report("no file with name #{filename} found")
+ end
+
+ else
+ report('file must be of type eps/ps/ai/pdf')
+ end
+
+ return ok
+
+ end
+
+end
+
+# ook pdf -> pdf onder optie 0, andere kleurruimte
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('debug')
+commandline.registerflag('verbose')
+commandline.registerflag('nopipe')
+
+commandline.registervalue('method',2)
+commandline.registervalue('offset',0)
+
+commandline.registervalue('prefix')
+commandline.registervalue('suffix')
+
+commandline.registervalue('inputpath')
+commandline.registervalue('outputpath')
+
+commandline.registerflag('watch')
+commandline.registerflag('force')
+
+commandline.registervalue('delay',2)
+
+commandline.registervalue('colormodel','cmyk')
+commandline.registervalue('pattern','')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registeraction('convert', 'convert ps into pdf')
+commandline.registeraction('request', 'handles exa request file')
+commandline.registeraction('watch', 'watch folders for conversions (untested)')
+
+commandline.expand
+
+logger.verbose if (commandline.option('verbose') || commandline.option('debug'))
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/newtexexec.rb b/Master/texmf-dist/scripts/context/ruby/newtexexec.rb
new file mode 100644
index 00000000000..27f8eeed96f
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/newtexexec.rb
@@ -0,0 +1,609 @@
+banner = ['TeXExec', 'version 6.1.0', '1997-2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+require 'base/variables'
+require 'base/system'
+
+require 'base/tex'
+require 'base/texutil'
+
+require 'ftools' # needed ?
+
+require 'base/kpse' # needed ?
+# require 'base/pdf' # needed ?
+require 'base/state' # needed ?
+require 'base/file' # needed ?
+
+class Commands
+
+ include CommandBase
+
+ def make
+ if job = TEX.new(logger) then
+ prepare(job)
+ # bonus, overloads language switch !
+ job.setvariable('language','all') if @commandline.option('all')
+ if @commandline.arguments.length > 0 then
+ if @commandline.arguments.first == 'all' then
+ job.setvariable('texformats',job.defaulttexformats)
+ job.setvariable('mpsformats',job.defaultmpsformats)
+ else
+ job.setvariable('texformats',@commandline.arguments)
+ job.setvariable('mpsformats',@commandline.arguments)
+ end
+ end
+ job.makeformats
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ end
+ end
+
+ def check
+ if job = TEX.new(logger) then
+ job.checkcontext
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ end
+ end
+
+ def main
+ if @commandline.arguments.length>0 then
+ process
+ else
+ help
+ end
+ end
+
+ def process
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processtex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ end
+ end
+
+ def mptex
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmptex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ end
+ end
+
+ def mpxtex
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmpxtex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ end
+ end
+
+ # hard coded goodies # to be redone as s-ctx-.. with vars passed as such
+
+ def listing
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ backspace = @commandline.checkedoption('backspace', '1.5cm')
+ topspace = @commandline.checkedoption('topspace', '1.5cm')
+ pretty = @commandline.option('pretty')
+ f << "% interface=english\n"
+ f << "\\setupbodyfont[11pt,tt]\n"
+ f << "\\setuplayout\n"
+ f << " [topspace=#{topspace},backspace=#{backspace},\n"
+ f << " header=0cm,footer=1.5cm,\n"
+ f << " width=middle,height=middle]\n"
+ f << "\\setuptyping[lines=yes]\n"
+ f << "\\setuptyping[option=color]\n" if pretty
+ f << "\\starttext\n";
+ files.each do |filename|
+ report("list file: #{filename}")
+ cleanname = cleantexfilename(filename).downcase
+ f << "\\page\n"
+ f << "\\setupfootertexts[\\tttf #{cleanname}][\\tttf \\pagenumber]\n"
+ f << "\\typefile{#{filename}}\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to list')
+ end
+ else
+ report('no files to list')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def figures
+ # this one will be redone using rlxtools
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ job.runtexutil(files,"--figures", true)
+ figures = @commandline.checkedoption('method', 'a').downcase
+ paperoffset = @commandline.checkedoption('paperoffset', '0pt')
+ backspace = @commandline.checkedoption('backspace', '1.5cm')
+ topspace = @commandline.checkedoption('topspace', '1.5cm')
+ boxtype = @commandline.checkedoption('boxtype','')
+ f << "% format=english\n";
+ f << "\\setuplayout\n";
+ f << " [topspace=#{topspace},backspace=#{backspace},\n"
+ f << " header=1.5cm,footer=0pt,\n";
+ f << " width=middle,height=middle]\n";
+ if @commandline.option('fullscreen') then
+ f << "\\setupinteraction\n";
+ f << " [state=start]\n";
+ f << "\\setupinteractionscreen\n";
+ f << " [option=max]\n";
+ end
+ boxtype += "box" unless boxtype.empty? || (boxtype =~ /box$/io)
+ f << "\\starttext\n";
+ f << "\\showexternalfigures[alternative=#{figures},offset=#{paperoffset},size=#{boxtype}]\n";
+ f << "\\stoptext\n";
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ File.silentdelete('texutil.tuf')
+ else
+ report('no figures to show')
+ end
+ else
+ report('no figures to show')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def modules
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ msuffixes = ['tex','mp','pl','pm','rb']
+ if files.length > 0 then
+ files.each do |fname|
+ fnames = Array.new
+ if FileTest.file?(fname) then
+ fnames << fname
+ else
+ msuffixes.each do |fsuffix|
+ fnames << File.suffixed(fname,fsuffix)
+ end
+ end
+ fnames.each do |ffname|
+ if msuffixes.include?(File.splitname(ffname)[1]) && FileTest.file?(ffname) then
+ if mod = File.open(job.tempfilename('tex'),'w') then
+ # will become a call to ctxtools
+ job.runtexutil(ffname,"--documents", true)
+ if ted = File.silentopen(File.suffixed(ffname,'ted')) then
+ firstline = ted.gets
+ if firstline =~ /interface=/o then
+ mod << firstline
+ else
+ mod << "% interface=en\n"
+ end
+ ted.close
+ else
+ mod << "% interface=en\n"
+ end
+ mod << "\\usemodule[abr-01,mod-01]\n"
+ mod << "\\def\\ModuleNumber{1}\n"
+ mod << "\\starttext\n"
+ # todo: global file too
+ mod << "\\readlocfile{#{File.suffixed(ffname,'ted')}}{}{}\n"
+ mod << "\\stoptext\n"
+ mod.close
+ job.setvariable('interface','english') # redundant
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ ["dvi", "pdf","tuo"].each do |s|
+ File.silentrename(job.tempfilename(s),File.suffixed(ffname,s));
+ end
+ end
+ end
+ end
+ end
+ else
+ report('no modules to process')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def arrange
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ emptypages = @commandline.checkedoption('addempty', '')
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ textwidth = @commandline.checkedoption('textwidth', '0cm')
+ backspace = @commandline.checkedoption('backspace', '0cm')
+ topspace = @commandline.checkedoption('topspace', '0cm')
+ f << "\\definepapersize\n"
+ f << " [offset=#{paperoffset}]\n"
+ f << "\\setuplayout\n"
+ f << " [backspace=#{backspace},\n"
+ f << " topspace=#{topspace},\n"
+ f << " marking=on,\n" if @commandline.option('marking')
+ f << " width=middle,\n"
+ f << " height=middle,\n"
+ f << " location=middle,\n"
+ f << " header=0pt,\n"
+ f << " footer=0pt]\n"
+ unless @commandline.option('noduplex') then
+ f << "\\setuppagenumbering\n"
+ f << " [alternative=doublesided]\n"
+ end
+ f << "\\starttext\n"
+ files.each do |filename|
+ report("arranging file #{filename}")
+ f << "\\insertpages\n"
+ f << " [#{filename}]\n"
+ f << " [#{addempty}]\n" unless addempty.empty?
+ f << " [width=#{textwidth}]\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to arrange')
+ end
+ else
+ report('no files to arrange')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def select
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ selection = @commandline.checkedoption('selection', '')
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ textwidth = @commandline.checkedoption('textwidth', '0cm')
+ backspace = @commandline.checkedoption('backspace', '0cm')
+ topspace = @commandline.checkedoption('topspace', '0cm')
+ paperformat = @commandline.checkedoption('paperformat', 'A4*A4').split(/[\*x]/o)
+ from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4'
+ if from == 'fit' or to == 'fit' then
+ f << "\\getfiguredimensions[#{files.first}]\n"
+ if from == 'fit' then
+ f << "\\expanded{\\definepapersize[from-fit][width=\\figurewidth,height=\\figureheight]}\n"
+ from = 'from-fit'
+ end
+ if to == 'fit' then
+ f << "\\expanded{\\definepapersize[to-fit][width=\\figurewidth,height=\\figureheight]}\n"
+ to = 'to-fit'
+ end
+ end
+ job.setvariable('paperformat','') # else overloaded later on
+ f << "\\setuppapersize[#{from}][#{to}]\n"
+ f << "\\definepapersize\n";
+ f << " [offset=#{paperoffset}]\n";
+ f << "\\setuplayout\n";
+ f << " [backspace=#{backspace},\n";
+ f << " topspace=#{topspace},\n";
+ f << " marking=on,\n" if @commandline.option('marking')
+ f << " width=middle,\n";
+ f << " height=middle,\n";
+ f << " location=middle,\n";
+ f << " header=0pt,\n";
+ f << " footer=0pt]\n";
+ f << "\\setupexternalfigures\n";
+ f << " [directory=]\n";
+ f << "\\starttext\n";
+ unless selection.empty? then
+ f << "\\filterpages\n"
+ f << " [#{files.first}][#{selection}][width=#{textwidth}]\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to selectt')
+ end
+ else
+ report('no files to select')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def copy
+ copyortrim(false,'copy')
+ end
+
+ def trim
+ copyortrim(true,'trim')
+ end
+
+ def copyortrim(trim=false)
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ scale = @commandline.checkedoption('scale')
+ scale = (scale * 1000).to_i if scale < 10
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ f << "\\starttext\n"
+ files.each do |filename|
+ result = @commandline.checkedoption('result','texexec')
+ if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then
+ report("copying file: #{filename}")
+ f << "\\getfiguredimensions\n"
+ f << " [#{filename}]\n"
+ f << " [page=1"
+ f << ",\n size=trimbox" if trim
+ f << "]\n"
+ f << "\\definepapersize\n"
+ f << " [copy]\n"
+ f << " [width=\\naturalfigurewidth,\n"
+ f << " height=\\naturalfigureheight]\n"
+ f << "\\setuppapersize\n"
+ f << " [copy][copy]\n"
+ f << "\\setuplayout\n"
+ f << " [page]\n"
+ f << "\\setupexternalfigures\n"
+ f << " [directory=]\n"
+ f << "\\copypages\n"
+ f << " [#[filename}]\n"
+ f << " [scale=#{scale},\n"
+ f << " marking=on,\n" if @commandline.option('markings')
+ f << " size=trimbox,\n" if trim
+ f << " offset=#{paperoffset}]\n"
+ end
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report("no files to #{what}")
+ end
+ else
+ report("no files to #{what}")
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def combine
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @commandline.arguments.sort
+ if files.length > 0 then
+ if f = File.open(job.tempfilename('tex'),'w') then
+ paperoffset = @commandline.checkedoption('paperoffset', '0cm')
+ combination = @commandline.checkedoption('combination','2*2').split(/[\*x]/o)
+ paperformat = @commandline.checkedoption('paperoffset', 'A4*A4').split(/[\*x]/o)
+ nx, ny = combination[0] || '2', combination[1] || combination[0] || '2'
+ from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4'
+ f << "\\setuppapersize[#{from}][#{to}]\n"
+ f << "\\setuplayout\n"
+ f << " [topspace=#{paperoffset},\n"
+ f << " backspace=#{paperoffset},\n"
+ f << " header=0pt,\n"
+ f << " footer=1cm,\n"
+ f << " width=middle,\n"
+ f << " height=middle]\n"
+ if @commandline.option('nobanner') then
+ f << "\\setuplayout\n"
+ f << " [footer=0cm]\n"
+ end
+ f << "\\setupexternalfigures\n"
+ f << " [directory=]\n"
+ f << "\\starttext\n"
+ files.each do |filename|
+ result = @commandline.checkedoption('result','texexec')
+ if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then
+ report("combination file: #{filename}")
+ cleanname = cleantexfilename(filename).downcase
+ f << "\\setupfootertexts\n"
+ f << " [\\tttf #{cleanname}\\quad\\quad\\currentdate\\quad\\quad\\pagenumber]\n"
+ f << "\\combinepages[#{filename}][nx=#{nx},ny=#{ny}]\n"
+ f << "\\page\n"
+ end
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to list')
+ end
+ else
+ report('no files to list')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ private
+
+ def prepare(job)
+
+ job.booleanvars.each do |k|
+ job.setvariable(k,@commandline.option(k))
+ end
+ job.stringvars.each do |k|
+ job.setvariable(k,@commandline.option(k))
+ end
+ job.standardvars.each do |k|
+ job.setvariable(k,@commandline.option(k))
+ end
+ job.knownvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+
+ if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then
+ job.setvariable('texengine',str)
+ elsif @commandline.oneof('pdfetex','pdftex','pdf') then
+ job.setvariable('texengine','pdfetex')
+ elsif @commandline.oneof('xetex','xtx') then
+ job.setvariable('texengine','xetex')
+ elsif @commandline.oneof('aleph') then
+ job.setvariable('texengine','aleph')
+ else
+ job.setvariable('texengine','standard')
+ end
+
+ if (str = @commandline.option('backend')) && ! str.standard? && ! str.empty? then
+ job.setvariable('backend',str)
+ elsif @commandline.oneof('pdfetex','pdftex','pdf') then
+ job.setvariable('backend','pdftex')
+ elsif @commandline.oneof('dvipdfmx','dvipdfm','dpx','dpm') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('xetex','xtx') then
+ job.setvariable('backend','xetex')
+ elsif @commandline.oneof('aleph') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('dvips','ps') then
+ job.setvariable('backend','dvips')
+ else
+ job.setvariable('backend','standard')
+ end
+
+ if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then
+ job.setvariable('mpsengine',@commandline.option('engine'))
+ else
+ job.setvariable('mpsengine','standard')
+ end
+
+ end
+
+ def cleantexfilename(filename)
+ filename.gsub(/([\$\_\#])/) do "\\$1" end.gsub(/([\~])/) do "\\string$1" end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('make', 'make formats')
+commandline.registeraction('check', 'check versions')
+commandline.registeraction('process', 'process file')
+commandline.registeraction('mptex', 'process mp file')
+commandline.registeraction('mpxtex', 'process mpx file')
+
+commandline.registeraction('listing', 'list of file content')
+commandline.registeraction('figures', 'generate overview of figures')
+commandline.registeraction('modules', 'generate module documentation')
+commandline.registeraction('arrange', 'impose pages (booklets)')
+commandline.registeraction('select', 'select pages from file(s)')
+commandline.registeraction('copy', 'copy pages from file(s)')
+commandline.registeraction('trim', 'trim pages from file(s)')
+commandline.registeraction('combine', 'combine multiple pages')
+
+@@extrastringvars = [
+ 'pages', 'background', 'backspace', 'topspace', 'boxtype', 'tempdir',
+ 'printformat', 'paperformat', 'method', 'scale', 'selection',
+ 'combination', 'paperoffset', 'textwidth', 'addempty', 'logfile',
+ 'startline', 'endline', 'startcolumn', 'endcolumn', 'scale'
+]
+
+@@extrabooleanvars = [
+ 'centerpage', 'noduplex', 'color', 'pretty',
+ 'fullscreen', 'screensaver', 'markings'
+]
+
+if job = TEX.new(logger) then
+
+ job.setextrastringvars(@@extrastringvars)
+ job.setextrabooleanvars(@@extrabooleanvars)
+
+ job.booleanvars.each do |k|
+ commandline.registerflag(k)
+ end
+ job.stringvars.each do |k|
+ commandline.registervalue(k,'')
+ end
+ job.standardvars.each do |k|
+ commandline.registervalue(k,'standard')
+ end
+ job.knownvars.each do |k|
+ commandline.registervalue(k,'')
+ end
+
+end
+
+# todo: register flags -> first one true
+
+commandline.registerflag('pdf')
+commandline.registerflag('pdftex')
+commandline.registerflag('pdfetex')
+
+commandline.registerflag('dvipdfmx')
+commandline.registerflag('dvipdfm')
+commandline.registerflag('dpx')
+commandline.registerflag('dpm')
+
+commandline.registerflag('dvips')
+commandline.registerflag('ps')
+
+commandline.registerflag('xetex')
+commandline.registerflag('xtx')
+
+commandline.registerflag('aleph')
+
+commandline.registerflag('all')
+commandline.registerflag('fast')
+
+# generic
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('verbose')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/newtexutil.rb b/Master/texmf-dist/scripts/context/ruby/newtexutil.rb
new file mode 100644
index 00000000000..af003a8e7b2
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/newtexutil.rb
@@ -0,0 +1,96 @@
+banner = ['TeXUtil ', 'version 9.1.0', '1997-2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+require 'base/file'
+require 'base/texutil'
+
+class Commands
+
+ include CommandBase
+
+ def references
+ filename = @commandline.argument('first')
+ if not filename.empty? and FileTest.file?(File.suffixed(filename,'tuo')) then
+ if tu = TeXUtil::Converter.new(logger) and tu.loaded(filename) then
+ tu.saved if tu.processed
+ end
+ end
+ end
+
+ def main
+ if @commandline.arguments.length>0 then
+ references
+ else
+ help
+ end
+ end
+
+ def purgefiles
+ system("texmfstart ctxtools --purge #{@commandline.argument.join(' ')}")
+ end
+
+ def purgeallfiles
+ system("texmfstart ctxtools --purgeall #{@commandline.argument.join(' ')}")
+ end
+
+ def documentation
+ system("texmfstart ctxtools --document #{@commandline.argument.join(' ')}")
+ end
+
+ def analyzefile
+ system("texmfstart pdftools --analyze #{@commandline.argument.join(' ')}")
+ end
+
+ def filterpages # obsolete
+ system("texmfstart ctxtools --purge #{@commandline.argument.join(' ')}")
+ end
+
+ def figures
+ report("this code is not yet converted from perl to ruby")
+ end
+
+ def logfile
+ report("this code is not yet converted from perl to ruby")
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+# main feature
+
+commandline.registeraction('references', 'convert tui file into tuo file')
+
+# todo features
+
+commandline.registeraction('figures', 'generate figure dimensions file')
+commandline.registeraction('logfile', 'filter essential log messages')
+
+# backward compatibility features
+
+commandline.registeraction('purgefiles', 'remove most temporary files')
+commandline.registeraction('purgeallfiles', 'remove all temporary files')
+commandline.registeraction('documentation', 'generate documentation file from source')
+commandline.registeraction('analyzefile', 'analyze pdf file')
+
+# old feature, not needed any longer due to extension of pdftex
+
+commandline.registeraction('filterpages')
+
+# generic features
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('verbose')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/rlxtools.rb b/Master/texmf-dist/scripts/context/ruby/rlxtools.rb
new file mode 100644
index 00000000000..7962474eb10
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/rlxtools.rb
@@ -0,0 +1,262 @@
+#!/usr/bin/env ruby
+
+# program : rlxtools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2004-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+banner = ['RlxTools', 'version 1.0.1', '2004/2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+require 'base/system'
+require 'base/kpse'
+
+require 'ftools'
+require 'rexml/document'
+
+class Commands
+
+ include CommandBase
+
+ # <?xml version='1.0 standalone='yes'?>
+ # <rl:manipulators>
+ # <rl:manipulator name='lowres' suffix='pdf'>
+ # <rl:step>
+ # texmfstart
+ # --verbose
+ # --iftouched=<rl:value name='path'/>/<rl:value name='file'/>,<rl:value name='path'/>/<rl:value name='prefix'/><rl:value name='file'/>
+ # pstopdf
+ # --method=5
+ # --inputpath=<rl:value name='path'/>
+ # --outputpath=<rl:value name='path'/>/<rl:value name='prefix'/>
+ # <rl:value name='file'/>
+ # </rl:step>
+ # </rl:manipulator>
+ # </rl:manipulators>
+ #
+ # <?xml version='1.0' standalone='yes'?>
+ # <rl:library>
+ # <rl:usage>
+ # <rl:type>figure</rl:type>
+ # <rl:state>found</rl:state>
+ # <rl:file>cow.pdf</rl:file>
+ # <rl:suffix>pdf</rl:suffix>
+ # <rl:path>.</rl:path>
+ # <rl:conversion>lowres</rl:conversion>
+ # <rl:prefix>lowres/</rl:prefix>
+ # <rl:width>276.03125pt</rl:width>
+ # <rl:height>200.75pt</rl:height>
+ # </rl:usage>
+ # </r:library>
+
+ def manipulate
+
+ procname = @commandline.argument('first') || ''
+ filename = @commandline.argument('second') || ''
+
+ procname = Kpse.found(procname)
+
+ if procname.empty? || ! FileTest.file?(procname) then
+ report('provide valid manipulator file')
+ elsif filename.empty? || ! FileTest.file?(filename) then
+ report('provide valid resource log file')
+ else
+ begin
+ data = REXML::Document.new(File.new(filename))
+ rescue
+ report('provide valid resource log file (xml error)')
+ return
+ end
+ begin
+ proc = REXML::Document.new(File.new(procname))
+ rescue
+ report('provide valid manipulator file (xml error)')
+ return
+ end
+ report("manipulator file: #{procname}")
+ report("resourcelog file: #{filename}")
+ begin
+ nofrecords, nofdone = 0, 0
+ REXML::XPath.each(data.root,"/rl:library/rl:usage") do |usage|
+ nofrecords += 1
+ variables = Hash.new
+ usage.elements.each do |e|
+ variables[e.name] = e.text.to_s
+ end
+ report("processing record #{nofrecords} (#{variables['file'] || 'noname'}: #{variables.size} entries)")
+ if conversion = variables['conversion'] then
+ report("testing for conversion #{conversion}")
+ if suffix = variables['suffix'] then
+ if file = variables['file'] then
+ report("conversion #{conversion} for suffix #{suffix} for file #{file}")
+ else
+ report("conversion #{conversion} for suffix #{suffix}")
+ end
+ pattern = "@name='#{conversion}' and @suffix='#{suffix}'"
+ if steps = REXML::XPath.first(proc.root,"/rl:manipulators/rl:manipulator[#{pattern}]") then
+ localsteps = steps.deep_clone
+ ['rl:old','rl:new'].each do |tag|
+ REXML::XPath.each(localsteps,tag) do |extras|
+ REXML::XPath.each(extras,"rl:value") do |value|
+ if name = value.attributes['name'] then
+ substititute(value,variables[name.to_s])
+ end
+ end
+ end
+ end
+ old = REXML::XPath.first(localsteps,"rl:old")
+ new = REXML::XPath.first(localsteps,"rl:new")
+ if old && new then
+ old, new = justtext(old.to_s), justtext(new.to_s)
+ variables['old'], variables['new'] = old, new
+ begin
+ [old,new].each do |d|
+ File.makedirs(File.dirname(d))
+ end
+ rescue
+ report("error during path creation")
+ end
+ report("old file #{old}")
+ report("new file #{new}")
+ level = if File.needsupdate(old,new) then 2 else 0 end
+ else
+ level = 1
+ end
+ if level>0 then
+ REXML::XPath.each(localsteps,"rl:step") do |command|
+ REXML::XPath.each(command,"rl:old") do |value|
+ replace(value,old)
+ end
+ REXML::XPath.each(command,"rl:new") do |value|
+ replace(value,new)
+ end
+ REXML::XPath.each(command,"rl:value") do |value|
+ if name = value.attributes['name'] then
+ substititute(value,variables[name.to_s])
+ end
+ end
+ str = justtext(command.to_s)
+ # str.gsub!(/(\.\/)+/io, '')
+ report("command #{str}")
+ System.run(str) unless @commandline.option('test')
+ report("synchronizing #{old} and #{new}")
+ File.syncmtimes(old,new) if level > 1
+ nofdone += 1
+ end
+ else
+ report("no need for a manipulation")
+ end
+ else
+ report("no manipulator found")
+ end
+ else
+ report("no suffix specified")
+ end
+ else
+ report("no conversion needed")
+ end
+ end
+ if nofdone > 0 then
+ jobname = filename.gsub(/\.(.*?)$/,'') # not 'tuo' here
+ tuoname = jobname + '.tuo'
+ if FileTest.file?(tuoname) && (f = File.open(tuoname,'a')) then
+ f.puts("%\n% number of rlx manipulations: #{nofdone}\n")
+ f.close
+ end
+ end
+ rescue
+ report('error in manipulating files')
+ end
+ begin
+ logname = "#{filename}.log"
+ File.delete(logname) if FileTest.file?(logname)
+ File.copy(filename,logname)
+ rescue
+ end
+ end
+
+ end
+
+ private
+
+ def justtext(str)
+ str = str.to_s
+ str.gsub!(/<[^>]*?>/o, '')
+ str.gsub!(/\s+/o, ' ')
+ str.gsub!(/&lt;/o, '<')
+ str.gsub!(/&gt;/o, '>')
+ str.gsub!(/&amp;/o, '&')
+ str.gsub!(/&quot;/o, '"')
+ str.gsub!(/[\/\\]+/o, '/')
+ return str.strip
+ end
+
+ def 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
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('manipulate', ' [--test] manipulatorfile resourselog')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('test')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/Master/texmf-dist/scripts/context/ruby/texmfstart.rb b/Master/texmf-dist/scripts/context/ruby/texmfstart.rb
new file mode 100644
index 00000000000..dc166bf92c3
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/texmfstart.rb
@@ -0,0 +1,847 @@
+#!/usr/bin/env ruby
+
+# program : texmfstart
+# copyright : PRAGMA Advanced Document Engineering
+# version : 1.5.5 - 2003/2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+# no special requirements, i.e. no exa modules/classes used
+
+# texmfstart [switches] filename [optional arguments]
+#
+# ruby2exe texmfstart --help -> avoids stub test
+#
+# Of couse I can make this into a nice class, which i'll undoubtely will
+# do when I feel the need. In that case it will be part of a bigger game.
+
+# turning this into a service would be nice, so some day ...
+
+# --locate => provides location
+# --exec => exec instead of system
+# --iftouched=a,b => only if timestamp a<>b
+#
+# file: path: bin:
+
+# texmfstart --exec bin:scite *.tex
+
+# we don't depend on other libs
+
+$ownpath = File.expand_path(File.dirname($0)) unless defined? $ownpath
+
+require "rbconfig"
+
+$mswindows = Config::CONFIG['host_os'] =~ /mswin/
+$separator = File::PATH_SEPARATOR
+$version = "1.6.2"
+
+if $mswindows then
+
+ require "win32ole"
+ require "Win32API"
+
+end
+
+exit if defined?(REQUIRE2LIB)
+
+$stdout.sync = true
+$stderr.sync = true
+
+$applications = Hash.new
+$suffixinputs = Hash.new
+$predefined = Hash.new
+
+$suffixinputs['pl'] = 'PERLINPUTS'
+$suffixinputs['rb'] = 'RUBYINPUTS'
+$suffixinputs['py'] = 'PYTHONINPUTS'
+$suffixinputs['lua'] = 'LUAINPUTS'
+$suffixinputs['jar'] = 'JAVAINPUTS'
+$suffixinputs['pdf'] = 'PDFINPUTS'
+
+$predefined['texexec'] = 'texexec.pl'
+$predefined['texutil'] = 'texutil.pl'
+$predefined['texfont'] = 'texfont.pl'
+
+$predefined['mptopdf'] = 'mptopdf.pl'
+$predefined['pstopdf'] = 'pstopdf.rb'
+
+$predefined['examplex'] = 'examplex.rb'
+$predefined['concheck'] = 'concheck.rb'
+
+$predefined['textools'] = 'textools.rb'
+$predefined['tmftools'] = 'tmftools.rb'
+$predefined['ctxtools'] = 'ctxtools.rb'
+$predefined['rlxtools'] = 'rlxtools.rb'
+$predefined['pdftools'] = 'pdftools.rb'
+$predefined['mpstools'] = 'mpstools.rb'
+$predefined['exatools'] = 'exatools.rb'
+$predefined['xmltools'] = 'xmltools.rb'
+
+
+if ENV['TEXMFSTART_MODE'] = 'experimental' then
+ $predefined['texexec'] = 'newtexexec.rb'
+ $predefined['pstopdf'] = 'newpstopdf.rb'
+end
+
+$scriptlist = 'rb|pl|py|lua|jar'
+$documentlist = 'pdf|ps|eps|htm|html'
+
+$editor = ENV['EDITOR'] || ENV['editor'] || 'scite'
+
+$crossover = true # to other tex tools, else only local
+
+$applications['unknown'] = ''
+$applications['perl'] = $applications['pl'] = 'perl'
+$applications['ruby'] = $applications['rb'] = 'ruby'
+$applications['python'] = $applications['py'] = 'python'
+$applications['lua'] = $applications['lua'] = 'lua'
+$applications['java'] = $applications['jar'] = 'java'
+
+if $mswindows then
+ $applications['pdf'] = ['',"pdfopen --page #{$page} --file",'acroread']
+ $applications['html'] = ['','netscape','mozilla','opera','iexplore']
+ $applications['ps'] = ['','gview32','gv','gswin32','gs']
+else
+ $applications['pdf'] = ["pdfopen --page #{$page} --file",'acroread']
+ $applications['html'] = ['netscape','mozilla','opera']
+ $applications['ps'] = ['gview','gv','gs']
+end
+
+$applications['htm'] = $applications['html']
+$applications['eps'] = $applications['ps']
+
+if $mswindows then
+
+ GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', ['P','P','N'], 'N')
+ GetLongPathName = Win32API.new('kernel32', 'GetLongPathName', ['P','P','N'], 'N')
+
+ def dowith_pathname (filename,filemethod)
+ filename = filename.gsub(/\\/o,'/') # no gsub! because filename can be frozen
+ case filename
+ when /\;/o then
+ # could be a path spec
+ return filename
+ when /\s+/o then
+ # danger lurking
+ buffer = ' ' * 260
+ length = filemethod.call(filename,buffer,buffer.size)
+ if length>0 then
+ return buffer.slice(0..length-1)
+ else
+ # when the path or file does not exist, nothing is returned
+ # so we try to handle the path separately from the basename
+ basename = File.basename(filename)
+ pathname = File.dirname(filename)
+ length = filemethod.call(pathname,buffer,260)
+ if length>0 then
+ return buffer.slice(0..length-1) + '/' + basename
+ else
+ return filename
+ end
+ end
+ else
+ # no danger
+ return filename
+ end
+ end
+
+ def longpathname (filename)
+ dowith_pathname(filename,GetLongPathName)
+ end
+
+ def shortpathname (filename)
+ dowith_pathname(filename,GetShortPathName)
+ end
+
+else
+
+ def longpathname (filename)
+ filename
+ end
+
+ def shortpathname (filename)
+ filename
+ end
+
+end
+
+class File
+
+ 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.timestamp(name)
+ begin
+ "#{File.stat(name).mtime}"
+ rescue
+ return 'unknown'
+ end
+ end
+
+ def File.syncmtimes(oldname,newname)
+ begin
+ if $mswindows then
+ # does not work (yet)
+ 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
+
+end
+
+def hashed (arr=[])
+ arg = if arr.class == String then arr.split(' ') else arr.dup end
+ hsh = Hash.new
+ if arg.length > 0
+ hsh['arguments'] = ''
+ done = false
+ arg.each do |s|
+ if done then
+ hsh['arguments'] += ' ' + s
+ else
+ kvl = s.split('=')
+ if kvl[0].sub!(/^\-+/,'') then
+ hsh[kvl[0]] = if kvl.length > 1 then kvl[1] else true end
+ else
+ hsh['file'] = s
+ done = true
+ end
+ end
+ end
+ end
+ return hsh
+end
+
+def launch(filename)
+ if $browser && $mswindows then
+ filename = filename.gsub(/\.[\/\\]/) do
+ Dir.getwd + '/'
+ end
+ report("launching #{filename}")
+ ie = WIN32OLE.new("InternetExplorer.Application")
+ ie.visible = true
+ ie.navigate(filename)
+ return true
+ else
+ return false
+ end
+end
+
+def expanded(arg) # no "other text files", too restricted
+ arg.gsub(/(env|environment)\:([a-zA-Z\-\_\.0-9]+)/o) do
+ method, original, resolved = $1, $2, ''
+ if resolved = ENV[original] then
+ report("environment variable #{original} expands to #{resolved}") unless $report
+ resolved
+ else
+ report("environment variable #{original} cannot be resolved") unless $report
+ original
+ end
+ end . gsub(/(kpse|loc|file|path)\:([a-zA-Z\-\_\.0-9]+)/o) do # was: \S
+ method, original, resolved = $1, $2, ''
+ if $program && ! $program.empty? then
+ pstrings = ["-progname=#{$program}"]
+ else
+ pstrings = ['','progname=context']
+ end
+ # auto suffix with texinputs as fall back
+ if ENV["_CTX_K_V_#{original}_"] then
+ resolved = ENV["_CTX_K_V_#{original}_"]
+ report("environment provides #{original} as #{resolved}") unless $report
+ resolved
+ else
+ pstrings.each do |pstr|
+ if resolved.empty? then
+ command = "kpsewhich #{pstr} #{original}"
+ report("running #{command}")
+ begin
+ resolved = `#{command}`.chomp
+ rescue
+ resolved = ''
+ end
+ end
+ # elsewhere in the tree
+ if resolved.empty? then
+ command = "kpsewhich #{pstr} -format=\"other text files\" #{original}"
+ report("running #{command}")
+ begin
+ resolved = `#{command}`.chomp
+ rescue
+ resolved = ''
+ end
+ end
+ end
+ if resolved.empty? then
+ original = File.dirname(original) if method =~ /path/
+ report("#{original} is not resolved") unless $report
+ ENV["_CTX_K_V_#{original}_"] = original if $crossover
+ original
+ else
+ resolved = File.dirname(resolved) if method =~ /path/
+ report("#{original} is resolved to #{resolved}") unless $report
+ ENV["_CTX_K_V_#{original}_"] = resolved if $crossover
+ resolved
+ end
+ end
+ end
+end
+
+def runcommand(command)
+ if $locate then
+ command = command.split(' ').collect do |c|
+ if c =~ /\//o then
+ begin
+ cc = File.expand_path(c)
+ c = cc if FileTest.file?(cc)
+ rescue
+ end
+ end
+ c
+ end . join(' ')
+ print command # to stdout and no newline
+ elsif $execute then
+ report("using 'exec' instead of 'system' call: #{command}")
+ begin
+ Dir.chdir($path) if ! $path.empty?
+ rescue
+ report("unable to chdir to: #{$path}")
+ end
+ exec(command)
+ else
+ report("using 'system' call: #{command}")
+ begin
+ Dir.chdir($path) if ! $path.empty?
+ rescue
+ report("unable to chdir to: #{$path}")
+ end
+ system(command)
+ end
+end
+
+def runoneof(application,fullname,browserpermitted)
+ if browserpermitted && launch(fullname) then
+ return true
+ else
+ report("starting #{$filename}") unless $report
+ output("\n") if $report && $verbose
+ applications = $applications[application]
+ if applications.class == Array then
+ if $report then
+ output([fullname,expanded($arguments)].join(' '))
+ return true
+ else
+ applications.each do |a|
+ return true if runcommand([a,fullname,expanded($arguments)].join(' '))
+ end
+ end
+ elsif applications.empty? then
+ if $report then
+ output([fullname,expanded($arguments)].join(' '))
+ return true
+ else
+ return runcommand([fullname,expanded($arguments)].join(' '))
+ end
+ else
+ if $report then
+ output([applications,fullname,expanded($arguments)].join(' '))
+ return true
+ else
+ return runcommand([applications,fullname,expanded($arguments)].join(' '))
+ end
+ end
+ return false
+ end
+end
+
+def report(str)
+ $stderr.puts(str) if $verbose
+end
+
+def output(str)
+ $stderr.puts(str)
+end
+
+def usage
+ print "version : #{$version} - 2003/2005 - www.pragma-ade.com\n"
+ print("\n")
+ print("usage : texmfstart [switches] filename [optional arguments]\n")
+ print("\n")
+ print("switches : --verbose --report --browser --direct --execute --locate --iftouched\n")
+ print(" --program --file --page --arguments --batch --edit --report --clear\n")
+ print(" --make --lmake --wmake --path --stubpath --indirect --before --after\n")
+ print("\n")
+ print("example : texmfstart pstopdf.rb cow.eps\n")
+ print(" texmfstart --locate examplex.rb\n")
+ print(" texmfstart --execute examplex.rb\n")
+ print(" texmfstart --browser examplap.pdf\n")
+ print(" texmfstart showcase.pdf\n")
+ print(" texmfstart --page=2 --file=showcase.pdf\n")
+ print(" texmfstart --program=yourtex yourscript.pl arg-1 arg-2\n")
+ print(" texmfstart --direct xsltproc kpse:somefile.xsl somefile.xml\n")
+ print(" texmfstart bin:xsltproc env:somepreset path:somefile.xsl somefile.xml\n")
+ print(" texmfstart --iftouched=normal,lowres downsample.rb normal lowres\n")
+ print(" texmfstart texmfstart bin:scite kpse:texmf.cnf\n")
+ print(" texmfstart texmfstart --exec bin:scite *.tex\n")
+ print(" texmfstart texmfstart --edit texmf.cnf\n")
+end
+
+# somehow registration does not work out (at least not under windows)
+
+def tag(name)
+ if $crossover then "_CTX_K_S_#{name}_" else "TEXMFSTART.#{name}" end
+end
+
+def registered?(filename)
+ return ENV[tag(filename)] != nil
+end
+
+def registered(filename)
+ return ENV[tag(filename)]
+end
+
+def register(filename,fullname)
+ if fullname && ! fullname.empty? then # && FileTest.file?(fullname)
+ ENV[tag(filename)] = fullname
+ report("registering '#{filename}' as '#{fullname}'")
+ return true
+ else
+ return false
+ end
+end
+
+def find(filename,program)
+ filename = filename.sub(/script:/o, '') # so we have bin: and script: and nothing
+ if $predefined.key?(filename) then
+ report("expanding '#{filename}' to '#{$predefined[filename]}'")
+ filename = $predefined[filename]
+ end
+ if registered?(filename) then
+ report("already located '#{filename}'")
+ return registered(filename)
+ end
+ # create suffix list
+ if filename =~ /^(.*)\.(.+)$/ then
+ filename = $1
+ suffixlist = [$2]
+ else
+ suffixlist = [$scriptlist.split('|'),$documentlist.split('|')].flatten
+ end
+ # first we honor a given path
+ if filename =~ /[\\\/]/ then
+ report("trying to honor '#{filename}'")
+ suffixlist.each do |suffix|
+ fullname = filename+'.'+suffix
+ if FileTest.file?(fullname) && register(filename,fullname)
+ return shortpathname(fullname)
+ end
+ end
+ end
+ filename.sub!(/^.*[\\\/]/, '')
+ # next we look at the current path and the callerpath
+ [['.','current'],[$ownpath,'caller'],[registered("THREAD"),'thread']].each do |p|
+ if p && ! p.empty? then
+ suffixlist.each do |suffix|
+ fname = "#{filename}.#{suffix}"
+ fullname = File.expand_path(File.join(p[0],fname))
+ report("locating '#{fname}' in #{p[1]} path '#{p[0]}'")
+ if FileTest.file?(fullname) && register(filename,fullname) then
+ report("'#{fname}' located in #{p[1]} path")
+ return shortpathname(fullname)
+ end
+ end
+ end
+ end
+ # now we consult environment settings
+ fullname = nil
+ suffixlist.each do |suffix|
+ begin
+ break unless $suffixinputs[suffix]
+ environment = ENV[$suffixinputs[suffix]] || ENV[$suffixinputs[suffix]+".#{$program}"]
+ if ! environment || environment.empty? then
+ begin
+ environment = `kpsewhich -expand-path=\$#{$suffixinputs[suffix]}`.chomp
+ rescue
+ environment = nil
+ else
+ if environment && ! environment.empty? then
+ report("using kpsewhich variable #{$suffixinputs[suffix]}")
+ end
+ end
+ elsif environment && ! environment.empty? then
+ report("using environment variable #{$suffixinputs[suffix]}")
+ end
+ if environment && ! environment.empty? then
+ environment.split($separator).each do |e|
+ e.strip!
+ e = '.' if e == '\.' # somehow . gets escaped
+ e += '/' unless e =~ /[\\\/]$/
+ fullname = e + filename + '.' + suffix
+ report("testing '#{fullname}'")
+ if FileTest.file?(fullname) then
+ break
+ else
+ fullname = nil
+ end
+ end
+ end
+ rescue
+ report("environment string '#{$suffixinputs[suffix]}' cannot be used to locate '#{filename}'")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ return shortpathname(fullname) if register(filename,fullname)
+ # then we fall back on kpsewhich
+ suffixlist.each do |suffix|
+ # TDS script scripts location as per 2004
+ if suffix =~ /(#{$scriptlist})/ then
+ begin
+ report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (1)")
+ fullname = `kpsewhich -progname=#{program} -format=texmfscripts #{filename}.#{suffix}`.chomp
+ rescue
+ report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (1)")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ # old TDS location: .../texmf/context/...
+ begin
+ report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (2)")
+ fullname = `kpsewhich -progname=#{program} -format="other text files" #{filename}.#{suffix}`.chomp
+ rescue
+ report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (2)")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ return shortpathname(fullname) if register(filename,fullname)
+ # let's take a look at the path
+ paths = ENV['PATH'].split($separator)
+ suffixlist.each do |s|
+ paths.each do |p|
+ report("checking #{p} for suffix #{s}")
+ if FileTest.file?(File.join(p,"#{filename}.#{s}")) then
+ fullname = File.join(p,"#{filename}.#{s}")
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ end
+ # bad luck, we need to search the tree ourselves
+ if (suffixlist.length == 1) && (suffixlist.first =~ /(#{$documentlist})/) then
+ report("aggressively locating '#{filename}' in document trees")
+ begin
+ texroot = `kpsewhich -expand-var=$SELFAUTOPARENT`.chomp
+ rescue
+ texroot = ''
+ else
+ texroot.sub!(/[\\\/][^\\\/]*?$/, '')
+ end
+ if not texroot.empty? then
+ sffxlst = suffixlist.join(',')
+ begin
+ report("locating '#{filename}' in document tree '#{texroot}/doc*'")
+ if (result = Dir.glob("#{texroot}/doc*/**/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then
+ fullname = result[0]
+ end
+ rescue
+ report("locating '#{filename}.#{suffix}' in tree '#{texroot}' aborted")
+ end
+ end
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ report("aggressively locating '#{filename}' in tex trees")
+ begin
+ textrees = `kpsewhich -expand-var=$TEXMF`.chomp
+ rescue
+ textrees = ''
+ end
+ if not textrees.empty? then
+ textrees.gsub!(/[\{\}\!]/, '')
+ textrees = textrees.split(',')
+ if (suffixlist.length == 1) && (suffixlist.first =~ /(#{$documentlist})/) then
+ speedup = ['doc**','**']
+ else
+ speedup = ['**']
+ end
+ sffxlst = suffixlist.join(',')
+ speedup.each do |speed|
+ textrees.each do |tt|
+ tt.gsub!(/[\\\/]$/, '')
+ if FileTest.directory?(tt) then
+ begin
+ report("locating '#{filename}' in tree '#{tt}/#{speed}/#{filename}.{#{sffxlst}}'")
+ if (result = Dir.glob("#{tt}/#{speed}/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then
+ fullname = result[0]
+ break
+ end
+ rescue
+ report("locating '#{filename}' in tree '#{tt}' aborted")
+ next
+ end
+ end
+ end
+ break if fullname && ! fullname.empty?
+ end
+ end
+ if register(filename,fullname) then
+ return shortpathname(fullname)
+ else
+ return ''
+ end
+end
+
+def run(fullname)
+ if ! fullname || fullname.empty? then
+ report("the file '#{$filename}' is not found")
+ elsif FileTest.file?(fullname) then
+ begin
+ case fullname
+ when /\.(#{$scriptlist})$/ then
+ return runoneof($1,fullname,false)
+ when /\.(#{$documentlist})$/ then
+ return runoneof($1,fullname,true)
+ else
+ return runoneof('unknown',fullname,false)
+ end
+ rescue
+ report("starting '#{$filename}' in program space '#{$program}' fails")
+ end
+ else
+ report("the file '#{$filename}' in program space '#{$program}' is not accessible")
+ end
+ return false
+end
+
+def direct(fullname)
+ begin
+ return runcommand([fullname.sub(/^(bin|binary)\:/, ''),expanded($arguments)].join(' '))
+ rescue
+ return false
+ end
+end
+
+def edit(filename)
+ begin
+ return runcommand([$editor,expanded(filename),expanded($arguments)].join(' '))
+ rescue
+ return false
+ end
+end
+
+def make(filename,windows=false,linux=false)
+ basename = filename.dup
+ basename.sub!(/\.[^.]+?$/, '')
+ basename.sub!(/^.*[\\\/]/, '')
+ basename = $stubpath + '/' + basename unless $stubpath.empty?
+ if basename == filename then
+ report('nothing made')
+ else
+ program = nil
+ if filename =~ /[\\\/]/ && filename =~ /\.(#{$scriptlist})$/ then
+ program = $applications[$1]
+ end
+ filename = "\"#{filename}\"" if filename =~ /\s/
+ program = 'texmfstart' if $indirect || ! program || program.empty?
+ begin
+ if windows && f = open(basename+'.bat','w') then
+ f.binmode
+ f.write("@echo off\015\012")
+ f.write("#{program} #{filename} %*\015\012")
+ f.close
+ report("windows stub '#{basename}.bat' made")
+ elsif linux && f = open(basename,'w') then
+ f.binmode
+ f.write("#!/bin/sh\012")
+ f.write("#{program} #{filename} $@\012")
+ f.close
+ report("unix stub '#{basename}' made")
+ end
+ rescue
+ report("failed to make stub '#{basename}'")
+ else
+ return true
+ end
+ end
+ return false
+end
+
+def process(&block)
+
+ if $iftouched then
+ files = $directives['iftouched'].split(',')
+ oldname, newname = files[0], files[1]
+ if oldname && newname && File.needsupdate(oldname,newname) then
+ report("file #{oldname}: #{File.timestamp(oldname)}")
+ report("file #{newname}: #{File.timestamp(newname)}")
+ report("file is touched, processing started")
+ yield
+ File.syncmtimes(oldname,newname)
+ else
+ report("file #{oldname} is untouched")
+ end
+ else
+ yield
+ end
+
+end
+
+def checktree(tree)
+ unless tree.empty? then
+ begin
+ setuptex = File.join(tree,'setuptex.tmf')
+ if FileTest.file?(setuptex) then
+ report('')
+ report("tex tree : #{setuptex}")
+ ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/'
+ ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME']
+ case RUBY_PLATFORM
+ when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin'
+ when /(linux)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux'
+ when /(darwin|rhapsody|nextstep)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx'
+ # when /(netbsd|unix)/i then # todo
+ else # todo
+ end
+ ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}"
+ report('')
+ report("preset : TEXPATH => #{ENV['TEXPATH']}")
+ report("preset : TEXOS => #{ENV['TEXOS']}")
+ report("preset : TEXMFOS => #{ENV['TEXMFOS']}")
+ report("preset : TMP => #{ENV['TMP']}")
+ report('')
+ IO.readlines(File.join(tree,'setuptex.tmf')).each do |line|
+ case line
+ when /^[\#\%]/ then
+ # comment
+ when /^(.*?)\s+\=\s+(.*)\s*$/ then
+ k, v = $1, $2
+ ENV[k] = v.gsub(/\%(.*?)\%/) do
+ ENV[$1] || ''
+ end
+ report("user set : #{k} => #{ENV[k]}")
+ end
+ end
+ else
+ report("no setup file '#{setuptex}'")
+ end
+ rescue
+ end
+ end
+end
+
+def execute(arguments)
+
+ arguments = arguments.split(/\s+/) if arguments.class == String
+
+ $directives = hashed(arguments)
+
+ $help = $directives['help'] || false
+ $batch = $directives['batch'] || false
+ $filename = $directives['file'] || ''
+ $program = $directives['program'] || 'context'
+ $direct = $directives['direct'] || false
+ $edit = $directives['edit'] || false
+ $page = $directives['page'] || 0
+ $browser = $directives['browser'] || false
+ $report = $directives['report'] || false
+ $verbose = $directives['verbose'] || (ENV['_CTX_VERBOSE_'] =~ /(y|yes|t|true|on)/io) || false
+ $arguments = $directives['arguments'] || ''
+ $execute = $directives['execute'] || $directives['exec'] || false
+ $locate = $directives['locate'] || false
+
+ $path = $directives['path'] || ''
+ $tree = $directives['tree'] || ''
+
+
+ $make = $directives['make'] || false
+ $unix = $directives['unix'] || false
+ $windows = $directives['windows'] || false
+ $stubpath = $directives['stubpath'] || ''
+ $indirect = $directives['indirect'] || false
+
+ $before = $directives['before'] || ''
+ $after = $directives['after'] || ''
+
+ $iftouched = $directives['iftouched'] || false
+
+ $openoffice = $directives['oo'] || false
+
+ $crossover = false if $directives['clear']
+
+ ENV['_CTX_VERBOSE_'] = 'yes' if $verbose
+
+ if $openoffice then
+ if ENV['OOPATH'] then
+ if FileTest.directory?(ENV['OOPATH']) then
+ report("using open office python")
+ if $mswindows then
+ $applications['python'] = $applications['py'] = "\"#{File.join(ENV['OOPATH'],'program','python.bat')}\""
+ else
+ $applications['python'] = $applications['py'] = File.join(ENV['OOPATH'],'python')
+ end
+ report("python path #{$applications['python']}")
+ else
+ report("environment variable 'OOPATH' does not exist")
+ end
+ else
+ report("environment variable 'OOPATH' is not set")
+ end
+ end
+
+ if $help || ! $filename || $filename.empty? then
+ usage
+ checktree($tree)
+ elsif $batch && $filename && ! $filename.empty? then
+ # todo, take commands from file and avoid multiple starts and checks
+ else
+ report("texmfstart version #{$version}")
+ checktree($tree)
+ if $make then
+ if $windows then
+ make($filename,true,false)
+ elsif $unix then
+ make($filename,false,true)
+ else
+ make($filename,$mswindows,!$mswindows)
+ end
+ elsif $browser && $filename =~ /^http\:\/\// then
+ launch($filename)
+ else
+ begin
+ process do
+ if $direct || $filename =~ /^bin\:/ then
+ direct($filename)
+ elsif $edit && ! $editor.empty? then
+ edit($filename)
+ else # script: or no prefix
+ command = find(shortpathname($filename),$program)
+ register("THREAD",File.dirname(File.expand_path(command)))
+ run(command)
+ end
+ end
+ rescue
+ report('fatal error in starting process')
+ end
+ end
+ end
+
+end
+
+execute(ARGV)
diff --git a/Master/texmf-dist/scripts/context/ruby/texsync.rb b/Master/texmf-dist/scripts/context/ruby/texsync.rb
new file mode 100644
index 00000000000..22b7d46c0fe
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/texsync.rb
@@ -0,0 +1,207 @@
+#!/usr/bin/env ruby
+
+# program : texsync
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2003-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# For the moment this script only handles the 'minimal' context
+# distribution. In due time I will add a few more options, like
+# synchronization of the iso image.
+
+banner = ['TeXSync', 'version 1.1.1', '2002/2004', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+# require 'base/tool'
+
+require 'rbconfig'
+
+class Commands
+
+ include CommandBase
+
+ @@formats = ['en','nl','de','cz','it','ro']
+ @@always = ['metafun','mptopdf','en','nl']
+ @@rsync = 'rsync -r -z -c --progress --stats "--exclude=*.fmt" "--exclude=*.efmt" "--exclude=*.mem"'
+
+ @@kpsewhich = Hash.new
+
+ @@kpsewhich['minimal'] = 'SELFAUTOPARENT'
+ @@kpsewhich['context'] = 'TEXMFLOCAL'
+ @@kpsewhich['documentation'] = 'TEXMFLOCAL'
+ @@kpsewhich['unknown'] = 'SELFAUTOPARENT'
+
+ def update
+
+ report
+
+ return unless destination = getdestination
+
+ texpaths = gettexpaths
+ address = option('address')
+ user = option('user')
+ tree = option('tree')
+ force = option('force')
+
+ ok = true
+ begin
+ report("synchronizing '#{tree}' from '#{address}' to '#{destination}'")
+ report
+ if texpaths then
+ texpaths.each do |path|
+ report("synchronizing path '#{path}' of '#{tree}' from '#{address}' to '#{destination}'")
+ command = "#{rsync} #{user}@#{address}::#{tree}/#{path} #{destination}/{path}"
+ ok = ok && system(command) if force
+ end
+ else
+ command = "#{@@rsync} #{user}@#{address}::#{tree} #{destination}"
+ ok = system(command) if force
+ end
+ rescue
+ report("error in running rsync")
+ ok = false
+ ensure
+ if force then
+ if ok then
+ if option('make') then
+ report("generating tex and metapost formats")
+ report
+ @@formats.delete_if do |f|
+ begin
+ `kpsewhich cont-#{f}`.chomp.empty?
+ rescue
+ end
+ end
+ str = [@@formats,@@always].flatten.uniq.join(' ')
+ begin
+ system("texexec --make --alone #{str}")
+ rescue
+ report("unable to generate formats '#{str}'")
+ else
+ report
+ end
+ else
+ report("regenerate the formats files if needed")
+ end
+ else
+ report("error in synchronizing '#{tree}'")
+ end
+ else
+ report("provide --force to execute '#{command}'") unless force
+ end
+ end
+
+ end
+
+ def list
+
+ report
+
+ address = option('address')
+ user = option('user')
+ result = nil
+
+ begin
+ report("fetching list of trees from '#{address}'")
+ command = "#{@@rsync} #{user}@#{address}::"
+ if option('force') then
+ result = `#{command}`.chomp
+ else
+ report("provide --force to execute '#{command}'")
+ end
+ rescue
+ result = nil
+ else
+ if result then
+ report("available trees:")
+ report
+ reportlines(result)
+ end
+ ensure
+ report("unable to fetch list") unless result
+ end
+
+ end
+
+ private
+
+ def gettexpaths
+ if option('full') then
+ texpaths = ['texmf','texmf-local','texmf-fonts','texmf-mswin','texmf-linux','texmf-macos']
+ elsif option('terse') then
+ texpaths = ['texmf','texmf-local','texmf-fonts']
+ case Config::CONFIG['host_os'] # or: Tool.ruby_platform
+ when /mswin/ then texpaths.push('texmf-mswin')
+ when /linux/ then texpaths.push('texmf-linux')
+ when /darwin/ then texpaths.push('texmf-macosx')
+ end
+ else
+ texpaths = nil
+ end
+ texpaths
+ end
+
+ def getdestination
+ if (destination = option('destination')) && ! destination.empty? then
+ begin
+ if @@kpsewhich.key?(destination) then
+ destination = @@kpsewhich[option('tree')] || @@kpsewhich['unknown']
+ destination = `kpsewhich --expand-var=$#{destination}`.chomp
+ elsif ! FileTest.directory?(destination) then
+ destination = nil
+ end
+ rescue
+ report("unable to determine destination tex root")
+ else
+ if ! destination || destination.empty? then
+ report("no destination is specified")
+ elsif not FileTest.directory?(destination) then
+ report("invalid destination '#{destination}'")
+ elsif not FileTest.writable?(destination) then
+ report("destination '#{destination}' is not writable")
+ else
+ report("using destination '#{destination}'")
+ return destination
+ end
+ end
+ else
+ report("unknown destination")
+ end
+ return nil
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('update', 'update installed tree')
+commandline.registeraction('list', 'list available trees')
+
+commandline.registerflag('terse', 'download as less as possible (esp binaries)')
+commandline.registerflag('full', 'download everything (all binaries)')
+commandline.registerflag('force', 'confirm action')
+commandline.registerflag('make', 'remake formats')
+
+commandline.registervalue('address', 'www.pragma-ade.com', 'adress of repository (www.pragma-ade)')
+commandline.registervalue('user', 'guest', 'user account (guest)')
+commandline.registervalue('tree', 'tex', 'tree to synchronize (tex)')
+commandline.registervalue('destination', nil, 'destination of tree (kpsewhich)')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/Master/texmf-dist/scripts/context/ruby/textools.rb b/Master/texmf-dist/scripts/context/ruby/textools.rb
new file mode 100644
index 00000000000..78982f175a1
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/textools.rb
@@ -0,0 +1,885 @@
+#!/usr/bin/env ruby
+
+# program : textools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on tex
+# related files.
+
+banner = ['TeXTools', 'version 1.2.2', '2002/2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+
+require 'ftools'
+
+# Remark
+#
+# The fixtexmftrees feature does not realy belong in textools, but
+# since it looks like no measures will be taken to make texlive (and
+# tetex) downward compatible with respect to fonts installed by
+# users, we provide this fixer. This option also moves script files
+# to their new location (only for context) in the TDS. Beware: when
+# locating scripts, the --format switch in kpsewhich should now use
+# 'texmfscripts' instead of 'other text files' (texmfstart is already
+# aware of this). Files will only be moved when --force is given. Let
+# me know if more fixes need to be made.
+
+class Commands
+
+ include CommandBase
+
+ def hidemapnames
+ report('hiding FontNames in map files')
+ xidemapnames(true)
+ end
+
+ def videmapnames
+ report('unhiding FontNames in map files')
+ xidemapnames(false)
+ end
+
+ def removemapnames
+
+ report('removing FontNames from map files')
+
+ if files = findfiles('map') then
+ report
+ files.sort.each do |fn|
+ gn = fn # + '.nonames'
+ hn = fn + '.original'
+ begin
+ if FileTest.file?(fn) && ! FileTest.file?(hn) then
+ if File.rename(fn,hn) then
+ if (fh = File.open(hn,'r')) && (gh = File.open(gn,'w')) then
+ report("processing #{fn}")
+ while str = fh.gets do
+ str.sub!(/^([^\%]+?)(\s+)([^\"\<\s]*?)(\s)/) do
+ $1 + $2 + " "*$3.length + $4
+ end
+ gh.puts(str)
+ end
+ fh.close
+ gh.close
+ else
+ report("no permissions to handle #{fn}")
+ end
+ else
+ report("unable to rename #{fn} to #{hn}")
+ end
+ else
+ report("not processing #{fn} due to presence of #{hn}")
+ end
+ rescue
+ report("error in handling #{fn}")
+ end
+ end
+ end
+
+ end
+
+ def restoremapnames
+
+ report('restoring FontNames in map files')
+
+ if files = findfiles('map') then
+ report
+ files.sort.each do |fn|
+ hn = fn + '.original'
+ begin
+ if FileTest.file?(hn) then
+ File.delete(fn) if FileTest.file?(fn)
+ report("#{fn} restored") if File.rename(hn,fn)
+ else
+ report("no original found for #{fn}")
+ end
+ rescue
+ report("error in restoring #{fn}")
+ end
+ end
+ end
+
+ end
+
+ def findfile
+
+ report('locating file in texmf tree')
+
+ # ! not in tree
+ # ? fuzzy
+ # . in tree
+ # > in tree and used
+
+ if filename = @commandline.argument('first') then
+ if filename && ! filename.empty? then
+ report
+ used = kpsefile(filename) || pathfile(filename)
+ if paths = texmfroots then
+ found, prefered = false, false
+ paths.each do |p|
+ if files = texmffiles(p,filename) then
+ found = true
+ files.each do |f|
+ # unreadable: report("#{if f == used then '>' else '.' end} #{f}")
+ if f == used then
+ prefered = true
+ report("> #{f}")
+ else
+ report(". #{f}")
+ end
+ end
+ end
+ end
+ if prefered then
+ report("! #{used}") unless found
+ else
+ report("> #{used}")
+ end
+ elsif used then
+ report("? #{used}")
+ else
+ report('no file found')
+ end
+ else
+ report('no file specified')
+ end
+ else
+ report('no file specified')
+ end
+
+ end
+
+ def unzipfiles
+
+ report('g-unzipping files')
+
+ if files = findfiles('gz') then
+ report
+ files.each do |f|
+ begin
+ system("gunzip -d #{f}")
+ rescue
+ report("unable to unzip file #{f}")
+ else
+ report("file #{f} is unzipped")
+ end
+ end
+ end
+
+ end
+
+ def fixafmfiles
+
+ report('fixing afm files')
+
+ if files = findfiles('afm') then
+ report
+ ok = false
+ files.each do |filename|
+ if filename =~ /\.afm$/io then
+ if f = File.open(filename) then
+ result = ''
+ done = false
+ while str = f.gets do
+ str.chomp!
+ str.strip!
+ if str.empty? then
+ # skip
+ elsif (str.length > 200) && (str =~ /^(comment|notice)\s(.*)\s*$/io) then
+ done = true
+ tag, words, len = $1, $2.split(' '), 0
+ result += tag
+ while words.size > 0 do
+ str = words.shift
+ len += str.length + 1
+ result += ' ' + str
+ if len > (70 - tag.length) then
+ result += "\n"
+ result += tag if words.size > 0
+ len = 0
+ end
+ end
+ result += "\n" if len>0
+ else
+ result += str + "\n"
+ end
+ end
+ f.close
+ if done then
+ ok = true
+ begin
+ if File.rename(filename,filename+'.original') then
+ if FileTest.file?(filename) then
+ report("something to fix in #{filename} but error in renaming (3)")
+ elsif f = File.open(filename,'w') then
+ f.puts(result)
+ f.close
+ report('file', filename, 'has been fixed')
+ else
+ report("something to fix in #{filename} but error in opening (4)")
+ File.rename(filename+'.original',filename) # gamble
+ end
+ else
+ report("something to fix in #{filename} but error in renaming (2)")
+ end
+ rescue
+ report("something to fix in #{filename} but error in renaming (1)")
+ end
+ else
+ report("nothing to fix in #{filename}")
+ end
+ else
+ report("error in opening #{filename}")
+ end
+ end
+ end
+ report('no files match the pattern') unless ok
+ end
+
+ end
+
+ def mactodos
+
+ report('fixing mac newlines')
+
+ if files = findfiles('tex') then
+ report
+ files.each do |filename|
+ begin
+ report("converting file #{filename}")
+ tmpfilename = filename + '.tmp'
+ if f = File.open(filename) then
+ if g = File.open(tmpfilename, 'w')
+ while str = f.gets do
+ g.puts(str.gsub(/\r/,"\n"))
+ end
+ if f.close && g.close && FileTest.file?(tmpfilename) then
+ File.delete(filename)
+ File.rename(tmpfilename,filename)
+ end
+ else
+ report("unable to open temporary file #{tmpfilename}")
+ end
+ else
+ report("unable to open #{filename}")
+ end
+ rescue
+ report("problems with fixing #{filename}")
+ end
+ end
+ end
+
+ end
+
+ def fixtexmftrees
+
+ if paths = @commandline.argument('first') then
+ paths = [paths] if ! paths.empty?
+ end
+ paths = texmfroots if paths.empty?
+
+ if paths then
+
+ moved = 0
+ force = @commandline.option('force')
+
+ report
+ report("checking TDS 2003 => TDS 2004 : map files")
+ # report
+
+ # move [map,enc] files from /texmf/[dvips,pdftex,dvipdfmx] -> /texmf/fonts/[*]
+
+ ['map','enc'].each do |suffix|
+ paths.each do |path|
+ ['dvips','pdftex','dvipdfmx'].each do |program|
+ report
+ report("checking #{suffix} files for #{program} on #{path}")
+ report
+ moved += movefiles("#{path}/#{program}","#{path}/fonts/#{suffix}/#{program}",suffix) do
+ # nothing
+ end
+ end
+ end
+ end
+
+ report
+ report("checking TDS 2003 => TDS 2004 : scripts")
+ # report
+
+ # move [rb,pl,py] files from /texmf/someplace -> /texmf/scripts/someplace
+
+ ['rb','pl','py'].each do |suffix|
+ paths.each do |path|
+ ['context'].each do |program|
+ report
+ report("checking #{suffix} files for #{program} on #{path}")
+ report
+ moved += movefiles("#{path}/#{program}","#{path}/scripts/#{program}",suffix) do |f|
+ f.gsub!(/\/(perl|ruby|python)tk\//o) do
+ "/#{$1}/"
+ end
+ end
+ end
+ end
+ end
+
+ begin
+ if moved>0 then
+ report
+ if force then
+ system('mktexlsr')
+ report
+ report("#{moved} files moved")
+ else
+ report("#{moved} files will be moved")
+ end
+ else
+ report('no files need to be moved')
+ end
+ rescue
+ report('you need to run mktexlsr')
+ end
+
+ end
+
+ end
+
+ def replacefile
+
+ report('replace file')
+
+ if newname = @commandline.argument('first') then
+ if newname && ! newname.empty? then
+ report
+ report("replacing #{newname}")
+ report
+ oldname = kpsefile(File.basename(newname))
+ force = @commandline.option('force')
+ if oldname && ! oldname.empty? then
+ oldname = File.expand_path(oldname)
+ newname = File.expand_path(newname)
+ report("old: #{oldname}")
+ report("new: #{newname}")
+ report
+ if newname == oldname then
+ report('unable to replace itself')
+ elsif force then
+ begin
+ File.copy(newname,oldname)
+ rescue
+ report('error in replacing the old file')
+ end
+ else
+ report('the old file will be replaced (use --force)')
+ end
+ else
+ report('nothing to replace')
+ end
+ else
+ report('no file specified')
+ end
+ else
+ report('no file specified')
+ end
+
+ end
+
+ private # general
+
+ def texmfroots
+ begin
+ paths = `kpsewhich -expand-path=\$TEXMF`.chomp
+ rescue
+ else
+ return paths.split(/#{File::PATH_SEPARATOR}/) if paths && ! paths.empty?
+ end
+ return nil
+ end
+
+ def texmffiles(root, filename)
+ begin
+ files = Dir.glob("#{root}/**/#{filename}")
+ rescue
+ else
+ return files if files && files.length>0
+ end
+ return nil
+ end
+
+ def pathfile(filename)
+ used = nil
+ begin
+ if ! filename || filename.empty? then
+ return nil
+ else
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
+ if FileTest.file?(File.join(path,filename)) then
+ used = File.join(path,filename)
+ break
+ end
+ end
+ end
+ rescue
+ used = nil
+ else
+ used = nil if used && used.empty?
+ end
+ return used
+ end
+
+ def kpsefile(filename)
+ used = nil
+ begin
+ if ! filename || filename.empty? then
+ return nil
+ else
+ used = `kpsewhich #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -progname=context #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -format=texmfscripts #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -progname=context -format=texmfscripts #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -format="other text files" #{filename}`.chomp
+ end
+ if used && used.empty? then
+ used = `kpsewhich -progname=context -format="other text files" #{filename}`.chomp
+ end
+ rescue
+ used = nil
+ else
+ used = nil if used && used.empty?
+ end
+ return used
+ end
+
+ def downcasefilenames
+
+ report('downcase filenames')
+
+ force = @commandline.option('force')
+
+ # if @commandline.option('recurse') then
+ # files = Dir.glob('**/*')
+ # else
+ # files = Dir.glob('*')
+ # end
+ # if files && files.length>0 then
+
+ if files = findfiles() then
+ files.each do |oldname|
+ if FileTest.file?(oldname) then
+ newname = oldname.downcase
+ if oldname != newname then
+ if force then
+ begin
+ File.rename(oldname,newname)
+ rescue
+ report("#{oldname} == #{oldname}\n")
+ else
+ report("#{oldname} => #{newname}\n")
+ end
+ else
+ report("(#{oldname} => #{newname})\n")
+ end
+ end
+ end
+ end
+ end
+ end
+
+ def stripformfeeds
+
+ report('strip formfeeds')
+
+ force = @commandline.option('force')
+
+ if files = findfiles() then
+ files.each do |filename|
+ if FileTest.file?(filename) then
+ begin
+ data = IO.readlines(filename).join('')
+ rescue
+ else
+ if data.gsub!(/\n*\f\n*/io,"\n\n") then
+ if force then
+ if f = open(filename,'w') then
+ report("#{filename} is stripped\n")
+ f.puts(data)
+ f.close
+ else
+ report("#{filename} cannot be stripped\n")
+ end
+ else
+ report("#{filename} will be stripped\n")
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ public
+
+ def showfont
+
+ file = @commandline.argument('first')
+
+ if file.empty? then
+ report('provide filename')
+ else
+ file.sub!(/\.afm$/,'')
+ begin
+ report("analyzing afm file #{file}.afm")
+ file = `kpsewhich #{file}.afm`.chomp
+ rescue
+ report('unable to run kpsewhich')
+ return
+ end
+
+ names = Array.new
+
+ if FileTest.file?(file) then
+ File.new(file).each do |line|
+ if line.match(/^C\s*([\-\d]+)\s*\;.*?\s*N\s*(.+?)\s*\;/o) then
+ names.push($2)
+ end
+ end
+ ranges = names.size
+ report("number of glyphs: #{ranges}")
+ ranges = ranges/256 + 1
+ report("number of subsets: #{ranges}")
+ file = File.basename(file).sub(/\.afm$/,'')
+ tex = File.open("textools.tex",'w')
+ map = File.open("textools.map",'w')
+ tex.puts("\\starttext\n")
+ tex.puts("\\loadmapfile[textools.map]\n")
+ for i in 1..ranges do
+ rfile = "#{file}-range-#{i}"
+ report("generating enc file #{rfile}.enc")
+ flushencoding("#{rfile}", (i-1)*256, i*256-1, names)
+ # catch console output
+ report("generating tfm file #{rfile}.tfm")
+ mapline = `afm2tfm #{file}.afm -T #{rfile}.enc #{rfile}.tfm`
+ # more robust replacement
+ mapline = "#{rfile} <#{rfile}.enc <#{file}.pfb"
+ # final entry in map file
+ mapline = "#{mapline} <#{file}.pfb"
+ map.puts("#{mapline}\n")
+ tex.puts("\\showfont[#{rfile}][unknown]\n")
+ end
+ tex.puts("\\stoptext\n")
+ report("generating map file textools.map")
+ report("generating tex file textools.tex")
+ map.close
+ tex.close
+ else
+ report("invalid file #{file}")
+ end
+ end
+
+ end
+
+ private
+
+ def flushencoding (file, from, to, names)
+ n = 0
+ out = File.open("#{file}.enc",'w')
+ out.puts("/#{file.gsub(/\-/,'')} [\n")
+ for i in from..to do
+ if names[i] then
+ n += 1
+ out.puts("/#{names[i]}\n")
+ else
+ out.puts("/.notdef\n")
+ end
+ end
+ out.puts("] def\n")
+ out.close
+ return n
+ end
+
+ private # specific
+
+ def movefiles(from_path,to_path,suffix,&block)
+ obsolete = 'obsolete'
+ force = @commandline.option('force')
+ moved = 0
+ if files = texmffiles(from_path, "*.#{suffix}") then
+ files.each do |filename|
+ newfilename = filename.sub(/^#{from_path}/, to_path)
+ yield(newfilename) if block
+ if FileTest.file?(newfilename) then
+ begin
+ File.rename(filename,filename+'.obsolete') if force
+ rescue
+ report("#{filename} cannot be made obsolete") if force
+ else
+ if force then
+ report("#{filename} is made obsolete")
+ else
+ report("#{filename} will become obsolete")
+ end
+ end
+ else
+ begin
+ File.makedirs(File.dirname(newfilename)) if force
+ rescue
+ end
+ begin
+ File.copy(filename,newfilename) if force
+ rescue
+ report("#{filename} cannot be copied to #{newfilename}")
+ else
+ begin
+ File.delete(filename) if force
+ rescue
+ report("#{filename} cannot be deleted") if force
+ else
+ if force then
+ report("#{filename} is moved to #{newfilename}")
+ moved += 1
+ else
+ report("#{filename} will be moved to #{newfilename}")
+ end
+ end
+ end
+ end
+ end
+ else
+ report('no matches found')
+ end
+ return moved
+ end
+
+ def xidemapnames(hide)
+
+ filter = /^([^\%]+?)(\s+)([^\"\<\s]*?)(\s)/
+ banner = '% textools:nn '
+
+ if files = findfiles('map') then
+ report
+ files.sort.each do |fn|
+ if fn.has_suffix?('map') then
+ begin
+ lines = IO.read(fn)
+ report("processing #{fn}")
+ if f = File.open(fn,'w') then
+ skip = false
+ if hide then
+ lines.each do |str|
+ if skip then
+ skip = false
+ elsif str =~ /#{banner}/ then
+ skip = true
+ elsif str =~ filter then
+ f.puts(banner+str)
+ str.sub!(filter) do
+ $1 + $2 + " "*$3.length + $4
+ end
+ end
+ f.puts(str)
+ end
+ else
+ lines.each do |str|
+ if skip then
+ skip = false
+ elsif str.sub!(/#{banner}/, '') then
+ f.puts(str)
+ skip = true
+ else
+ f.puts(str)
+ end
+ end
+ end
+ f.close
+ end
+ rescue
+ report("error in handling #{fn}")
+ end
+ end
+ end
+ end
+
+ end
+
+ public
+
+ def updatetree
+
+ nocheck = @commandline.option('nocheck')
+ merge = @commandline.option('merge')
+ delete = @commandline.option('delete')
+ force = @commandline.option('force')
+ root = @commandline.argument('first').gsub(/\\/,'/')
+ path = @commandline.argument('second').gsub(/\\/,'/')
+
+ if FileTest.directory?(root) then
+ report("scanning #{root}")
+ rootfiles = Dir.glob("#{root}/**/*")
+ else
+ report("provide source root")
+ return
+ end
+ if rootfiles.size > 0 then
+ report("#{rootfiles.size} files")
+ else
+ report("no files")
+ return
+ end
+ rootfiles.collect! do |rf|
+ rf.gsub(/\\/o, '/').sub(/#{root}\//o, '')
+ end
+ rootfiles = rootfiles.delete_if do |rf|
+ FileTest.directory?(File.join(root,rf))
+ end
+
+ if FileTest.directory?(path) then
+ report("scanning #{path}")
+ pathfiles = Dir.glob("#{path}/**/*")
+ else
+ report("provide destination root")
+ return
+ end
+ if pathfiles.size > 0 then
+ report("#{pathfiles.size} files")
+ else
+ report("no files")
+ return
+ end
+ pathfiles.collect! do |pf|
+ pf.gsub(/\\/o, '/').sub(/#{path}\//o, '')
+ end
+ pathfiles = pathfiles.delete_if do |pf|
+ FileTest.directory?(File.join(path,pf))
+ end
+
+ root = File.expand_path(root)
+ path = File.expand_path(path)
+
+ donepaths = Hash.new
+ copiedfiles = Hash.new
+
+ # update existing files, assume similar paths
+
+ report("")
+ pathfiles.each do |f| # destination
+ p = File.join(path,f)
+ if rootfiles.include?(f) then
+ r = File.join(root,f)
+ if p != r then
+ if nocheck or File.mtime(p) < File.mtime(r) then
+ copiedfiles[File.expand_path(p)] = true
+ report("updating '#{r}' to '#{p}'")
+ begin
+ begin File.makedirs(File.dirname(p)) if force ; rescue ; end
+ File.copy(r,p) if force
+ rescue
+ report("updating failed")
+ end
+ else
+ report("not updating '#{r}'")
+ end
+ end
+ end
+ end
+
+ # merging non existing files
+
+ report("")
+ rootfiles.each do |f|
+ donepaths[File.dirname(f)] = true
+ r = File.join(root,f)
+ if not pathfiles.include?(f) then
+ p = File.join(path,f)
+ if p != r then
+ if merge then
+ copiedfiles[File.expand_path(p)] = true
+ report("merging '#{r}' to '#{p}'")
+ begin
+ begin File.makedirs(File.dirname(p)) if force ; rescue ; end
+ File.copy(r,p) if force
+ rescue
+ report("merging failed")
+ end
+ else
+ report("not merging '#{r}'")
+ end
+ end
+ end
+ end
+
+ # deleting obsolete files
+
+ report("")
+ donepaths.keys.sort.each do |d|
+ pathfiles = Dir.glob("#{path}/#{d}/**/*")
+ pathfiles.each do |p|
+ r = File.join(root,d,File.basename(p))
+ if FileTest.file?(p) and not FileTest.file?(r) and not copiedfiles.key?(File.expand_path(p)) then
+ if delete then
+ report("deleting '#{p}'")
+ begin
+ File.delete(p) if force
+ rescue
+ report("deleting failed")
+ end
+ else
+ report("not deleting '#{p}'")
+ end
+ end
+ end
+ end
+
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('removemapnames' , '[pattern] [--recurse]')
+commandline.registeraction('restoremapnames' , '[pattern] [--recurse]')
+commandline.registeraction('hidemapnames' , '[pattern] [--recurse]')
+commandline.registeraction('videmapnames' , '[pattern] [--recurse]')
+commandline.registeraction('findfile' , 'filename [--recurse]')
+commandline.registeraction('unzipfiles' , '[pattern] [--recurse]')
+commandline.registeraction('fixafmfiles' , '[pattern] [--recurse]')
+commandline.registeraction('mactodos' , '[pattern] [--recurse]')
+commandline.registeraction('fixtexmftrees' , '[texmfroot] [--force]')
+commandline.registeraction('replacefile' , 'filename [--force]')
+commandline.registeraction('updatetree' , 'fromroot toroot [--force --nocheck --merge --delete]')
+commandline.registeraction('downcasefilenames', '[--recurse] [--force]') # not yet documented
+commandline.registeraction('stripformfeeds' , '[--recurse] [--force]') # not yet documented
+commandline.registeraction('showfont' , 'filename')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('recurse')
+commandline.registerflag('force')
+commandline.registerflag('merge')
+commandline.registerflag('delete')
+commandline.registerflag('nocheck')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/Master/texmf-dist/scripts/context/ruby/tmftools.rb b/Master/texmf-dist/scripts/context/ruby/tmftools.rb
new file mode 100644
index 00000000000..d125c5cae69
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/tmftools.rb
@@ -0,0 +1,133 @@
+#!/usr/bin/env ruby
+
+# program : tmftools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2005
+# author : Hans Hagen
+#
+# project : ConTeXt
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# The script based alternative is not slower than the kpse one.
+# Loading is a bit faster when the log file is used.
+
+# todo: create database
+
+# tmftools [some of the kpsewhich switches]
+
+# tmftools --analyze
+# tmftools --analyze > kpsewhat.log
+# tmftools --analyze --strict > kpsewhat.log
+# tmftools --analyze --delete --force "texmf-local/fonts/.*/somename"
+
+# the real thing
+
+banner = ['TMFTools', 'version 1.0.0 (experimental, no help yet)', '2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+require 'base/kpsefast'
+
+class Commands
+
+ include CommandBase
+
+ def init_kpse
+ k = KPSEFAST.new
+ k.rootpath = @commandline.option('rootpath')
+ k.treepath = @commandline.option('treepath')
+ k.progname = @commandline.option('progname')
+ k.engine = @commandline.option('engine')
+ k.format = @commandline.option('format')
+ k.diskcache = @commandline.option('diskcache')
+ k.renewcache = @commandline.option('renewcache')
+ k.load_cnf
+ k.expand_variables
+ k.load_lsr
+ return k
+ end
+
+ def main
+ if option = @commandline.option('expand-braces') and not option.empty? then
+ puts init_kpse.expand_braces(option)
+ elsif option = @commandline.option('expand-path') and not option.empty? then
+ puts init_kpse.expand_path(option)
+ elsif option = @commandline.option('expand-var') and not option.empty? then
+ if option == '*' then
+ init_kpse.list_expansions()
+ else
+ puts init_kpse.expand_var(option)
+ end
+ elsif option = @commandline.option('show-path') and not option.empty? then
+ puts init_kpse.show_path(option)
+ elsif option = @commandline.option('var-value') and not option.empty? then
+ if option == '*' then
+ init_kpse.list_variables()
+ else
+ puts init_kpse.expand_var(option)
+ end
+ elsif @commandline.arguments.size > 0 then
+ kpse = init_kpse
+ @commandline.arguments.each do |option|
+ puts kpse.find_file(option)
+ end
+ else
+ help
+ end
+ end
+
+ def analyze
+ pattern = @commandline.argument('first')
+ strict = @commandline.option('strict')
+ sort = @commandline.option('sort')
+ delete = @commandline.option('delete') and @commandline.option('force')
+ init_kpse.analyze_files(pattern, strict, sort, delete)
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+# kpsewhich compatible options
+
+commandline.registervalue('expand-braces','')
+commandline.registervalue('expand-path','')
+commandline.registervalue('expand-var','')
+commandline.registervalue('show-path','')
+commandline.registervalue('var-value','')
+
+commandline.registervalue('engine','')
+commandline.registervalue('progname','')
+commandline.registervalue('format','')
+
+# additional goodies
+
+commandline.registervalue('rootpath','')
+commandline.registervalue('treepath','')
+commandline.registervalue('sort','')
+
+commandline.registerflag('diskcache')
+commandline.registerflag('renewcache')
+commandline.registerflag('strict')
+commandline.registerflag('delete')
+commandline.registerflag('force')
+
+commandline.registeraction('analyze', "[--strict --sort --rootpath --treepath]\n[--delete [--force]] [pattern]")
+
+# general purpose options
+
+commandline.registerflag('verbose')
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/xmltools.rb b/Master/texmf-dist/scripts/context/ruby/xmltools.rb
new file mode 100644
index 00000000000..a57f7445020
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/xmltools.rb
@@ -0,0 +1,352 @@
+#!/usr/bin/env ruby
+
+# program : xmltools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on tex
+# related files.
+
+banner = ['XMLTools', 'version 1.1.1', '2002/2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+
+class String
+
+ def astring(n=10)
+ gsub(/(\d+)/o) do $1.to_s.rjust(n) end.gsub(/ /o, '0')
+ end
+
+ def xstring
+ if self =~ /\'/o then
+ "\"#{self.gsub(/\"/, '&quot;')}\""
+ else
+ "\'#{self}\'"
+ end
+ end
+
+end
+
+class Array
+
+ def asort(n=10)
+ sort {|x,y| x.astring(n) <=> y.astring(n)}
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def dir
+
+ @xmlns = "xmlns='http://www.pragma-ade.com/rlg/xmldir.rng'"
+
+ pattern = @commandline.option('pattern')
+ recurse = @commandline.option('recurse')
+ stripname = @commandline.option('stripname')
+ longname = @commandline.option('longname')
+ url = @commandline.option('url')
+ outputfile = @commandline.option('output')
+ root = @commandline.option('root')
+
+ def generate(output,files,url,root,longname)
+
+ class << output
+ def xputs(str,n=0)
+ puts("#{' '*n}#{str}")
+ end
+ end
+
+ dirname = ''
+ output.xputs("<?xml version='1.0'?>\n\n")
+ if ! root || root.empty? then
+ rootatt = @xmlns
+ else
+ rootatt = " #{@xmlns} root='#{root}'"
+ end
+ rootatt += " timestamp='#{Time.now}'"
+ if url.empty? then
+ output.xputs("<files #{rootatt}>\n")
+ else
+ output.xputs("<files url='#{url}'#{rootatt}>\n")
+ end
+ files.each do |f|
+ bn, dn = File.basename(f), File.dirname(f)
+ if dirname != dn then
+ output.xputs("</directory>\n", 2) if dirname != ''
+ output.xputs("<directory name='#{dn}'>\n", 2)
+ dirname = dn
+ end
+ if longname && dn != '.' then
+ output.xputs("<file name='#{dn}/#{bn}'>\n", 4)
+ else
+ output.xputs("<file name='#{bn}'>\n", 4)
+ end
+ output.xputs("<base>#{bn.sub(/\..*$/,'')}</base>\n", 6)
+ if File.stat(f).file? then
+ bt = bn.sub(/^.*\./,'')
+ if bt != bn then
+ output.xputs("<type>#{bt}</type>\n", 6)
+ end
+ output.xputs("<size>#{File.stat(f).size}</size>\n", 6)
+ end
+ output.xputs("<date>#{File.stat(f).mtime.strftime("%Y-%m-%d %H:%M")}</date>\n", 6)
+ output.xputs("</file>\n", 4)
+ end
+ output.xputs("</directory>\n", 2) if dirname != ''
+ output.xputs("</files>\n")
+
+ end
+
+ if pattern.empty? then
+ report('provide --pattern=')
+ return
+ end
+
+ unless outputfile.empty? then
+ begin
+ output = File.open(outputfile,'w')
+ rescue
+ report("unable to open #{outputfile}")
+ return
+ end
+ else
+ report('provide --output')
+ return
+ end
+
+ if stripname && pattern.class == String && ! pattern.empty? then
+ pattern = File.dirname(pattern)
+ end
+
+ pattern = '*' if pattern.empty?
+
+ unless root.empty? then
+ unless FileTest.directory?(root) then
+ report("unknown root #{root}")
+ return
+ end
+ begin
+ Dir.chdir(root)
+ rescue
+ report("unable to change to root #{root}")
+ return
+ end
+ end
+
+ generate(output, globbed(pattern, recurse), url, root, longname)
+
+ output.close if output
+
+ end
+
+ alias ls :dir
+
+ def mmlpages
+
+ file = @commandline.argument('first')
+ eps = @commandline.option('eps')
+ jpg = @commandline.option('jpg')
+ png = @commandline.option('png')
+ style = @commandline.option('style')
+ modes = @commandline.option('modes')
+
+ file = file.sub(/\.xml/io, '')
+ long = "#{file}-mmlpages"
+ if FileTest.file?(file+'.xml') then
+ style = "--arg=\"style=#{style}\"" unless style.empty?
+ modes = "--mode=#{modes}" unless modes.empty?
+ if system("texmfstart texexec.pl --batch --pdf --once --result=#{long} --use=mmlpag #{style} #{modes} #{file}.xml") then
+ if eps then
+ if f = open("#{file}-mmlpages.txt") then
+ while line = f.gets do
+ data = Hash.new
+ if fields = line.split then
+ fields.each do |fld|
+ key, value = fld.split('=')
+ data[key] = value if key && value
+ end
+ if data.key?('p') then
+ page = data['p']
+ name = "#{long}-#{page.to_i-1}"
+ if eps then
+ report("generating eps file #{name}")
+ if system("pdftops -eps -f #{page} -l #{page} #{long}.pdf #{name}.eps") then
+ if data.key?('d') then
+ if epsfile = IO.read("#{name}.eps") then
+ epsfile.sub!(/^(\%\%BoundingBox:.*?$)/i) do
+ newline = $1 + "\n%%Baseline: #{data['d']}\n"
+ if data.key?('w') && data.key?('h') then
+ newline += "%%PositionWidth: #{data['w']}\n"
+ newline += "%%PositionHeight: #{data['h']}\n"
+ newline += "%%PositionDepth: #{data['d']}"
+ end
+ newline
+ end
+ if g = File.open("#{name}.eps",'wb') then
+ g.write(epsfile)
+ g.close
+ end
+ end
+ end
+ else
+ report("error in generating eps from #{name}")
+ end
+ end
+ end
+ end
+ end
+ f.close
+ else
+ report("missing data log file #{file}")
+ end
+ end
+ if png then
+ report("generating png file for #{long}")
+ system("imagemagick #{long}.pdf #{long}-%d.png")
+ end
+ if jpg then
+ report("generating jpg files for #{long}")
+ system("imagemagick #{long}.pdf #{long}-%d.jpg")
+ end
+ else
+ report("error in processing file #{file}")
+ end
+ system("texmfstart texutil --purge")
+ else
+ report("error in processing file #{file}")
+ end
+
+ end
+
+ def analyze
+
+ file = @commandline.argument('first')
+ result = @commandline.option('output')
+
+ if FileTest.file?(file) then
+ if data = IO.read(file) then
+ report("xml file #{file} loaded")
+ elements = Hash.new
+ attributes = Hash.new
+ entities = Hash.new
+ data.scan(/<([^>\s\/\!\?]+)([^>]*?)>/o) do
+ element, attributelist = $1, $2
+ if elements.key?(element) then
+ elements[element] += 1
+ else
+ elements[element] = 1
+ end
+ attributelist.scan(/\s*([^\=]+)\=([\"\'])(.*?)(\2)/) do
+ key, value = $1, $3
+ attributes[element] = Hash.new unless attributes.key?(element)
+ attributes[element][key] = Hash.new unless attributes[element].key?(key)
+ if attributes[element][key].key?(value) then
+ attributes[element][key][value] += 1
+ else
+ attributes[element][key][value] = 1
+ end
+ end
+ end
+ data.scan(/\&([^\;]+)\;/o) do
+ entity = $1
+ if entities.key?(entity) then
+ entities[entity] += 1
+ else
+ entities[entity] = 1
+ end
+ end
+ result = file.gsub(/\..*?$/, '') + '.xlg' if result.empty?
+ if f = File.open(result,'w') then
+ report("saving report in #{result}")
+ f.puts "<?xml version='1.0'?>\n"
+ f.puts "<document>\n"
+ if entities.length>0 then
+ f.puts " <entities>\n"
+ entities.keys.asort.each do |entity|
+ f.puts " <entity name=#{entity.xstring} n=#{entities[entity].to_s.xstring}/>\n"
+ end
+ f.puts " </entities>\n"
+ end
+ if elements.length>0 then
+ f.puts " <elements>\n"
+ elements.keys.sort.each do |element|
+ if attributes.key?(element) then
+ f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}>\n"
+ if attributes.key?(element) then
+ attributes[element].keys.asort.each do |attribute|
+ f.puts " <attribute name=#{attribute.xstring}>\n"
+ attributes[element][attribute].keys.asort.each do |value|
+ f.puts " <instance value=#{value.xstring} n=#{attributes[element][attribute][value].to_s.xstring}/>\n"
+ end
+ f.puts " </attribute>\n"
+ end
+ end
+ f.puts " </element>\n"
+ else
+ f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}/>\n"
+ end
+ end
+ f.puts " </elements>\n"
+ end
+ f.puts "</document>\n"
+ else
+ report("unable to open file '#{result}'")
+ end
+ else
+ report("unable to load file '#{file}'")
+ end
+ else
+ report("unknown file '#{file}'")
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('dir', 'generate directory listing')
+commandline.registeraction('mmlpages','generate graphic from mathml')
+commandline.registeraction('analyze', 'report entities and elements')
+
+# commandline.registeraction('dir', 'filename --pattern= --output= [--recurse --stripname --longname --url --root]')
+# commandline.registeraction('mmlpages','filename [--eps --jpg --png --style= --mode=]')
+
+commandline.registeraction('ls')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('stripname')
+commandline.registerflag('longname')
+commandline.registerflag('recurse')
+
+commandline.registervalue('pattern')
+commandline.registervalue('url')
+commandline.registervalue('output')
+commandline.registervalue('root')
+
+commandline.registerflag('eps')
+commandline.registerflag('png')
+commandline.registerflag('jpg')
+commandline.registervalue('style')
+commandline.registervalue('modes')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')