diff options
Diffstat (limited to 'scripts/context/ruby/base/tex.rb')
-rw-r--r-- | scripts/context/ruby/base/tex.rb | 2299 |
1 files changed, 2299 insertions, 0 deletions
diff --git a/scripts/context/ruby/base/tex.rb b/scripts/context/ruby/base/tex.rb new file mode 100644 index 000000000..84025693b --- /dev/null +++ b/scripts/context/ruby/base/tex.rb @@ -0,0 +1,2299 @@ +# module : base/tex +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# todo: +# +# - write systemcall for mpost to file so that it can be run faster +# - use -8bit and -progname +# + +# report ? + +require 'fileutils' + +require 'base/variables' +require 'base/kpse' +require 'base/system' +require 'base/state' +require 'base/pdf' +require 'base/file' +require 'base/ctx' +require 'base/mp' + +class String + + def standard? + begin + self == 'standard' + rescue + false + end + end + +end + +# class String + # def utf_bom? + # self.match(/^\357\273\277/o).length>0 rescue false + # end +# end + +class Array + + def standard? + begin + self.include?('standard') + rescue + false + end + end + + def join_path + self.join(File::PATH_SEPARATOR) + end + +end + +class TEX + + # The make-part of this class was made on a rainy day while listening + # to "10.000 clowns on a rainy day" by Jan Akkerman. Unfortunately the + # make method is not as swinging as this live cd. + + include Variables + + @@texengines = Hash.new + @@mpsengines = Hash.new + @@backends = Hash.new + @@mappaths = Hash.new + @@runoptions = Hash.new + @@tcxflag = Hash.new + @@draftoptions = Hash.new + @@synctexcoptions = Hash.new + @@texformats = Hash.new + @@mpsformats = Hash.new + @@prognames = Hash.new + @@texmakestr = Hash.new + @@texprocstr = Hash.new + @@mpsmakestr = Hash.new + @@mpsprocstr = Hash.new + @@texmethods = Hash.new + @@mpsmethods = Hash.new + @@pdftex = 'pdftex' # new default, pdfetex is gone + @@luafiles = "luafiles.tmp" + @@luatarget = "lua/context" + + @@platformslash = if System.unix? then "\\\\" else "\\" end + + ['tex','etex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdftex' end + ['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end + ['xetex'] .each do |e| @@texengines[e] = 'xetex' end + ['petex'] .each do |e| @@texengines[e] = 'petex' end + ['luatex'] .each do |e| @@texengines[e] = 'luatex' end + + ['metapost','mpost', 'standard'] .each do |e| @@mpsengines[e] = 'mpost' end + + ['pdfetex','pdftex','pdf','pdftex','standard'] .each do |b| @@backends[b] = 'pdftex' end + ['dvipdfmx','dvipdfm','dpx','dpm'] .each do |b| @@backends[b] = 'dvipdfmx' end + ['xetex','xtx'] .each do |b| @@backends[b] = 'xetex' end + ['petex'] .each do |b| @@backends[b] = 'dvipdfmx' end + ['aleph'] .each do |b| @@backends[b] = 'dvipdfmx' end + ['dvips','ps','dvi'] .each do |b| @@backends[b] = 'dvips' end + ['dvipsone'] .each do |b| @@backends[b] = 'dvipsone' end + ['acrobat','adobe','distiller'] .each do |b| @@backends[b] = 'acrobat' end + ['xdv','xdv2pdf'] .each do |b| @@backends[b] = 'xdv2pdf' end + + ['tex','standard'] .each do |b| @@mappaths[b] = 'dvips' end + ['pdftex','pdfetex'] .each do |b| @@mappaths[b] = 'pdftex' end + ['aleph','omega','xetex','petex'] .each do |b| @@mappaths[b] = 'dvipdfmx' end + ['dvipdfm', 'dvipdfmx', 'xdvipdfmx'] .each do |b| @@mappaths[b] = 'dvipdfmx' end + ['xdv','xdv2pdf'] .each do |b| @@mappaths[b] = 'dvips' end + + # todo norwegian (no) + + ['plain'] .each do |f| @@texformats[f] = 'plain' end + ['cont-en','en','english','context','standard'].each do |f| @@texformats[f] = 'cont-en' end + ['cont-nl','nl','dutch'] .each do |f| @@texformats[f] = 'cont-nl' end + ['cont-de','de','german'] .each do |f| @@texformats[f] = 'cont-de' end + ['cont-it','it','italian'] .each do |f| @@texformats[f] = 'cont-it' end + ['cont-fr','fr','french'] .each do |f| @@texformats[f] = 'cont-fr' end + ['cont-cs','cs','cont-cz','cz','czech'] .each do |f| @@texformats[f] = 'cont-cs' end + ['cont-ro','ro','romanian'] .each do |f| @@texformats[f] = 'cont-ro' end + ['cont-gb','gb','cont-uk','uk','british'] .each do |f| @@texformats[f] = 'cont-gb' end + ['cont-pe','pe','persian'] .each do |f| @@texformats[f] = 'cont-pe' end + ['cont-xp','xp','experimental'] .each do |f| @@texformats[f] = 'cont-xp' end + ['mptopdf'] .each do |f| @@texformats[f] = 'mptopdf' end + + ['latex'] .each do |f| @@texformats[f] = 'latex.ltx' end + + ['plain','mpost'] .each do |f| @@mpsformats[f] = 'mpost' end + ['metafun','context','standard'] .each do |f| @@mpsformats[f] = 'metafun' end + + ['pdftex','pdfetex','aleph','omega','petex', + 'xetex','luatex'] .each do |p| @@prognames[p] = 'context' end + ['mpost'] .each do |p| @@prognames[p] = 'metafun' end + ['latex','pdflatex'] .each do |p| @@prognames[p] = 'latex' end + + ['plain','default','standard','mptopdf'] .each do |f| @@texmethods[f] = 'plain' end + ['cont-en','cont-nl','cont-de','cont-it', + 'cont-fr','cont-cs','cont-ro','cont-gb', + 'cont-pe','cont-xp'] .each do |f| @@texmethods[f] = 'context' end + ['latex','pdflatex'] .each do |f| @@texmethods[f] = 'latex' end + + ['plain','default','standard'] .each do |f| @@mpsmethods[f] = 'plain' end + ['metafun'] .each do |f| @@mpsmethods[f] = 'metafun' end + + @@texmakestr['plain'] = @@platformslash + "dump" + @@mpsmakestr['plain'] = @@platformslash + "dump" + + ['cont-en','cont-nl','cont-de','cont-it', + 'cont-fr','cont-cs','cont-ro','cont-gb', + 'cont-pe','cont-xp'] .each do |f| @@texprocstr[f] = @@platformslash + "emergencyend" end + + @@runoptions['aleph'] = ['--8bit'] + @@runoptions['luatex'] = ['--file-line-error'] + @@runoptions['mpost'] = ['--8bit'] + @@runoptions['pdfetex'] = ['--8bit'] # obsolete + @@runoptions['pdftex'] = ['--8bit'] # pdftex is now pdfetex + # @@runoptions['petex'] = [] + @@runoptions['xetex'] = ['--8bit','-output-driver="xdvipdfmx -E -d 4 -V 5"'] + @@draftoptions['pdftex'] = ['--draftmode'] + @@synctexcoptions['pdftex'] = ['--synctex=1'] + @@synctexcoptions['luatex'] = ['--synctex=1'] + @@synctexcoptions['xetex'] = ['--synctex=1'] + + @@tcxflag['aleph'] = true + @@tcxflag['luatex'] = false + @@tcxflag['mpost'] = false + @@tcxflag['pdfetex'] = true + @@tcxflag['pdftex'] = true + @@tcxflag['petex'] = false + @@tcxflag['xetex'] = false + + @@mainbooleanvars = [ + 'batchmode', 'nonstopmode', 'fast', 'final', + 'paranoid', 'notparanoid', 'nobanner', 'once', 'allpatterns', 'draft', + 'nompmode', 'nomprun', 'automprun', 'combine', + 'nomapfiles', 'local', + 'arrange', 'noarrange', + 'forcexml', 'foxet', + 'alpha', 'beta', 'luatex', + 'mpyforce', 'forcempy', + 'forcetexutil', 'texutil', + 'globalfile', 'autopath', + 'purge', 'purgeall', 'keep', 'autopdf', 'xpdf', 'simplerun', 'verbose', + 'nooptionfile', 'nobackend', 'noctx', 'utfbom', + 'mkii','mkiv', + 'synctex', + ] + @@mainstringvars = [ + 'modefile', 'result', 'suffix', 'response', 'path', + 'filters', 'usemodules', 'environments', 'separation', 'setuppath', + 'arguments', 'input', 'output', 'randomseed', 'modes', 'mode', 'filename', + 'ctxfile', 'printformat', 'paperformat', 'paperoffset', + 'timeout', 'passon' + ] + @@mainstandardvars = [ + 'mainlanguage', 'bodyfont', 'language' + ] + @@mainknownvars = [ + 'engine', 'distribution', 'texformats', 'mpsformats', 'progname', 'interface', + 'runs', 'backend' + ] + + @@extrabooleanvars = [] + @@extrastringvars = [] + + def booleanvars + [@@mainbooleanvars,@@extrabooleanvars].flatten.uniq + end + def stringvars + [@@mainstringvars,@@extrastringvars].flatten.uniq + end + def standardvars + [@@mainstandardvars].flatten.uniq + end + def knownvars + [@@mainknownvars].flatten.uniq + end + def allbooleanvars + [@@mainbooleanvars,@@extrabooleanvars].flatten.uniq + end + def allstringvars + [@@mainstringvars,@@extrastringvars,@@mainstandardvars,@@mainknownvars].flatten.uniq + end + + def setextrastringvars(vars) + # @@extrastringvars << vars -- problems in 1.9 + @@extrastringvars = [@@extrastringvars,vars].flatten + end + def setextrabooleanvars(vars) + # @@extrabooleanvars << vars -- problems in 1.9 + @@extrabooleanvars = [@@extrabooleanvars,vars].flatten + end + + # def jobvariables(names=nil) + # if [names ||[]].flatten.size == 0 then + # names = [allbooleanvars,allstringvars].flatten + # end + # data = Hash.new + # names.each do |name| + # if allbooleanvars.include?(name) then + # data[name] = if getvariable(name) then "yes" else "no" end + # else + # data[name] = getvariable(name) + # end + # end + # data + # end + + # def setjobvariables(names=nil) + # assignments = Array.new + # jobvariables(names).each do |k,v| + # assignments << "#{k}=\{#{v}\}" + # end + # "\setvariables[exe][#{assignments.join(", ")}]" + # end + + @@temprunfile = 'texexec' + @@temptexfile = 'texexec.tex' + + def initialize(logger=nil) + if @logger = logger then + def report(str='') + @logger.report(str) + end + else + def report(str='') + puts(str) + end + end + @cleanups = Array.new + @variables = Hash.new + @startuptime = Time.now + # options + booleanvars.each do |k| + setvariable(k,false) + end + stringvars.each do |k| + setvariable(k,'') + end + standardvars.each do |k| + setvariable(k,'standard') + end + setvariable('distribution', Kpse.distribution) + setvariable('texformats', defaulttexformats) + setvariable('mpsformats', defaultmpsformats) + setvariable('progname', 'standard') # or '' + setvariable('interface', 'standard') + setvariable('engine', 'standard') # replaced by tex/mpsengine + setvariable('backend', 'pdftex') + setvariable('runs', '8') + setvariable('randomseed', rand(1440).to_s) # we want the same seed for one run + # files + setvariable('files', []) + # defaults + setvariable('texengine', 'standard') + setvariable('mpsengine', 'standard') + setvariable('backend', 'standard') + setvariable('error', '') + end + + def error? + not getvariable('error').empty? + end + + def runtime + Time.now - @startuptime + end + + def reportruntime + report("runtime: #{runtime}") + end + + def runcommand(something) + command = [something].flatten.join(' ') + report("running: #{command}") if getvariable('verbose') + system(command) + end + + def inspect(name=nil) + if ! name || name.empty? then + name = [booleanvars,stringvars,standardvars,knownvars] + end + str = '' # allocate + [name].flatten.each do |n| + if str = getvariable(n) then + str = str.join(" ") if str.class == Array + unless (str.class == String) && str.empty? then + report("option '#{n}' is set to '#{str}'") + end + end + end + end + + def tempfilename(suffix='') + @@temprunfile + if suffix.empty? then '' else ".#{suffix}" end + end + + def cleanup + @cleanups.each do |name| + begin + File.delete(name) if FileTest.file?(name) + rescue + report("unable to delete #{name}") + end + end + end + + def cleanuptemprunfiles + begin + Dir.glob("#{@@temprunfile}*").each do |name| + if File.file?(name) && (File.splitname(name)[1] !~ /(pdf|dvi)/o) then + File.delete(name) rescue false + end + end + rescue + end + ['mpgraph.mp'].each do |file| + (File.delete(file) if (FileTest.size?(file) rescue 10) < 10) rescue false + end + end + + def backends() @@backends.keys.sort end + + def texengines() @@texengines.keys.sort end + def mpsengines() @@mpsengines.keys.sort end + def texformats() @@texformats.keys.sort end + def mpsformats() @@mpsformats.keys.sort end + + def defaulttexformats() ['en','nl','mptopdf'] end + def defaultmpsformats() ['metafun'] end + + def texmakeextras(format) @@texmakestr[format] || '' end + def mpsmakeextras(format) @@mpsmakestr[format] || '' end + def texprocextras(format) @@texprocstr[format] || '' end + def mpsprocextras(format) @@mpsprocstr[format] || '' end + + def texmethod(format) @@texmethods[str] || @@texmethods['standard'] end + def mpsmethod(format) @@mpsmethods[str] || @@mpsmethods['standard'] end + + def runoptions(engine) + options = if getvariable('draft') then @@draftoptions[engine] else [] end + options = if getvariable('synctex') then @@synctexcoptions[engine] else [] end + begin + if str = getvariable('passon') then + options = [options,str.split(' ')].flatten + end + rescue + end + if @@runoptions.key?(engine) then + [options,@@runoptions[engine]].flatten.join(' ') + else + options.join(' ') + end + end + + # private + + def cleanuplater(name) + begin + @cleanups.push(File.expand_path(name)) + rescue + @cleanups.push(name) + end + end + + def openedfile(name) + begin + f = File.open(name,'w') + rescue + report("file '#{File.expand_path(name)}' cannot be opened for writing") + return nil + else + cleanuplater(name) if f + return f + end + end + + def prefixed(format,engine) + # format + case engine + when /etex|pdftex|pdfetex|aleph|xetex|luatex/io then + "*#{format}" + else + format + end + end + + def quoted(str) + if str =~ /^[^\"].* / then "\"#{str}\"" else str end + end + + def getarrayvariable(str='') + str = getvariable(str) + if str.class == String then str.split(',') else str.flatten end + end + + def validtexformat(str) validsomething(str,@@texformats,'tex') end + def validmpsformat(str) validsomething(str,@@mpsformats,'mp' ) end + def validtexengine(str) validsomething(str,@@texengines,'pdftex') end + def validmpsengine(str) validsomething(str,@@mpsengines,'mpost' ) end + + def validtexmethod(str) [validsomething(str,@@texmethods)].flatten.first end + def validmpsmethod(str) [validsomething(str,@@mpsmethods)].flatten.first end + + def validsomething(str,something,type=nil) + if str then + list = [str].flatten.collect do |s| + if something[s] then + something[s] + elsif type && s =~ /\.#{type}$/ then + s + else + nil + end + end .compact.uniq + if list.length>0 then + if str.class == String then list.first else list end + else + false + end + else + false + end + end + + def validbackend(str) + if str && @@backends.key?(str) then + @@backends[str] + else + @@backends['standard'] + end + end + + def validprogname(str) + if str then + [str].flatten.each do |s| + s = s.sub(/\.\S*/,"") + return @@prognames[s] if @@prognames.key?(s) + end + return str[0].sub(/\.\S*/,"") + else + return nil + end + end + + # we no longer support the & syntax + + def formatflag(engine=nil,format=nil) + case getvariable('distribution') + when 'standard' then prefix = "--fmt" + when /web2c/io then prefix = web2cformatflag(engine) + when /miktex/io then prefix = "--undump" + else return "" + end + if format then + "#{prefix}=#{format.sub(/\.\S+$/,"")}" + else + prefix + end + end + + def web2cformatflag(engine=nil) + # funny that we've standardized on the fmt suffix (at the cost of + # upward compatibility problems) but stuck to the bas/mem/fmt flags + if engine then + case validmpsengine(engine) + when /mpost/ then "-mem" + when /mfont/ then "-bas" + else "-fmt" + end + else + "-fmt" + end + end + + def prognameflag(progname=nil) + case getvariable('distribution') + when 'standard' then prefix = "-progname" + when /web2c/io then prefix = "-progname" + when /miktex/io then prefix = "-alias" + else return "" + end + if progname and not progname.empty? then + "#{prefix}=#{progname}" + else + prefix + end + end + + def iniflag() # should go to kpse and kpse should become texenv + if Kpse.miktex? then + "-initialize" + else + "--ini" + end + end + def tcxflag(engine) + if @@tcxflag[engine] then + file = "natural.tcx" + if Kpse.miktex? then + "-tcx=#{file}" + else + "-translate-file=#{file}" + end + else + "" + end + end + + def filestate(file) + File.mtime(file).strftime("%d/%m/%Y %H:%M:%S") + end + + # will go to context/process context/listing etc + + def contextversion # ook elders gebruiken + filename = Kpse.found('context.tex') + version = 'unknown' + begin + if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+.*?)\}/) then + version = $1 + end + rescue + end + return version + end + + def cleanupluafiles + File.delete(@@luafiles) rescue false + end + + def compileluafiles + begin + Dir.glob("lua/context/*.luc").each do |luc| + File.delete(luc) rescue false + end + rescue + end + if data = (IO.readlines(@@luafiles) rescue nil) then + report("compiling lua files (using #{File.expand_path(@@luafiles)})") + begin + FileUtils.makedirs(@@luatarget) rescue false + data.each do |line| + luafile = line.chomp + lucfile = File.basename(luafile).gsub(/\..*?$/,'') + ".luc" + if runcommand(["luac","-s","-o",quoted(File.join(Dir.getwd,@@luatarget,lucfile)),quoted(luafile)]) then + report("#{File.basename(luafile)} converted to #{File.basename(lucfile)}") + else + report("#{File.basename(luafile)} not converted to #{File.basename(lucfile)}") + end + end + rescue + report("fatal error in compilation") + end + else + report("no lua compilations needed") + end + File.delete(@@luafiles) rescue false + end + + # we need engine methods + + def makeformats + + checktestversion + + report("using search method '#{Kpse.searchmethod}'") + if getvariable('fast') then + report('using existing database') + else + report('updating file database') + Kpse.update # obsolete here + if getvariable('luatex') then + begin + runcommand(["luatools","--generate","--verbose"]) + rescue + report("run 'luatools --generate' manualy") + exit + end + end + end + # goody + if getvariable('texformats') == 'standard' then + setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + end + # prepare + texformats = validtexformat(getarrayvariable('texformats')) + mpsformats = validmpsformat(getarrayvariable('mpsformats')) + texengine = validtexengine(getvariable('texengine')) + mpsengine = validmpsengine(getvariable('mpsengine')) + # save current path + savedpath = Dir.getwd + # generate tex formats + unless texformats || mpsformats then + report('provide valid format (name.tex, name.mp, ...) or format id (metafun, en, nl, ...)') + setvariable('error','no format specified') + end + if texformats && texengine then + report("using tex engine #{texengine}") + texformatpath = if getvariable('local') then '.' else Kpse.formatpath(texengine,true) end + # can be empty, to do + report("using tex format path #{texformatpath}") + Dir.chdir(texformatpath) rescue false + if FileTest.writable?(texformatpath) then + # from now on we no longer support this; we load + # all patterns and if someone wants another + # interface language ... cook up a fmt or usr file + # + # if texformats.length > 0 then + # makeuserfile + # makeresponsefile + # end + if texengine == 'luatex' then + cleanupluafiles + texformats.each do |texformat| + report("generating tex format #{texformat}") + flags = ['--ini','--compile'] + flags << '--verbose' if getvariable('verbose') + flags << '--mkii' if getvariable('mkii') + run_luatools("#{flags.join(" ")} #{texformat}") + end + compileluafiles + else + texformats.each do |texformat| + report("generating tex format #{texformat}") + progname = validprogname([getvariable('progname'),texformat,texengine]) + runcommand([quoted(texengine),prognameflag(progname),iniflag,tcxflag(texengine),prefixed(texformat,texengine),texmakeextras(texformat)]) + end + end + else + report("unable to make format due to lack of permissions") + texformatpath = '' + setvariable('error','no permissions to write') + end + if not mpsformats then + # we want metafun to be in sync + setvariable('mpsformats',defaultmpsformats) + mpsformats = validmpsformat(getarrayvariable('mpsformats')) + end + else + texformatpath = '' + end + # generate mps formats + if mpsformats && mpsengine then + report("using mp engine #{mpsengine}") + mpsformatpath = if getvariable('local') then '.' else Kpse.formatpath(mpsengine,false) end + report("using mps format path #{mpsformatpath}") + Dir.chdir(mpsformatpath) rescue false + if FileTest.writable?(mpsformatpath) then + mpsformats.each do |mpsformat| + report("generating mps format #{mpsformat}") + progname = validprogname([getvariable('progname'),mpsformat,mpsengine]) + # if not runcommand([quoted(mpsengine),prognameflag(progname),iniflag,tcxflag(mpsengine),runoptions(mpsengine),mpsformat,mpsmakeextras(mpsformat)]) then + if not runcommand([quoted(mpsengine),prognameflag(progname),iniflag,runoptions(mpsengine),mpsformat,mpsmakeextras(mpsformat)]) then + setvariable('error','no format made') + end + end + else + report("unable to make format due to lack of permissions") + mpsformatpath = '' + setvariable('error','file permission problem') + end + else + mpsformatpath = '' + end + # check for problems + report("") + report("tex engine path: #{texformatpath}") unless texformatpath.empty? + report("mps engine path: #{mpsformatpath}") unless mpsformatpath.empty? + report("") + [['fmt','tex'],['mem','mps']].each do |f| + [[texformatpath,'global'],[mpsformatpath,'global'],[savedpath,'current']].each do |p| + begin + Dir.chdir(p[0]) + rescue + else + Dir.glob("*.#{f[0]}").each do |file| + report("#{f[1]}: #{filestate(file)} > #{File.expand_path(file)} (#{File.size(file)})") + end + end + end + end + begin + lucdir = File.join(texformatpath,@@luatarget) + Dir.chdir(lucdir) + rescue + else + Dir.glob("*.luc").each do |file| + report("luc: #{filestate(file)} > #{File.expand_path(file)} (#{File.size(file)})") + end + end + # to be sure, go back to current path + begin + Dir.chdir(savedpath) + rescue + end + # finalize + cleanup + report("") + reportruntime + end + + def checkcontext + + # todo : report texmf.cnf en problems + + # basics + report("current distribution: #{Kpse.distribution}") + report("context source date: #{contextversion}") + formatpaths = Kpse.formatpaths + globpattern = "**/{#{formatpaths.join(',')}}/*/*.{fmt,efmt,ofmt,xfmt,mem}" + report("format path: #{formatpaths.join(' ')}") + # utilities + report('start of analysis') + results = Array.new + # ['texexec','texutil','ctxtools'].each do |program| + ['texexec'].each do |program| + result = `texmfstart #{program} --help` + result.sub!(/.*?(#{program}[^\n]+)\n.*/mi) do $1 end + results.push("#{result}") + end + # formats + cleanuptemprunfiles + if formats = Dir.glob(globpattern) then + formats.sort.each do |name| + cleanuptemprunfiles + if f = open(tempfilename('tex'),'w') then + # kind of aleph-run-out-of-par safe + f << "\\starttext\n" + f << " \\relax test \\relax\n" + f << "\\stoptext\n" + f << "\\endinput\n" + f.close + if FileTest.file?(tempfilename('tex')) then + format = File.basename(name) + engine = if name =~ /(pdftex|pdfetex|aleph|xetex|luatex)[\/\\]#{format}/ then $1 else '' end + if engine.empty? then + engineflag = "" + else + engineflag = "--engine=#{$1}" + end + case format + when /cont\-([a-z]+)/ then + interface = $1.sub(/cont\-/,'') + results.push('') + results.push("testing interface #{interface}") + flags = ['--noctx','--process','--batch','--once',"--interface=#{interface}",engineflag] + # result = Kpse.pipescript('texexec',tempfilename,flags) + result = runtexexec([tempfilename], flags, 1) + if FileTest.file?("#{@@temprunfile}.log") then + logdata = IO.read("#{@@temprunfile}.log") + if logdata =~ /^\s*This is (.*?)[\s\,]+(.*?)$/moi then + if validtexengine($1.downcase) then + results.push("#{$1} #{$2.gsub(/\(format.*$/,'')}".strip) + end + end + if logdata =~ /^\s*(ConTeXt)\s+(.*int:\s+[a-z]+.*?)\s*$/moi then + results.push("#{$1} #{$2}".gsub(/\s+/,' ').strip) + end + else + results.push("format #{format} does not work") + end + when /metafun/ then + # todo + when /mptopdf/ then + # todo + end + else + results.push("error in creating #{tempfilename('tex')}") + end + end + cleanuptemprunfiles + end + end + report('end of analysis') + report + results.each do |line| + report(line) + end + cleanuptemprunfiles + + end + + private + + def makeuserfile # not used in luatex (yet) + language = getvariable('language') + mainlanguage = getvariable('mainlanguage') + bodyfont = getvariable('bodyfont') + if f = openedfile("cont-fmt.tex") then + f << "\\unprotect\n" + case language + when 'all' then + f << "\\preloadallpatterns\n" + when '' then + f << "% no language presets\n" + when 'standard' + f << "% using defaults\n" + else + languages = language.split(',') + languages.each do |l| + f << "\\installlanguage[\\s!#{l}][\\c!state=\\v!start]\n" + end + mainlanguage = languages.first + end + unless mainlanguage == 'standard' then + f << "\\setupcurrentlanguage[\\s!#{mainlanguage}]\n"; + end + unless bodyfont == 'standard' then + # ~ will become obsolete when lmr is used + f << "\\definetypescriptsynonym[cmr][#{bodyfont}]" + # ~ is already obsolete for some years now + f << "\\definefilesynonym[font-cmr][font-#{bodyfont}]\n" + end + f << "\\protect\n" + f << "\\endinput\n" + f.close + end + end + + def makeresponsefile + interface = getvariable('interface') + if f = openedfile("mult-def.tex") then + case interface + when 'standard' then + f << "% using default response interface" + else + f << "\\def\\currentresponses\{#{interface}\}\n" + end + f << "\\endinput\n" + f.close + end + end + + private # will become baee/context + + @@preamblekeys = [ + ['tex','texengine'], + ['engine','texengine'], + ['program','texengine'], + ['translate','tcxfilter'], + ['tcx','tcxfilter'], + ['output','backend'], + ['mode','mode'], + ['ctx','ctxfile'], + ['version','contextversion'], + ['format','texformats'], + ['interface','texformats'], + ] + + @@re_utf_bom = /^\357\273\277/o + + def scantexpreamble(filename) + begin + if FileTest.file?(filename) and tex = File.open(filename,'rb') then + bomdone = false + while str = tex.gets and str.chomp! do + unless bomdone then + if str.sub!(@@re_utf_bom, '') + report("utf mode forced (bom found)") + setvariable('utfbom',true) + end + bomdone = true + end + if str =~ /^\%\s*(.*)/o then + # we only accept lines with key=value pairs + vars, ok = Hash.new, true + $1.split(/\s+/o).each do |s| + k, v = s.split('=') + if k && v then + vars[k] = v + else + ok = false + break + end + end + if ok then + # we have a valid line + + @@preamblekeys.each do |v| + setvariable(v[1],vars[v[0]]) if vars.key?(v[0]) && vars[v[0]] + end + +if getvariable('given.backend') == "standard" or getvariable('given.backend') == "" then + setvariable('backend',@@backends[getvariable('texengine')] || 'standard') +end + break + end + else + break + end + end + tex.close + end + rescue + # well, let's not worry too much + end + end + + def scantexcontent(filename) + if FileTest.file?(filename) and tex = File.open(filename,'rb') then + while str = tex.gets do + case str.chomp + when /^\%/o then + # next + # when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then + when /\\(starttekst|stoptekst|startonderdeel|startoverzicht)/o then + setvariable('texformats','nl') ; break + when /\\(stelle|verwende|umgebung|benutze)/o then + setvariable('texformats','de') ; break + when /\\(stel|gebruik|omgeving)/o then + setvariable('texformats','nl') ; break + when /\\(use|setup|environment)/o then + setvariable('texformats','en') ; break + when /\\(usa|imposta|ambiente)/o then + setvariable('texformats','it') ; break + when /(height|width|style)=/o then + setvariable('texformats','en') ; break + when /(hoehe|breite|schrift)=/o then + setvariable('texformats','de') ; break + when /(hoogte|breedte|letter)=/o then + setvariable('texformats','nl') ; break + when /(altezza|ampiezza|stile)=/o then + setvariable('texformats','it') ; break + when /externfiguur/o then + setvariable('texformats','nl') ; break + when /externalfigure/o then + setvariable('texformats','en') ; break + when /externeabbildung/o then + setvariable('texformats','de') ; break + when /figuraesterna/o then + setvariable('texformats','it') ; break + end + end + tex.close + end + + end + + private # will become base/context + + def pushresult(filename,resultname) + fname = File.unsuffixed(filename) + rname = File.unsuffixed(resultname) + if ! rname.empty? && (rname != fname) then + report("outputfile #{rname}") + ['tuo','tuc','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed(fname,s),File.suffixed('texexec',s)) + end + ['tuo','tuc'].each do |s| + File.silentrename(File.suffixed(rname,s),File.suffixed(fname,s)) if FileTest.file?(File.suffixed(rname,s)) + end + end + end + + def popresult(filename,resultname) + fname = File.unsuffixed(filename) + rname = File.unsuffixed(resultname) + if ! rname.empty? && (rname != fname) then + report("renaming #{fname} to #{rname}") + ['tuo','tuc','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed(fname,s),File.suffixed(rname,s)) + end + report("restoring #{fname}") + unless $fname == 'texexec' then + ['tuo','tuc','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed('texexec',s),File.suffixed(fname,s)) + end + end + end + end + + def makestubfile(rawname,rawbase,forcexml=false) + if tmp = openedfile(File.suffixed(rawbase,'run')) then + tmp << "\\starttext\n" + if forcexml then + # tmp << checkxmlfile(rawname) + if getvariable('mkiv') then + tmp << "\\xmlprocess{\\xmldocument}{#{rawname}}{}\n" + else + tmp << "\\processXMLfilegrouped{#{rawname}}\n" + end + else + tmp << "\\processfile{#{rawname}}\n" + end + tmp << "\\stoptext\n" + tmp.close + return "run" + else + return File.splitname(rawname)[1] + end + end + + # def checkxmlfile(rawname) + # tmp = '' + # if FileTest.file?(rawname) && (xml = File.open(rawname)) then + # xml.each do |line| + # case line + # when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then + # category, key, value, rest = $1, $2, $3, $4 + # case category + # when 'job' then + # case key + # when 'control' then + # setvariable(value,if rest.empty? then true else rest end) + # when 'mode', 'modes' then + # tmp << "\\enablemode[#{value}]\n" + # when 'stylefile', 'environment' then + # tmp << "\\environment #{value}\n" + # when 'module' then + # tmp << "\\usemodule[#{value}]\n" + # when 'interface' then + # contextinterface = value + # when 'ctxfile' then + # setvariable('ctxfile', value) + # report("using source driven ctxfile #{value}") + # end + # end + # when /<[a-z]+/io then # beware of order, first pi test + # break + # end + # end + # xml.close + # end + # return tmp + # end + + def extendvariable(name,value) + set = getvariable(name).split(',') + set << value + str = set.uniq.join(',') + setvariable(name,str) + end + + def checkxmlfile(rawname) + if FileTest.file?(rawname) && (xml = File.open(rawname,'rb')) then + xml.each do |line| + case line + when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then + category, key, value, rest = $1, $2, $3, $4 + case category + when 'job' then + case key + when 'control' then + setvariable(value,if rest.empty? then true else rest end) + when /^(mode)(s|)$/ then + extendvariable('modes',value) + when /^(stylefile|environment)(s|)$/ then + extendvariable('environments',value) + when /^(use|)(module)(s|)$/ then + extendvariable('usemodules',value) + when /^(filter)(s|)$/ then + extendvariable('filters',value) + when 'interface' then + contextinterface = value + when 'ctxfile' then + setvariable('ctxfile', value) + report("using source driven ctxfile #{value}") + end + end + when /<[a-z]+/io then # beware of order, first pi test + break + end + end + xml.close + end + end + +end + +class TEX + + def timedrun(delay, &block) + delay = delay.to_i rescue 0 + if delay > 0 then + begin + report("job started with timeout '#{delay}'") + timeout(delay) do + yield block + end + rescue TimeoutError + report("job aborted due to timeout '#{delay}'") + setvariable('error','timeout') + rescue + report("job aborted due to error") + setvariable('error','fatal error') + else + report("job finished within timeout '#{delay}'") + end + else + yield block + end + end + + def processtex # much to do: mp, xml, runs etc + setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing document '#{filename}'") + timedrun(getvariable('timeout')) do + processfile + end + end + reportruntime + end + + def processmptex + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing graphic '#{filename}'") + runtexmp(filename) + end + reportruntime + end + + private + + def load_map_files(filename) # tui basename + # c \usedmapfile{=}{lm-texnansi} + begin + str = "" + IO.read(filename).scan(/^c\s+\\usedmapfile\{(.*?)\}\{(.*?)\}\s*$/o) do + str << "\\loadmapfile[#{$2}.map]\n" + end + rescue + return "" + else + return str + end + end + + public + + # def run_luatools(args) + # dirty trick: we know that the lua path is relative to the ruby path; of course this + # will not work well when stubs are used + # [(ENV["_CTX_K_S_texexec_"] or ENV["_CTX_K_S_THREAD_"] or ENV["TEXMFSTART.THREAD"]), File.dirname($0)].each do |path| + # if path then + # script = "#{path}/../lua/luatools.lua" + # if FileTest.file?(script) then + # return runcommand("luatex --luaonly #{script} #{args}") + # end + # end + # end + # return runcommand("texmfstart luatools #{args}") + # end + + def run_luatools(args) + return runcommand("luatools #{args}") + end + + def processmpgraphic + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing graphic '#{filename}'") + runtexmp(filename,'',false) # no purge + mapspecs = load_map_files(File.suffixed(filename,'temp','tui')) + unless getvariable('keep') then + # not enough: purge_mpx_files(filename) + Dir.glob(File.suffixed(filename,'temp*','*')).each do |fname| + File.delete(fname) unless File.basename(filename) == File.basename(fname) + end + end + begin + data = IO.read(File.suffixed(filename,'log')) + basename = filename.sub(/\.mp$/, '') + if data =~ /output files* written\:\s*(.*)$/moi then + files, number, range, list = $1.split(/\s+/), 0, false, [] + files.each do |fname| + if fname =~ /^.*\.(\d+)$/ then + if range then + (number+1 .. $1.to_i).each do |i| + list << i + end + range = false + else + number = $1.to_i + list << number + end + elsif fname =~ /\.\./ then + range = true + else + range = false + next + end + end + begin + if getvariable('combine') then + fullname = "#{basename}.#{number}" + File.open("texexec.tex",'w') do |f| + f << "\\setupoutput[pdftex]\n" + f << "\\setupcolors[state=start]\n" + f << mapspecs + f << "\\starttext\n" + list.each do |number| + f << "\\startTEXpage\n" + f << "\\convertMPtoPDF{#{fullname}}{1}{1}" + f << "\\stopTEXpage\n" + end + f << "\\stoptext\n" + end + report("converting graphic '#{fullname}'") + runtex("texexec.tex") + pdffile = File.suffixed(basename,'pdf') + File.silentrename("texexec.pdf",pdffile) + report ("#{basename}.* converted to #{pdffile}") + else + list.each do |number| + begin + fullname = "#{basename}.#{number}" + File.open("texexec.tex",'w') do |f| + f << "\\setupoutput[pdftex]\n" + f << "\\setupcolors[state=start]\n" + f << mapspecs + f << "\\starttext\n" + f << "\\startTEXpage\n" + f << "\\convertMPtoPDF{#{fullname}}{1}{1}" + f << "\\stopTEXpage\n" + f << "\\stoptext\n" + end + report("converting graphic '#{fullname}'") + runtex("texexec.tex") + if files.length>1 then + pdffile = File.suffixed(basename,number.to_s,'pdf') + else + pdffile = File.suffixed(basename,'pdf') + end + File.silentrename("texexec.pdf",pdffile) + report ("#{fullname} converted to #{pdffile}") + end + end + end + rescue + report ("error when converting #{fullname} (#{$!})") + end + end + rescue + report("error in converting #{filename}") + end + end + reportruntime + end + + def processmpstatic + if filename = getvariable('filename') then + filename += ".mp" unless filename =~ /\..+?$/ + if FileTest.file?(filename) then + begin + data = IO.read(filename) + File.open("texexec.tex",'w') do |f| + f << "\\setupoutput[pdftex]\n" + f << "\\setupcolors[state=start]\n" + data.sub!(/^%mpenvironment\:\s*(.*?)$/moi) do + f << $1 + "\n" + end + f << "\\starttext\n" + f << "\\startMPpage\n" + f << data.gsub(/end\.*\s*$/m, '') # a bit of a hack + f << "\\stopMPpage\n" + f << "\\stoptext\n" + end + report("converting static '#{filename}'") + runtex("texexec.tex") + pdffile = File.suffixed(filename,'pdf') + File.silentrename("texexec.pdf",pdffile) + report ("#{filename} converted to #{pdffile}") + rescue + report("error in converting #{filename} (#{$!}") + end + end + end + reportruntime + end + + def processmpxtex + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing text of graphic '#{filename}'") + processmpx(filename,false,true,true) + end + reportruntime + end + + def deleteoptionfile(rawname) + ['top','top.keep'].each do |suffix| + begin + File.delete(File.suffixed(rawname,suffix)) + rescue + end + end + end + + def makeoptionfile(rawname, jobname, jobsuffix, finalrun, fastdisabled, kindofrun, currentrun=1) + begin + # jobsuffix = orisuffix + if topname = File.suffixed(rawname,'top') and opt = File.open(topname,'w') then + report("writing option file #{topname}") + # local handies + opt << "\% #{topname}\n" + opt << "\\unprotect\n" + # + # feedback and basic control + # + if getvariable('batchmode') then + opt << "\\batchmode\n" + end + if getvariable('nonstopmode') then + opt << "\\nonstopmode\n" + end + if getvariable('paranoid') then + opt << "\\def\\maxreadlevel{1}\n" + end + if getvariable('nomapfiles') then + opt << "\\disablemapfiles\n" + end + if getvariable('nompmode') || getvariable('nomprun') || getvariable('automprun') then + opt << "\\runMPgraphicsfalse\n" + end + if getvariable('utfbom') then + opt << "\\enableregime[utf]" + end + progname = validprogname(['metafun']) # [getvariable('progname'),mpsformat,mpsengine] + opt << "\\def\\MPOSTformatswitch\{#{prognameflag(progname)} #{formatflag('mpost')}=\}\n" + # + # process info + # + opt << "\\setupsystem[\\c!n=#{kindofrun},\\c!m=#{currentrun}]\n" + if (str = File.unixfied(getvariable('modefile'))) && ! str.empty? then + opt << "\\readlocfile{#{str}}{}{}\n" + end + if (str = File.unixfied(getvariable('result'))) && ! str.empty? then + opt << "\\setupsystem[file=#{str}]\n" + elsif (str = getvariable('suffix')) && ! str.empty? then + opt << "\\setupsystem[file=#{jobname}.#{str}]\n" + end + opt << "\\setupsystem[\\c!method=2]\n" # 1=oldtexexec 2=newtexexec (obsolete) + opt << "\\setupsystem[\\c!type=#{Tool.ruby_platform()}]\n" + if (str = File.unixfied(getvariable('path'))) && ! str.empty? then + opt << "\\usepath[#{str}]\n" unless str.empty? + end + if (str = getvariable('mainlanguage').downcase) && ! str.empty? && ! str.standard? then + opt << "\\setuplanguage[#{str}]\n" + end + if (str = getvariable('arguments')) && ! str.empty? then + opt << "\\setupenv[#{str}]\n" + end + if (str = getvariable('setuppath')) && ! str.empty? then + opt << "\\setupsystem[\\c!directory=\{#{str}\}]\n" + end + if (str = getvariable('randomseed')) && ! str.empty? then + report("using randomseed #{str}") + opt << "\\setupsystem[\\c!random=#{str}]\n" + end + if (str = getvariable('input')) && ! str.empty? then + opt << "\\setupsystem[inputfile=#{str}]\n" + else + opt << "\\setupsystem[inputfile=#{rawname}]\n" + end + # + # modes + # + # we handle both "--mode" and "--modes", else "--mode" is mapped onto "--modefile" + if (str = getvariable('modes')) && ! str.empty? then + opt << "\\enablemode[#{str}]\n" + end + if (str = getvariable('mode')) && ! str.empty? then + opt << "\\enablemode[#{str}]\n" + end + # + # options + # + opt << "\\startsetups *runtime:options\n" + if str = validbackend(getvariable('backend')) then + opt << "\\setupoutput[#{str}]\n" + elsif str = validbackend(getvariable('output')) then + opt << "\\setupoutput[#{str}]\n" + end + if getvariable('color') then + opt << "\\setupcolors[\\c!state=\\v!start]\n" + end + if (str = getvariable('separation')) && ! str.empty? then + opt << "\\setupcolors[\\c!split=#{str}]\n" + end + if (str = getvariable('paperformat')) && ! str.empty? && ! str.standard? then + if str =~ /^([a-z]+\d+)([a-z]+\d+)$/io then # A5A4 A4A3 A2A1 ... + opt << "\\setuppapersize[#{$1.upcase}][#{$2.upcase}]\n" + else # ...*... + pf = str.upcase.split(/[x\*]/o) + pf << pf[0] if pf.size == 1 + opt << "\\setuppapersize[#{pf[0]}][#{pf[1]}]\n" + end + end + if (str = getvariable('background')) && ! str.empty? then + opt << "\\defineoverlay[whatever][{\\externalfigure[#{str}][\\c!factor=\\v!max]}]\n" + opt << "\\setupbackgrounds[\\v!page][\\c!background=whatever]\n" + end + if getvariable('centerpage') then + opt << "\\setuplayout[\\c!location=\\v!middle,\\c!marking=\\v!on]\n" + end + if getvariable('noarrange') then + opt << "\\setuparranging[\\v!disable]\n" + elsif getvariable('arrange') then + arrangement = Array.new + if finalrun then + arrangement << "\\v!doublesided" unless getvariable('noduplex') + case getvariable('printformat') + when '' then arrangement << "\\v!normal" + when /.*up/oi then arrangement << ["2UP","\\v!rotated"] + when /.*down/oi then arrangement << ["2DOWN","\\v!rotated"] + when /.*side/oi then arrangement << ["2SIDE","\\v!rotated"] + end + else + arrangement << "\\v!disable" + end + opt << "\\setuparranging[#{arrangement.flatten.join(',')}]\n" if arrangement.size > 0 + end + if (str = getvariable('pages')) && ! str.empty? then + if str.downcase == 'odd' then + opt << "\\chardef\\whichpagetoshipout=1\n" + elsif str.downcase == 'even' then + opt << "\\chardef\\whichpagetoshipout=2\n" + else + pagelist = Array.new + str.split(/\,/).each do |page| + pagerange = page.split(/\D+/o) + if pagerange.size > 1 then + pagerange.first.to_i.upto(pagerange.last.to_i) do |p| + pagelist << p.to_s + end + else + pagelist << page + end + end + opt << "\\def\\pagestoshipout\{#{pagelist.join(',')}\}\n"; + end + end + opt << "\\stopsetups\n" + # + # styles and modules + # + opt << "\\startsetups *runtime:modules\n" + begin getvariable('filters' ).split(',').uniq.each do |f| opt << "\\useXMLfilter[#{f}]\n" end ; rescue ; end + begin getvariable('usemodules' ).split(',').uniq.each do |m| opt << "\\usemodule [#{m}]\n" end ; rescue ; end + begin getvariable('environments').split(',').uniq.each do |e| opt << "\\environment #{e} \n" end ; rescue ; end + opt << "\\stopsetups\n" + # + opt << "\\protect \\endinput\n" + # + opt.close + else + report("unable to write option file #{topname}") + end + rescue + report("fatal error in writing option file #{topname} (#{$!})") + end + end + + def takeprecautions + ENV['MPXCOMAND'] = '0' # else loop + if getvariable('paranoid') then + ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 'f' + ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'p' + ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'p' + elsif getvariable('notparanoid') then + ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 't' + ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'a' + ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'a' + end + if ENV['OPENIN_ANY'] && (ENV['OPENIN_ANY'] == 'p') then # first test redundant + setvariable('paranoid', true) + end + if ENV.key?('SHELL_ESCAPE') && (ENV['SHELL_ESCAPE'] == 'f') then + setvariable('automprun',true) + end + done = false + ['TXRESOURCES','MPRESOURCES','MFRESOURCES'].each do |res| + [getvariable('runpath'),getvariable('path')].each do |pat| + unless pat.empty? then + if ENV.key?(res) then + # ENV[res] = if ENV[res].empty? then pat else pat + ":" + ENV[res] end +if ENV[res].empty? then + ENV[res] = pat +elsif ENV[res] == pat || ENV[res] =~ /^#{pat}\:/ || ENV[res] =~ /\:#{pat}\:/ then + # skip +else + ENV[res] = pat + ":" + ENV[res] +end + else + ENV[res] = pat + end + report("setting #{res} to #{ENV[res]}") unless done + end + end + done = true + end + end + + def checktestversion + # + # one can set TEXMFALPHA and TEXMFBETA for test versions + # but keep in mind that the format as well as the test files + # then need the --alpha or --beta flag + # + done, tree = false, '' + ['alpha', 'beta'].each do |what| + if getvariable(what) then + if ENV["TEXMF#{what.upcase}"] then + done, tree = true, ENV["TEXMF#{what.upcase}"] + elsif ENV["TEXMFLOCAL"] then + done, tree = true, File.join(File.dirname(ENV['TEXMFLOCAL']), "texmf-#{what}") + end + end + break if done + end + if done then + tree = tree.strip + ENV['TEXMFPROJECT'] = tree + report("using test tree '#{tree}'") + ['MP', 'MF', 'TX'].each do |ctx| + ENV['CTXDEV#{ctx}PATH'] = '' + end + unless (FileTest.file?(File.join(tree,'ls-r')) || FileTest.file?(File.join(tree,'ls-R'))) then + report("no ls-r/ls-R file for tree '#{tree}' (run: mktexlsr #{tree})") + end + end + # puts `kpsewhich --expand-path=$TEXMF` + # exit + end + + def runtex(filename) + checktestversion + texengine = validtexengine(getvariable('texengine')) + texformat = validtexformat(getarrayvariable('texformats').first) + report("tex engine: #{texengine}") + report("tex format: #{texformat}") + if texengine && texformat then + fixbackendvars(@@mappaths[texengine]) + if texengine == "luatex" then + # currently we use luatools to start luatex but some day we should + # find a clever way to directly call luatex (problem is that we need + # to feed the explicit location of the format and lua initialization + # file) + run_luatools("--fmt=#{texformat} #{filename}") + else + progname = validprogname([getvariable('progname'),texformat,texengine]) + runcommand([quoted(texengine),prognameflag(progname),formatflag(texengine,texformat),tcxflag(texengine),runoptions(texengine),filename,texprocextras(texformat)]) + end + # true + else + false + end + end + + def runmp(mpname,mpx=false) + checktestversion + mpsengine = validmpsengine(getvariable('mpsengine')) + mpsformat = validmpsformat(getarrayvariable('mpsformats').first) + if mpsengine && mpsformat then + ENV["MPXCOMMAND"] = "0" unless mpx + progname = validprogname([getvariable('progname'),mpsformat,mpsengine]) + mpname.gsub!(/\.mp$/,"") # temp bug in mp + # runcommand([quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),tcxflag(mpsengine),runoptions(mpsengine),mpname,mpsprocextras(mpsformat)]) + runcommand([quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),runoptions(mpsengine),mpname,mpsprocextras(mpsformat)]) + true + else + false + end + end + + def runtexmp(filename,filetype='',purge=true) + checktestversion + mpname = File.suffixed(filename,filetype,'mp') + if File.atleast?(mpname,10) then + # first run needed + File.silentdelete(File.suffixed(mpname,'mpt')) + doruntexmp(mpname,nil,true,purge) + mpgraphics = checkmpgraphics(mpname) + mplabels = checkmplabels(mpname) + if mpgraphics || mplabels then + # second run needed + doruntexmp(mpname,mplabels,true,purge) + else + # no labels + end + end + end + + def runtexmpjob(filename,filetype='') + checktestversion + mpname = File.suffixed(filename,filetype,'mp') + if File.atleast?(mpname,25) && (data = File.silentread(mpname)) then + textranslation = if data =~ /^\%\s+translate.*?\=([\w\d\-]+)/io then $1 else '' end + mpjobname = if data =~ /collected graphics of job \"(.+?)\"/io then $1 else '' end + if ! mpjobname.empty? and File.unsuffixed(filename) =~ /#{mpjobname}/ then # don't optimize + options = Array.new + options.push("--mptex") + options.push("--nomp") + options.push("--mpyforce") if getvariable('forcempy') || getvariable('mpyforce') + options.push("--translate=#{textranslation}") unless textranslation.empty? + options.push("--batch") if getvariable('batchmode') + options.push("--nonstop") if getvariable('nonstopmode') + options.push("--output=ps") # options.push("--dvi") + options.push("--nobackend") + return runtexexec(mpname,options,2) + end + end + return false + end + + def runtexutil(filename=[], options=['--ref','--ij','--high'], old=false) + [filename].flatten.each do |fname| + if old then + Kpse.runscript('texutil',fname,options) + else + begin + logger = Logger.new('TeXUtil') + if tu = TeXUtil::Converter.new(logger) and tu.loaded(fname) then + ok = tu.processed && tu.saved && tu.finalized + end + rescue + Kpse.runscript('texutil',fname,options) + end + end + end + end + + def runluacheck(jobname) + if false then + # test-pos.tex / 6 meg tua file: 18.6 runtime + old, new = File.suffixed(jobname,'tua'), File.suffixed(jobname,'tuc') + if FileTest.file?(old) then + report("converting #{old} into #{new}") + system("luac -s -o #{new} #{old}") + end + else + # test-pos.tex / 6 meg tua file: 17.5 runtime + old, new = File.suffixed(jobname,'tua'), File.suffixed(jobname,'tuc') + if FileTest.file?(old) then + report("renaming #{old} into #{new}") + File.rename(old,new) rescue false + end + end + end + + # 1=tex 2=mptex 3=mpxtex 4=mpgraphic 5=mpstatic + + def runtexexec(filename=[], options=[], mode=nil) + begin + if mode and job = TEX.new(@logger) then + options.each do |option| + case option + when /^\-*(.*?)\=(.*)$/o then + job.setvariable($1,$2) + when /^\-*(.*?)$/o then + job.setvariable($1,true) + end + end + job.setvariable("files",filename) + case mode + when 1 then job.processtex + when 2 then job.processmptex + when 3 then job.processmpxtex + when 4 then job.processmpgraphic + when 5 then job.processmpstatic + end + job.inspect && Kpse.inspect if getvariable('verbose') + return true + else + Kpse.runscript('texexec',filename,options) + end + rescue + Kpse.runscript('texexec',filename,options) + end + end + + def fixbackendvars(backend) + if backend then + ENV['backend'] = backend ; + ENV['progname'] = backend unless validtexengine(backend) + ENV['TEXFONTMAPS'] = ['.',"\$TEXMF/fonts/{data,map}/{#{backend},pdftex,dvips,}//",'./fonts//'].join_path + report("fixing backend map path for #{backend}: #{ENV['TEXFONTMAPS']}") if getvariable('verbose') + else + report("unable to fix backend map path") if getvariable('verbose') + end + end + + def runbackend(rawname) + unless getvariable('nobackend') then + case validbackend(getvariable('backend')) + when 'dvipdfmx' then + fixbackendvars('dvipdfm') + runcommand("dvipdfmx -d 4 -V 5 #{File.unsuffixed(rawname)}") + when 'xetex' then + # xetex now runs its own backend + xdvfile = File.suffixed(rawname,'xdv') + if FileTest.file?(xdvfile) then + fixbackendvars('dvipdfm') + runcommand("xdvipdfmx -q -d 4 -V 5 -E #{xdvfile}") + end + when 'xdv2pdf' then + xdvfile = File.suffixed(rawname,'xdv') + if FileTest.file?(xdvfile) then + fixbackendvars('xdv2pdf') + runcommand("xdv2pdf #{xdvfile}") + end + when 'dvips' then + fixbackendvars('dvips') + mapfiles = '' + begin + if tuifile = File.suffixed(rawname,'tui') and FileTest.file?(tuifile) then + IO.read(tuifile).scan(/^c \\usedmapfile\{.\}\{(.*?)\}\s*$/o) do + mapfiles += "-u +#{$1} " ; + end + end + rescue + mapfiles = '' + end + runcommand("dvips #{mapfiles} #{File.unsuffixed(rawname)}") + when 'pdftex' then + # no need for postprocessing + else + report("no postprocessing needed") + end + end + end + + def processfile + + takeprecautions + report("using search method '#{Kpse.searchmethod}'") if getvariable('verbose') + + rawname = getvariable('filename') + jobname = getvariable('filename') + + if getvariable('autopath') then + jobname = File.basename(jobname) + inppath = File.dirname(jobname) + else + inppath = '' + end + + jobname, jobsuffix = File.splitname(jobname,'tex') + + jobname = File.unixfied(jobname) + inppath = File.unixfied(inppath) + + orisuffix = jobsuffix # still needed ? + + if jobsuffix =~ /^(htm|html|xhtml|xml|fo|fox|rlg|exa)$/io then + setvariable('forcexml',true) + end + + dummyfile = false + + # fuzzy code snippet: (we kunnen kpse: prefix gebruiken) + + unless FileTest.file?(File.suffixed(jobname,jobsuffix)) then + if FileTest.file?(rawname + '.tex') then + jobname = rawname.dup + jobsuffix = 'tex' + end + end + + # we can have funny names, like 2005.10.10 (given without suffix) + + rawname = jobname + '.' + jobsuffix + rawpath = File.dirname(rawname) + rawbase = File.basename(rawname) + + unless FileTest.file?(rawname) then + inppath.split(',').each do |ip| + break if dummyfile = FileTest.file?(File.join(ip,rawname)) + end + end + + forcexml = getvariable('forcexml') + + if dummyfile || forcexml then # after ctx? + jobsuffix = makestubfile(rawname,rawbase,forcexml) + checkxmlfile(rawname) + end + + # preprocess files + + unless getvariable('noctx') then + ctx = CtxRunner.new(rawname,@logger) + if pth = getvariable('path') then + pth.split(',').each do |p| + ctx.register_path(p) + end + end + if getvariable('ctxfile').empty? then + if rawname == rawbase then + ctx.manipulate(File.suffixed(rawname,'ctx'),'jobname.ctx') + else + ctx.manipulate(File.suffixed(rawname,'ctx'),File.join(rawpath,'jobname.ctx')) + end + else + ctx.manipulate(File.suffixed(getvariable('ctxfile'),'ctx')) + end + ctx.savelog(File.suffixed(rawbase,'ctl')) + + envs = ctx.environments + mods = ctx.modules + flags = ctx.flags + mdes = ctx.modes + + flags.each do |f| + f.sub!(/^\-+/,'') + if f =~ /^(.*?)=(.*)$/ then + setvariable($1,$2) + else + setvariable(f,true) + end + end + + report("using flags #{flags.join(' ')}") if flags.size > 0 + + # merge environment and module specs + + envs << getvariable('environments') unless getvariable('environments').empty? + mods << getvariable('usemodules') unless getvariable('usemodules') .empty? + mdes << getvariable('modes') unless getvariable('modes') .empty? + + envs = envs.uniq.join(',') + mods = mods.uniq.join(',') + mdes = mdes.uniq.join(',') + + report("using search method '#{Kpse.searchmethod}'") if getvariable('verbose') + + report("using environments #{envs}") if envs.length > 0 + report("using modules #{mods}") if mods.length > 0 + report("using modes #{mdes}") if mdes.length > 0 + + setvariable('environments', envs) + setvariable('usemodules', mods) + setvariable('modes', mdes) + end + + # end of preprocessing and merging + + setvariable('nomprun',true) if orisuffix == 'mpx' # else cylic run + PDFview.setmethod('xpdf') if getvariable('xpdf') + PDFview.closeall if getvariable('autopdf') + + runonce = getvariable('once') + finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange')) + suffix = getvariable('suffix') + result = getvariable('result') + globalfile = getvariable('globalfile') + forcexml = getvariable('forcexml') # can be set in ctx file + +if dummyfile || forcexml then # after ctx? + jobsuffix = makestubfile(rawname,rawbase,forcexml) + checkxmlfile(rawname) +end + + result = File.unixfied(result) + + if globalfile || FileTest.file?(rawname) then + + if not dummyfile and not globalfile and not forcexml then + scantexpreamble(rawname) + scantexcontent(rawname) if getvariable('texformats').standard? + end + result = File.suffixed(rawname,suffix) unless suffix.empty? + + pushresult(rawbase,result) + + method = validtexmethod(validtexformat(getvariable('texformats'))) + + report("tex processing method: #{method}") + + case method + + when 'context' then + if getvariable('simplerun') || runonce then + makeoptionfile(rawbase,jobname,orisuffix,true,true,3,1) unless getvariable('nooptionfile') + ok = runtex(if dummyfile || forcexml then rawbase else rawname end) + if ok then + ok = runtexutil(rawbase) if getvariable('texutil') || getvariable('forcetexutil') + runluacheck(rawbase) + runbackend(rawbase) + popresult(rawbase,result) + end + if getvariable('keep') then + ['top','log','run'].each do |suffix| + File.silentrename(File.suffixed(rawbase,suffix),File.suffixed(rawbase,suffix+'.keep')) + end + end + else +# goto tmp/jobname when present + mprundone, ok, stoprunning = false, true, false + texruns, nofruns = 0, getvariable('runs').to_i + state = FileState.new + ['tub','tuo','tuc'].each do |s| + state.register(File.suffixed(rawbase,s)) + end + if getvariable('automprun') then # check this + ['mprun','mpgraph'].each do |s| + state.register(File.suffixed(rawbase,s,'mp'),'randomseed') + end + end + while ! stoprunning && (texruns < nofruns) && ok do + texruns += 1 + report("TeX run #{texruns}") + unless getvariable('nooptionfile') then + if texruns == nofruns then + makeoptionfile(rawbase,jobname,orisuffix,false,false,4,texruns) # last + elsif texruns == 1 then + makeoptionfile(rawbase,jobname,orisuffix,false,false,1,texruns) # first + else + makeoptionfile(rawbase,jobname,orisuffix,false,false,2,texruns) # unknown + end + end +# goto . + + ok = runtex(File.suffixed(if dummyfile || forcexml then rawbase else rawname end,jobsuffix)) + +if getvariable('texengine') == "xetex" then + ok = true +end + +############################ + +# goto tmp/jobname when present + if ok && (nofruns > 1) then + unless getvariable('nompmode') then + mprundone = runtexmpjob(rawbase, "mpgraph") + mprundone = runtexmpjob(rawbase, "mprun") + end + ok = runtexutil(rawbase) + runluacheck(rawbase) + state.update + stoprunning = state.stable? + end + end + if not ok then + setvariable('error','error in tex file') + end + if (nofruns == 1) && getvariable('texutil') then + ok = runtexutil(rawbase) + runluacheck(rawbase) + end + if ok && finalrun && (nofruns > 1) then + makeoptionfile(rawbase,jobname,orisuffix,true,finalrun,4,texruns) unless getvariable('nooptionfile') + report("final TeX run #{texruns}") +# goto . + ok = runtex(File.suffixed(if dummyfile || forcexml then rawbase else rawname end,jobsuffix)) +# goto tmp/jobname when present + end + if getvariable('keep') then + ['top','log','run'].each do |suffix| + File.silentrename(File.suffixed(rawbase,suffix),File.suffixed(rawbase,suffix+'.keep')) + end + else + File.silentrename(File.suffixed(rawbase,'top'),File.suffixed(rawbase,'tmp')) + end + # ['tmp','top','log'].each do |s| # previous tuo file / runtime option file / log file + # File.silentdelete(File.suffixed(rawbase,s)) + # end + if ok then +# goto . + runbackend(rawbase) + popresult(rawbase,result) +# goto tmp/jobname when present +# skip next + end + if true then # autopurge + begin + File.open(File.suffixed(rawbase, 'tuo'),'rb') do |f| + ok = 0 + f.each do |line| + case ok + when 1 then + # next line is empty + ok = 2 + when 2 then + if line =~ /^\%\s+\>\s+(.*?)\s+(\d+)/moi then + filename, n = $1, $2 + done = File.delete(filename) rescue false + if done && getvariable('verbose') then + report("deleting #{filename} (#{n} times used)") + end + else + break + end + else + if line =~ /^\%\s+temporary files\:\s+(\d+)/moi then + if $1.to_i == 0 then + break + else + ok = 1 + end + end + end + end + end + rescue + # report("fatal error #{$!}") + end + end + end + + Kpse.runscript('ctxtools',rawbase,'--purge') if getvariable('purge') + Kpse.runscript('ctxtools',rawbase,'--purge --all') if getvariable('purgeall') + + # runcommand('mtxrun','--script','ctxtools',rawbase,'--purge') if getvariable('purge') + # runcommand('mtxrun','--script','ctxtools',rawbase,'--purge --all') if getvariable('purgeall') + + when 'latex' then + + ok = runtex(rawname) + + else + + ok = runtex(rawname) + + end + + if (dummyfile or forcexml) and FileTest.file?(rawbase) then + begin + File.delete(File.suffixed(rawbase,'run')) + rescue + report("unable to delete stub file") + end + end + + if ok and getvariable('autopdf') then + PDFview.open(File.suffixed(if result.empty? then rawbase else result end,'pdf')) + end + + else + report("nothing to process") + end + + end + + # The labels are collected in the mergebe hash. Here we merge the relevant labels + # into beginfig/endfig. We could as well do this in metafun itself. Maybe some + # day ... (it may cost a bit of string space but that is cheap nowadays). + + def doruntexmp(mpname,mergebe=nil,context=true,purge=true) + texfound = false + mpname = File.suffixed(mpname,'mp') + mpcopy = File.suffixed(mpname,'mp.copy') + mpkeep = File.suffixed(mpname,'mp.keep') + setvariable('mp.file',mpname) + setvariable('mp.line','') + setvariable('mp.error','') + if mpdata = File.silentread(mpname) then + # mpdata.gsub!(/^\%.*\n/o,'') + File.silentrename(mpname,mpcopy) + texfound = mergebe || (mpdata =~ /btex .*? etex/mo) + if mp = openedfile(mpname) then + if mergebe then + mpdata.gsub!(/beginfig\s*\((\d+)\)\s*\;(.+?)endfig\s*\;/mo) do + n, str = $1, $2 + if str =~ /^(.*?)(verbatimtex.*?etex)\s*\;(.*)$/mo then + "beginfig(#{n})\;\n#{$1}#{$2}\;\n#{mergebe[n]}\n#{$3}\;endfig\;\n" + else + "beginfig(#{n})\;\n#{mergebe[n]}\n#{str}\;endfig\;\n" + end + end + unless mpdata =~ /beginfig\s*\(\s*0\s*\)/o then + mp << mergebe['0'] if mergebe.key?('0') + end + end + # mp << MPTools::splitmplines(mpdata) + mp << mpdata + mp << "\n" + # mp << "end" + # mp << "\n" + mp.close + end + processmpx(mpname,true,true,purge) if texfound + if getvariable('batchmode') then + options = ' --interaction=batch' + elsif getvariable('nonstopmode') then + options = ' --interaction=nonstop' + else + options = '' + end + # todo plain|mpost|metafun + begin + ok = runmp(mpname) + rescue + end + if f = File.silentopen(File.suffixed(mpname,'log')) then + while str = f.gets do + if str =~ /^l\.(\d+)\s(.*?)\n/o then + setvariable('mp.line',$1) + setvariable('mp.error',$2) + break + end + end + f.close + end + File.silentrename(mpname, mpkeep) + File.silentrename(mpcopy, mpname) + end + end + + # todo: use internal mptotext function and/or turn all btex/etex into textexts + + def processmpx(mpname,force=false,context=true,purge=true) + unless force then + mpname = File.suffixed(mpname,'mp') + if File.atleast?(mpname,10) && (data = File.silentread(mpname)) then + if data =~ /(btex|etex|verbatimtex|textext)/o then + force = true + end + end + end + if force then + begin + mptex = File.suffixed(mpname,'temp','tex') + mpdvi = File.suffixed(mpname,'temp','dvi') + mplog = File.suffixed(mpname,'temp','log') + mpmpx = File.suffixed(mpname,'mpx') + File.silentdelete(mptex) + if true then + report("using internal mptotex converter") + ok = MPTools::mptotex(mpname,mptex,'context') + else + command = "mpto #{mpname} > #{mptex}" + report(command) if getvariable('verbose') + ok = system(command) + end + # not "ok && ..." because of potential problem with return code and redirect (>) + if FileTest.file?(mptex) && File.appended(mptex, "\\end\n") then + # to be replaced by runtexexec([filenames],options,1) + if localjob = TEX.new(@logger) then + localjob.setvariable('files',mptex) + localjob.setvariable('backend','dvips') + localjob.setvariable('engine',getvariable('engine')) unless getvariable('engine').empty? + localjob.setvariable('once',true) + localjob.setvariable('nobackend',true) + if context then + localjob.setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + elsif getvariable('interface').empty? then + localjob.setvariable('texformats',['plain']) + else + localjob.setvariable('texformats',[getvariable('interface')]) + end + localjob.processtex + ok = true # todo + else + ok = false + end + # so far + command = "dvitomp #{mpdvi} #{mpmpx}" + report(command) if getvariable('verbose') + ok = ok && FileTest.file?(mpdvi) && system(command) + purge_mpx_files(mpname) if purge + end + rescue + # error in processing mpx file + end + end + end + + def purge_mpx_files(mpname) + unless getvariable('keep') then + ['tex', 'log', 'tui', 'tuo', 'tuc', 'top'].each do |suffix| + File.silentdelete(File.suffixed(mpname,'temp',suffix)) + end + end + end + + def checkmpgraphics(mpname) + # in practice the checksums will differ because of multiple instances + # ok, we could save the mpy/mpo files by number, but not now + mpoptions = '' + if getvariable('makempy') then + mpoptions += " --makempy " + end + mponame = File.suffixed(mpname,'mpo') + mpyname = File.suffixed(mpname,'mpy') + pdfname = File.suffixed(mpname,'pdf') + tmpname = File.suffixed(mpname,'tmp') + if getvariable('mpyforce') || getvariable('forcempy') then + mpoptions += " --force " + else + return false unless File.atleast?(mponame,32) + mpochecksum = FileState.new.checksum(mponame) + return false if mpochecksum.empty? + # where does the checksum get into the file? + # maybe let texexec do it? + # solution: add one if not present or update when different + begin + mpydata = IO.read(mpyname) + if mpydata then + if mpydata =~ /^\%\s*mpochecksum\s*\:\s*([A-Z0-9]+)$/mo then + checksum = $1 + if mpochecksum == checksum then + return false + end + end + end + rescue + # no file + end + end + # return Kpse.runscript('makempy',mpname) + # only pdftex + flags = ['--noctx','--process','--batch','--once'] + result = runtexexec([mponame], flags, 1) + runcommand(["pstoedit","-ssp -dt -f mpost", pdfname,tmpname]) + tmpdata = IO.read(tmpname) + if tmpdata then + if mpy = openedfile(mpyname) then + mpy << "% mpochecksum: #{mpochecksum}\n" + tmpdata.scan(/beginfig(.*?)endfig/mo) do |s| + mpy << "begingraphictextfig#{s}endgraphictextfig\n" + end + mpy.close() + end + end + File.silentdelete(tmpname) + File.silentdelete(pdfname) + return true + end + + def checkmplabels(mpname) + mpname = File.suffixed(mpname,'mpt') + if File.atleast?(mpname,10) && (mp = File.silentopen(mpname)) then + labels = Hash.new + while str = mp.gets do + t = if str =~ /^%\s*setup\s*:\s*(.*)$/o then $1 else '' end + if str =~ /^%\s*figure\s*(\d+)\s*:\s*(.*)$/o then + labels[$1] = labels[$1] || '' + unless t.empty? then + labels[$1] += "#{t}\n" + t = '' + end + labels[$1] += "#{$2}\n" + end + end + mp.close + if labels.size>0 then + return labels + else + return nil + end + end + return nil + end + +end |