summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/scripts
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2006-11-16 01:02:47 +0000
committerKarl Berry <karl@freefriends.org>2006-11-16 01:02:47 +0000
commit3c85b55e2f245fb7edac7e445f107e6d0bb423ae (patch)
treec62b7f80edafe0455310d08e09f4218127bf9e87 /Master/texmf-dist/scripts
parente5ecff27a26ef59e1dbe9702677687fd7107be3a (diff)
context update
git-svn-id: svn://tug.org/texlive/trunk@2444 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts')
-rw-r--r--Master/texmf-dist/scripts/context/lua/scite-ctx.lua925
-rw-r--r--Master/texmf-dist/scripts/context/perl/makempy.pl6
-rw-r--r--Master/texmf-dist/scripts/context/perl/mptopdf.pl165
-rw-r--r--Master/texmf-dist/scripts/context/perl/pdftrimwhite.pl525
-rw-r--r--Master/texmf-dist/scripts/context/perl/texexec.pl74
-rw-r--r--Master/texmf-dist/scripts/context/perl/texexec.rme2
-rw-r--r--Master/texmf-dist/scripts/context/perl/texshow.pl82
-rw-r--r--Master/texmf-dist/scripts/context/perl/texutil.pl32
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/ctx.rb117
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/exa.rb406
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/file.rb10
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpse.rb93
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpse/drb.rb57
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpse/soap.rb79
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpse/trees.rb84
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpsedirect.rb34
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb483
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpseremote.rb116
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/kpserunner.rb87
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/merge.rb139
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/mp.rb167
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/pdf.rb34
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/state.rb2
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/switch.rb43
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/system.rb25
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/tex.rb1283
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/texutil.rb308
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/tool.rb2
-rw-r--r--Master/texmf-dist/scripts/context/ruby/base/variables.rb87
-rw-r--r--Master/texmf-dist/scripts/context/ruby/ctxtools.rb1251
-rw-r--r--Master/texmf-dist/scripts/context/ruby/fcd_start.rb32
-rw-r--r--Master/texmf-dist/scripts/context/ruby/graphics/gs.rb21
-rw-r--r--Master/texmf-dist/scripts/context/ruby/imgtopdf.rb86
-rw-r--r--Master/texmf-dist/scripts/context/ruby/mpstools.rb2
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb82
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newpstopdf.rb524
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newtexexec.rb613
-rw-r--r--Master/texmf-dist/scripts/context/ruby/newtexutil.rb100
-rw-r--r--Master/texmf-dist/scripts/context/ruby/pdftools.rb816
-rw-r--r--Master/texmf-dist/scripts/context/ruby/pstopdf.rb527
-rw-r--r--Master/texmf-dist/scripts/context/ruby/rlxtools.rb83
-rw-r--r--Master/texmf-dist/scripts/context/ruby/rscortool.rb63
-rw-r--r--Master/texmf-dist/scripts/context/ruby/rsfiltool.rb340
-rw-r--r--Master/texmf-dist/scripts/context/ruby/rslibtool.rb114
-rw-r--r--Master/texmf-dist/scripts/context/ruby/runtools.rb505
-rw-r--r--Master/texmf-dist/scripts/context/ruby/texexec.rb708
-rw-r--r--Master/texmf-dist/scripts/context/ruby/texmfstart.rb2268
-rw-r--r--Master/texmf-dist/scripts/context/ruby/textools.rb67
-rw-r--r--Master/texmf-dist/scripts/context/ruby/texutil.rb96
-rw-r--r--Master/texmf-dist/scripts/context/ruby/tmftools.rb61
-rw-r--r--Master/texmf-dist/scripts/context/ruby/www/admin.rb215
-rw-r--r--Master/texmf-dist/scripts/context/ruby/www/common.rb80
-rw-r--r--Master/texmf-dist/scripts/context/ruby/www/dir.rb155
-rw-r--r--Master/texmf-dist/scripts/context/ruby/www/exa.rb386
-rw-r--r--Master/texmf-dist/scripts/context/ruby/www/lib.rb1399
-rw-r--r--Master/texmf-dist/scripts/context/ruby/www/login.rb13
-rw-r--r--Master/texmf-dist/scripts/context/ruby/wwwclient.rb677
-rw-r--r--Master/texmf-dist/scripts/context/ruby/wwwserver.rb293
-rw-r--r--Master/texmf-dist/scripts/context/ruby/wwwwatch.rb483
-rw-r--r--Master/texmf-dist/scripts/context/ruby/xmltools.rb303
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/ctxtools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/exatools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/makempy.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/mpstools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/mptopdf.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/pdftools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/pstopdf.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/rlxtools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/runtools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/texexec.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/texfont.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/textools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/texutil.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/tmftools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/mswin/xmltools.bat2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/ctxtools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/exatools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/makempy2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/mpstools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/mptopdf2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/pdftools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/pstopdf2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/rlxtools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/runtools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/texexec2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/texfont2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/textools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/texutil2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/tmftools2
-rw-r--r--Master/texmf-dist/scripts/context/stubs/unix/xmltools2
90 files changed, 15214 insertions, 2676 deletions
diff --git a/Master/texmf-dist/scripts/context/lua/scite-ctx.lua b/Master/texmf-dist/scripts/context/lua/scite-ctx.lua
new file mode 100644
index 00000000000..5836bcbfb71
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/lua/scite-ctx.lua
@@ -0,0 +1,925 @@
+-- version : 1.0.0 - 07/2005
+-- author : Hans Hagen - PRAGMA ADE - www.pragma-ade.com
+-- copyright : public domain or whatever suits
+-- remark : part of the context distribution
+
+-- todo: name space for local functions
+
+-- loading: scite-ctx.properties
+
+-- # environment variable
+-- #
+-- # CTXSPELLPATH=t:/spell
+-- #
+-- # auto language detection
+-- #
+-- # % version =1.0 language=uk
+-- # <?xml version='1.0' language='uk' ?>
+
+-- ext.lua.startup.script=$(SciteDefaultHome)/scite-ctx.lua
+--
+-- # extension.$(file.patterns.context)=scite-ctx.lua
+-- # extension.$(file.patterns.example)=scite-ctx.lua
+--
+-- # ext.lua.reset=1
+-- # ext.lua.auto.reload=1
+-- # ext.lua.startup.script=t:/lua/scite-ctx.lua
+--
+-- ctx.menulist.default=\
+-- wrap=wrap_text|\
+-- unwrap=unwrap_text|\
+-- sort=sort_text|\
+-- document=document_text|\
+-- quote=quote_text|\
+-- compound=compound_text|\
+-- check=check_text
+--
+-- ctx.spellcheck.language=auto
+-- ctx.spellcheck.wordsize=4
+-- ctx.spellcheck.wordpath=ENV(CTXSPELLPATH)
+--
+-- ctx.spellcheck.wordfile.all=spell-uk.txt,spell-nl.txt
+--
+-- ctx.spellcheck.wordfile.uk=spell-uk.txt
+-- ctx.spellcheck.wordfile.nl=spell-nl.txt
+-- ctx.spellcheck.wordsize.uk=4
+-- ctx.spellcheck.wordsize.nl=4
+--
+-- command.name.21.*=CTX Action List
+-- command.subsystem.21.*=3
+-- command.21.*=show_menu $(ctx.menulist.default)
+-- command.groupundo.21.*=yes
+-- command.shortcut.21.*=Shift+F11
+--
+-- command.name.22.*=CTX Check Text
+-- command.subsystem.22.*=3
+-- command.22.*=check_text
+-- command.groupundo.22.*=yes
+-- command.shortcut.22.*=Ctrl+L
+--
+-- command.name.23.*=CTX Wrap Text
+-- command.subsystem.23.*=3
+-- command.23.*=wrap_text
+-- command.groupundo.23.*=yes
+-- command.shortcut.23.*=Ctrl+M
+--
+-- # command.21.*=check_text
+-- # command.21.*=dofile e:\context\lua\scite-ctx.lua
+
+-- generic functions
+
+local crlf = "\n"
+
+function traceln(str)
+ trace(str .. crlf)
+ io.flush()
+end
+
+table.len = table.getn
+table.join = table.concat
+
+function table.found(tab, str)
+ local l, r, p
+ if string.len(str) == 0 then
+ return false
+ else
+ l, r = 1, table.len(tab)
+ while l <= r do
+ p = math.floor((l+r)/2)
+ if str < tab[p] then
+ r = p - 1
+ elseif str > tab[p] then
+ l = p + 1
+ else
+ return true
+ end
+ end
+ return false
+ end
+end
+
+function string.grab(str, delimiter)
+ local list = {}
+ for snippet in string.gfind(str,delimiter) do
+ table.insert(list, snippet)
+ end
+ return list
+end
+
+function string.join(list, delimiter)
+ local size, str = table.len(list), ''
+ if size > 0 then
+ str = list[1]
+ for i = 2, size, 1 do
+ str = str .. delimiter .. list[i]
+ end
+ end
+ return str
+end
+
+function string.spacy(str)
+ if string.find(str,"^%s*$") then
+ return true
+ else
+ return false
+ end
+end
+
+function string.alphacmp(a,b,i) -- slow but ok
+ if i and i > 0 then
+ return string.lower(string.gsub(string.sub(a,i),'0',' ')) < string.lower(string.gsub(string.sub(b,i),'0',' '))
+ else
+ return string.lower(a) < string.lower(b)
+ end
+end
+
+function table.alphasort(list,i)
+ table.sort(list, function(a,b) return string.alphacmp(a,b,i) end)
+end
+
+function io.exists(filename)
+ local ok, result, message = pcall(io.open,filename)
+ if result then
+ io.close(result)
+ return true
+ else
+ return false
+ end
+end
+
+function os.envvar(str)
+ if os.getenv(str) ~= '' then
+ return os.getenv(str)
+ elseif os.getenv(string.upper(str)) ~= '' then
+ return os.getenv(string.upper(str))
+ elseif os.getenv(string.lower(str)) ~= '' then
+ return os.getenv(string.lower(str))
+ else
+ return ''
+ end
+end
+
+function string.expand(str)
+ return string.gsub(str, "ENV%((%w+)%)", os.envvar)
+end
+
+function string.strip(str)
+ return string.gsub(string.gsub(str,"^%s+",''),"%s+$",'')
+end
+
+function string.replace(original,pattern,replacement)
+ local str = string.gsub(original,pattern,replacement)
+-- print(str) -- indirect, since else str + nofsubs
+ return str -- indirect, since else str + nofsubs
+end
+
+-- support functions, maybe editor namespace
+
+-- function column_of_position(position)
+-- local line = editor:LineFromPosition(position)
+-- local oldposition = editor.CurrentPos
+-- local column = 0
+-- editor:GotoPos(position)
+-- while editor.CurrentPos ~= 0 and line == editor:LineFromPosition(editor.CurrentPos) do
+-- editor:CharLeft()
+-- column = column + 1
+-- end
+-- editor:GotoPos(oldposition)
+-- if line > 0 then
+-- return column -1
+-- else
+-- return column
+-- end
+-- end
+
+-- function line_of_position(position)
+-- return editor:LineFromPosition(position)
+-- end
+
+function extend_to_start()
+ local selectionstart = editor.SelectionStart
+ local selectionend = editor.SelectionEnd
+ local line = editor:LineFromPosition(selectionstart)
+ if line > 0 then
+ while line == editor:LineFromPosition(selectionstart-1) do
+ selectionstart = selectionstart - 1
+ editor:SetSel(selectionstart,selectionend)
+ end
+ else
+ selectionstart = 0
+ end
+ editor:SetSel(selectionstart,selectionend)
+ return selectionstart
+end
+
+function extend_to_end() -- editor:LineEndExtend() does not work
+ local selectionstart = editor.SelectionStart
+ local selectionend = editor.SelectionEnd
+ local line = editor:LineFromPosition(selectionend)
+ while line == editor:LineFromPosition(selectionend+1) do
+ selectionend = selectionend + 1
+ editor:SetSel(selectionstart,selectionend)
+ end
+ editor:SetSel(selectionstart,selectionend)
+ return selectionend
+end
+
+function getfiletype()
+ local firstline = editor:GetLine(0)
+ if editor.Lexer == SCLEX_TEX then
+ return 'tex'
+ elseif editor.Lexer == SCLEX_XML then
+ return 'xml'
+ elseif string.find(firstline,"^%%") then
+ return 'tex'
+ elseif string.find(firstline,"^<%?xml") then
+ return 'xml'
+ else
+ return 'unknown'
+ end
+end
+
+-- inspired by LuaExt's scite_Files
+
+function get_dir_list(mask)
+ local f
+ if props['PLAT_GTK'] and props['PLAT_GTK'] ~= "" then
+ f = io.popen('ls -1 ' .. mask)
+ else
+ mask = string.gsub(mask, '/','\\')
+ local tmpfile = 'scite-ctx.tmp'
+ local cmd = 'dir /b "' .. mask .. '" > ' .. tmpfile
+ os.execute(cmd)
+ f = io.open(tmpfile)
+ end
+ local files = {}
+ if not f then -- path check added
+ return files
+ end
+ for line in f:lines() do
+ table.insert(files, line)
+ end
+ f:close()
+ return files
+end
+
+-- banner
+
+print("loading scite-ctx.lua definition file")
+print("")
+print("- see scite-ctx.properties for configuring info")
+print("")
+print("- ctx.spellcheck.wordpath set to " .. props['ctx.spellcheck.wordpath'])
+if string.find(string.lower(props['ctx.spellcheck.wordpath']), "ctxspellpath") then
+ if os.getenv('ctxspellpath') then
+ print("- ctxspellpath set to " .. os.getenv('CTXSPELLPATH'))
+ else
+ print("- 'ctxspellpath is not set")
+ end
+ print("- ctx.spellcheck.wordpath expands to " .. string.expand(props['ctx.spellcheck.wordpath']))
+end
+print("")
+print("- ctx.wraptext.length is set to " .. props['ctx.wraptext.length'])
+if props['ctx.helpinfo'] ~= '' then
+ print("- key bindings:")
+ print("")
+ print(string.replace(string.strip(props['ctx.helpinfo']),"%s*\|%s*","\n")) -- indirect, since else str + nofsubs
+end
+print("")
+print("- recognized first lines:")
+print("")
+print("xml <?xml version='1.0' language='nl'")
+print("tex % language=nl")
+
+
+-- text functions
+
+-- written while listening to Talk Talk
+
+local magicstring = string.rep("<ctx-crlf/>", 2)
+
+function wrap_text()
+
+ -- We always go to the end of a line, so in fact some of
+ -- the variables set next are not needed.
+
+ local length = props["ctx.wraptext.length"]
+
+ if length == '' then length = 80 else length = tonumber(length) end
+
+ local startposition = editor.SelectionStart
+ local endposition = editor.SelectionEnd
+
+ if startposition == endposition then return end
+
+ editor:LineEndExtend()
+
+ startposition = editor.SelectionStart
+ endposition = editor.SelectionEnd
+
+ -- local startline = line_of_position(startposition)
+ -- local endline = line_of_position(endposition)
+ -- local startcolumn = column_of_position(startposition)
+ -- local endcolumn = column_of_position(endposition)
+ --
+ -- editor:SetSel(startposition,endposition)
+
+ local startline = props['SelectionStartLine']
+ local endline = props['SelectionEndLine']
+ local startcolumn = props['SelectionStartColumn'] - 1
+ local endcolumn = props['SelectionEndColumn'] - 1
+
+ local indentation = string.rep(' ', startcolumn)
+-- local selection = string.gsub(editor:GetSelText(),"[\n\r][\n\r]+", ' ' .. magicstring .. ' ')
+ local selection = string.gsub(editor:GetSelText(),"[\n\r][\n\r]", "\n")
+ local selection = string.gsub(selection,"\n\n+", ' ' .. magicstring .. ' ')
+ local replacement = ''
+ local templine = ''
+
+ selection = string.gsub(selection,"^%s", '')
+
+ for snippet in string.gfind(selection, "%S+") do
+ if snippet == magicstring then
+ replacement = replacement .. templine .. "\n\n"
+ templine = ''
+ elseif string.len(templine) + string.len(snippet) > length then
+ replacement = replacement .. templine .. "\n"
+ templine = indentation .. snippet
+ elseif string.len(templine) == 0 then
+ templine = indentation .. snippet
+ else
+ templine = templine .. ' ' .. snippet
+ end
+ end
+
+ replacement = replacement .. templine
+ replacement = string.gsub(replacement, "^%s+", '')
+
+ if endcolumn == 0 then
+ replacement = replacement .. "\n"
+ end
+
+ editor:ReplaceSel(replacement)
+
+end
+
+function unwrap_text()
+
+ local startposition = editor.SelectionStart
+ local endposition = editor.SelectionEnd
+
+ if startposition == endposition then return end
+
+ editor:HomeExtend()
+ editor:LineEndExtend()
+
+ startposition = editor.SelectionStart
+ endposition = editor.SelectionEnd
+
+ local magicstring = string.rep("<multiplelines/>", 2)
+ local selection = string.gsub(editor:GetSelText(),"[\n\r][\n\r]+", ' ' .. magicstring .. ' ')
+ local replacement = ''
+
+ for snippet in string.gfind(selection, "%S+") do
+ if snippet == magicstring then
+ replacement = replacement .. "\n"
+ else
+ replacement = replacement .. snippet .. "\n"
+ end
+ end
+
+ if endcolumn == 0 then replacement = replacement .. "\n" end
+
+ editor:ReplaceSel(replacement)
+
+end
+
+function sort_text()
+
+ local startposition = editor.SelectionStart
+ local endposition = editor.SelectionEnd
+
+ if startposition == endposition then return end
+
+ -- local startcolumn = column_of_position(startposition)
+ -- local endcolumn = column_of_position(endposition)
+ --
+ -- editor:SetSel(startposition,endposition)
+
+ local startline = props['SelectionStartLine']
+ local endline = props['SelectionEndLine']
+ local startcolumn = props['SelectionStartColumn'] - 1
+ local endcolumn = props['SelectionEndColumn'] - 1
+
+ startposition = extend_to_start()
+ endposition = extend_to_end()
+
+ local selection = string.gsub(editor:GetSelText(), "%s*$", '')
+
+ list = string.grab(selection,"[^\n\r]+")
+ table.alphasort(list, startcolumn)
+ local replacement = table.concat(list, "\n")
+
+ editor:GotoPos(startposition)
+ editor:SetSel(startposition,endposition)
+
+ if endcolumn == 0 then replacement = replacement .. "\n" end
+
+ editor:ReplaceSel(replacement)
+
+end
+
+function document_text()
+
+ local startposition = editor.SelectionStart
+ local endposition = editor.SelectionEnd
+
+ if startposition == endposition then return end
+
+ startposition = extend_to_start()
+ endposition = extend_to_end()
+
+ editor:SetSel(startposition,endposition)
+
+ local filetype = getfiletype()
+
+ local replacement = ''
+ for i = editor:LineFromPosition(startposition), editor:LineFromPosition(endposition) do
+ local str = editor:GetLine(i)
+ if filetype == 'xml' then
+ if string.find(str,"^<%!%-%- .* %-%->%s*$") then
+ replacement = replacement .. string.gsub(str,"^<%!%-%- (.*) %-%->(%s*)$", "%1\n")
+ elseif not string.spacy(str) then
+ replacement = replacement .. '<!-- ' .. string.gsub(str,"(%s*)$", '') .. " -->\n"
+ else
+ replacement = replacement .. str
+ end
+ else
+ if string.find(str,"^%%D%s+$") then
+ replacement = replacement .. "\n"
+ elseif string.find(str,"^%%D ") then
+ replacement = replacement .. string.gsub(str,"^%%D ", '')
+ else
+ replacement = replacement .. '%D ' .. str
+ end
+ end
+ end
+
+ editor:ReplaceSel(string.gsub(replacement, "[\n\r]$", ''))
+
+end
+
+function quote_text()
+
+ local filetype, leftquotation, rightquotation = getfiletype(), '', ''
+
+ if filetype == 'xml' then
+ leftquotation, rightquotation = "<quotation>", "</quotation>"
+ leftquote, rightquote = "<quotation>", "</quote>"
+ else
+ leftquotation, rightquotation = "\\quotation {", "}"
+ leftquote, rightquote = "\\quote {", "}"
+ end
+
+ local replacement = editor:GetSelText()
+ replacement = string.gsub(replacement, "\`\`(.-)\'\'", leftquotation .. "%1" .. rightquotation)
+ replacement = string.gsub(replacement, "\"(.-)\"", leftquotation .. "%1" .. rightquotation)
+ replacement = string.gsub(replacement, "\`(.-)\'", leftquote .. "%1" .. rightquote )
+ replacement = string.gsub(replacement, "\'(.-)\'", leftquote .. "%1" .. rightquote )
+ editor:ReplaceSel(replacement)
+
+end
+
+function compound_text()
+
+ local filetype = getfiletype()
+
+ if filetype == 'xml' then
+ editor:ReplaceSel(string.gsub(editor:GetSelText(),"(>[^<%-][^<%-]+)([-\/])(%w%w+)","%1<compound token='%2'/>%3"))
+ else
+ editor:ReplaceSel(string.gsub(editor:GetSelText(),"([^\|])([-\/]+)([^\|])","%1|%2|%3"))
+ end
+
+end
+
+-- written while listening to Alanis Morissette's acoustic
+-- Jagged Little Pill and Tori Amos' Beekeeper after
+-- reinstalling on my good old ATH-7
+
+local language = props["ctx.spellcheck.language"]
+local wordsize = props["ctx.spellcheck.wordsize"]
+local wordpath = props["ctx.spellcheck.wordpath"]
+
+if language == '' then language = 'uk' end
+if wordsize == '' then wordsize = 4 else wordsize = tonumber(wordsize) end
+
+local wordfile = ""
+local wordlist = {}
+local worddone = 0
+
+-- we use wordlist as a hash so that we can add entries without the
+-- need to sort and also use a fast (built in) search
+
+-- function kpsewhich_file(filename,filetype,progname)
+-- local progflag, typeflag = '', ''
+-- local tempname = os.tmpname()
+-- if progname then
+-- progflag = " --progname=" .. progname .. " "
+-- end
+-- if filetype then
+-- typeflag = " --format=" .. filetype .. " "
+-- end
+-- local command = "kpsewhich" .. progflag .. typeflag .. " " .. filename .. " > " .. tempname
+-- os.execute(command)
+-- for line in io.lines(tempname) do
+-- return string.gsub(line, "\s*$", '')
+-- end
+-- end
+
+function check_text()
+
+ local dlanguage = props["ctx.spellcheck.language"]
+ local dwordsize = props["ctx.spellcheck.wordsize"]
+ local dwordpath = props["ctx.spellcheck.wordpath"]
+
+ if dlanguage ~= '' then dlanguage = tostring(language) end
+ if dwordsize ~= '' then dwordsize = tonumber(wordsize) end
+
+ local firstline, skipfirst = editor:GetLine(0), false
+ local filetype, wordskip, wordgood = getfiletype(), '', ''
+
+ if filetype == 'tex' then
+ wordskip = "\\"
+ elseif filetype == 'xml' then
+ wordskip = "<"
+ wordgood = ">"
+ end
+
+ if props["ctx.spellcheck.language"] == 'auto' then
+ if filetype == 'tex' then
+ -- % version =1.0 language=uk
+ firstline = string.gsub(firstline, "^%%%s*", '')
+ firstline = string.gsub(firstline, "%s*$", '')
+ for key, val in string.gfind(firstline,"(%w+)=(%w+)") do
+ if key == "language" then
+ language = val
+ traceln("auto document language " .. "'" .. language .. "' (tex)")
+ end
+ end
+ skipfirst = true
+ elseif filetype == 'xml' then
+ -- <?xml version='1.0' language='uk' ?>
+ firstline = string.gsub(firstline, "^%<%?xml%s*", '')
+ firstline = string.gsub(firstline, "%s*%?%>%s*$", '')
+ for key, val in string.gfind(firstline,"(%w+)=[\"\'](.-)[\"\']") do
+ if key == "language" then
+ language = val
+ traceln("auto document language " .. "'" .. language .. "' (xml)")
+ end
+ end
+ skipfirst = true
+ end
+ end
+
+ local fname = props["ctx.spellcheck.wordfile." .. language]
+ local fsize = props["ctx.spellcheck.wordsize." .. language]
+
+ if fsize ~= '' then wordsize = tonumber(fsize) end
+
+ if fname ~= '' and fname ~= wordfile then
+ wordfile, worddone, wordlist = fname, 0, {}
+ for filename in string.gfind(wordfile,"[^%,]+") do
+ if wordpath ~= '' then
+ filename = string.expand(wordpath) .. '/' .. filename
+ end
+ if io.exists(filename) then
+ traceln("loading " .. filename)
+ for line in io.lines(filename) do
+ if not string.find(line,"^[\%\#\-]") then
+ str = string.gsub(line,"%s*$", '')
+ rawset(wordlist,str,true) -- table.insert(wordlist,str)
+ worddone = worddone + 1
+ end
+ end
+ else
+ traceln("unknown file '" .. filename .."'")
+ end
+ end
+ traceln(worddone .. " words loaded")
+ end
+
+ reset_text()
+
+ if worddone == 0 then
+ traceln("no (valid) language or wordfile specified")
+ else
+ traceln("start checking")
+ if wordskip ~= '' then
+ traceln("ignoring " .. wordskip .. "..." .. wordgood)
+ end
+ local i, j, lastpos, startpos, endpos, snippet, len, first = 0, 0, -1, 0, 0, '', 0, 0
+ local ok, skip, ch = false, false, ''
+ if skipfirst then first = string.len(firstline) end
+ for k = first, editor.TextLength do
+ ch = editor:textrange(k,k+1)
+ if wordgood ~= '' and ch == wordgood then
+ skip = false
+ elseif ch == wordskip then
+ skip = true
+ end
+ if string.find(ch,"%w") and not string.find(ch,"%d") then
+ if not skip then
+ if ok then
+ endpos = k
+ else
+ startpos = k
+ endpos = k
+ ok = true
+ end
+ end
+ elseif ok and not skip then
+ len = endpos - startpos + 1
+ if len >= wordsize then
+ snippet = editor:textrange(startpos,endpos+1)
+ i = i + 1
+ if wordlist[snippet] or wordlist[string.lower(snippet)] then -- table.found(wordlist,snippet)
+ j = j + 1
+ else
+ editor:StartStyling(startpos,INDICS_MASK)
+ editor:SetStyling(len,INDIC2_MASK) -- INDIC0_MASK+2
+ end
+ end
+ ok = false
+ elseif wordgood == '' then
+ skip = (ch == wordskip)
+ end
+ end
+ traceln(i .. " words checked, " .. (i-j) .. " errors")
+ end
+
+end
+
+function reset_text()
+ editor:StartStyling(0,INDICS_MASK)
+ editor:SetStyling(editor.TextLength,INDIC_PLAIN)
+end
+
+-- menu
+
+local menuactions = {}
+local menufunctions = {}
+
+function UserListShow(menutrigger, menulist)
+ local menuentries = {}
+ local list = string.grab(menulist,"[^%|]+")
+ menuactions = {}
+ for i=1, table.len(list) do
+ if list[i] ~= '' then
+ for key, val in string.gfind(list[i],"%s*(.+)=(.+)%s*") do
+ table.insert(menuentries,key)
+ rawset(menuactions,key,val)
+ end
+ end
+ end
+ local menustring = table.join(menuentries,'|')
+ if menustring == "" then
+ traceln("There are no templates defined for this file type.")
+ else
+ editor.AutoCSeparator = string.byte('|')
+ editor:UserListShow(menutrigger,menustring)
+ editor.AutoCSeparator = string.byte(' ')
+ end
+end
+
+function OnUserListSelection(trigger,choice)
+ if menufunctions[trigger] and menuactions[choice] then
+ return menufunctions[trigger](menuactions[choice])
+ else
+ return false
+ end
+end
+
+-- main menu
+
+local menutrigger = 12
+
+function show_menu(menulist)
+ UserListShow(menutrigger, menulist)
+end
+
+function process_menu(action)
+ if not string.find(action,"%(%)$") then
+ assert(loadstring(action .. "()"))()
+ else
+ assert(loadstring(action))()
+ end
+end
+
+menufunctions[12] = process_menu
+
+-- templates
+
+local templatetrigger = 13
+
+-- local ctx_template_paths = { "./ctx-templates", "../ctx-templates", "../../ctx-templates" }
+-- local ctx_auto_templates = false
+-- local ctx_template_list = ""
+-- local ctx_dir_list = { }
+-- local ctx_dir_name = "./ctx-templates"
+
+-- local ctx_path_list = {}
+-- local ctx_path_done = {}
+
+-- function ctx_list_loaded()
+-- return ctx_dir_list and table.getn(ctx_dir_list) > 0
+-- end
+
+-- function insert_template(templatelist)
+-- if props["ctx.template.scan"] == "yes" then
+-- local current = props["FileDir"] .. "+" .. props["FileExt"] -- no name
+-- local rescan = props["ctx.template.rescan"] == "yes"
+-- local suffix = props["ctx.template.suffix."..props["FileExt"]] -- alas, no suffix expansion here
+-- if rescan then
+-- print("re-scanning enabled")
+-- end
+-- if current ~= ctx_file_path then
+-- rescan = true
+-- ctx_file_path = current
+-- ctx_file_done = false
+-- ctx_template_list = ""
+-- end
+-- if not ctx_file_done or rescan then
+-- local pattern = "*.*"
+-- for i, pathname in ipairs(ctx_template_paths) do
+-- print("scanning " .. pathname .. " for " .. pattern)
+-- ctx_dir_name = pathname
+-- ctx_dir_list = get_dir_list(pathname .. "/" .. pattern)
+-- if ctx_list_loaded() then
+-- break
+-- end
+-- end
+-- ctx_file_done = true
+-- end
+-- if ctx_list_loaded() then
+-- ctx_template_list = ""
+-- local pattern = "%." .. suffix .. "$"
+-- for j, filename in ipairs(ctx_dir_list) do
+-- if string.find(filename,pattern) then
+-- local menuname = string.gsub(filename,"%..-$","")
+-- if ctx_template_list ~= "" then
+-- ctx_template_list = ctx_template_list .. "|"
+-- end
+-- ctx_template_list = ctx_template_list .. menuname .. "=" .. ctx_dir_name .. "/" .. filename
+-- end
+-- end
+-- else
+-- print("no template files found")
+-- end
+-- if ctx_template_list == "" then
+-- ctx_auto_templates = false
+-- print("no file related templates found")
+-- else
+-- ctx_auto_templates = true
+-- templatelist = ctx_template_list
+-- end
+-- end
+-- if templatelist ~= "" then
+-- UserListShow(templatetrigger, templatelist)
+-- end
+-- end
+
+local ctx_template_paths = { "./ctx-templates", "../ctx-templates", "../../ctx-templates" }
+local ctx_auto_templates = false
+local ctx_template_list = ""
+
+local ctx_path_list = {}
+local ctx_path_done = {}
+local ctx_path_name = {}
+
+function ctx_list_loaded(path)
+ return ctx_path_list[path] and table.getn(ctx_path_list[path]) > 0
+end
+
+function insert_template(templatelist)
+ if props["ctx.template.scan"] == "yes" then
+ local path = props["FileDir"]
+ local rescan = props["ctx.template.rescan"] == "yes"
+ local suffix = props["ctx.template.suffix." .. props["FileExt"]] -- alas, no suffix expansion here
+ local current = path .. "+" .. props["FileExt"]
+ if rescan then
+ print("re-scanning enabled")
+ end
+ ctx_template_list = ""
+ if not ctx_path_done[path] or rescan then
+ local pattern = "*.*"
+ for i, pathname in ipairs(ctx_template_paths) do
+ print("scanning " .. string.gsub(path,"\\","/") .. "/" .. pathname)
+ ctx_path_name[path] = pathname
+ ctx_path_list[path] = get_dir_list(pathname .. "/" .. pattern)
+ if ctx_list_loaded(path) then
+ print("finished locating template files")
+ break
+ end
+ end
+ if ctx_list_loaded(path) then
+ print(table.getn(ctx_path_list[path]) .. " template files found")
+ else
+ print("no template files found")
+ end
+ end
+ if ctx_list_loaded(path) then
+ ctx_template_list = ""
+ local pattern = "%." .. suffix .. "$"
+ local n = 0
+ for j, filename in ipairs(ctx_path_list[path]) do
+ if string.find(filename,pattern) then
+ n = n + 1
+ local menuname = string.gsub(filename,"%..-$","")
+ if ctx_template_list ~= "" then
+ ctx_template_list = ctx_template_list .. "|"
+ end
+ ctx_template_list = ctx_template_list .. menuname .. "=" .. ctx_path_name[path] .. "/" .. filename
+ end
+ end
+ if not ctx_path_done[path] then
+ print(n .. " suitable template files found")
+ end
+ end
+ ctx_path_done[path] = true
+ if ctx_template_list == "" then
+ ctx_auto_templates = false
+ else
+ ctx_auto_templates = true
+ templatelist = ctx_template_list
+ end
+ else
+ ctx_auto_templates = false
+ end
+ if templatelist ~= "" then
+ UserListShow(templatetrigger, templatelist)
+ end
+end
+
+
+-- ctx.template.[whatever].[filetype]
+-- ctx.template.[whatever].data.[filetype]
+-- ctx.template.[whatever].file.[filetype]
+-- ctx.template.[whatever].list.[filetype]
+
+function process_template_one(action)
+ local text = nil
+ if ctx_auto_templates then
+ local f = io.open(action,"r")
+ if f then
+ text = string.gsub(f:read("*all"),"\n$","")
+ f:close()
+ else
+ print("unable to auto load template file " .. text)
+ text = nil
+ end
+ end
+ if not text or text == "" then
+ text = props["ctx.template." .. action .. ".file"]
+ if not text or text == "" then
+ text = props["ctx.template." .. action .. ".data"]
+ if not text or text == "" then
+ text = props["ctx.template." .. action]
+ end
+ else
+ local f = io.open(text,"r")
+ if f then
+ text = string.gsub(f:read("*all"),"\n$","")
+ f:close()
+ else
+ print("unable to load template file " .. text)
+ text = nil
+ end
+ end
+ end
+ if text then
+ text = string.replace(text,"\\n","\n")
+ local pos = string.find(text,"%?")
+ text = string.replace(text,"%?","")
+ editor:insert(editor.CurrentPos,text)
+ if pos then
+ editor.CurrentPos = editor.CurrentPos + pos - 1
+ editor.SelectionStart = editor.CurrentPos
+ editor.SelectionEnd = editor.CurrentPos
+ editor:GotoPos(editor.CurrentPos)
+ end
+ end
+end
+
+menufunctions[13] = process_template_one
+menufunctions[14] = process_template_two
+
+-- command.name.26.*=Open Logfile
+-- command.subsystem.26.*=3
+-- command.26.*=open_log
+-- command.save.before.26.*=2
+-- command.groupundo.26.*=yes
+-- command.shortcut.26.*=Ctrl+E
+
+function open_log()
+ scite.Open(props['FileName'] .. ".log")
+end
diff --git a/Master/texmf-dist/scripts/context/perl/makempy.pl b/Master/texmf-dist/scripts/context/perl/makempy.pl
index 49fd9bbd21a..f263dd4259d 100644
--- a/Master/texmf-dist/scripts/context/perl/makempy.pl
+++ b/Master/texmf-dist/scripts/context/perl/makempy.pl
@@ -46,8 +46,6 @@ my $format = "plain" ; # can be "context" for plain users too
"force" => \$force ,
"pdftops" => \$pmethod ,
"xpdf" => \$pmethod ,
-# "acrobat" => \$amethod , # nowadays the reader is not that clear about this being permitted
- "reader" => \$amethod ,
"gs" => \$gmethod ,
"ghostscript" => \$gmethod ,
"noclean" => \$noclean ) ;
@@ -202,15 +200,13 @@ sub show_help_info
report ("--force : force processing (ignore checksum)" ) ;
report ("--silent : don't show messages" ) ;
print "\n" ;
- report ("--acrobat : use acrobat (reader) for pdf->ps (on unix)") ;
report ("--pdftops : use pdftops (xpdf) pdf->ps") ;
report ("--ghostscript : use ghostscript (gs) for pdf->ps") ;
print "\n" ;
report ("input file : metapost file with graphics") ;
report ("programs needed : texexec and english context") ;
report (" : pdftops from the xpdf suite, or") ; # page size buggy
- report (" : pdf2ps and ghostscript, or") ;
- report (" : acrobat reader for unix, and") ;
+ report (" : pdf2ps and ghostscript, and") ;
report (" : pstoedit and ghostscript") ;
report ("output file : metapost file with pictures") ;
exit }
diff --git a/Master/texmf-dist/scripts/context/perl/mptopdf.pl b/Master/texmf-dist/scripts/context/perl/mptopdf.pl
index efbc5ba0122..f85c36891b9 100644
--- a/Master/texmf-dist/scripts/context/perl/mptopdf.pl
+++ b/Master/texmf-dist/scripts/context/perl/mptopdf.pl
@@ -5,7 +5,7 @@ eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $
#D \module
#D [ file=mptopdf.pl,
-#D version=2005.10.31,
+#D version=2000.05.29,
#D title=converting MP to PDF,
#D subtitle=\MPTOPDF,
#D author=Hans Hagen,
@@ -22,6 +22,7 @@ eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $
use Config ;
use Getopt::Long ;
use strict ;
+use File::Basename ;
$Getopt::Long::passthrough = 1 ; # no error message
$Getopt::Long::autoabbrev = 1 ; # partial switch accepted
@@ -36,88 +37,118 @@ my $PassOn = '' ;
"passon" => \$PassOn,
"latex" => \$Latex ) ;
-my $program = "MPtoPDF 1.3" ;
-my $pattern = $ARGV[0] ;
+my $program = "MPtoPDF 1.3.2" ;
+my $pattern = "@ARGV" ; # was $ARGV[0]
+my $miktex = 0 ;
my $done = 0 ;
my $report = '' ;
my $texlatexswitch = " --tex=latex --format=latex " ;
my $mplatexswitch = " --tex=latex " ;
-my $dosish = ($Config{'osname'} =~ /^(ms)?dos|^os\/2|^(ms|cyg)win/i) ;
-my $miktex = ($ENV{"TEXSYSTEM"} =~ /miktex/io);
-my $escapeshell = ( ($ENV{'SHELL'}) && ($ENV{'SHELL'} =~ m/sh/i ));
+my $dosish = ($Config{'osname'} =~/^(ms)?dos|^os\/2|^mswin/i) ;
+my $escapeshell = (($ENV{'SHELL'}) && ($ENV{'SHELL'} =~ m/sh/i ));
+
+if ($ENV{"TEXSYSTEM"}) {
+ $miktex = ($ENV{"TEXSYSTEM"} =~ /miktex/io) ;
+}
my @files ;
my $command = my $mpbin = '' ;
-sub CopyFile # agressive copy, works for open files like in gs
- { my ($From,$To) = @_ ;
- return unless open(INP,"<$From") ; binmode INP ;
- return unless open(OUT,">$To") ; binmode OUT ;
- while (<INP>) { print OUT $_ }
+# agressive copy, works for open files like in gs
+
+sub CopyFile {
+ my ($From,$To) = @_ ;
+ return unless open(INP,"<$From") ;
+ return unless open(OUT,">$To") ;
+ binmode INP ;
+ binmode OUT ;
+ while (<INP>) {
+ print OUT $_ ;
+ }
close (INP) ;
- close (OUT) }
-
-if (($pattern eq '')||($Help))
- { print "\n$program : provide MP output file (or pattern)\n" ;
- exit }
-elsif ($pattern =~ /\.mp$/io)
- { shift @ARGV ; my $rest = join(" ", @ARGV) ;
- if (open(INP,$pattern))
- { while (<INP>)
- { if (/(documentstyle|documentclass|begin\{document\})/io)
- { $Latex = 1 ; last } }
- close (INP) }
- if ($RawMP)
- { if ($Latex)
- { $rest .= " $mplatexswitch" }
+ close (OUT) ;
+}
+
+if (($pattern eq '')||($Help)) {
+ print "\n$program : provide MP output file (or pattern)\n" ;
+ exit ;
+} elsif ($pattern =~ /\.mp$/io) {
+ shift @ARGV ; my $rest = join(" ", @ARGV) ;
+ if (open(INP,$pattern)) {
+ while (<INP>) {
+ if (/(documentstyle|documentclass|begin\{document\})/io) {
+ $Latex = 1 ; last ;
+ }
+ }
+ close (INP) ;
+ }
+ if ($RawMP) {
+ if ($Latex) {
+ $rest .= " $mplatexswitch" ;
+ }
if ($MetaFun) {
- $mpbin = 'mpost --mem=metafun --progname=mpost' ;
+ $mpbin = "mpost --progname=mpost --mem=metafun" ;
} else {
- $mpbin = 'mpost --mem=mpost' ;
+ $mpbin = "mpost --mem=mpost" ;
}
+ } else {
+ if ($Latex) {
+ $rest .= " $texlatexswitch" ;
+ }
+ $mpbin = "texexec --mptex $PassOn" ;
}
- else
- { if ($Latex)
- { $rest .= " $texlatexswitch" }
- $mpbin = 'texexec --mptex $PassOn' }
+ print "\n$program : running '$mpbin'\n" ;
my $error = system ("$mpbin $rest $pattern") ;
- if ($error)
- { print "\n$program : error while processing mp file\n" ; exit }
- else
- { $pattern =~ s/\.mp$//io ;
- @files = glob "$pattern.*" } }
-elsif (-e $pattern)
- { @files = ($pattern) }
-elsif ($pattern =~ /.\../o)
- { @files = glob "$pattern" }
-else
- { $pattern .= '.*' ;
- @files = glob "$pattern" }
-
-foreach my $file (@files)
- { $_ = $file ;
- if (s/\.(\d+|mps)$// && -e $file)
- { if ($miktex)
- { $command = "pdfetex -undump=mptopdf" }
- else
- { $command = "pdfetex --fmt=mptopdf --progname=context" }
- if ($dosish)
- { $command = "$command \\relax $file" }
- else
- { $command = "$command \\\\relax $file" }
- #~ print $command ;
+ if ($error) {
+ print "\n$program : error while processing mp file\n" ;
+ exit ;
+ } else {
+ $pattern =~ s/\.mp$//io ;
+ @files = glob "$pattern.*" ;
+ }
+} elsif (-e $pattern) {
+ @files = ($pattern) ;
+} elsif ($pattern =~ /.\../o) {
+ @files = glob "$pattern" ;
+} else {
+ $pattern .= '.*' ;
+ @files = glob "$pattern" ;
+}
+
+foreach my $file (@files) {
+ $_ = $file ;
+ if (s/\.(\d+|mps)$// && -e $file) {
+ if ($miktex) {
+ $command = "pdfetex -undump=mptopdf" ;
+ } else {
+ $command = "pdfetex -fmt=mptopdf -progname=context" ;
+ }
+ if ($dosish) {
+ $command = "$command \\relax $file" ;
+ } else {
+ $command = "$command \\\\relax $file" ;
+ }
system($command) ;
- rename ("$_.pdf", "$_-$1.pdf") ;
- if (-e "$_.pdf") { CopyFile ("$_.pdf", "$_-$1.pdf") }
- if ($done) { $report .= " +" }
+ my $pdfsrc = basename($_).".pdf";
+ rename ($pdfsrc, "$_-$1.pdf") ;
+ if (-e $pdfsrc) {
+ CopyFile ($pdfsrc, "$_-$1.pdf") ;
+ }
+ if ($done) {
+ $report .= " +" ;
+ }
$report .= " $_-$1.pdf" ;
- ++$done } }
+ ++$done ;
+ }
+}
-if ($report eq '')
- { $report = '*' }
+if ($report eq '') {
+ $report = '*' ;
+}
-if ($done)
- { print "\n$program : $pattern is converted to$report\n" }
-else
- { print "\n$program : no filename matches $pattern\n" }
+if ($done) {
+ print "\n$program : $pattern is converted to$report\n" ;
+} else {
+ print "\n$program : no filename matches $pattern\n" ;
+}
diff --git a/Master/texmf-dist/scripts/context/perl/pdftrimwhite.pl b/Master/texmf-dist/scripts/context/perl/pdftrimwhite.pl
new file mode 100644
index 00000000000..08c1de5d778
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/perl/pdftrimwhite.pl
@@ -0,0 +1,525 @@
+eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q'
+ if 0;
+
+#D \module
+#D [ file=pdftrimwhite.pl,
+#D version=2000.07.13,
+#D title=PDF postprocessing,
+#D subtitle=cropping whitespace from pdf files,
+#D author=Hans Hagen,
+#D date=\currentdate,
+#D copyright=PRAGMA ADE]
+
+#C This module is part of the \CONTEXT\ macro||package and is
+#C therefore copyrighted by \PRAGMA. See readme.pdf for
+#C details.
+
+#D This script can be used to crop margins that contain
+#D useless information from a \PDF\ image. It does so by:
+#D
+#D \startitemize[packed,n]
+#D \som cropping the image into an alternative file
+#D \som determining the boundingbox of the alternative
+#D \som cropping the image into a resulting file
+#D \stoppacked
+#D
+#D In the process, some checks are carried out. Step~1 is
+#D taken care of by \PDFTEX, step~2 by \GHOSTSCRIPT, using a
+#D file generated by \PDFTOPS, and \PDFTEX\ is responsible
+#D for step~3.
+#D
+#D \startuseMPgraphic{original}
+#D numeric n ; n = 1cm ;
+#D path p ; p := fullsquare xyscaled (8n,12n) ;
+#D path q ; q := fullsquare xyscaled (2n,3n) shifted (n,n) ;
+#D path r ; r := ((0,0)--(3n,0)) shifted (0, 5.5n) ;
+#D path s ; s := ((0,0)--(3n,0)) shifted (0,-5.5n) ;
+#D path t ; t := (-2n,-4n) ;
+#D path u ; u := p enlarged -.75n ;
+#D path v ; v := p enlarged (-1.75n,-2n) shifted (n,1.25n) ;
+#D path w ; w := q enlarged .25n ;
+#D fill p withcolor .7white ;
+#D fill q withcolor .7green ;
+#D draw r withpen pencircle scaled .25n withcolor .7green ;
+#D draw s withpen pencircle scaled .25n withcolor .7green ;
+#D draw t withpen pencircle scaled .50n withcolor .7green ;
+#D draw u withpen pencircle scaled .10n withcolor white ;
+#D draw v withpen pencircle scaled .10n withcolor .7red ;
+#D draw w withpen pencircle scaled .10n ;
+#D verbatimtex \tttf \setupframed[frame=off,align=left] etex ;
+#D label (btex \framed{crap} etex, center r) ;
+#D label (btex \framed{crap} etex, center s) ;
+#D label (btex \framed{crap} etex, center t) ;
+#D label (btex \framed{graphic} etex, center q) ;
+#D label.urt(btex \framed{page} etex, llcorner p) ;
+#D label.urt(btex \framed{crop} etex, llcorner u) ;
+#D label.lft(btex \framed{leftcrop\\
+#D rightcrop\\
+#D topcrop\\
+#D bottomcrop} etex, .5[ulcorner v,llcorner v]) ;
+#D label.bot(btex \framed{offset} etex, .5[llcorner w,lrcorner w]) ;
+#D \stopuseMPgraphic
+#D
+#D \placefigure
+#D [here][fig:cropcrap]
+#D {Crops and offsets.}
+#D {\useMPgraphic{original}}
+#D
+#D The \TEX\ part has two alternatives, one using \CONTEXT, and
+#D another using plain \TEX. The \CONTEXT\ method is slower but
+#D can be extended more easily.
+#D
+#D The script is executed as follows:
+#D
+#D \starttyping
+#D cropcrap <original> [<result>] [<switches>]
+#D \stoptyping
+#D
+#D The next call crops \type {test.pdf} to its natural
+#D boundingbox.
+#D
+#D \starttyping
+#D cropcrap test
+#D \stoptyping
+#D
+#D If the file has some crap at the bottom, you can say:
+#D
+#D \starttyping
+#D cropcrap test --bottomcrop=2cm
+#D \stoptyping
+#D
+#D This clips 2cm from the bottom. You can clip on all sides
+#D individually, in combination or at once, like in:
+#D
+#D \starttyping
+#D cropcrap test --bottomcrop=2cm --crop=1cm
+#D \stoptyping
+#D
+#D The final result is a tightly cropped image. In order to get
+#D a 5mm margin around this image, you can say:
+#D
+#D \starttyping
+#D cropcrap test --bottomcrop=2cm --offset=5mm
+#D \stoptyping
+#D
+#D By default, the script intercepts logging messages and
+#D writes them to a logfile with the same name as the
+#D resulting image and the prefix \type {log}. If no name is
+#D given, the name \type {cropcrap} is used for all resulting
+#D files.
+#D
+#D By default, \CONTEXT\ is used. When installed properly, you
+#D can also use plain \TEX, by adding a switch \type
+#D {--plain}. Partial switched are accepted, so the next call
+#D is valid:
+#D
+#D \starttyping
+#D cropcrap test result --bot=2cm --off=5mm --plain
+#D \stoptyping
+#D
+#D The current implementation uses an intermediate \POSTSCRIPT\
+#D file. This may change as \GHOSTSCRIPT\ gets more clever with
+#D \PDF\ files.
+#D
+#D In \in {figure} [fig:cropcrap] the green rectangle is the
+#D picture we want to keep. Around this picture, we want a
+#D margin, represented by the black rectangle, and specified by
+#D \type {--offset}. The white rectangle is the cropbox
+#D defined by \type {--crop}. That way we get rid of header
+#D and footerlines. The red rectangle results from an
+#D additional \type {--leftcrop} and \type {-bottomcrop} and
+#D takes care of some content, as represented by the green
+#D dot.
+#D
+#D The \type {--verbose} switch can be used to disable the
+#D interception of log messages.
+
+#D We load a few \PERL\ modules \unknown\
+
+use Config ;
+use Getopt::Long ;
+
+use strict ;
+
+#D \unknown\ and initialize them.
+
+Getopt::Long::Configure
+ ("auto_abbrev",
+ "ignore_case",
+ "pass_through") ;
+
+#D Before fetching the switches, we initialize the
+#D variables.
+
+my $Crop = "0cm" ;
+
+my $LeftCrop = "0cm" ;
+my $RightCrop = "0cm" ;
+my $TopCrop = "0cm" ;
+my $BottomCrop = "0cm" ;
+
+my $Offset = "0cm" ;
+
+my $GSbin = "" ;
+my $Verbose = 0 ;
+my $Help = 0 ;
+my $UsePlain = 0 ;
+
+my $Page = 1 ;
+
+#D On \MSWINDOWS\ and \UNIX\ the following defaults, combined
+#D with the check later, should work out okay.
+
+my $pdfps = "pdftops" ;
+my $gs = "gs" ;
+
+my $thisisunix = $Config{'osname'} !~ /dos|mswin/i ;
+
+#D When no resulting file is given, we use \type {cropcrap}
+#D as name (checked later).
+
+my $figurefile = "" ;
+my $resultfile = "" ;
+my $tempfile = "" ;
+
+my $programname = "cropcrap" ;
+
+#D Messages are temporarily saved and written to a log file
+#D afterwards.
+
+my $results = "" ;
+my $pipe = "" ;
+my $result = "" ;
+
+#D Unfortunately we need this information, first since
+#D \PDFTOPS\ does not honor the cropbox, and second because
+#D the vertical coordinated are swapped.
+
+my $pwidth = 597 ;
+my $pheight = 847 ;
+my $hoffset = 0 ;
+my $voffset = 0 ;
+
+#D A few more variables.
+
+my $width = my $height = my $llx = my $lly = my $urx = my $ury = 0 ;
+
+#D Here are the switches we accept. The \type {--gsbin} switch
+#D is a bonus one, and the \type {--help} switch comes
+#D naturally.
+
+&GetOptions
+ ( "leftcrop=s" => \$LeftCrop ,
+ "rightcrop=s" => \$RightCrop ,
+ "topcrop=s" => \$TopCrop ,
+ "bottomcrop=s" => \$BottomCrop,
+ "crop=s" => \$Crop ,
+ "offset=s" => \$Offset ,
+ "verbose" => \$Verbose ,
+ "gsbin=s" => \$GSbin ,
+ "plain" => \$UsePlain ,
+ "page=i" => \$Page ,
+ "help" => \$Help ) ;
+
+#D If asked for, or if no file is given, we provide some
+#D help information.
+
+sub PrintHelp
+ { print "This is PdfTrimWhite\n\n" .
+ "usage:\n\n" .
+ "cropcrap [switches] filename result\n\n" .
+ "switches:\n\n" .
+ "--crop=<dimen>\n" .
+ "--offset=<dimen>\n" .
+ "--leftcrop=<dimen>\n" .
+ "--rightcrop=<dimen>\n" .
+ "--topcrop=<dimen>\n" .
+ "--bottomcrop=<dimen>\n" .
+ "--gsbin=<string>\n" .
+ "--page=<number>\n" .
+ "--plain\n" .
+ "--verbose\n" }
+
+#D The preparations:
+
+sub GetItRight
+ { if ($Help)
+ { PrintHelp() ; exit }
+ $figurefile = $ARGV[0] ; $figurefile =~ s/\.pdf$//oi ;
+ $resultfile = $ARGV[1] ; $resultfile =~ s/\.pdf$//oi ;
+ $tempfile = "pdftrimwhite-$resultfile" ;
+ if ($figurefile eq '')
+ { PrintHelp() ; exit }
+ unless ($thisisunix)
+ { $gs = "gswin32c" }
+ if ($GSbin ne '')
+ { $gs = $GSbin }
+ unless (-e "$figurefile.pdf")
+ { print "Something is terribly wrong: no file found\n" ;
+ exit }
+ if (($resultfile eq '')||($resultfile=~/(^\-|\.)/io))
+ { $resultfile = $programname }
+ $pipe = "2>&1" ;
+ if ($thisisunix)
+ { $pipe = "2>&1" } }
+
+#D Something common.
+
+sub SavePageData
+ { return "% saving page data
+\\immediate\\openout\\scratchwrite=$figurefile.tmp
+\\immediate\\write\\scratchwrite
+ {\\HOffsetBP\\space\\VOffsetBP\\space
+ \\FigureWidthBP\\space\\FigureHeightBP}
+\\immediate\\closeout\\scratchwrite\n" }
+
+sub MakePageConTeXt
+ { return "% the real work
+\\definepapersize
+ [Crap]
+ [width=\\FigureWidth,
+ height=\\FigureHeight]
+\\setuppapersize
+ [Crap][Crap]
+\\setuplayout
+ [topspace=0cm,backspace=0pt,
+ height=middle,width=middle,
+ header=0pt,footer=0pt]
+\\starttext
+ \\startstandardmakeup
+ \\clip
+ [voffset=\\VOffset,
+ hoffset=\\HOffset,
+ width=\\FigureWidth,
+ height=\\FigureHeight]
+ {\\externalfigure[$figurefile.pdf][page=$Page]\\hss}
+ \\stopstandardmakeup
+\\stoptext\n" }
+
+sub MakePagePlainTeX
+ { return "% the real work
+\\output{}
+\\hoffset=-1in
+\\voffset=\\hoffset
+\\pdfpageheight=\\FigureHeight
+\\pdfpagewidth=\\FigureWidth
+\\vbox to \\pdfpageheight
+ {\\offinterlineskip
+ \\vskip-\\VOffset
+ \\hbox to \\pdfpagewidth{\\hskip-\\HOffset\\box0\\hss}
+ \\vss}
+\\end\n" }
+
+sub CalculateClip
+ { return "% some calculations
+\\dimen0=\\figurewidth
+\\dimen2=\\figureheight
+\\dimen4=$Crop
+\\dimen6=$Crop
+\\advance\\dimen4 by $LeftCrop
+\\advance\\dimen6 by $TopCrop
+\\advance\\dimen0 by -\\dimen4
+\\advance\\dimen0 by -$Crop
+\\advance\\dimen0 by -$RightCrop
+\\advance\\dimen2 by -\\dimen6
+\\advance\\dimen2 by -$Crop
+\\advance\\dimen2 by -$BottomCrop
+\\edef\\FigureWidth {\\the\\dimen0}
+\\edef\\FigureHeight{\\the\\dimen2}
+\\edef\\HOffset {\\the\\dimen4}
+\\edef\\VOffset {\\the\\dimen6}
+\\ScaledPointsToWholeBigPoints{\\number\\dimen0}\\FigureWidthBP
+\\ScaledPointsToWholeBigPoints{\\number\\dimen2}\\FigureHeightBP
+\\ScaledPointsToWholeBigPoints{\\number\\dimen4}\\HOffsetBP
+\\ScaledPointsToWholeBigPoints{\\number\\dimen6}\\VOffsetBP\n" }
+
+sub RecalculateClip
+ { return "% some calculations
+\\dimen0=${width}bp
+\\dimen2=${height}bp
+\\dimen4=${hoffset}bp
+\\dimen6=${pheight}bp
+\\advance\\dimen0 by $Offset
+\\advance\\dimen0 by $Offset
+\\advance\\dimen2 by $Offset
+\\advance\\dimen2 by $Offset
+\\advance\\dimen4 by ${llx}bp
+\\advance\\dimen4 by -$Offset
+\\advance\\dimen6 by -${lly}bp
+\\advance\\dimen6 by $Offset
+\\advance\\dimen6 by -\\dimen2
+\\advance\\dimen6 by $TopCrop
+\\edef\\FigureWidth {\\the\\dimen0}
+\\edef\\FigureHeight{\\the\\dimen2}
+\\edef\\HOffset {\\the\\dimen4}
+\\edef\\VOffset {\\the\\dimen6}\n" }
+
+#D The previous scripts could be more sparse, but for the
+#D moment we prefer readability. Both scripts save some
+#D information in temporary file. We choose between them with
+#D the following sub routine.
+
+#D The first pass:
+
+sub PrepareConTeXt
+ { return "% interface=en
+\\setupoutput[pdftex]
+\\getfiguredimensions[$figurefile.pdf][page=$Page]\n" }
+
+sub PreparePlainTeX
+ { return "% plain tex alternative, needs recent supp-mis
+\\input supp-mis
+\\pdfoutput=1
+\\newdimen\\figurewidth
+\\newdimen\\figureheight
+\\setbox0=\\hbox
+ {\\immediate\\pdfximage page $Page {$figurefile.pdf}\\pdfrefximage\\pdflastximage}
+\\figurewidth=\\wd0
+\\figureheight=\\ht0\n" }
+
+sub PrepareFirstPass
+ { open (TEX, ">$tempfile.tex") ;
+ if ($UsePlain)
+ { print TEX
+ PreparePlainTeX .
+ CalculateClip .
+ SavePageData .
+ MakePagePlainTeX }
+ else
+ { print TEX
+ PrepareConTeXt .
+ CalculateClip .
+ SavePageData .
+ MakePageConTeXt }
+ close TEX }
+
+#D The second pass looks much like the first one, but this
+#D time we don't save information, use the natural
+#D boundingbox, and provide the offset.
+
+sub SetupConTeXt
+ { return "% interface=en
+\\setupoutput[pdftex]\n" }
+
+sub SetupPlainTeX
+ { return "% plain tex alternative
+\\pdfoutput=1
+\\setbox0=\\hbox
+ {\\immediate\\pdfximage page $Page {$figurefile.pdf}\\pdfrefximage\\pdflastximage}\n" }
+
+sub PrepareSecondPass
+ { open (TEX, ">$tempfile.tex") ;
+ if ($UsePlain)
+ { print TEX
+ SetupPlainTeX .
+ RecalculateClip .
+ MakePagePlainTeX }
+ else
+ { print TEX
+ SetupConTeXt .
+ RecalculateClip .
+ MakePageConTeXt }
+ close TEX }
+
+#D The information we save in the first pass, is loaded here.
+
+sub FetchPaperSize
+ { open (TMP,"$figurefile.tmp") ;
+ while (<TMP>)
+ { chomp ;
+ if (/^(\d+) (\d+) (\d+) (\d+) *$/oi)
+ { $hoffset = $1 ;
+ $voffset = $2 ;
+ $pwidth = $3 ;
+ $pheight = $4 ;
+ last } }
+ close (TMP) }
+
+#D Here we try to find the natural boundingbox. We need to
+#D pick up the page dimensions here.
+
+sub RunTeX
+ { if ($UsePlain)
+ { $result = `pdftex -prog=pdftex -fmt=plain -int=batchmode $tempfile` }
+ else
+ { $result = `texexec --batch --once --purge $tempfile` }
+ print $result if $Verbose ; $results .= "$result\n" }
+
+sub FindBoundingBox
+ { $result = `$gs -sDEVICE=bbox -dNOPAUSE -dBATCH $tempfile.pdf $pipe` ;
+ print $result if $Verbose ; $results .= "$result\n" }
+
+sub IdentifyCropBox
+ { RunTeX() ;
+ FetchPaperSize () ;
+ FindBoundingBox() }
+
+#D Just to be sure, we check if there is some image data, so
+#D that we can retry if something went wrong. Unfortunately we cannot
+#D safely check on a high res boundingbox.
+
+my $digits = '([\-\d\.]+)' ;
+
+sub ValidatedCropBox
+ { if ($result =~ /BoundingBox:\s*$digits\s+$digits\s+$digits\s+$digits\s*/mois)
+ { $llx = $1 ; $lly = $2 ; $urx = $3 ; $ury = $4 }
+ else
+ { print "Something is terribly wrong: no boundingbox:\n$result\n" ; exit }
+ $width = abs($urx - $llx) ;
+ $height = abs($ury - $lly) ;
+ if ($width&&$height)
+ { return 1 }
+ else
+ { unless ($width)
+ { print "Something seems wrong: no width\n" ;
+ $LeftCrop = "0cm" ; $RightCrop = "0cm" ; $Crop = "0cm" }
+ unless ($height)
+ { print "Something seems wrong: no height\n" ;
+ $TopCrop = "0cm" ; $BottomCrop = "0cm" ; $Crop = "0cm" }
+ return 0 } }
+
+#D This is the main cropping routine.
+
+sub FixCropBox
+ { RunTeX() }
+
+#D For error tracing we save the log information in a file.
+
+sub RenameResult
+ { unlink "$resultfile.pdf" ;
+ rename "$tempfile.pdf", "$resultfile.pdf" }
+
+sub SaveLogInfo
+ { open (LOG, ">$resultfile.log") ;
+ print LOG $results ;
+ close (LOG) }
+
+#D We remove all temporary files.
+
+sub CleanUp
+ { unless ($Verbose)
+ { unlink "$tempfile.tex" ;
+ unlink "$tempfile.tuo" ;
+ unlink "$tempfile.tui" ;
+ unlink "$figurefile.tmp" } }
+
+#D Here it all comes together.
+
+GetItRight() ;
+
+PrepareFirstPass() ;
+
+IdentifyCropBox () ;
+
+unless (ValidatedCropBox())
+ { PrepareFirstPass() ;
+ IdentifyCropBox () }
+
+if (ValidatedCropBox())
+ { PrepareSecondPass() ;
+ FixCropBox() }
+
+RenameResult() ;
+SaveLogInfo() ;
+
+CleanUp () ;
diff --git a/Master/texmf-dist/scripts/context/perl/texexec.pl b/Master/texmf-dist/scripts/context/perl/texexec.pl
index 2f3b89e0461..fb564bbd5ed 100644
--- a/Master/texmf-dist/scripts/context/perl/texexec.pl
+++ b/Master/texmf-dist/scripts/context/perl/texexec.pl
@@ -11,7 +11,7 @@ eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' && eval 'exec perl -w -
#D copyright={PRAGMA / Hans Hagen \& Ton Otten}]
#C
#C This module is part of the \CONTEXT\ macro||package and is
-#C therefore copyrighted by \PRAGMA. See licen-en.pdf for
+#C therefore copyrighted by \PRAGMA. See readme.pdf for
#C details.
# Thanks to Tobias Burnus for the german translations.
@@ -151,6 +151,7 @@ my $PdfCombine = 0;
my $PdfOpen = 0;
my $PdfClose = 0;
my $AutoPdf = 0;
+my $UseXPdf = 0;
my $PrintFormat = 'standard';
my $ProducePdfT = 0;
my $ProducePdfM = 0;
@@ -255,6 +256,7 @@ my $MakeMpy = '';
"once" => \$RunOnce,
"output=s" => \$OutputFormat,
"pages=s" => \$Pages,
+ "paper=s" => \$PaperFormat,
"paperformat=s" => \$PaperFormat,
"passon=s" => \$PassOn,
"path=s" => \$InpPath,
@@ -275,6 +277,7 @@ my $MakeMpy = '';
"selection=s" => \$Selection,
"combination=s" => \$Combination,
"noduplex" => \$NoDuplex,
+ "offset=s" => \$PaperOffset,
"paperoffset=s" => \$PaperOffset,
"backspace=s" => \$BackSpace,
"topspace=s" => \$TopSpace,
@@ -310,6 +313,7 @@ my $MakeMpy = '';
"pdfclose" => \$PdfClose,
"pdfopen" => \$PdfOpen,
"autopdf" => \$AutoPdf,
+ "xpdf" => \$UseXPdf,
"modefile=s" => \$ModeFile, # additional modes file
"globalfile" => \$GlobalFile,
"nomapfiles" => \$NoMapFiles,
@@ -351,6 +355,12 @@ if ( $ARGV[0] && $ARGV[0] =~ /\.mpx$/io ) { # catch -tex=.... bug in mpost
####
+if ($Version) {
+ $Purge = 1 ;
+ }
+
+####
+
if ($Paranoid) {
$ENV{shell_escape} = 'f' ;
$ENV{openout_any} = 'p' ;
@@ -406,6 +416,16 @@ if ($AutoPdf) {
$PdfOpen = $PdfClose = 1 ;
}
+my $PdfOpenCall = "" ;
+
+if ($PdfOpen) {
+ $PdfOpenCall = "pdfopen --file" ;
+}
+
+if ($UseXPdf && !$dosish) {
+ $PdfOpenCall = "xpdfopen" ;
+}
+
# this is our hook into paranoid path extensions, assumes that
# these three vars are part of path specs in texmf.cnf
@@ -900,7 +920,7 @@ $OutputFormats{dviwindo} = "dviwindo";
$OutputFormats{dviview} = "dviview";
-my @ConTeXtFormats = ( "nl", "en", "de", "cz", "uk", "it", "ro", "xx");
+my @ConTeXtFormats = ( "nl", "en", "de", "fr", "cz", "uk", "it", "ro", "xx");
sub SetInterfaces {
my ( $short, $long, $full ) = @_;
@@ -915,8 +935,9 @@ sub SetInterfaces {
SetInterfaces( "nl", "dutch", "dutch" );
SetInterfaces( "en", "english", "english" );
SetInterfaces( "de", "german", "german" );
+SetInterfaces( "fr", "french", "french" );
SetInterfaces( "cz", "czech", "czech" );
-SetInterfaces( "uk", "brittish", "english" );
+SetInterfaces( "uk", "british", "english" );
SetInterfaces( "it", "italian", "italian" );
SetInterfaces( "no", "norwegian", "norwegian" );
SetInterfaces( "ro", "romanian", "romanian" );
@@ -1203,6 +1224,11 @@ sub MakeOptionFile {
print OPT "\\setupcolors[\\c!split=$Separation]\n";
}
if ($SetupPath) { print OPT "\\setupsystem[\\c!directory=\{$SetupPath\}]\n" }
+ if ($dosish) {
+ print OPT "\\setupsystem[\\c!type=mswin]\n"
+ } else { # no darwin handling in old texexec
+ print OPT "\\setupsystem[\\c!type=unix]\n"
+ }
print OPT "\\setupsystem[\\c!n=$KindOfRun]\n";
$_ = $PaperFormat;
#unless (($PdfArrange)||($PdfSelect)||($PdfCombine)||($PdfCopy))
@@ -1945,11 +1971,11 @@ sub RunConTeXtFile {
{
unlink "$JobName.$JobSuffix";
}
- if ((!$Problems) && ($PdfOpen)) {
+ if ((!$Problems) && ($PdfOpen) && ($PdfOpenCall)) {
if ($Result ne '') {
- System("pdfopen --file $Result.pdf") if -f "$Result.pdf"
+ System("$PdfOpenCall $Result.pdf") if -f "$Result.pdf"
} else {
- System("pdfopen --file $JobName.pdf") if -f "$JobName.pdf"
+ System("$PdfOpenCall $JobName.pdf") if -f "$JobName.pdf"
}
}
}
@@ -2008,7 +2034,11 @@ sub checktexformatpath {
if ($texformats eq '') {
if ($UseEnginePath) {
if ($dosish) {
- $texformats = `kpsewhich --engine=$TeXExecutable --expand-var=\$TEXFORMATS` ;
+ if ( $TeXShell =~ /MikTeX/io ) {
+ $texformats = `kpsewhich --alias=$TeXExecutable --expand-var=\$TEXFORMATS` ;
+ } else {
+ $texformats = `kpsewhich --engine=$TeXExecutable --expand-var=\$TEXFORMATS` ;
+ }
} else {
$texformats = `kpsewhich --engine=$TeXExecutable --expand-var=\\\$TEXFORMATS` ;
}
@@ -2363,7 +2393,11 @@ sub LocatedFormatPath { # watch out $engine is lowercase in kpse
# expanded paths
print " assuming engine : $EnginePath\n";
if (($UseEnginePath)&&($EngineDone)) {
- $FormatPath = `$kpsewhich --engine=$EnginePath --show-path=fmt` ;
+ if ( $TeXShell =~ /MikTeX/io ) {
+ $FormatPath = `$kpsewhich --alias=$EnginePath --show-path=fmt` ;
+ } else {
+ $FormatPath = `$kpsewhich --engine=$EnginePath --show-path=fmt` ;
+ }
} else {
$FormatPath = `$kpsewhich --show-path=fmt` ;
}
@@ -2375,7 +2409,11 @@ sub LocatedFormatPath { # watch out $engine is lowercase in kpse
if ($FormatPath eq '') {
if (($UseEnginePath)&&($EngineDone)) {
if ($dosish) {
- $FormatPath = `$kpsewhich --engine=$EnginePath --expand-path=\$TEXFORMATS` ;
+ if ( $TeXShell =~ /MikTeX/io ) {
+ $FormatPath = `$kpsewhich --alias=$EnginePath --expand-path=\$TEXFORMATS` ;
+ } else {
+ $FormatPath = `$kpsewhich --engine=$EnginePath --expand-path=\$TEXFORMATS` ;
+ }
} else {
$FormatPath = `$kpsewhich --engine=$EnginePath --expand-path=\\\$TEXFORMATS` ;
}
@@ -2998,7 +3036,7 @@ sub show_version_info {
$contextversion = <IN>;
close IN;
}
- $contextversion =~ s/.*contextversion\{([0-9.]+)\}.*/$1/s;
+ $contextversion =~ s/.*contextversion\{([0-9\.\:\s]+)\}.*/$1/s;
}
$EnterBatchMode = 1;
$Format = 'cont-en';
@@ -3007,7 +3045,7 @@ sub show_version_info {
print " tex : $texengine\n" ;
print " context : ver: $contextversion\n" ;
print " cont-en : $type\n" ;
- foreach my $a (qw(cz de it nl ro uk xx)) {
+ foreach my $a (qw(cz de fr it nl ro uk xx)) {
my $test = Pipe("$kpsewhich -format='fmt' cont-$a") ;
if (defined $test && $test) {
$Format = 'cont-' . $a;
@@ -3063,6 +3101,9 @@ $TotalTime = time - $TotalTime;
unless ($HelpAsked) { print "\n total run time : $TotalTime seconds\n" }
+print "\n" ;
+print " warning : use 'texmfstart texexec' instead\n" ;
+
if ($Problems) { exit 1 }
__DATA__
@@ -3108,8 +3149,9 @@ interface user interface
=en English
=nl Dutch
=de German
+=fr French
=cz Czech
-=uk Brittish
+=uk British
=it Italian
-----------
language main hyphenation language
@@ -3136,7 +3178,7 @@ module typeset tex/pl/mp module
-----------
mptex run an MetaPost plus btex-etex cycle
-----------
-mpxtex generatet an MetaPostmpx file
+mpxtex generate an MetaPost mpx file
-----------
noarrange process but ignore arrange
-----------
@@ -3149,8 +3191,8 @@ automprun MetaPost at runtime when needed
once run TeX only once (no TeXUtil either)
-----------
output specials to use
-=pdftex Han The Than's pdf backend
-=dvips Thomas Rokicky's dvi to ps converter
+=pdftex Han The Thanh's pdf backend
+=dvips Tomas Rokicky's dvi to ps converter
=dvipsone YandY's dvi to ps converter
=dviwindo YandY's windows previewer
=dvipdfm Mark Wicks' dvi to pdf converter
@@ -3243,7 +3285,7 @@ verbose shows some additional info
-----------
help show this or more, e.g. '--help interface'
-----------
-alone bypass utilities (e.g. fmtutil for non-standard fmt's)
+alone bypass utilities (e.g. fmtutil for non-standard fmts)
-----------
texutil force TeXUtil run
-----------
diff --git a/Master/texmf-dist/scripts/context/perl/texexec.rme b/Master/texmf-dist/scripts/context/perl/texexec.rme
index f38b4be7fe0..43f142f9da4 100644
--- a/Master/texmf-dist/scripts/context/perl/texexec.rme
+++ b/Master/texmf-dist/scripts/context/perl/texexec.rme
@@ -120,7 +120,7 @@ for miktex set TeXVirginFlag to --initialize
for miktex set TeXPassString to --alias=context --translate-file=natural.tcx
for miktex set TeXBatchFlag to --interaction=batchmode
for miktex set TeXNonStopFlag to --interaction=nonstopmode
-for miktex set MpToTeXExecutable to mptotex
+for miktex set MpToTeXExecutable to mpto
for miktex set MpVirginFlag to --initialize
for miktex set MpPassString to --alias=mpost --translate-file=natural.tcx
for miktex set MpBatchFlag to --interaction=batchmode
diff --git a/Master/texmf-dist/scripts/context/perl/texshow.pl b/Master/texmf-dist/scripts/context/perl/texshow.pl
index 79222c9f03c..629c28f99ec 100644
--- a/Master/texmf-dist/scripts/context/perl/texshow.pl
+++ b/Master/texmf-dist/scripts/context/perl/texshow.pl
@@ -3,7 +3,7 @@ eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' && eval 'exec perl -w -
#D \module
#D [ file=texshow.pl,
-#D version=2005.01.06,
+#D version=2006.08.04,
#D title=TeXShow,
#D subtitle=showing \CONTEXT\ commands,
#D author=Taco Hoekwater,
@@ -23,6 +23,12 @@ eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' && eval 'exec perl -w -
#D one to be used.
#D
#D Hans Hagen - Januari 2005
+#D
+#D ChangeLog:
+#D \startitemize
+#D \item Add keyboard bindings for quitting the app: Ctrl-q,Ctrl-x,Alt-F4 (2006/07/19)
+#D \item Support for define --resolve (2006/08/04)
+#D \stopitemize
use strict;
use Getopt::Long ;
@@ -63,7 +69,7 @@ my %crosslinks;
print "\n";
-show('TeXShow-XML 0.2 beta','Taco Hoekwater 2004',"/");
+show('TeXShow-XML 0.3 beta','Taco Hoekwater 2006',"/");
print "\n";
@@ -220,6 +226,11 @@ sub initialize_display {
$mainwindow -> bind ( "<BackSpace>", sub { delete_request() } ) ;
$mainwindow -> bind ( "<Prior>", sub { prev_command() } ) ;
$mainwindow -> bind ( "<Next>", sub { next_command() } ) ;
+ $mainwindow -> bind ( "<Control-x>", sub { exit(0) } ) ;
+ $mainwindow -> bind ( "<Control-X>", sub { exit(0) } ) ;
+ $mainwindow -> bind ( "<Control-q>", sub { exit(0) } ) ;
+ $mainwindow -> bind ( "<Control-Q>", sub { exit(0) } ) ;
+ $mainwindow -> bind ( "<Alt-F4>", sub { exit(0) } ) ;
}
sub show {
@@ -312,41 +323,50 @@ sub setups_found {
sub load_setup {
my ($path,$filename) = @_;
+ my $localdefs = {};
unless (keys %{$setups{$filename}}) {
if (open(IN,"<${path}$filename.xml")) {
my $position = 0 ;
local $/ = '</cd:command>';
while (my $data= <IN>) {
- if ($data =~ /\<\/cd:interface/) {
- next;
- }
- if ($data =~ /\<cd:interface/) {
- $data =~ s/^(.*?)\<cd:command/\<cd:command/sm;
- my $meta = $1;
- }
- #
- $data =~ s/\s*\n\s*//g;
- $data =~ /\<cd:command(.*?)\>/;
- my $info = $1;
- my ($name,$environment) = ('','');
- while ($info =~ s/^\s*(.*?)\s*=\s*(["'])(.*?)\2\s*//) {
- my $a = $1; my $b = $3;
- if ($a eq 'name') {
- $name = $b;
- } elsif ($a eq 'type') {
- $environment = $b;
+ next if $data =~ /\<\/cd:interface/;
+ if ($data =~ /\<cd:interface/) {
+ $data =~ s/^(.*?)\<cd:command/\<cd:command/sm;
+ my $meta = $1;
+ while ($meta =~ s!<cd:define name=(['"])(.*?)\1>(.*?)</cd:define>!!sm) {
+ my $localdef = $2;
+ my $localval = $3;
+ $localdefs->{$localdef} = $localval;
+ }
+ }
+ #
+ if (keys %$localdefs) {
+ while ($data =~ /<cd:resolve/) {
+ $data =~ s/<cd:resolve name=(['"])(.*?)\1\/>/$localdefs->{$2}/ms;
+ }
+ }
+ $data =~ s/\s*\n\s*//g;
+ $data =~ /\<cd:command(.*?)\>/;
+ my $info = $1;
+ my ($name,$environment) = ('','');
+ while ($info =~ s/^\s*(.*?)\s*=\s*(["'])(.*?)\2\s*//) {
+ my $a = $1; my $b = $3;
+ if ($a eq 'name') {
+ $name = $b;
+ } elsif ($a eq 'type') {
+ $environment = $b;
+ }
+ }
+ my $cmd = $name;
+ if ($environment) {
+ $cmd = "start" . $name;
+ }
+ $setups {$filename}{$cmd} = $data ;
+ $trees {$filename}{$cmd} = undef;
+ $positions {$filename}{$cmd} = ++$position ;
+ $crosslinks{$filename}[$position] = $cmd ;
}
- }
- my $cmd = $name;
- if ($environment) {
- $cmd = "start" . $name;
- }
- $setups {$filename}{$cmd} = $data ;
- $trees {$filename}{$cmd} = undef;
- $positions {$filename}{$cmd} = ++$position ;
- $crosslinks{$filename}[$position] = $cmd ;
- }
- close IN;
+ close IN;
# now get explanations as well ...
my $explname = $filename;
$explname =~ s/cont-/expl-/;
diff --git a/Master/texmf-dist/scripts/context/perl/texutil.pl b/Master/texmf-dist/scripts/context/perl/texutil.pl
index 0a0d2975354..ca364501584 100644
--- a/Master/texmf-dist/scripts/context/perl/texutil.pl
+++ b/Master/texmf-dist/scripts/context/perl/texutil.pl
@@ -44,7 +44,7 @@ eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $
#D binary version, like scanning illustrations other than \EPS.
#D I would suggest to keep an eye on the version number:
-$Program = "TeXUtil 9.0.0 - ConTeXt / PRAGMA ADE 1992-2004" ;
+$Program = "TeXUtil 9.0.1 - ConTeXt / PRAGMA ADE 1992-2006" ;
#D By the way, this is my first \PERL\ script, which means
#D that it will be improved as soon as I find new and/or more
@@ -54,6 +54,11 @@ $Program = "TeXUtil 9.0.0 - ConTeXt / PRAGMA ADE 1992-2004" ;
#D users in english as well as some other languages. One can
#D set his favourite language by saying something like:
+#D This one has a real old date, 1992. This is because it is
+#D a converted modula program that was used in the very early
+#D days of our \TEX\ history (macros for proper inclusion of
+#D graphics were among the first thatwe wrote).
+
#D \starttypen
#D perl texutil.pl --int=de --fig *.eps *.tif *.pdf *.png *.jpg
#D \stoptypen
@@ -823,7 +828,7 @@ sub InitializeKeys
{ my $filename = $ARGV[0] ;
return unless (open(TEX,"$filename.tex")) ;
for ($i=0;$i<=255;$i++)
- { @Filter[$i] = $i }
+ { $Filter[$i] = $i }
if ($TcXPath eq '')
{ foreach (@paths)
{ my $p = checked_path($_) . 'kpsewhich' ;
@@ -848,7 +853,7 @@ sub InitializeKeys
{ Report ("LoadedFilter", $Translation) ;
while (<ASC>)
{ if (/^(\d+)\s*(\d+)/)
- { @Filter[$2] = $1 } }
+ { $Filter[$2] = $1 } }
close (ASC) }
elsif ($TcXPath ne '')
{ Report ("WrongFilterPath", $TcXPath) }
@@ -861,7 +866,7 @@ sub HandleKey
{ ++$SortN ;
$RestOfLine =~ s/\{(.*)\}/$1/o ;
my ($lan, $enc, $str, $chr, $map, $alf) = split(/\}\s*\{/, $RestOfLine) ;
- if ($str =~ /^(\d+)/) { $str = ''.chr(@Filter[$1]) }
+ if ($str =~ /^(\d+)/) { $str = ''.chr($Filter[$1]) }
$map = chr(ord($MAP[$i])+128) ;
$STR[$SortN] = $str ;
$CHR[$SortN] = $chr ;
@@ -1279,8 +1284,8 @@ sub HandleRegister # the } { makes sure that local {} is ok
#
if ($Key eq "")
{ $Key = SanitizedString($Entry) }
- if ($SortMethod ne '')
- { $ProcessHigh = 0 }
+ # if ($SortMethod ne '')
+ # { $ProcessHigh = 0 }
if ($ProcessHigh)
{ $Key = HighConverted($Key) }
$KeyTag = substr $Key, 0, 1 ;
@@ -2075,7 +2080,7 @@ sub SaveFigurePresets
{ $Figures[$NOfFigures] .= ",t=\{$FTit\}" }
if ($FCre)
{ $Figures[$NOfFigures] .= ",c=\{$FCre\}" }
- $Figures[$NOfFigures] .= ",s=$FSiz]\n" } }
+ $Figures[$NOfFigures] .= ",s=$FSiz]" } }
#D The \EPS\ to \PDF\ conversion pipe to \GHOSTSCRIPT\ is
#D inspired by a script posted by Sebastian Ratz at the
@@ -2580,8 +2585,10 @@ sub FlushFigures
open ( TUF, ">$OutputFile" ) ;
print TUF "%\n" . "% $Program / Figures\n" . "%\n" ;
print TUF "\\thisisfigureversion\{1996.06.01\}\n" . "%\n" ;
+ # a joins is nicer
for ($n=1 ; $n<=$NOfFigures ; ++$n)
- { print TUF $Figures[$n] }
+ { print TUF "$Figures[$n]%\n" }
+ print TUF "\\endinput";
close (TUF) ;
if ($NOfFigures)
{ Report("OutputFile", $OutputFile ) }
@@ -2772,7 +2779,9 @@ if ($PurgeAllFiles)
{ push @forsuresuffixes, @texnonesuffixes ; @texnonesuffixes = [] }
sub PurgeFiles # no my in foreach
- { my $pattern = $ARGV[0] ; my $strippedname, $basename ;
+ { my $pattern = $ARGV[0] ;
+ my $strippedname ;
+ my $basename ;
my @files = () ;
if ($pattern eq '')
{ $pattern = "*.*" ;
@@ -2895,8 +2904,11 @@ elsif ($PurgeFiles ) { PurgeFiles }
elsif ($PurgeAllFiles ) { PurgeFiles }
elsif ($AnalyzeFile ) { AnalyzeFile }
elsif ($FilterPages ) { FilterPages }
-elsif ($SciteApi ) { GenerateSciteApi }
elsif ($ProcessHelp ) { ShowHelpInfo } # redundant
else { ShowHelpInfo }
+print "\n" ;
+print " remark : 'texutil' is now part of 'texexec'\n" ;
+print " warning : use 'texmfstart texutil' instead\n" ;
+
#D So far.
diff --git a/Master/texmf-dist/scripts/context/ruby/base/ctx.rb b/Master/texmf-dist/scripts/context/ruby/base/ctx.rb
index 852c3f7046e..0baf5a88b1b 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/ctx.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/ctx.rb
@@ -23,6 +23,8 @@ class CtxRunner
attr_reader :environments, :modules, :filters
+ @@suffix = 'prep'
+
def initialize(jobname=nil,logger=nil)
if @logger = logger then
def report(str='')
@@ -52,17 +54,42 @@ class CtxRunner
end
if not @ctxname then
- report('provide ctx file')
+ report('no ctx file specified')
return
end
- if not FileTest.file?(@ctxname) and defaultname and FileTest.file?(defaultname) then
- @ctxname = defaultname
- end
-
+ # name can be kpse:res-make.ctx
if not FileTest.file?(@ctxname) then
- report('provide ctx file')
- return
+ fullname, done = '', false
+ if @ctxname =~ /^kpse:/ then
+ begin
+ if fullname = Kpse.found(@ctxname.sub(/^kpse:/,'')) then
+ @ctxname, done = fullname, true
+ end
+ rescue
+ # should not happen
+ end
+ else
+ ['..','../..'].each do |path|
+ begin
+ fullname = File.join(path,@ctxname)
+ if FileTest.file?(fullname) then
+ @ctxname, done = fullname, true
+ end
+ rescue
+ # probably strange join
+ end
+ break if done
+ end
+ end
+ if ! done && defaultname && FileTest.file?(defaultname) then
+ report("using default ctxfile #{defaultname}")
+ @ctxname = defaultname
+ end
+ if not done then
+ report('no ctx file found')
+ return false
+ end
end
@xmldata = IO.read(@ctxname)
@@ -101,16 +128,44 @@ class CtxRunner
@modules << justtext(mod)
end
REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:filter") do |fil|
- @filters << justtext(mod)
+ @filters << justtext(fil)
+ end
+ commands = Hash.new
+ REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor") do |pre|
+ begin
+ commands[pre.attributes['name']] = pre
+ rescue
+ end
+ end
+ suffix = @@suffix
+ begin
+ suffix = REXML::XPath.match(@xmldata.root,"/ctx:job/ctx:preprocess/@suffix").to_s
+ rescue
+ suffix = @@suffix
+ else
+ if suffix && suffix.empty? then suffix = @@suffix end
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
+ begin
+ variables['old'] = @jobname
+ variables['new'] = ""
+ REXML::XPath.each(pattern,"ctx:value") do |value|
+ if name = value.attributes['name'] then
+ substititute(value,variables[name.to_s])
+ end
+ end
+ rescue
+ report('unable to resolve file pattern')
+ return
+ end
pattern = justtext(pattern)
Dir.glob(pattern).each do |oldfile|
- newfile = "#{oldfile}.prep"
+ newfile = "#{oldfile}.#{suffix}"
if File.needsupdate(oldfile,newfile) then
+ report("#{oldfile} needs preprocessing")
begin
File.delete(newfile)
rescue
@@ -118,11 +173,17 @@ class CtxRunner
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
+ if command = commands[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}")
+ begin
+ if suf = command.attributes['suffix'] then
+ newfile = "#{oldfile}.#{suf}"
+ end
+ rescue
+ end
+ report("preprocessing #{oldfile} into #{newfile} 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
@@ -172,7 +233,8 @@ class CtxRunner
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"
+ # log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{File.basename(prep)}</ctx:prepfile>\n"
+ log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{prep}</ctx:prepfile>\n"
end
log << "</ctx:preplist>\n"
log.close
@@ -196,17 +258,27 @@ class CtxRunner
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
+ done = false
+ if name and not name.empty? then
+ ['.',File.dirname(@ctxname),'..','../..'].each do |path|
+ begin
+ fullname = if path == '.' then name else File.join(path,name) end
+ if FileTest.file?(fullname) then
+ if f = File.open(fullname,'r') and i = REXML::Document.new(f) then
+ report("including ctx file #{name}")
+ REXML::XPath.each(i.root,"*") do |ii|
+ xmldata.root.insert_after(e,ii)
+ more = true
+ end
+ end
+ done = true
+ end
+ rescue
end
end
- else
- report("no valid ctx inclusion file #{name}")
+ break if done
end
+ report("no valid ctx inclusion file #{name}") unless done
rescue Exception
# skip this file
ensure
@@ -278,7 +350,10 @@ class CtxRunner
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
+ when 'full' then str
+ when 'complete' then str
+ when 'expand' then File.expand_path(str).gsub(/\\/,"/")
+ else str
end
end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/exa.rb b/Master/texmf-dist/scripts/context/ruby/base/exa.rb
new file mode 100644
index 00000000000..fdc5b5093e1
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/exa.rb
@@ -0,0 +1,406 @@
+# \setuplayout[width=3cm]
+#
+# tex.setup.setuplayout.width.[integer|real|dimension|string|key]
+# tex.[mp]var.whatever.width.[integer|real|dimension|string|key]
+
+require 'ftools'
+require 'md5'
+
+# this can become a lua thing
+
+# no .*? but 0-9a-z\:\. because other too slow (and greedy)
+
+class Hash
+
+ def subset(pattern)
+ h = Hash.new
+ r = /^#{pattern.gsub('.','\.')}/
+ self.keys.each do |k|
+ h[k] = self[k].dup if k =~ r
+ end
+ return h
+ end
+
+end
+
+module ExaEncrypt
+
+ def ExaEncrypt.encrypt_base(logger, oldfilename, newfilename)
+ if FileTest.file?(oldfilename) then
+ logger.report("checking #{oldfilename}") if logger
+ if data = IO.read(oldfilename) then
+ done = false
+ # cfg file:
+ #
+ # banner : exa configuration file
+ # user : domain, name = password, projectlist
+ #
+ if data =~ /^\s*banner\s*\:\s*exa\s*configuration\s*file/ then
+ data.gsub!(/^(\s*user\s*\:\s*.+?\s*\,\s*.+?\s*\=\s*)(.+?)(\s*\,\s*.+\s*)$/) do
+ pre, password, post = $1, $2, $3
+ unless password =~ /MD5:/i then
+ done = true
+ password = "MD5:" + MD5.new(password).hexdigest.upcase
+ end
+ "#{pre}#{password}#{post}"
+ end
+ else
+ data.gsub!(/<exa:password([^>]*?)>(.*?)<\/exa:password>/mois) do
+ attributes, password = $1, $2
+ unless password =~ /^([0-9A-F][0-9A-F])+$/ then
+ done = true
+ password = MD5.new(password).hexdigest.upcase
+ attributes = " encryption='md5'#{attributes}"
+ end
+ "<exa:password#{attributes}>#{password}</exa:password>"
+ end
+ end
+ begin
+ File.open(newfilename,'w') do |f|
+ f.puts(data)
+ end
+ rescue
+ logger.report("#{newfilename} cannot be written") if logger
+ else
+ logger.report("#{oldfilename} encrypted into #{newfilename}") if done and logger
+ end
+ end
+ end
+ end
+
+end
+
+module ExaModes
+
+ @@modefile = 'examodes.tex'
+
+ @@request = /(<exa:request.*?)(>.*?<\/exa:request>)/mo
+ @@redone = /<exa:request[^>]*?texified=([\'\"])yes\1.*?>/mo
+ @@reload = /<(exa:variable)([^>]+?label\=)([\"\'])([0-9A-Za-z\-\.\:]+?)(\3[^\/]*?)>(.*?)<(\/exa:variable)>/mo
+ @@recalc = /<(exa:variable)([^>]+?label\=)([\"\'])([0-9A-Za-z\-\.\:]+?)([\.\:]calcmath)(\3[^\/]*?)>(.*?)<(\/exa:variable)>/mo
+ @@rename = /<(exa:variable)([^>]+?label\=)([\"\'])([0-9A-Za-z\-\.\:]+?)(\3[^\/]*?)>(.*?)<(\/exa:variable)>/mo
+ @@refile = /<(exa:filename|exa:filelist)>(.*?)<(\/\1)>/mo
+
+ def ExaModes.cleanup_request(logger,filename='request.exa',modefile=@@modefile)
+ begin File.delete(filename+'.raw') ; rescue ; end
+ begin File.delete(modefile) ; rescue ; end
+ if FileTest.file?(filename) then
+ data, done = nil, false
+ begin
+ data = IO.read(filename)
+ rescue
+ data = nil
+ end
+ if data =~ @@request and data !~ @@redone then
+ data.gsub!(@@rename) do
+ done = true
+ '<' + $1 + $2 + $3 + $4 + $5 + '>' +
+ texifiedstr($4,$6) +
+ '<' + $7 + '>'
+ end
+ data.gsub!(@@refile) do
+ done = true
+ '<' + $1 + '>' +
+ cleanpath($2) +
+ '<' + $3 + '>'
+ end
+ data.gsub!(@@recalc) do
+ done = true
+ '<' + $1 + $2 + $3 + $4 + ":raw" + $6 + '>' + $7 + '<' + $8 + '>' +
+ '<' + $1 + $2 + $3 + $4 + $6 + '>' +
+ calculatortexmath($7,false) +
+ '<' + $8 + '>'
+ end
+ if done then
+ data.gsub!(@@request) do
+ $1 + " texified='yes'" + $2
+ end
+ begin File.copy(filename, filename+'.raw') ; rescue ; end
+ begin
+ logger.report("rewriting #{filename}") if logger
+ File.open(filename,'w') do |f|
+ f.puts(data)
+ end
+ rescue
+ logger.report("#{filename} cannot be rewritten") if logger
+ end
+ end
+ else
+ logger.report("#{filename} is already ok") if logger
+ end
+ @variables = Hash.new
+ data.scan(@@reload) do
+ @variables[$4] = $5
+ end
+ vars = @variables.subset('data.tex.var')
+ mpvars = @variables.subset('data.tex.mpvar')
+ modes = @variables.subset('data.tex.mode')
+ setups = @variables.subset('data.tex.setup')
+ if not (modes.empty? and setups.empty? and vars.empty? and mpvars.empty?) then
+ begin
+ File.open(modefile,'w') do |mod|
+ logger.report("saving modes and setups in #{modefile}") if logger
+ if not modes.empty? then
+ for key in modes.keys do
+ k = key.dup
+ k.gsub!(/\./,'-')
+ mod.puts("\\enablemode[#{k}-#{modes[key]}]\n")
+ if modes[key] =~ /(on|yes|start)/o then # ! ! ! ! !
+ mod.puts("\\enablemode[#{k}]\n")
+ end
+ end
+ mod.puts("\n\\readfile{cont-mod}{}{}\n")
+ end
+ if not setups.empty? then
+ for key in setups.keys
+ if key =~ /^(.+?)\.(.+?)\.(.+?)$/o then
+ command, key, type, value = $1, $2, $3, setups[key]
+ value = cleanedup(key,type,value)
+ mod.puts("\\#{$1}[#{key}=#{value}]\n")
+ elsif key =~ /^(.+?)\.(.+?)$/o then
+ command, type, value = $1, $2, setups[key]
+ mod.puts("\\#{$1}[#{value}]\n")
+ end
+ end
+ end
+ savevaroptions(vars, 'setvariables', mod)
+ savevaroptions(mpvars,'setMPvariables',mod)
+ end
+ rescue
+ logger.report("#{modefile} cannot be saved") if logger
+ end
+ else
+ logger.report("#{modefile} is not created") if logger
+ end
+ end
+ end
+
+ private
+
+ def ExaModes.autoparenthesis(str)
+ if str =~ /[\+\-]/o then '[1]' + str + '[1]' else str end
+ end
+
+ def ExaModes.cleanedup(key,type,value)
+ if type == 'dimension' then
+ unless value =~ /(cm|mm|in|bp|sp|pt|dd|em|ex)/o
+ value + 'pt'
+ else
+ value
+ end
+ elsif type == 'calcmath' then
+ '{' + calculatortexmath(value,true) + '}'
+ elsif type =~ /^filename|filelist$/ or key =~ /^filename|filelist$/ then
+ cleanpath(value)
+ else
+ value
+ end
+ end
+
+ def ExaModes.cleanpath(str)
+ (str ||'').gsub(/\\/o,'/')
+ end
+
+ def ExaModes.texifiedstr(key,val)
+ case key
+ when 'filename' then
+ cleanpath(val)
+ when 'filelist' then
+ cleanpath(val)
+ else
+ val
+ end
+ end
+
+ def ExaModes.savevaroptions(vars,setvariables,mod)
+ if not vars.empty? then
+ for key in vars.keys do
+ # var.whatever.width.dimension.value
+ if key =~ /^(.+?)\.(.+?)\.(.+?)$/o then
+ tag, key, type, value = $1, $2, $3, vars[key]
+ value = cleanedup(key,type,value)
+ mod.puts("\\#{setvariables}[#{tag}][#{key}=#{value}]\n")
+ elsif key =~ /^(.+?)\.(.+?)$/o then
+ tag, key, value = $1, $2, vars[key]
+ mod.puts("\\#{setvariables}[#{tag}][#{key}=#{value}]\n")
+ end
+ end
+ end
+ end
+
+ def ExaModes.calculatortexmath(str,tx=true)
+ if tx then
+ bdisp, edisp = "\\displaymath\{", "\}"
+ binln, einln = "\\inlinemath\{" , "\}"
+ egraf = "\\endgraf"
+ else
+ bdisp, edisp = "<displaytexmath>", "</displaytexmath>"
+ binln, einln = "<inlinetexmath>" , "</inlinetexmath>"
+ egraf = "<p/>"
+ end
+ str.gsub!(/\n\s*\n+/mois, "\\ENDGRAF ")
+ str.gsub!(/(\[\[)\s*(.*?)\s*(\]\])/mos) do
+ $1 + docalculatortexmath($2) + $3
+ end
+ str.gsub!(/(\\ENDGRAF)+\s*(\[\[)\s*(.*?)\s*(\]\])/mois) do
+ $1 + bdisp + $3 + edisp
+ end
+ str.gsub!(/(\[\[)\s*(.*?)\s*(\]\])/o) do
+ binln + $2 + einln
+ end
+ str.gsub!(/\\ENDGRAF/mos, egraf)
+ str
+ end
+
+ def ExaModes.docalculatortexmath(str)
+ str.gsub!(/\n/o) { ' ' }
+ str.gsub!(/\s+/o) { ' ' }
+ str.gsub!(/&gt;/o) { '>' }
+ str.gsub!(/&lt;/o) { '<' }
+ str.gsub!(/&.*?;/o) { }
+ level = 0
+ str.gsub!(/([\(\)])/o) do |chr|
+ if chr == '(' then
+ level = level + 1
+ chr = '[' + level.to_s + ']'
+ elsif chr == ')' then
+ chr = '[' + level.to_s + ']'
+ level = level - 1
+ end
+ chr
+ end
+ # ...E...
+ loop do
+ break unless str.gsub!(/([\d\.]+)E([\-\+]{0,1}[\d\.]+)/o) do
+ "\{\\SCINOT\{#{$1}\}\{#{$2}\}\}"
+ end
+ end
+ # ^-..
+ loop do
+ break unless str.gsub!(/\^([\-\+]*\d+)/o) do
+ "\^\{#{$1}\}"
+ end
+ end
+ # ^(...)
+ loop do
+ break unless str.gsub!(/\^(\[\d+\])(.*?)\1/o) do
+ "\^\{#{$2}\}"
+ end
+ end
+ # 1/x^2
+ loop do
+ break unless str.gsub!(/([\d\w\.]+)\/([\d\w\.]+)\^([\d\w\.]+)/o) do
+ "@\{#{$1}\}\{#{$2}\^\{#{$3}\}\}"
+ end
+ end
+ # int(a,b,c)
+ loop do
+ break unless str.gsub!(/(int|sum|prod)(\[\d+\])(.*?),(.*?),(.*?)\2/o) do
+ "\\#{$1.upcase}\^\{#{$4}\}\_\{#{$5}\}\{#{autoparenthesis($3)}\}"
+ end
+ end
+ # int(a,b)
+ loop do
+ break unless str.gsub!(/(int|sum|prod)(\[\d+\])(.*?),(.*?)\2/o) do
+ "\\#{$1.upcase}\_\{#{$4}\}\{#{autoparenthesis($3)}\}"
+ end
+ end
+ # int(a)
+ loop do
+ break unless str.gsub!(/(int|sum|prod)(\[\d+\])(.*?)\2/o) do
+ "\\#{$1.upcase}\{#{autoparenthesis($3)}\}"
+ end
+ end
+ # sin(x) => {\sin(x)}
+ loop do
+ break unless str.gsub!(/(median|min|max|round|sqrt|sin|cos|tan|sinh|cosh|tanh|ln|log)\s*(\[\d+\])(.*?)\2/o) do
+ "\{\\#{$1.upcase}\{#{$2}#{$3}#{$2}\}\}"
+ end
+ end
+ # mean
+ str.gsub!(/(mean)(\[\d+\])(.*?)\2/o) do
+ "\{\\OVERLINE\{#{$3}\}\}"
+ end
+ # sin x => {\sin(x)}
+ # ...
+ # (1+x)/(1+x) => \frac{1+x}{1+x}
+ loop do
+ break unless str.gsub!(/(\[\d+\])(.*?)\1\/(\[\d+\])(.*?)\3/o) do
+ "@\{#{$2}\}\{#{$4}\}"
+ end
+ end
+ # (1+x)/x => \frac{1+x}{x}
+ loop do
+ break unless str.gsub!(/(\[\d+\])(.*?)\1\/([a-zA-Z0-9]+)/o) do
+ "@\{#{$2}\}\{#{$3}\}"
+ end
+ end
+ # 1/(1+x) => \frac{1}{1+x}
+ loop do
+ break unless str.gsub!(/([a-zA-Z0-9]+)\/(\[\d+\])(.*?)\2/o) do
+ "@\{#{$1}\}\{#{$3}\}"
+ end
+ end
+ # 1/x => \frac{1}{x}
+ loop do
+ break unless str.gsub!(/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)/o) do
+ "@\{#{$1}\}\{#{$2}\}"
+ end
+ end
+ #
+ str.gsub!(/\@/o) do
+ "\\FRAC "
+ end
+ str.gsub!(/\*/o) do
+ " "
+ end
+ str.gsub!(/\<\=/o) do
+ "\\LE "
+ end
+ str.gsub!(/\>\=/o) do
+ "\\GE "
+ end
+ str.gsub!(/\=/o) do
+ "\\EQ "
+ end
+ str.gsub!(/\</o) do
+ "\\LT "
+ end
+ str.gsub!(/\>/) do
+ "\\GT "
+ end
+ str.gsub!(/(D)(\[\d+\])(.*?)\2/o) do
+ "\{\\FRAC\{\\MBOX{d}\}\{\\MBOX{d}x\}\{#{$2}#{$3}#{$2}\}\}"
+ end
+ str.gsub!(/(exp)(\[\d+\])(.*?)\2/o) do
+ "\{e^\{#{$3}\}\}"
+ end
+ str.gsub!(/(abs)(\[\d+\])(.*?)\2/o) do
+ "\{\\left\|#{$3}\\right\|\}"
+ end
+ str.gsub!(/D([x|y])/o) do
+ "\\FRAC\{\{\\rm d\}#{$1}\}\{\{\\rm d\}x\}"
+ end
+ str.gsub!(/D([f|g])(\[\d+\])(.*?)\2/o) do
+ "\{\\rm #{$1}\}'#{$2}#{$3}#{$2}"
+ end
+ str.gsub!(/([f|g])(\[\d+\])(.*?)\2/o) do
+ "\{\\rm #{$1}\}#{$2}#{$3}#{$2}"
+ end
+ str.gsub!(/(pi|inf)/io) do
+ "\\#{$1} "
+ end
+ loop do
+ break unless str.gsub!(/(\[\d+?\])(.*?)\1/o) do
+ "\\left(#{$2}\\right)"
+ end
+ end
+ str.gsub!(/\\([A-Z]+?)([\s\{\^\_\\])/io) do
+ "\\#{$1.downcase}#{$2}"
+ end
+ str
+ end
+
+end
+
+# ExaModes.cleanup_request()
diff --git a/Master/texmf-dist/scripts/context/ruby/base/file.rb b/Master/texmf-dist/scripts/context/ruby/base/file.rb
index 42fb346c409..39bb7d46731 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/file.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/file.rb
@@ -105,12 +105,13 @@ class File
end
def File.silentdelete(filename)
- begin File.delete(filename) ; rescue ; end
+ File.delete(filename) rescue false
end
def File.silentcopy(oldname,newname)
return if File.expand_path(oldname) == File.expand_path(newname)
- begin File.copy(oldname,newname) ; rescue ; end
+ File.makedirs(File.dirname(newname)) rescue false
+ File.copy(oldname,newname) rescue false
end
def File.silentrename(oldname,newname)
@@ -118,11 +119,12 @@ class File
# 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
+ File.delete(newname) rescue false
begin
File.rename(oldname,newname)
rescue
- begin File.copy(oldname,newname) ; rescue ; end
+ File.makedirs(File.dirname(newname)) rescue false
+ File.copy(oldname,newname) rescue false
end
end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpse.rb b/Master/texmf-dist/scripts/context/ruby/base/kpse.rb
index dc4898ffc58..b0321672fc1 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/kpse.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpse.rb
@@ -18,6 +18,26 @@ require 'rbconfig'
#
# miktex has mem|fmt|base paths
+class String
+
+ def split_path
+ if self =~ /\;/o || self =~ /^[a-z]\:/io then
+ self.split(";")
+ else
+ self.split(":")
+ end
+ end
+
+end
+
+class Array
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+end
+
module Kpse
@@located = Hash.new
@@ -32,11 +52,21 @@ module Kpse
@@crossover = true
@@mswindows = Config::CONFIG['host_os'] =~ /mswin/
- @@distribution = 'miktex' if ENV['PATH'] =~ /miktex[\\\/]bin/o
+ # @@distribution = 'miktex' if ENV['PATH'] =~ /miktex[\\\/]bin/o
- @@usekpserunner = false || ENV['KPSEFAST'] == 'yes'
+ if ENV['PATH'] =~ /(.*?)miktex[\\\/]bin/i then
+ @@distribution = 'miktex' unless $1 =~ /(texmf\-mswin[\/\\]bin|bin[\/\\]win32)/i
+ end
+
+ @@re_true = /yes|on|true|1/i
- require 'base/tool' if @@usekpserunner
+ if (ENV['KPSEFAST'] =~ @@re_true) || (ENV['CTXMINIMAL'] =~ @@re_true) then
+ @@usekpserunner = true
+ require 'base/kpsefast'
+ require 'base/kpserunner'
+ else
+ @@usekpserunner = false
+ end
if @@crossover then
ENV.keys.each do |k|
@@ -113,12 +143,10 @@ module Kpse
begin
if @@problems then
results = ''
+ elsif @@usekpserunner then
+ results = KpseRunner.kpsewhich(arguments).chomp
else
- if @@usekpserunner then
- results = KpseRunner.kpsewhich(arguments).chomp
- else
- results = `kpsewhich #{arguments}`.chomp
- end
+ results = `kpsewhich #{arguments}`.chomp
end
rescue
puts "unable to run kpsewhich" if @@tracing
@@ -132,7 +160,7 @@ module Kpse
# maybe we should check for writeability
unless @@paths.key?('formatpaths') then
begin
- setpath('formatpaths',run("--show-path=fmt").gsub(/\\/,'/').split(File::PATH_SEPARATOR))
+ setpath('formatpaths',run("--show-path=fmt").gsub(/\\/,'/').split_path)
rescue
setpath('formatpaths',[])
end
@@ -189,30 +217,51 @@ module Kpse
end
# locate writable path
if ! formatpath.empty? then
- formatpath.split(File::PATH_SEPARATOR).each do |fp|
- fp.gsub!(/\\/,'/')
+ formatpaths, done = formatpath.split_path, false
+ formatpaths.collect! do |fp|
+ fp.gsub!(/\\/o,'/')
+ fp.gsub!(/\/\/$/o,'/')
# 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}\/*$/,'')
+ fp.sub!(/^!!/o,'')
+ fp.sub!(/\/+$/o,'')
+ fp.sub!(/(unsetengine|unset)/o,if enginepath then engine else '' end)
+ fp
+ end
+ formatpaths.delete_if do |fp|
+ fp.empty? || fp == '.'
+ end
+ # the engine path may not yet be present, find first writable
+ formatpaths.each do |fp|
+ # strip (possible engine) and test for writeability
+ fpp = fp.sub(/#{engine}\/*$/o,'')
+ if FileTest.directory?(fpp) && FileTest.writable?(fpp) then
+ # use this path
+ formatpath, done = fp.dup, true
+ break
+ end
+ end
+ unless done then
+ formatpaths.each do |fp|
+ fpp = fp.sub(/#{engine}\/*$/o,'')
+ File.makedirs(fpp) rescue false # maybe we don't have an path yet
if FileTest.directory?(fpp) && FileTest.writable?(fpp) then
# use this path
- formatpath = fp.dup
+ formatpath, done = fp.dup, true
break
end
end
end
+ unless done then
+ formatpath = '.'
+ end
end
# needed !
- begin File.makedirs(formatpath) ; rescue ; end ;
+ File.makedirs(formatpath) rescue false
# fall back to current path
formatpath = '.' if formatpath.empty? || ! FileTest.writable?(formatpath)
# append engine but prevent duplicates
formatpath = File.join(formatpath.sub(/\/*#{engine}\/*$/,''), engine) if enginepath
- begin File.makedirs(formatpath) ; rescue ; end ;
+ File.makedirs(formatpath) rescue false
setpath(engine,formatpath)
# ENV['engine'] = savedengine
end
@@ -283,6 +332,10 @@ module Kpse
`#{cmd}`
end
+ def Kpse.searchmethod
+ if @@usekpserunner then 'kpsefast' else 'kpsewhich' end
+ end
+
private
def Kpse.setvariable(key,value)
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpse/drb.rb b/Master/texmf-dist/scripts/context/ruby/base/kpse/drb.rb
new file mode 100644
index 00000000000..db1ce0eec87
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpse/drb.rb
@@ -0,0 +1,57 @@
+require 'drb'
+require 'base/kpse/trees'
+
+class KpseServer
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ end
+
+ def start
+ puts "starting drb service at port #{@port}"
+ DRb.start_service("druby://localhost:#{@port}", KpseTrees.new)
+ trap(:INT) do
+ DRb.stop_service
+ end
+ DRb.thread.join
+ end
+
+ def stop
+ # todo
+ end
+
+end
+
+class KpseClient
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @kpse = nil
+ end
+
+ def start
+ # only needed when callbacks are used / slow, due to Socket::getaddrinfo
+ # DRb.start_service
+ end
+
+ def object
+ @kpse = DRbObject.new(nil,"druby://localhost:#{@port}")
+ end
+
+end
+
+
+# SERVER_URI="druby://localhost:8787"
+#
+# # Start a local DRbServer to handle callbacks.
+# #
+# # Not necessary for this small example, but will be required
+# # as soon as we pass a non-marshallable object as an argument
+# # to a dRuby call.
+# DRb.start_service
+#
+# timeserver = DRbObject.new_with_uri(SERVER_URI)
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpse/soap.rb b/Master/texmf-dist/scripts/context/ruby/base/kpse/soap.rb
new file mode 100644
index 00000000000..c9ed75c446b
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpse/soap.rb
@@ -0,0 +1,79 @@
+require 'soap/rpc/standaloneServer'
+require 'soap/rpc/driver'
+
+require 'base/kpse/trees'
+
+class KpseService < SOAP::RPC::StandaloneServer
+
+ def on_init
+ kpse = KpseTrees.new
+ add_method(kpse, 'choose', 'files', 'environment')
+ add_method(kpse, 'load', 'files', 'environment')
+ add_method(kpse, 'expand_variables', 'tree')
+ add_method(kpse, 'expand_braces', 'tree', 'str')
+ add_method(kpse, 'expand_path', 'tree', 'str')
+ add_method(kpse, 'expand_var', 'tree', 'str')
+ add_method(kpse, 'show_path', 'tree', 'str')
+ add_method(kpse, 'var_value', 'tree', 'str')
+ add_method(kpse, 'find_file', 'tree', 'filename')
+ add_method(kpse, 'find_files', 'tree', 'filename', 'first')
+ end
+
+end
+
+class KpseServer
+
+ @@url = 'http://kpse.thismachine.org/KpseService'
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @server = nil
+ end
+
+ def start
+ puts "starting soap service at port #{@port}"
+ @server = KpseService.new('KpseServer', @@url, '0.0.0.0', @port.to_i)
+ trap(:INT) do
+ @server.shutdown
+ end
+ status = @server.start
+ end
+
+ def stop
+ @server.shutdown rescue false
+ end
+
+end
+
+class KpseClient
+
+ @@url = 'http://kpse.thismachine.org/KpseService'
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @kpse = nil
+ end
+
+ def start
+ @kpse = SOAP::RPC::Driver.new("http://localhost:#{port}/", @@url)
+ @kpse.add_method('choose','files', 'environment')
+ @kpse.add_method('load','files', 'environment')
+ @kpse.add_method('expand_variables', 'tree')
+ @kpse.add_method('expand_braces', 'tree', 'str')
+ @kpse.add_method('expand_path', 'tree', 'str')
+ @kpse.add_method('expand_var', 'tree', 'str')
+ @kpse.add_method('show_path', 'tree', 'str')
+ @kpse.add_method('var_value', 'tree', 'str')
+ @kpse.add_method('find_file', 'tree', 'filename')
+ @kpse.add_method('find_files', 'tree', 'filename', 'first')
+ end
+
+ def object
+ @kpse
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpse/trees.rb b/Master/texmf-dist/scripts/context/ruby/base/kpse/trees.rb
new file mode 100644
index 00000000000..9c872eb18c1
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpse/trees.rb
@@ -0,0 +1,84 @@
+require 'monitor'
+require 'base/kpsefast'
+
+class KpseTrees < Monitor
+
+ def initialize
+ @trees = Hash.new
+ end
+
+ def pattern(filenames)
+ filenames.join('|').gsub(/\\+/o,'/').downcase
+ end
+
+ def choose(filenames,environment)
+ current = pattern(filenames)
+ load(filenames,environment) unless @trees[current]
+ puts "enabling tree #{current}"
+ current
+ end
+
+ def fetch(filenames,environment) # will send whole object !
+ current = pattern(filenames)
+ load(filenames,environment) unless @trees[current]
+ puts "fetching tree #{current}"
+ @trees[current]
+ end
+
+ def load(filenames,environment)
+ current = pattern(filenames)
+ puts "loading tree #{current}"
+ @trees[current] = KpseFast.new
+ @trees[current].push_environment(environment)
+ @trees[current].load_cnf(filenames)
+ @trees[current].expand_variables
+ @trees[current].load_lsr
+ end
+
+ def set(tree,key,value)
+ case key
+ when 'progname' then @trees[tree].progname = value
+ when 'engine' then @trees[tree].engine = value
+ when 'format' then @trees[tree].format = value
+ end
+ end
+ def get(tree,key)
+ case key
+ when 'progname' then @trees[tree].progname
+ when 'engine' then @trees[tree].engine
+ when 'format' then @trees[tree].format
+ end
+ end
+
+ def load_cnf(tree)
+ @trees[tree].load_cnf
+ end
+ def load_lsr(tree)
+ @trees[tree].load_lsr
+ end
+ def expand_variables(tree)
+ @trees[tree].expand_variables
+ end
+ def expand_braces(tree,str)
+ @trees[tree].expand_braces(str)
+ end
+ def expand_path(tree,str)
+ @trees[tree].expand_path(str)
+ end
+ def expand_var(tree,str)
+ @trees[tree].expand_var(str)
+ end
+ def show_path(tree,str)
+ @trees[tree].show_path(str)
+ end
+ def var_value(tree,str)
+ @trees[tree].var_value(str)
+ end
+ def find_file(tree,filename)
+ @trees[tree].find_file(filename)
+ end
+ def find_files(tree,filename,first)
+ @trees[tree].find_files(filename,first)
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpsedirect.rb b/Master/texmf-dist/scripts/context/ruby/base/kpsedirect.rb
new file mode 100644
index 00000000000..6fa8c8601a9
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpsedirect.rb
@@ -0,0 +1,34 @@
+class KpseDirect
+
+ attr_accessor :progname, :format, :engine
+
+ def initialize
+ @progname, @format, @engine = '', '', ''
+ end
+
+ def expand_path(str)
+ clean_name(`kpsewhich -expand-path=#{str}`.chomp)
+ end
+
+ def expand_var(str)
+ clean_name(`kpsewhich -expand-var=#{str}`.chomp)
+ end
+
+ def find_file(str)
+ clean_name(`kpsewhich #{_progname_} #{_format_} #{str}`.chomp)
+ end
+
+ def _progname_
+ if @progname.empty? then '' else "-progname=#{@progname}" end
+ end
+ def _format_
+ if @format.empty? then '' else "-format=\"#{@format}\"" end
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb b/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb
index 52ab28d0fe7..20c07c70a4e 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb
@@ -10,6 +10,26 @@
# todo: multiple cnf files
+class String
+
+ def split_path
+ if self =~ /\;/o || self =~ /^[a-z]\:/io then
+ self.split(";")
+ else
+ self.split(":")
+ end
+ end
+
+end
+
+class Array
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+end
+
class File
def File.locate_file(path,name)
@@ -37,7 +57,70 @@ class File
end
-class KPSEFAST
+module KpseUtil
+
+ # to be adapted, see loading cnf file
+
+ @@texmftrees = ['texmf-local','texmf.local','../..','texmf'] # '../..' is for gwtex
+ @@texmfcnf = 'texmf.cnf'
+
+ def KpseUtil::identify
+ # we mainly need to identify the local tex stuff and wse assume that
+ # the texmfcnf variable is set; otherwise we need to expand the
+ # TEXMF variable and that takes time since it may involve more
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ ENV['SELFAUTOPARENT'] = ownpath
+ else
+ ENV['SELFAUTOPARENT'] = '.' # fall back
+ # may be too tricky:
+ #
+ # (ENV['PATH'] ||'').split_path.each do |p|
+ # if p.gsub!(/texmf.*?$/o, '') then
+ # ENV['SELFAUTOPARENT'] = p
+ # break
+ # end
+ # end
+ end
+ filenames = Array.new
+ if ENV['TEXMFCNF'] && ! ENV['TEXMFCNF'].empty? then
+ ENV['TEXMFCNF'].to_s.split_path.each do |path|
+ filenames << File.join(path,@@texmfcnf)
+ end
+ elsif ENV['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.',@@texmfcnf)
+ else
+ @@texmftrees.each do |tree|
+ filenames << File.join(ENV['SELFAUTOPARENT'],tree,'web2c',@@texmfcnf)
+ end
+ end
+ loop do
+ busy = false
+ filenames.collect! do |f|
+ f.gsub(/\$([a-zA-Z0-9\_\-]+)/o) do
+ if (! ENV[$1]) || (ENV[$1] == $1) then
+ "$#{$1}"
+ else
+ busy = true
+ ENV[$1]
+ end
+ end
+ end
+ break unless busy
+ end
+ filenames.delete_if do |f|
+ ! FileTest.file?(f)
+ end
+ return filenames
+ end
+
+ def KpseUtil::environment
+ Hash.new.merge(ENV)
+ end
+
+end
+
+class KpseFast
# formats are an incredible inconsistent mess
@@ -45,6 +128,8 @@ class KPSEFAST
@@formats = Hash.new
@@suffixmap = Hash.new
+ @@texmfcnf = 'texmf.cnf'
+
@@suffixes['gf'] = ['.<resolution>gf'] # todo
@@suffixes['pk'] = ['.<resolution>pk'] # todo
@@suffixes['tfm'] = ['.tfm']
@@ -121,7 +206,7 @@ class KPSEFAST
@@formats['bst'] = ''
@@formats['cnf'] = ''
@@formats['ls-R'] = ''
- @@formats['fmt'] = ''
+ @@formats['fmt'] = 'TEXFORMATS'
@@formats['map'] = 'TEXFONTMAPS'
@@formats['mem'] = 'MPMEMS'
@@formats['mf'] = 'MFINPUTS'
@@ -170,27 +255,41 @@ class KPSEFAST
@@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'
+ @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'
+
+ @environment = ENV
+ end
+
+ def set(key,value)
+ case key
+ when 'progname' then @progname = value
+ when 'engine' then @engine = value
+ when 'format' then @format = value
+ end
+ end
+
+ def push_environment(env)
+ @environment = env
end
# {$SELFAUTOLOC,$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}
@@ -201,36 +300,42 @@ class KPSEFAST
#
# 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
+ def load_cnf(filenames=nil)
+ unless filenames then
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ @environment['SELFAUTOPARENT'] = ownpath
+ else
+ @environment['SELFAUTOPARENT'] = '.'
+ end
+ unless @treepath.empty? then
+ unless @rootpath.empty? then
+ @treepath = @treepath.split(',').collect do |p| File.join(@rootpath,p) end.join(',')
+ end
+ @environment['TEXMF'] = @treepath
+ # only the first one
+ @environment['TEXMFCNF'] = File.join(@treepath.split(',').first,'texmf/web2c')
+ end
unless @rootpath.empty? then
- @treepath = @treepath.split(',').collect do |p| File.join(@rootpath,p) end.join(',')
+ @environment['TEXMFCNF'] = File.join(@rootpath,'texmf/web2c')
+ @environment['SELFAUTOPARENT'] = @rootpath
+ @isolate = true
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')
+ filenames = Array.new
+ if @environment['TEXMFCNF'] and not @environment['TEXMFCNF'].empty? then
+ @environment['TEXMFCNF'].to_s.split_path.each do |path|
+ filenames << File.join(path,@@texmfcnf)
+ end
+ elsif @environment['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.',@@texmfcnf)
+ else
+ ['texmf-local','texmf'].each do |tree|
+ filenames << File.join(@environment['SELFAUTOPARENT'],tree,'web2c',@@texmfcnf)
+ end
end
end
# <root>/texmf/web2c/texmf.cnf
+ filenames = _expanded_path_(filenames)
@rootpath = filenames.first
3.times do
@rootpath = File.dirname(@rootpath)
@@ -280,9 +385,9 @@ class KPSEFAST
@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]
+ if @environment[key] then
+ if FileTest.directory?(@environment[key]) then
+ @cachepath = @environment[key]
@cachefile = [@rootpath.gsub(/[^A-Z0-9]/io, '-').gsub(/\-+/,'-'),File.basename(@cachefile)].join('-')
break
end
@@ -342,14 +447,14 @@ class KPSEFAST
def expand_variables
@expansions = Hash.new
if @isolate then
- @variables['TEXMFCNF'] = ENV['TEXMFCNF'].dup
- @variables['SELFAUTOPARENT'] = ENV['SELFAUTOPARENT'].dup
+ @variables['TEXMFCNF'] = @environment['TEXMFCNF'].dup
+ @variables['SELFAUTOPARENT'] = @environment['SELFAUTOPARENT'].dup
else
- ENV.keys.each do |e|
+ @environment.keys.each do |e|
if e =~ /^([a-zA-Z]+)\_(.*)\s*$/o then
- @expansions["#{$1}.#{$2}"] = ENV[e].dup
+ @expansions["#{$1}.#{$2}"] = (@environment[e] ||'').dup
else
- @expansions[e] = ENV[e].dup
+ @expansions[e] = (@environment[e] ||'').dup
end
end
end
@@ -465,7 +570,8 @@ class KPSEFAST
end
end
pathlist = pathlist.uniq.collect do |path|
- p = path.gsub(/^\/+/o) do '' end
+ p = path
+ # p.gsub(/^\/+/o) do '' end
# p.gsub!(/(.)\/\/(.)/o) do "#{$1}/#{$2}" end
# p.gsub!(/\/\/+$/o) do '//' end
p.gsub!(/\/\/+/o) do '//' end
@@ -485,8 +591,8 @@ class KPSEFAST
end
def var_of_format_or_suffix(str)
- if @@formats.key?(@format) then
- @@formats[@format]
+ if @@formats.key?(str) then
+ @@formats[str]
elsif @@suffixmap.key?(File.extname(str)) then # extname includes .
@@formats[@@suffixmap[File.extname(str)]] # extname includes .
else
@@ -496,7 +602,7 @@ class KPSEFAST
end
-class KPSEFAST
+class KpseFast
# test things
@@ -536,23 +642,23 @@ class KPSEFAST
puts ""
puts @variables ["KPSE_TEST_PATTERN_#{i}"]
puts ""
- puts expand_path("KPSE_TEST_PATTERN_#{i}").split(File::PATH_SEPARATOR)
+ puts expand_path("KPSE_TEST_PATTERN_#{i}").split_path
puts ""
end
end
end
-class KPSEFAST
+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)
+ _expanded_path_(original_variable(str).split_path).join_path
end
def expand_path(str) # output complete path expansion of STRING.
- _expanded_path_(expanded_variable(str).split(";")).join(File::PATH_SEPARATOR)
+ _expanded_path_(expanded_variable(str).split_path).join_path
end
def expand_var(str) # output variable expansion of STRING.
@@ -560,7 +666,7 @@ class KPSEFAST
end
def show_path(str) # output search path for file type NAME
- expanded_path(var_of_format(str)).join(File::PATH_SEPARATOR)
+ expanded_path(str).join_path
end
def var_value(str) # output the value of variable $STRING.
@@ -569,97 +675,117 @@ class KPSEFAST
end
-class KPSEFAST
+class KpseFast
+
+ def _is_cnf_?(filename)
+ filename == File.basename((@cnffiles.first rescue @@texmfcnf) || @@texmfcnf)
+ end
def find_file(filename)
- find_files(filename,true)
+ if _is_cnf_?(filename) then
+ @cnffiles.first rescue ''
+ else
+ [find_files(filename,true)].flatten.first || ''
+ end
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, '')
+ if _is_cnf_?(filename) then
+ result = @cnffiles.dup
+ else
+ if @remember then
+ # stamp = "#{filename}--#{@format}--#{@engine}--#{@progname}"
+ stamp = "#{filename}--#{@engine}--#{@progname}"
+ return @found[stamp] if @found.key?(stamp)
+ end
+ pathlist = expanded_path(filename)
+ result = []
+ filelist = if @files.key?(filename) then @files[filename].uniq else nil end
done = false
- if not done and filelist then
- # checking for exact match
- if filelist.include?(pathname) then
- result << pathname
+ if pathlist.size == 0 then
+ if FileTest.file?(filename) then
done = true
+ result << '.'
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
+ else
+ pathlist.each do |path|
+ doscan = if path =~ /^\!\!/o then false else true end
+ recurse = if path =~ /\/\/$/o then true else false end
+ pathname = path.dup
+ pathname.gsub!(/^\!+/o, '')
+ done = false
+ if not done and filelist then
+ # checking for exact match
+ if filelist.include?(pathname) then
+ result << pathname
done = true
end
- break if done
+ if not done and recurse then
+ # checking for fuzzy //
+ pathname.gsub!(/\/+$/o, '/.*')
+ # pathname.gsub!(/\/\//o,'/[\/]*/')
+ pathname.gsub!(/\/\//o,'/.*?/')
+ re = /^#{pathname}/
+ filelist.each do |f|
+ if re =~ f then
+ result << f # duplicates will be filtered later
+ done = true
+ end
+ break if done
+ end
+ end
end
+ if not done and doscan then
+ # checking for path itself
+ pname = pathname.sub(/\.\*$/,'')
+ if not pname =~ /\*/o and FileTest.file?(File.join(pname,filename)) then
+ result << pname
+ done = true
+ end
+ end
+ break if done and first
end
end
- if not done and 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
+ 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 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)
+ 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
- elsif FileTest.file?(File.join(pathname,filename)) then
- result << pathname
- done = true
+ break if done and first
end
- break if done and first
end
end
+ result = result.uniq.collect do |pathname|
+ File.join(pathname,filename)
+ end
+ @found[stamp] = result if @remember
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 KpseFast
class FileData
attr_accessor :tag, :name, :size, :date
@@ -673,12 +799,9 @@ class KPSEFAST
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}"
+ when 1 then "deleted | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 2 then "present | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 3 then "obsolete | #{' '*8} | #{' '*16} | #{@name}"
end
end
end
@@ -769,88 +892,16 @@ class KPSEFAST
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
+# if false then
- 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 = KPSEFAST.new # (root)
- k.set_test_patterns
- k.load_cnf
- k.expand_variables
- k.load_lsr
+ # k.show_test_patterns
- k.show_test_patterns
# puts k.list_variables
# puts k.list_expansions
# k.list_lsr
@@ -876,6 +927,6 @@ if false then
# puts "expand value $TEXINPUTS.context"
# puts k.var_value("$TEXINPUTS.context")
- exit
+ # exit
-end
+# end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpseremote.rb b/Master/texmf-dist/scripts/context/ruby/base/kpseremote.rb
new file mode 100644
index 00000000000..9a73b88b00e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpseremote.rb
@@ -0,0 +1,116 @@
+require 'base/kpsefast'
+
+case ENV['KPSEMETHOD']
+ when /soap/o then require 'base/kpse/soap'
+ when /drb/o then require 'base/kpse/drb'
+ else require 'base/kpse/drb'
+end
+
+class KpseRemote
+
+ @@port = ENV['KPSEPORT'] || 7000
+ @@method = ENV['KPSEMETHOD'] || 'drb'
+
+ def KpseRemote::available?
+ @@method && @@port
+ end
+
+ def KpseRemote::start_server(port=nil)
+ kpse = KpseServer.new(port || @@port)
+ kpse.start
+ end
+
+ def KpseRemote::start_client(port=nil) # keeps object in server
+ kpseclient = KpseClient.new(port || @@port)
+ kpseclient.start
+ kpse = kpseclient.object
+ tree = kpse.choose(KpseUtil::identify, KpseUtil::environment)
+ [kpse, tree]
+ end
+
+ def KpseRemote::fetch(port=nil) # no need for defining methods but slower, send whole object
+ kpseclient = KpseClient.new(port || @@port)
+ kpseclient.start
+ kpseclient.object.fetch(KpseUtil::identify, KpseUtil::environment) rescue nil
+ end
+
+ def initialize(port=nil)
+ if KpseRemote::available? then
+ begin
+ @kpse, @tree = KpseRemote::start_client(port)
+ rescue
+ @kpse, @tree = nil, nil
+ end
+ else
+ @kpse, @tree = nil, nil
+ end
+ end
+
+ def progname=(value)
+ @kpse.set(@tree,'progname',value)
+ end
+ def format=(value)
+ @kpse.set(@tree,'format',value)
+ end
+ def engine=(value)
+ @kpse.set(@tree,'engine',value)
+ end
+
+ def progname
+ @kpse.get(@tree,'progname')
+ end
+ def format
+ @kpse.get(@tree,'format')
+ end
+ def engine
+ @kpse.get(@tree,'engine')
+ end
+
+ def load
+ @kpse.load(KpseUtil::identify, KpseUtil::environment)
+ end
+ def okay?
+ @kpse && @tree
+ end
+ def set(key,value)
+ @kpse.set(@tree,key,value)
+ end
+ def load_cnf
+ @kpse.load_cnf(@tree)
+ end
+ def load_lsr
+ @kpse.load_lsr(@tree)
+ end
+ def expand_variables
+ @kpse.expand_variables(@tree)
+ end
+ def expand_braces(str)
+ clean_name(@kpse.expand_braces(@tree,str))
+ end
+ def expand_path(str)
+ clean_name(@kpse.expand_path(@tree,str))
+ end
+ def expand_var(str)
+ clean_name(@kpse.expand_var(@tree,str))
+ end
+ def show_path(str)
+ clean_name(@kpse.show_path(@tree,str))
+ end
+ def var_value(str)
+ clean_name(@kpse.var_value(@tree,str))
+ end
+ def find_file(filename)
+ clean_name(@kpse.find_file(@tree,filename))
+ end
+ def find_files(filename,first=false)
+ # dodo: each filename
+ @kpse.find_files(@tree,filename,first)
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpserunner.rb b/Master/texmf-dist/scripts/context/ruby/base/kpserunner.rb
new file mode 100644
index 00000000000..cfc2ad4fbfd
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/kpserunner.rb
@@ -0,0 +1,87 @@
+require 'base/kpsefast'
+
+module KpseRunner
+
+ @@kpse = nil
+
+ def KpseRunner.kpsewhich(arg='')
+ options, arguments = split_args(arg)
+ unless @@kpse then
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ require 'base/kpseremote'
+ @@kpse = KpseRemote.new
+ else
+ @@kpse = nil
+ end
+ if @@kpse && @@kpse.okay? then
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ else
+ require 'base/kpsefast'
+ @@kpse = KpseFast.new
+ @@kpse.load_cnf
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ @@kpse.expand_variables
+ @@kpse.load_lsr
+ end
+ else
+ @@kpse.progname = options['progname'] || ''
+ @@kpse.engine = options['engine'] || ''
+ @@kpse.format = options['format'] || ''
+ @@kpse.expand_variables
+ end
+ if option = options['expand-braces'] and not option.empty? then
+ @@kpse.expand_braces(option)
+ elsif option = options['expand-path'] and not option.empty? then
+ @@kpse.expand_path(option)
+ elsif option = options['expand-var'] and not option.empty? then
+ @@kpse.expand_var(option)
+ elsif option = options['show-path'] and not option.empty? then
+ @@kpse.show_path(option)
+ elsif option = options['var-value'] and not option.empty? then
+ @@kpse.expand_var(option)
+ elsif arguments.size > 0 then
+ files = Array.new
+ arguments.each do |option|
+ if file = @@kpse.find_file(option) and not file.empty? then
+ files << file
+ end
+ end
+ files.join("\n")
+ else
+ ''
+ end
+ end
+
+ def KpseRunner.kpsereset
+ @@kpse = nil
+ end
+
+ private
+
+ def KpseRunner.split_args(arg)
+ vars, args = Hash.new, Array.new
+ arg.gsub!(/([\"\'])(.*?)\1/o) do
+ $2.gsub(' ','<space/>')
+ end
+ arg = arg.split(/\s+/o)
+ arg.collect! do |a|
+ a.gsub('<space/>',' ')
+ end
+ arg.each do |a|
+ if a =~ /^(.*?)\=(.*?)$/o then
+ k, v = $1, $2
+ vars[k.sub(/^\-+/,'')] = v
+ else
+ args << a
+ end
+ end
+ # puts vars.inspect
+ # puts args.inspect
+ return vars, args
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/merge.rb b/Master/texmf-dist/scripts/context/ruby/base/merge.rb
new file mode 100644
index 00000000000..c6ea6c09e66
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/merge.rb
@@ -0,0 +1,139 @@
+# module : base/merge
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# --selfmerg ewill create stand alone script (--selfcleanup does the opposite)
+
+# this module will package all the used modules in the file itself
+# so that we can relocate the file at wish, usage:
+#
+# merge:
+#
+# unless SelfMerge::ok? && SelfMerge::merge then
+# puts("merging should happen on the path were the base inserts reside")
+# end
+#
+# cleanup:
+#
+# unless SelfMerge::cleanup then
+# puts("merging should happen on the path were the base inserts reside")
+# end
+
+module SelfMerge
+
+ @@kpsemergestart = "\# kpse_merge_start"
+ @@kpsemergestop = "\# kpse_merge_stop"
+ @@kpsemergefile = "\# kpse_merge_file: "
+ @@kpsemergedone = "\# kpse_merge_done: "
+
+ @@filename = File.basename($0)
+ @@ownpath = File.expand_path(File.dirname($0))
+ @@modroot = '(base|graphics|rslb|www)' # needed in regex in order not to mess up SelfMerge
+ @@modules = $".collect do |file| File.expand_path(file) end
+
+ @@modules.delete_if do |file|
+ file !~ /^#{@@ownpath}\/#{@@modroot}.*$/
+ end
+
+ def SelfMerge::ok?
+ begin
+ @@modules.each do |file|
+ return false unless FileTest.file?(file)
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::merge
+ begin
+ if SelfMerge::ok? && rbfile = IO.read(@@filename) then
+ begin
+ inserts = "#{@@kpsemergestart}\n\n"
+ @@modules.each do |file|
+ inserts << "#{@@kpsemergefile}'#{file}'\n\n"
+ inserts << IO.read(file).gsub(/^#.*?\n$/,'')
+ inserts << "\n\n"
+ end
+ inserts << "#{@@kpsemergestop}\n\n"
+ # no gsub! else we end up in SelfMerge
+ rbfile.sub!(/#{@@kpsemergestart}\s*#{@@kpsemergestop}/mois) do
+ inserts
+ end
+ rbfile.gsub!(/^(.*)(require [\"\'].*?#{@@modroot}.*)$/) do
+ pre, post = $1, $2
+ if pre =~ /#{@@kpsemergedone}/ then
+ "#{pre}#{post}"
+ else
+ "#{pre}#{@@kpsemergedone}#{post}"
+ end
+ end
+ rescue
+ return false
+ else
+ begin
+ File.open(@@filename,'w') do |f|
+ f << rbfile
+ end
+ rescue
+ return false
+ end
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::cleanup
+ begin
+ if rbfile = IO.read(@@filename) then
+ begin
+ rbfile.sub!(/#{@@kpsemergestart}(.*)#{@@kpsemergestop}\s*/mois) do
+ "#{@@kpsemergestart}\n\n#{@@kpsemergestop}\n\n"
+ end
+ rbfile.gsub!(/^(.*#{@@kpsemergedone}.*)$/) do
+ str = $1
+ if str =~ /require [\"\']/ then
+ str.gsub(/#{@@kpsemergedone}/, '')
+ else
+ str
+ end
+ end
+ rescue
+ return false
+ else
+ begin
+ File.open(@@filename,'w') do |f|
+ f << rbfile
+ end
+ rescue
+ return false
+ end
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::replace
+ if SelfMerge::ok? then
+ SelfMerge::cleanup
+ SelfMerge::merge
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/mp.rb b/Master/texmf-dist/scripts/context/ruby/base/mp.rb
new file mode 100644
index 00000000000..b30be111091
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/base/mp.rb
@@ -0,0 +1,167 @@
+# module : base/mp
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2005-2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+module MPTools
+
+ @@definitions, @@start, @@stop, @@before, @@after = Hash.new, Hash.new, Hash.new, Hash.new, Hash.new
+
+
+ @@definitions['plain'] = <<EOT
+\\gdef\\mpxshipout{\\shipout\\hbox\\bgroup
+ \\setbox0=\\hbox\\bgroup}
+
+\\gdef\\stopmpxshipout{\\egroup \\dimen0=\\ht0 \\advance\\dimen0\\dp0
+ \\dimen1=\\ht0 \\dimen2=\\dp0
+ \\setbox0=\\hbox\\bgroup
+ \\box0
+ \\ifnum\\dimen0>0 \\vrule width1sp height\\dimen1 depth\\dimen2
+ \\else \\vrule width1sp height1sp depth0sp\\relax
+ \\fi\\egroup
+ \\ht0=0pt \\dp0=0pt \\box0 \\egroup}
+EOT
+
+ @@start ['plain'] = ""
+ @@before['plain'] = "\\mpxshipout"
+ @@after ['plain'] = "\\stopmpxshipout"
+ @@stop ['plain'] = "\\end{document}"
+
+ @@definitions['context'] = <<EOT
+\\ifx\\startMPXpage\\undefined
+
+ \\ifx\\loadallfontmapfiles\\undefined \\let\\loadallfontmapfiles\\relax \\fi
+
+ \\gdef\\startMPXpage
+ {\\shipout\\hbox
+ \\bgroup
+ \\setbox0=\\hbox
+ \\bgroup}
+
+ \\gdef\\stopMPXpage
+ {\\egroup
+ \\dimen0=\\ht0
+ \\advance\\dimen0\\dp0
+ \\dimen1=\\ht0
+ \\dimen2=\\dp0
+ \\setbox0=\\hbox\\bgroup
+ \\box0
+ \\ifnum\\dimen0>0
+ \\vrule width 1sp height \\dimen1 depth \\dimen2
+ \\else
+ \\vrule width 1sp height 1sp depth 0sp \\relax
+ \\fi
+ \\egroup
+ \\ht0=0pt
+ \\dp0=0pt
+ \\loadallfontmapfiles
+ \\box0
+ \\egroup}
+
+\\fi
+
+\\ifx\\starttext\\undefined
+
+ \\let\\starttext\\relax
+ \\def\\stoptext{\\end{document}}
+
+\\fi
+EOT
+
+ @@start ['context'] = "\\starttext"
+ @@before['context'] = "\\startMPXpage"
+ @@after ['context'] = "\\stopMPXpage"
+ @@stop ['context'] = "\\stoptext"
+
+ # todo: \usemodule[m-mpx ] and test fo defined
+
+ def MPTools::mptotex(from,to=nil,method='plain')
+ begin
+ if from && data = IO.read(from) then
+ f = if to then File.open(to,'w') else $stdout end
+ f.puts("% file: #{from}")
+ f.puts("")
+ f.puts(@@definitions[method])
+ unless @@start[method].empty? then
+ f.puts("")
+ f.puts(@@start[method])
+ end
+ data.gsub!(/[^\\]%.*?$/mo) do
+ ''
+ end
+ data.scan(/(verbatim|b)tex\s*(.*?)\s*etex/mo) do
+ tag, text = $1, $2
+ f.puts("")
+ if tag == 'b' then
+ f.puts(@@before[method])
+ f.puts("#{text}%")
+ f.puts(@@after [method])
+ else
+ f.puts("#{text}")
+ end
+ f.puts("")
+ end
+ f.puts("")
+ f.puts(@@stop[method])
+ f.close
+ else
+ return false
+ end
+ rescue
+ File.delete(to) rescue false
+ return false
+ else
+ return true
+ end
+ end
+
+ @@splitMPlines = false
+
+ def MPTools::splitmplines(str)
+ if @@splitMPlines then
+ btex, verbatimtex, strings, result = Array.new, Array.new, Array.new, str.dup
+ # protect texts
+ result.gsub!(/btex\s*(.*?)\s*etex/) do
+ btex << $1
+ "btex(#{btex.length-1})"
+ end
+ result.gsub!(/verbatimtex\s*(.*?)\s*etex/) do
+ verbatimtex << $1
+ "verbatimtex(#{verbatimtex.length-1})"
+ end
+ result.gsub!(/\"(.*?)\"/) do
+ strings << $1
+ "\"#{strings.length-1}\""
+ end
+ result.gsub!(/\;/) do
+ ";\n"
+ end
+ result.gsub!(/(.{80,})(\-\-\-|\-\-|\.\.\.|\.\.)/) do
+ "#{$1}#{$2}\n"
+ end
+ result.gsub!(/\n[\s\n]+/mois) do
+ "\n"
+ end
+ result.gsub!(/btex\((\d+)\)/) do
+ "btex #{btex[$1.to_i]} etex"
+ end
+ result.gsub!(/verbatimtex\((\d+)\)/) do
+ "verbatimtex #{verbatimtex[$1.to_i]} etex"
+ end
+ result.gsub!(/\"(\d+)\"/) do
+ "\"#{strings[$1.to_i]}\""
+ end
+ # return result # let's catch xetex bug
+ return result.gsub(/\^\^(M|J)/o, "\n")
+ else
+ # return str # let's catch xetex bug
+ return str.gsub(/\^\^(M|J)/o, "\n")
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/pdf.rb b/Master/texmf-dist/scripts/context/ruby/base/pdf.rb
index d8cbf9e0523..9f4e9a6c3f6 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/pdf.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/pdf.rb
@@ -1,14 +1,34 @@
module PDFview
- @files = Hash.new
+ @files = Hash.new
+ @opencalls = Hash.new
+ @closecalls = Hash.new
+ @allcalls = Hash.new
+
+ @method = 'default' # 'xpdf'
+
+ @opencalls['default'] = "pdfopen --file"
+ @opencalls['xpdf'] = "xpdfopen"
+
+ @closecalls['default'] = "pdfclose --file"
+ @closecalls['xpdf'] = nil
+
+ @allcalls['default'] = "pdfclose --all"
+ @allcalls['xpdf'] = nil
+
+ def PDFview.setmethod(method)
+ @method = method
+ end
def PDFview.open(*list)
begin
[*list].flatten.each do |file|
filename = fullname(file)
if FileTest.file?(filename) then
- result = `pdfopen --file #{filename} 2>&1`
- @files[filename] = true
+ if @opencalls[@method] then
+ result = `#{@opencalls[@method]} #{filename} 2>&1`
+ @files[filename] = true
+ end
end
end
rescue
@@ -20,7 +40,9 @@ module PDFview
filename = fullname(file)
begin
if @files.key?(filename) then
- result = `pdfclose --file #{filename} 2>&1`
+ if @closecalls[@method] then
+ result = `#{@closecalls[@method]} #{filename} 2>&1`
+ end
else
closeall
return
@@ -33,7 +55,9 @@ module PDFview
def PDFview.closeall
begin
- result = `pdfclose --all 2>&1`
+ if @allcalls[@method] then
+ result = `#{@allcalls[@method]} 2>&1`
+ end
rescue
end
@files.clear
diff --git a/Master/texmf-dist/scripts/context/ruby/base/state.rb b/Master/texmf-dist/scripts/context/ruby/base/state.rb
index f57231592a7..4b208812875 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/state.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/state.rb
@@ -56,7 +56,7 @@ class FileState
sum = ''
begin
if FileTest.file?(filename) && (data = IO.read(filename)) then
- data.gsub!(/\n.*?(#{[omit].flatten.join('|')}).*?\n/ms,"\n") if omit
+ data.gsub!(/\n.*?(#{[omit].flatten.join('|')}).*?\n/) do "\n" end if omit
sum = MD5.new(data).hexdigest.upcase
end
rescue
diff --git a/Master/texmf-dist/scripts/context/ruby/base/switch.rb b/Master/texmf-dist/scripts/context/ruby/base/switch.rb
index 64d518bd42f..5187a795f03 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/switch.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/switch.rb
@@ -33,12 +33,18 @@ end
class File
+ @@update_eps = 1
+
def File.needsupdate(oldname,newname)
begin
- if $mswindows then
- return File.stat(oldname).mtime > File.stat(newname).mtime
+ oldtime = File.stat(oldname).mtime.to_i
+ newtime = File.stat(newname).mtime.to_i
+ if newtime >= oldtime then
+ return false
+ elsif oldtime-newtime < @@update_eps then
+ return false
else
- return File.stat(oldname).mtime != File.stat(newname).mtime
+ return true
end
rescue
return true
@@ -46,11 +52,12 @@ class File
end
def File.syncmtimes(oldname,newname)
+ return
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)
+ # does not work (yet) / gives future timestamp
+ # t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
+ # File.utime(0,t,oldname,newname)
else
t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
File.utime(0,t,oldname,newname)
@@ -493,15 +500,21 @@ class CommandLine
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
+ begin
+ re = /^#{key}/i
+ rescue
+ key = key.inspect.sub(/^\"(.*)\"$/) do $1 end
+ re = /^#{key}/i
+ ensure
+ if option =~ re then
+ case n
+ when 0
+ foundkey, foundkind, n = option, kind, 1
+ when 1
+ # ambiguous matches, like --fix => --fixme --fixyou
+ foundkey, foundkind = nil, nil
+ break
+ end
end
end
end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/system.rb b/Master/texmf-dist/scripts/context/ruby/base/system.rb
index ed8c2756e2b..267a22cb98f 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/system.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/system.rb
@@ -33,6 +33,13 @@ module System
if @@mswindows then 'nul' else '/dev/null/' end
end
+ def System.unix?
+ not @@mswindows
+ end
+ def System.mswin?
+ @@mswindows
+ end
+
def System.binnames(str)
if @@binnames.key?(str) then
@@binnames[str]
@@ -41,6 +48,18 @@ module System
end
end
+ def System.prependengine(str)
+ if str =~ /^\S+\.(pl|rb|lua|py)/io then
+ case $1
+ when 'pl' then return "perl #{str}"
+ when 'rb' then return "ruby #{str}"
+ when 'lua' then return "lua #{str}"
+ when 'py' then return "python #{str}"
+ end
+ end
+ return str
+ end
+
def System.locatedprogram(program)
if @@located.key?(program) then
return @@located[program]
@@ -49,7 +68,7 @@ module System
if binname =~ /\..*$/io then
@@binpaths.each do |path|
if FileTest.file?(str = File.join(path,binname)) then
- return @@located[program] = str
+ return @@located[program] = System.prependengine(str)
end
end
end
@@ -57,7 +76,7 @@ module System
@@binpaths.each do |path|
@@binsuffixes.each do |suffix|
if FileTest.file?(str = File.join(path,"#{binname}#{suffix}")) then
- return @@located[program] = str
+ return @@located[program] = System.prependengine(str)
end
end
end
@@ -74,7 +93,7 @@ module System
end
program = program + ' ' + arguments if ! arguments.empty?
program.gsub!(/\s+/io, ' ')
- program.gsub!(/(\.\/)+/io, '')
+ #program.gsub!(/(\/\.\/)+/io, '/')
program.gsub!(/\\/io, '/')
return program
end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/tex.rb b/Master/texmf-dist/scripts/context/ruby/base/tex.rb
index 2cc9d25422e..a6403dcde2b 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/tex.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/tex.rb
@@ -8,8 +8,11 @@
# info : j.hagen@xs4all.nl
# www : www.pragma-ade.com
-# todo: write systemcall for mpost to file so that it can be run
-# faster
+# todo:
+#
+# - write systemcall for mpost to file so that it can be run faster
+# - use -8bit and -progname
+#
# report ?
@@ -20,6 +23,7 @@ require 'base/state'
require 'base/pdf'
require 'base/file'
require 'base/ctx'
+require 'base/mp'
class String
@@ -33,6 +37,12 @@ class String
end
+# class String
+ # def utf_bom?
+ # self.match(/^\357\273\277/o).length>0 rescue false
+ # end
+# end
+
class Array
def standard?
@@ -43,6 +53,10 @@ class Array
end
end
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
end
class TEX
@@ -56,6 +70,7 @@ class TEX
@@texengines = Hash.new
@@mpsengines = Hash.new
@@backends = Hash.new
+ @@mappaths = Hash.new
@@runoptions = Hash.new
@@texformats = Hash.new
@@mpsformats = Hash.new
@@ -68,11 +83,32 @@ class TEX
@@texmethods = Hash.new
@@mpsmethods = Hash.new
- ['tex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdfetex' end
+ @@pdftex = 'pdftex' # new default, pdfetex is gone
+
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |p|
+ if System.unix? then
+ pp, pe = "#{p}/pdftex" , "#{p}/pdfetex"
+ else
+ pp, pe = "#{p}/pdftex.exe", "#{p}/pdfetex.exe"
+ end
+ if FileTest.file?(pe) then
+ # we assume no update
+ @@pdftex = 'pdfetex'
+ break
+ elsif FileTest.file?(pp) then
+ # we assume an update
+ @@pdftex = 'pdftex'
+ break
+ end
+ end
+
+ ['etex','pdfetex','standard'] .each do |e| @@texengines[e] = @@pdftex end
+ ['tex','pdftex'] .each do |e| @@texengines[e] = 'pdftex' end
['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end
['xetex'] .each do |e| @@texengines[e] = 'xetex' end
+ ['luatex'] .each do |e| @@texengines[e] = 'luatex' end
- ['metapost','mpost','standard'] .each do |e| @@mpsengines[e] = 'mpost' 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
@@ -80,6 +116,13 @@ class TEX
['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
+ ['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'] .each do |b| @@mappaths[b] = 'dvipdfm' end
+ ['dvipdfm', 'dvipdfmx', 'xdvipdfmx'] .each do |b| @@mappaths[b] = 'dvipdfm' end
+ ['xdv','xdv2pdf'] .each do |b| @@mappaths[b] = 'dvips' end
# todo norwegian (no)
@@ -88,6 +131,7 @@ class TEX
['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-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
@@ -98,12 +142,13 @@ class TEX
['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
+ ['pdftex','pdfetex','aleph','omega',
+ 'xetex','luatex'] .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
+ 'cont-fr','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
@@ -113,28 +158,35 @@ class TEX
@@mpsmakestr['plain'] = "\\dump"
['cont-en','cont-nl','cont-de','cont-it',
- 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = "\\emergencyend" end
+ 'cont-fr','cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = "\\emergencyend" end
- @@runoptions['xetex'] = ['--no-pdf']
+ # @@runoptions['xetex'] = ['--output-driver \\\"-d 4 -V 5\\\"'] # we need the pos pass
+ @@runoptions['xetex'] = ['--8bit -no-pdf'] # from now on we assume (x)dvipdfmx to be used
+ @@runoptions['pdfetex'] = ['--8bit ']
+ @@runoptions['pdftex'] = ['--8bit '] # pdftex is now pdfetex
+ @@runoptions['luatex'] = ['--8bit ']
+ @@runoptions['aleph'] = ['--8bit ']
@@booleanvars = [
'batchmode', 'nonstopmode', 'fast', 'fastdisabled', 'silentmode', 'final',
'paranoid', 'notparanoid', 'nobanner', 'once', 'allpatterns',
- 'nompmode', 'nomprun', 'automprun',
+ 'nompmode', 'nomprun', 'automprun', 'combine',
'nomapfiles', 'local',
'arrange', 'noarrange',
'forcexml', 'foxet',
+ 'alpha', 'beta',
'mpyforce', 'forcempy',
'forcetexutil', 'texutil',
'globalfile', 'autopath',
- 'purge', 'purgeall', 'autopdf', 'simplerun', 'verbose',
- 'nooptionfile'
+ 'purge', 'purgeall', 'keep', 'autopdf', 'xpdf', 'simplerun', 'verbose',
+ 'nooptionfile', 'nobackend', 'noctx', 'utfbom'
]
@@stringvars = [
'modefile', 'result', 'suffix', 'response', 'path',
'filters', 'usemodules', 'environments', 'separation', 'setuppath',
- 'arguments', 'input', 'output', 'randomseed', 'modes', 'filename',
- 'modefile', 'ctxfile'
+ 'arguments', 'input', 'output', 'randomseed', 'modes', 'mode', 'filename',
+ 'ctxfile', 'printformat', 'paperformat', 'paperoffset',
+ 'timeout'
]
@@standardvars = [
'mainlanguage', 'bodyfont', 'language'
@@ -148,16 +200,22 @@ class TEX
@@extrastringvars = []
def booleanvars
- [@@booleanvars,@@extrabooleanvars].flatten
+ [@@booleanvars,@@extrabooleanvars].flatten.uniq
end
def stringvars
- [@@stringvars,@@extrastringvars].flatten
+ [@@stringvars,@@extrastringvars].flatten.uniq
end
def standardvars
- @@standardvars
+ [@@standardvars].flatten.uniq
end
def knownvars
- @@knownvars
+ [@@knownvars].flatten.uniq
+ end
+ def allbooleanvars
+ [@@booleanvars,@@extrabooleanvars].flatten.uniq
+ end
+ def allstringvars
+ [@@stringvars,@@extrastringvars,@@standardvars,@@knownvars].flatten.uniq
end
def setextrastringvars(vars)
@@ -201,13 +259,18 @@ class TEX
setvariable('engine', 'standard') # replaced by tex/mpsengine
setvariable('backend', 'pdftex')
setvariable('runs', '8')
- setvariable('randomseed', rand(1440).to_s)
+ 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
@@ -218,10 +281,17 @@ class TEX
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
unless (str.class == String) && str.empty? then
@@ -249,11 +319,14 @@ class TEX
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
+ 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
@@ -301,11 +374,12 @@ class TEX
end
def prefixed(format,engine)
+ # format
case engine
- when /etex|eetex|pdfetex|pdfeetex|pdfxtex|xpdfetex|eomega|aleph|xetex/io then
- "*#{format}"
- else
- format
+ when /etex|pdftex|pdfetex|aleph|xetex|luatex/io then
+ "*#{format}"
+ else
+ format
end
end
@@ -318,18 +392,24 @@ class TEX
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 validtexformat(str) validsomething(str,@@texformats,'tex') end
+ def validmpsformat(str) validsomething(str,@@mpsformats,'mp' ) end
+ def validtexengine(str) validsomething(str,@@texengines,'pdfetex') 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)
+ def validsomething(str,something,type=nil)
if str then
list = [str].flatten.collect do |s|
- something[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
@@ -384,20 +464,20 @@ class TEX
# 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"
+ when /mpost/ then "-mem"
+ when /mfont/ then "-bas"
+ else "-fmt"
end
else
- "--fmt"
+ "-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"
+ when 'standard' then prefix = "-progname"
+ when /web2c/io then prefix = "-progname"
+ when /miktex/io then prefix = "-alias"
else return ""
end
if progname then
@@ -422,7 +502,7 @@ class TEX
if Kpse.miktex? then
"-tcx=#{file}"
else
- "--translate-file=#{file}"
+ "-translate-file=#{file}"
end
end
@@ -436,7 +516,7 @@ class TEX
filename = Kpse.found('context.tex')
version = 'unknown'
begin
- if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+)\}/) then
+ if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+.*?)\}/) then
version = $1
end
rescue
@@ -445,6 +525,10 @@ class TEX
end
def makeformats
+
+ checktestversion
+
+ report("using search method '#{Kpse.searchmethod}'")
if getvariable('fast') then
report('using existing database')
else
@@ -463,24 +547,27 @@ class TEX
# 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, ...)')
+ end
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)
+ Dir.chdir(texformatpath) rescue false
+ if FileTest.writable?(texformatpath) then
+ if texformats.length > 0 then
+ makeuserfile
+ makeresponsefile
+ end
+ texformats.each do |texformat|
+ report("generating tex format #{texformat}")
+ runcommand([quoted(texengine),prognameflag(progname),iniflag,tcxflag,prefixed(texformat,texengine),texmakeextras(texformat)])
+ end
+ else
+ report("unable to make format due to lack of permissions")
+ texformatpath = ''
end
else
texformatpath = ''
@@ -490,15 +577,16 @@ class TEX
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)
+ Dir.chdir(mpsformatpath) rescue false
+ if FileTest.writable?(mpsformatpath) then
+ mpsformats.each do |mpsformat|
+ report("generating mps format #{mpsformat}")
+ # runcommand([quoted(mpsengine),prognameflag(progname),iniflag,tcxflag,mpsformat,mpsmakeextras(mpsformat)])
+ runcommand([quoted(mpsengine),iniflag,tcxflag,mpsformat,mpsmakeextras(mpsformat)])
+ end
+ else
+ report("unable to make format due to lack of permissions")
+ mpsformatpath = ''
end
else
mpsformatpath = ''
@@ -560,7 +648,7 @@ class TEX
f.close
if FileTest.file?(tempfilename('tex')) then
format = File.basename(name)
- engine = if name =~ /(pdfetex|aleph|xetex)[\/\\]#{format}/ then $1 else '' end
+ engine = if name =~ /(pdftex|pdfetex|aleph|xetex|luatex)[\/\\]#{format}/ then $1 else '' end
if engine.empty? then
engineflag = ""
else
@@ -571,8 +659,8 @@ class TEX
interface = $1.sub(/cont\-/,'')
results.push('')
results.push("testing interface #{interface}")
- flags = ['--process','--batch','--once',"--interface=#{interface}",engineflag]
- # result = Kpse.pipescript('newtexexec',tempfilename,flags)
+ 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")
@@ -663,6 +751,7 @@ class TEX
@@preamblekeys = [
['tex','texengine'],
+ ['engine','texengine'],
['program','texengine'],
['translate','tcxfilter'],
['tcx','tcxfilter'],
@@ -670,22 +759,42 @@ class TEX
['mode','mode'],
['ctx','ctxfile'],
['version','contextversion'],
- ['format','texformat'],
- ['interface','texformat']
+ ['format','texformats'],
+ ['interface','texformats'],
]
+ @@re_utf_bom = /^\357\273\277/o
+
def scantexpreamble(filename)
begin
if FileTest.file?(filename) and tex = File.open(filename) 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
- vars = Hash.new
+ # we only accept lines with key=value pairs
+ vars, ok = Hash.new, true
$1.split(/\s+/o).each do |s|
k, v = s.split('=')
- vars[k] = v
+ if k && v then
+ vars[k] = v
+ else
+ ok = false
+ break
+ end
end
- @@preamblekeys.each do |v|
- setvariable(v[1],vars[v[0]]) if vars.key?(v[0])
+ 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
+ break
end
else
break
@@ -699,37 +808,37 @@ class TEX
end
def scantexcontent(filename)
- if FileTest.file?(filename) and tex = File.open(filename) then
+ 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
+ setvariable('texformats','nl') ; break
when /\\(stelle|verwende|umgebung|benutze)/o then
- setvariable('texformat','de') ; break
+ setvariable('texformats','de') ; break
when /\\(stel|gebruik|omgeving)/o then
- setvariable('texformat','nl') ; break
+ setvariable('texformats','nl') ; break
when /\\(use|setup|environment)/o then
- setvariable('texformat','en') ; break
+ setvariable('texformats','en') ; break
when /\\(usa|imposta|ambiente)/o then
- setvariable('texformat','it') ; break
+ setvariable('texformats','it') ; break
when /(height|width|style)=/o then
- setvariable('texformat','en') ; break
+ setvariable('texformats','en') ; break
when /(hoehe|breite|schrift)=/o then
- setvariable('texformat','de') ; break
+ setvariable('texformats','de') ; break
when /(hoogte|breedte|letter)=/o then
- setvariable('texformat','nl') ; break
+ setvariable('texformats','nl') ; break
when /(altezza|ampiezza|stile)=/o then
- setvariable('texformat','it') ; break
+ setvariable('texformats','it') ; break
when /externfiguur/o then
- setvariable('texformat','nl') ; break
+ setvariable('texformats','nl') ; break
when /externalfigure/o then
- setvariable('texformat','en') ; break
+ setvariable('texformats','en') ; break
when /externeabbildung/o then
- setvariable('texformat','de') ; break
+ setvariable('texformats','de') ; break
when /figuraesterna/o then
- setvariable('texformat','it') ; break
+ setvariable('texformats','it') ; break
end
end
tex.close
@@ -770,36 +879,11 @@ class TEX
end
end
- def makestubfile(rawname,forcexml=false)
- if tmp = File.open(File.suffixed(rawname,'run'),'w') then
+ def makestubfile(rawname,rawbase,forcexml=false)
+ if tmp = openedfile(File.suffixed(rawbase,'run')) 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 << checkxmlfile(rawname)
tmp << "\\processXMLfilegrouped{#{rawname}}\n"
else
tmp << "\\processfile{#{rawname}}\n"
@@ -812,16 +896,113 @@ class TEX
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)) 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}'")
+ rescue
+ report("job aborted due to 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}'")
- processfile
+ timedrun(getvariable('timeout')) do
+ processfile
+ end
end
reportruntime
end
@@ -835,152 +1016,284 @@ class TEX
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 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*(.*)$/mois 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 processmpxtex
getarrayvariable('files').each do |filename|
setvariable('filename',filename)
report("processing text of graphic '#{filename}'")
- processmpx(filename,true)
+ processmpx(filename,false,true,true)
end
reportruntime
end
def deleteoptionfile(rawname)
- begin
- File.delete(File.suffixed(rawname,'top'))
- rescue
+ ['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)
- # 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"
+ 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"
+ if getvariable('utfbom') then
+ opt << "\\enableregime[utf]"
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"]
+ opt << "\\setupsystem[\\c!n=#{kindofrun},\\c!m=#{currentrun}]\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
+ 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 = 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 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 pf.size == 1
+ opt << "\\setuppapersize[#{pf[0]}][#{pf[1]}]\n"
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"
+ 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 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
+ # 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
+ if (str = getvariable('arguments')) && ! str.empty? then
+ opt << "\\setupenv[#{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
- 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
+ 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(/\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
- else
- pagelist << page
end
+ opt << "\\def\\pagestoshipout\{#{pagelist.join(',')}\}\n";
end
- opt << "\\def\\pagestoshipout\{pagelist.join(',')\}\n";
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
+ else
+ report("unable to write option file #{topname}")
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
+ rescue
+ report("fatal error in writing option file #{topname} (#{$!})")
end
end
@@ -1001,6 +1314,7 @@ class TEX
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
@@ -1009,12 +1323,47 @@ class TEX
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)
progname = validprogname(getvariable('progname'))
@@ -1022,45 +1371,51 @@ class TEX
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)
+ fixbackendvars(@@mappaths[texengine])
+ runcommand([quoted(texengine),prognameflag(progname),formatflag(texengine,texformat),tcxflag,runoptions(texengine),filename,texprocextras(texformat)])
+ # true
else
false
end
end
- def runmp(filename)
+ def runmp(mpname,mpx=false)
+ checktestversion
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)
+ ENV["MPXCOMMAND"] = "0" unless mpx
+ # runcommand([quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),tcxflag,runoptions(mpsengine),filename,mpsprocextras(mpsformat)])
+ runcommand([quoted(mpsengine),formatflag(mpsengine,mpsformat),tcxflag,runoptions(mpsengine),mpname,mpsprocextras(mpsformat)])
+ true
else
false
end
end
- def runtexmp(filename,filetype='')
- mpfile = File.suffixed(filename,filetype,'mp')
- if File.atleast?(mpfile,25) then
+ def runtexmp(filename,filetype='',purge=true)
+ checktestversion
+ mpname = File.suffixed(filename,filetype,'mp')
+ if File.atleast?(mpname,25) then
# first run needed
- File.silentdelete(File.suffixed(mpfile,'mpt'))
- doruntexmp(mpfile,false)
- mpgraphics = checkmpgraphics(mpfile)
- mplabels = checkmplabels(mpfile)
+ 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(mpfile,mplabels)
+ doruntexmp(mpname,mplabels,true,purge)
+ else
+ # no labels
end
end
end
def runtexmpjob(filename,filetype='')
- mpfile = File.suffixed(filename,filetype,'mp')
- if File.atleast?(mpfile,25) && (data = File.silentread(mpfile)) then
+ 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
@@ -1071,8 +1426,9 @@ class TEX
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)
+ options.push("--output=ps") # options.push("--dvi")
+ options.push("--nobackend")
+ return runtexexec(mpname,options,2)
end
end
return false
@@ -1086,7 +1442,7 @@ class TEX
begin
logger = Logger.new('TeXUtil')
if tu = TeXUtil::Converter.new(logger) and tu.loaded(fname) then
- tu.saved if tu.processed
+ ok = tu.processed && tu.saved && tu.finalized
end
rescue
Kpse.runscript('texutil',fname,options)
@@ -1095,16 +1451,17 @@ class TEX
end
end
- # 1=tex 2=mptex 3=mpxtex
+ # 1=tex 2=mptex 3=mpxtex 4=mpgraphic
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)
+ case option
+ when /^\-*(.*?)\=(.*)$/o then
+ job.setvariable($1,$2)
+ when /^\-*(.*?)$/o then
+ job.setvariable($1,true)
end
end
job.setvariable("files",filename)
@@ -1112,6 +1469,7 @@ class TEX
when 1 then job.processtex
when 2 then job.processmptex
when 3 then job.processmpxtex
+ when 4 then job.processmpgraphic
end
job.inspect && Kpse.inspect if getvariable('verbose')
return true
@@ -1124,49 +1482,65 @@ class TEX
end
def fixbackendvars(backend)
- ENV['backend'] = backend ;
- ENV['progname'] = backend unless validtexengine(backend)
- ENV['TEXFONTMAPS'] = ".;\$TEXMF/fonts/map/{#{backend},pdftex,dvips,}//"
+ if backend then
+ report("fixing backend map path for #{backend}") if getvariable('verbose')
+ ENV['backend'] = backend ;
+ ENV['progname'] = backend unless validtexengine(backend)
+ ENV['TEXFONTMAPS'] = ['.',"\$TEXMF/fonts/map/{#{backend},pdftex,dvips,}//",'./fonts//'].join_path
+ else
+ report("unable to fix backend map path") if getvariable('verbose')
+ end
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
+ 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 -d 4 -V 5 #{xdvfile}")
end
- rescue
+ 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 = ''
- end
- system("dvips #{mapfiles} #{File.unsuffixed(rawname)}")
- when 'pdftex' then
- # no need for postprocessing
- else
- report("no postprocessing needed")
+ 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
-
- rawname = getvariable('filename')
-
- jobname = getvariable('filename')
- suffix = getvariable('suffix')
- result = getvariable('result')
-
+ report("using search method '#{Kpse.searchmethod}'") if getvariable('verbose')
+ rawname = getvariable('filename')
+ jobname = getvariable('filename')
+ suffix = getvariable('suffix')
+ result = getvariable('result')
+ forcexml = getvariable('forcexml')
runonce = getvariable('once')
finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange'))
globalfile = getvariable('globalfile')
@@ -1188,9 +1562,13 @@ class TEX
setvariable('nomprun',true) if orisuffix == 'mpx' # else cylic run
+ PDFview.setmethod('xpdf') if getvariable('xpdf')
+
PDFview.closeall if getvariable('autopdf')
- forcexml = jobsuffix.match(/^(xml|fo|fox|rlg|exa)$/io) # nil or match
+ if jobsuffix =~ /^(htm|html|xhtml|xml|fo|fox|rlg|exa)$/io then
+ forcexml = true
+ end
dummyfile = false
@@ -1207,53 +1585,66 @@ class TEX
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
+ if dummyfile || forcexml then
+ jobsuffix = makestubfile(rawname,rawbase,forcexml)
+ checkxmlfile(rawname)
+ 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'))
+ unless getvariable('noctx') then
+ ctx = CtxRunner.new(rawname,@logger)
+ 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
+ envs = ctx.environments
+ mods = ctx.modules
- # merge environment and module specs
+ # merge environment and module specs
- envs << getvariable('environments') unless getvariable('environments').empty?
- mods << getvariable('modules') unless getvariable('modules') .empty?
+ envs << getvariable('environments') unless getvariable('environments').empty?
+ mods << getvariable('modules') unless getvariable('modules') .empty?
- envs = envs.uniq.join(',')
- mods = mods.uniq.join(',')
+ envs = envs.uniq.join(',')
+ mods = mods.uniq.join(',')
- report("using environments #{envs}") if envs.length > 0
- report("using modules #{mods}") if mods.length > 0
+ 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
- setvariable('environments', envs)
- setvariable('modules', mods)
+ setvariable('environments', envs)
+ setvariable('modules', mods)
+ end
# 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)
+ pushresult(rawbase,result)
method = validtexmethod(validtexformat(getvariable('texformats')))
@@ -1263,64 +1654,114 @@ class TEX
when 'context' then
if getvariable('simplerun') || runonce then
- makeoptionfile(rawname,jobname,orisuffix,true,true,3) unless getvariable('nooptionfile')
- ok = runtex(rawname)
+ 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(rawname) if getvariable('texutil') || getvariable('forcetexutil')
- runbackend(rawname)
- popresult(rawname,result)
+ ok = runtexutil(rawbase) if getvariable('texutil') || getvariable('forcetexutil')
+ 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
- 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))
+ state.register(File.suffixed(rawbase,s))
end
if getvariable('automprun') then # check this
['mprun','mpgraph'].each do |s|
- state.register(File.suffixed(rawname,s,'mp'),'randomseed')
+ state.register(File.suffixed(rawbase,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')
+ 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
- ok = runtex(File.suffixed(rawname,jobsuffix))
+ ok = runtex(File.suffixed(if dummyfile || forcexml then rawbase else rawname end,jobsuffix))
if ok && (nofruns > 1) then
unless getvariable('nompmode') then
- mprundone = runtexmpjob(rawname, "mpgraph")
- mprundone = runtexmpjob(rawname, "mprun")
+ mprundone = runtexmpjob(rawbase, "mpgraph")
+ mprundone = runtexmpjob(rawbase, "mprun")
end
- ok = runtexutil(rawname)
+ ok = runtexutil(rawbase)
state.update
stoprunning = state.stable?
end
end
- ok = runtexutil(rawname) if (nofruns == 1) && getvariable('texutil')
+ if not ok then
+ setvariable('error','error in tex file')
+ end
+ ok = runtexutil(rawbase) if (nofruns == 1) && getvariable('texutil')
if ok && finalrun && (nofruns > 1) then
- makeoptionfile(rawname,jobname,orisuffix,true,finalrun,4) unless getvariable('nooptionfile')
+ makeoptionfile(rawbase,jobname,orisuffix,true,finalrun,4,texruns) unless getvariable('nooptionfile')
report("final TeX run #{texruns}")
- ok = runtex(File.suffixed(rawname,jobsuffix))
+ ok = runtex(File.suffixed(if dummyfile || forcexml then rawbase else rawname end,jobsuffix))
end
- ['tmp','top'].each do |s| # previous tuo file / runtime option file
- File.silentdelete(File.suffixed(rawname,s))
+ 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
- File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp'))
+ # ['tmp','top','log'].each do |s| # previous tuo file / runtime option file / log file
+ # File.silentdelete(File.suffixed(rawbase,s))
+ # end
if ok then
- runbackend(rawname)
- popresult(rawname,result)
+ runbackend(rawbase)
+ popresult(rawbase,result)
+ end
+ if true then # autopurge
+ begin
+ File.open(File.suffixed(rawbase, 'tuo')) 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+)/mois 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+)/mois 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',rawname,'--purge') if getvariable('purge')
- Kpse.runscript('ctxtools',rawname,'--purgeall') if getvariable('purgeall')
+ Kpse.runscript('ctxtools',rawbase,'--purge') if getvariable('purge')
+ Kpse.runscript('ctxtools',rawbase,'--purge --all') if getvariable('purgeall')
when 'latex' then
@@ -1332,63 +1773,61 @@ class TEX
end
- if (dummyfile or forcexml) and FileTest.file?(rawname) then
+ if (dummyfile or forcexml) and FileTest.file?(rawbase) then
begin
- File.delete(File.suffixed(rawname,'run'))
+ 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 rawname else result end,'pdf'))
+ PDFview.open(File.suffixed(if result.empty? then rawbase else result end,'pdf'))
end
+ else
+ report("nothing to process")
end
end
- # mp specific
+ # 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=true,context=true)
+ def doruntexmp(mpname,mergebe=nil,context=true,purge=true)
texfound = false
- mpbetex = Hash.new
- mpfile = File.suffixed(mpname,'mp')
- mpcopy = File.suffixed(mpname,'copy','mp')
- setvariable('mp.file',mpfile)
+ 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(mpfile) then
+ if mpdata = File.silentread(mpname) 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
+ 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*\;/o) do
+ mpdata.gsub!(/beginfig\s*\((\d+)\)\s*\;(.+?)endfig\s*\;/mo) do
n, str = $1, $2
- if str =~ /(.*?)(verbatimtex.*?etex)\s*\;(.*)/o then
- "beginfig(#{n})\;\n$1$2\;\n#{mpbetex(n)}\n$3\;endfig\;\n"
+ if str =~ /^(.*?)(verbatimtex.*?etex)\s*\;(.*)$/mo then
+ "beginfig(#{n})\;\n#{$1}#{$2}\;\n#{mergebe[n]}\n#{$3}\;endfig\;\n"
else
- "beginfig(#{n})\;\n#{mpbetex(n)}\n#{str}\;endfig\;\n"
+ "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
- unless mpdata =~ /beginfig\s*\(\s*0\s*\)/o then
- mp << mpbetex[0] if mpbetex.key?(0)
- end
- mp << mpdata # ??
+ mp << MPTools::splitmplines(mpdata)
mp << "\n"
mp << "end"
mp << "\n"
mp.close
end
- processmpx(mpname) if texfound
+ processmpx(mpname,true,true,purge) if texfound
if getvariable('batchmode') then
options = ' --interaction=batch'
elsif getvariable('nonstopmode') then
@@ -1398,7 +1837,7 @@ class TEX
end
# todo plain|mpost|metafun
ok = runmp(mpname)
- if f = File.silentopen(File.suffixed(mpfile,'log')) then
+ 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)
@@ -1408,32 +1847,63 @@ class TEX
end
f.close
end
- File.silentrename(mpfile,"mptrace.tmp")
- File.silentrename(mpcopy, mpfile)
+ File.silentrename(mpname, mpkeep)
+ File.silentrename(mpcopy, mpname)
end
end
- def processmpx(mpname,context=true)
- mpname = File.suffixed(mpname,'mp')
- if File.atleast?(mpname,10) && (data = File.silentread(mpname)) then
+ # 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
- 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
+ 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
- ok = RunConTeXtFile(mptex)
+ localjob.setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty?
+ elsif getvariable('interface').empty? then
+ localjob.setvariable('texformats',['plain'])
else
- ok = RunSomeTeXFile(mptex)
- end
- ok = ok && FileTest.file?(mpdvi) && system("dvitomp #{mpdvi} #{mpmpx}")
- [mptex,mpdvi,mplog].each do |mpfil|
- File.silentdelete(mpfil)
+ 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
@@ -1441,6 +1911,14 @@ class TEX
end
end
+ def purge_mpx_files(mpname)
+ unless getvariable('keep') then
+ ['tex', 'log', 'tui', 'tuo', 'top'].each do |suffix|
+ File.silentdelete(File.suffixed(mpname,'temp',suffix))
+ end
+ end
+ end
+
def checkmpgraphics(mpname)
mpoptions = ''
if getvariable('makempy') then
@@ -1452,12 +1930,12 @@ class TEX
mponame = File.suffixed(mpname,'mpo')
mpyname = File.suffixed(mpname,'mpy')
return false unless File.atleast?(mponame,32)
- mpochecksum = State.new.checksum(mponame)
+ 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
- if f = File.open(mpyname) then
+ if f = File.silentopen(mpyname) then
str = f.gets.chomp
f.close
if str =~ /^\%\s*mpochecksum\s*\:\s*(\d+)/o then
@@ -1470,24 +1948,25 @@ class TEX
def checkmplabels(mpname)
mpname = File.suffixed(mpname,'mpt')
- if File.atleast?(mpname,10) && (mp = File.open(mpname)) then
+ if File.atleast?(mpname,10) && (mp = File.silentopen(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
+ 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"
+ labels[$1] += "#{$2}\n"
end
end
mp.close
- return labels if labels.size>0
+ if labels.size>0 then
+ return labels
+ else
+ return nil
+ end
end
return nil
end
diff --git a/Master/texmf-dist/scripts/context/ruby/base/texutil.rb b/Master/texmf-dist/scripts/context/ruby/base/texutil.rb
index c279bcc97a6..2bca831675e 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/texutil.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/texutil.rb
@@ -51,12 +51,21 @@ class TeXUtil
@logger = logger
end
+ def report(str)
+ @logger.report("fatal error in plugin (#{str}): #{$!}")
+ puts("\n")
+ $@.each do |line|
+ puts(" #{line}")
+ end
+ puts("\n")
+ end
+
def reset(name)
if @plugins.include?(name) then
begin
eval("#{name}").reset(@logger)
rescue Exception
- @logger.report("fatal error in resetting plugin")
+ report("resetting")
end
else
@logger.report("no plugin #{name}")
@@ -89,7 +98,7 @@ class TeXUtil
begin
eval("#{name}").reader(@logger,data.flatten)
rescue Exception
- @logger.report("fatal error in plugin reader #{name} (#{$!})")
+ report("reading")
end
else
@logger.report("no plugin #{name}")
@@ -107,7 +116,7 @@ class TeXUtil
begin
eval("#{p}").writer(@logger,handle)
rescue Exception
- @logger.report("fatal error in plugin writer #{p} (#{$!})")
+ report("writing")
end
end
end
@@ -117,7 +126,17 @@ class TeXUtil
begin
eval("#{p}").processor(@logger)
rescue Exception
- @logger.report("fatal error in plugin processor #{p} (#{$!})")
+ report("processing")
+ end
+ end
+ end
+
+ def finalizers
+ @plugins.each do |p|
+ begin
+ eval("#{p}").finalizer(@logger)
+ rescue Exception
+ report("finalizing")
end
end
end
@@ -126,6 +145,8 @@ class TeXUtil
class Sorter
+ @@downcase = true
+
def initialize(max=12)
@rep, @map, @exp, @div = Hash.new, Hash.new, Hash.new, Hash.new
@max = max
@@ -149,7 +170,7 @@ class TeXUtil
# sorter.expander('ijligature', 'y')
def expander(from,to=nil)
- from, to = converted(from), converted(to)
+ to = converted(to) # not from !!!
@max = [@max,to.length+1].max if to
@exp[from] = to || from || ''
end
@@ -179,16 +200,50 @@ class TeXUtil
end
if @map.size > 0 then
# watch out, order of match matters
- @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/ # o
+ if @@downcase then
+ @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/i # o
+ else
+ @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/ # o
+ end
else
- @rexb = /(\\[a-zA-Z]+|.)\s*/o
+ if @@downcase then
+ @rexb = /(\\[a-zA-Z]+|.)\s*/io
+ else
+ @rexb = /(\\[a-zA-Z]+|.)\s*/o
+ end
+ end
+ if false then
+ @exp.keys.each do |e|
+ @exp[e].downcase!
+ end
end
end
+ def replace(str)
+ if @rexa then
+ str.gsub(@rexa) do
+ @rep[$1.escaped]
+ end
+ else
+ str
+ end
+ end
+
+ def normalize(str)
+ # replace(str).gsub(/ +/,' ')
+ replace(str).gsub(/\s\s+/," \\space")
+ end
+
+ def tokenize(str)
+ str.gsub(/\\strchr\{(.*?)\}/o) do "\\#{$1}" end
+ end
+
def remap(str)
s = str.dup
- s.gsub!(/(\d+)/o) do
- $1.rjust(10,'a') # rest is b .. k
+ if true then # numbers are treated special
+ s.gsub!(/(\d+)/o) do
+ $1.rjust(10,'a') # rest is b .. k
+ end
end
if @rexa then
s.gsub!(@rexa) do
@@ -198,6 +253,9 @@ class TeXUtil
if @rexb then
s.gsub!(@rexb) do
token = $1.sub(/\\/o, '')
+ if @@downcase then
+ token.downcase!
+ end
if @exp.key?(token) then
@exp[token].ljust(@max,' ')
elsif @map.key?(token) then
@@ -210,17 +268,16 @@ class TeXUtil
s
end
- def preset(shortcuts=[],expansions=[],reductions=[],divisions=[])
- # maybe we should move this to sort-def.tex
+ def preset(shortcuts=[],expansions=[],reductions=[],divisions=[],language='')
'a'.upto('z') do |c| expander(c) ; division(c) end
+ 'A'.upto('Z') do |c| expander(c) ; division(c) end
expander('1','b') ; expander('2','c') ; expander('3','e') ; expander('4','f')
expander('5','g') ; expander('6','h') ; expander('7','i') ; expander('8','i')
expander('9','j') ; expander('0','a') ; expander('-','-') ;
- # 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
+ shortcuts.each do |s| shortcut(s[1],s[2]) if s[0] == '' || s[0] == language end
+ expansions.each do |e| expander(e[1],e[2]) if e[0] == '' || e[0] == language end
+ reductions.each do |r| reducer(r[1],r[2]) if r[0] == '' || r[0] == language end
+ divisions.each do |d| division(d[1],d[2]) if d[0] == '' || d[0] == language end
end
def simplify(str)
@@ -240,7 +297,7 @@ class TeXUtil
# what to do with xml and utf-8
# \"e etc
# unknown \cs
- s.gsub!(/\\[a-z][a-z]+\s*\{(.*?)\}/o) do $1 end
+ s.gsub!(/\\[a-zA-Z][a-zA-Z]+\s*\{(.*?)\}/o) do $1 end
return s
end
@@ -256,6 +313,7 @@ class TeXUtil
def converted(str)
if str then
+ # puts str
str.gsub(/([\+\-]*\d+)/o) do
n = $1.to_i
if n > 0 then
@@ -277,20 +335,18 @@ class TeXUtil
module MyFiles
- @@files = Hash.new
+ @@files, @@temps = Hash.new, Hash.new
def MyFiles::reset(logger)
- @@files = Hash.new
+ @@files, @@temps = Hash.new, 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
+ @@files[data[1]] = (@@files[data[1]] ||0) + 1
+ when 't' then # temporary file
+ @@temps[data[1]] = (@@temps[data[1]] ||0) + 1
end
end
@@ -299,14 +355,24 @@ class TeXUtil
@@files.keys.sort.each do |k|
handle << "% > #{k} #{@@files[k]/2}\n"
end
+ handle << logger.banner("temporary files: #{@@temps.size}")
+ @@temps.keys.sort.each do |k|
+ handle << "% > #{k} #{@@temps[k]}\n"
+ end
end
def MyFiles::processor(logger)
@@files.keys.sort.each do |k|
unless (@@files[k] % 2) == 0 then
- logger.report("check loading of file #{k}, begin/end problem")
+ logger.report("check loading of file '#{k}', begin/end problem")
end
end
+ @@temps.keys.sort.each do |k|
+ # logger.report("temporary file '#{k}' can be deleted")
+ end
+ end
+
+ def MyFiles::finalizer(logger)
end
end
@@ -337,6 +403,9 @@ class TeXUtil
def MyCommands::processor(logger)
end
+ def MyCommands::finalizer(logger)
+ end
+
end
end
@@ -367,7 +436,15 @@ class TeXUtil
def MyExtras::processor(logger)
@@programs.each do |p|
- cmd = "texmfstart #{@@programs[p.to_i]}"
+ # cmd = @@programs[p.to_i]
+ # logger.report("running #{cmd}")
+ # system(cmd)
+ end
+ end
+
+ def MyExtras::finalizer(logger)
+ @@programs.each do |p|
+ cmd = @@programs[p.to_i]
logger.report("running #{cmd}")
system(cmd)
end
@@ -395,7 +472,8 @@ class TeXUtil
attr_writer :sortkey
def build(sorter)
- @sortkey = sorter.remap(sorter.simplify(@key.downcase))
+ @sortkey = sorter.normalize(sorter.tokenize(@sortkey))
+ @sortkey = sorter.remap(sorter.simplify(@key.downcase)) # ??
if @sortkey.empty? then
@sortkey = sorter.remap(@command.downcase)
end
@@ -418,16 +496,23 @@ class TeXUtil
end
- @@synonyms = Hash.new
+ @@synonyms = Hash.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
def MySynonyms::reset(logger)
- @@synonyms = Hash.new
+ @@synonyms = Hash.new
+ @@sorter = Hash.new
+ @@languages = 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]))
+ case data[0]
+ when 'e' then
+ @@synonyms[data[1]] = Array.new unless @@synonyms.key?(data[1])
+ @@synonyms[data[1]].push(Synonym.new(data[1],data[2],data[3],data[4]))
+ when 'l' then
+ @@languages[data[1]] = data[2] || ''
end
end
@@ -441,17 +526,25 @@ class TeXUtil
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|
+ @@sorter[s] = Sorter.new
+ @@sorter[s].preset(
+ eval("MyKeys").shortcuts,
+ eval("MyKeys").expansions,
+ eval("MyKeys").reductions,
+ eval("MyKeys").divisions,
+ @@languages[s] || '')
+ @@sorter[s].prepare
@@synonyms[s].each_index do |i|
- @@synonyms[s][i].build(sorter)
+ @@synonyms[s][i].build(@@sorter[s])
end
@@synonyms[s] = @@synonyms[s].sort
end
end
+ def MySynonyms::finalizer(logger)
+ end
+
end
end
@@ -462,6 +555,9 @@ class TeXUtil
class Register
+ @@specialsymbol = "\000"
+ @@specialbanner = "" # \\relax"
+
@@debug = false
@@debug = true
@@ -475,6 +571,7 @@ class TeXUtil
@key = @entry.dup if @key.empty?
@sortkey = @key.dup
@nofentries, @nofpages = 0, 0
+ @normalizeentry = false
end
attr_reader :state, :type, :location, :key, :entry, :seetoo, :page, :realpage, :texthowto, :pagehowto
@@ -482,34 +579,55 @@ class TeXUtil
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
+ # @entry, @key = sorter.normalize(@entry), sorter.normalize(sorter.tokenize(@key))
+ @entry = sorter.normalize(sorter.tokenize(@entry)) if @normalizeentry
+ @key = sorter.normalize(sorter.tokenize(@key))
+ if false then
+ @entry, @key = [@entry, @key].collect do |target|
+ # +a+b+c &a&b&c a+b+c a&b&c
+ case target[0,1]
+ when '&' then target = target.sub(/^./o,'').gsub(/([^\\])\&/o) do "#{$1}#{@@split}" end
+ when '+' then target = target.sub(/^./o,'').gsub(/([^\\])\+/o) do "#{$1}#{@@split}" end
+ else target = target .gsub(/([^\\])[\&\+]/o) do "#{$1}#{@@split}" end
+ end
+ # {a}{b}{c}
+ # if target =~ /^\{(.*)\}$/o then
+ # $1.split(/\} \{/o).join(@@split) # space between } { is mandate
+ # else
+ target
+ # end
end
- # {a}{b}{c}
- # if target =~ /^\{(.*)\}$/o then
- # $1.split(/\} \{/o).join(@@split) # space between } { is mandate
- # else
- target
- # end
+ else
+ @entry, @key = cleanupsplit(@entry), cleanupsplit(@key)
end
@sortkey = sorter.simplify(@key)
+ # special = @sortkey =~ /^([^a-zA-Z\\])/o
+ special = @sortkey =~ /^([\`\~\!\@\#\$\%\^\&\*\(\)\_\-\+\=\{\}\[\]\:\;\"\'\|\<\,\>\.\?\/\d])/o
@sortkey = @sortkey.split(@@split).collect do |c| sorter.remap(c) end.join(@@split)
- # if ($Key eq "") { $Key = SanitizedString($Entry) }
- # if ($ProcessHigh){ $Key = HighConverted($Key) }
+ if special then
+ @sortkey = "#{@@specialsymbol}#{@sortkey}"
+ end
@sortkey = [
@sortkey.downcase,
@sortkey,
+ @entry,
@texthowto.ljust(10,' '),
- @state,
- @realpage.rjust(6,' '),
+ # @state, # no, messes up things
+ (@realpage ||'').rjust(6,' ').gsub(/0/,' '),
+ # (@realpage ||'').rjust(6,' '),
@pagehowto
].join(@@split)
end
+ def cleanupsplit(target)
+ # +a+b+c &a&b&c a+b+c a&b&c
+ case target[0,1]
+ when '&' then target.sub(/^./o,'').gsub(/([^\\])\&/o) do "#{$1}#{@@split}" end
+ when '+' then target.sub(/^./o,'').gsub(/([^\\])\+/o) do "#{$1}#{@@split}" end
+ else target .gsub(/([^\\])[\&\+]/o) do "#{$1}#{@@split}" end
+ end
+ end
+
def <=> (other)
@sortkey <=> other.sortkey
end
@@ -555,18 +673,20 @@ class TeXUtil
else
testalpha = entry.sortkey[0,1].downcase
end
- if testalpha != alpha.downcase or alphaclass != entry.class then
+ if (testalpha != alpha.downcase) || (alphaclass != entry.class) then
alpha = testalpha
alphaclass = entry.class
if alpha != ' ' then
flushsavedline(handle)
if alpha =~ /^[a-zA-Z]$/o then
character = alpha.dup
+ elsif alpha == @@specialsymbol then
+ character = @@specialbanner
elsif alpha.length > 1 then
# character = "\\getvalue\{#{alpha}\}"
character = "\\#{alpha}"
else
- character = "\\#{alpha}"
+ character = "\\unknown"
end
handle << "\\registerentry{#{entry.type}}{#{character}}\n"
end
@@ -614,6 +734,7 @@ class TeXUtil
Register.flushsavedline(handle)
handle << "\\registersee{#{entry.type}}{#{entry.pagehowto},#{entry.texthowto}}{#{entry.seetoo}}{#{entry.page}}\n" ;
lastpage, lastrealpage = entry.page, entry.realpage
+ copied = false # no page !
elsif @@savedhowto != entry.pagehowto and ! entry.pagehowto.empty? then
@@savedhowto = entry.pagehowto
end
@@ -648,11 +769,13 @@ class TeXUtil
end
@@registers = Hash.new
- @@sorter = Sorter.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
def MyRegisters::reset(logger)
@@registers = Hash.new
- @@sorter = Sorter.new
+ @@sorter = Hash.new
+ @@languages = Hash.new
end
def MyRegisters::reader(logger,data)
@@ -669,6 +792,8 @@ class TeXUtil
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))
+ when 'l' then
+ @@languages[data[1]] = data[2] || ''
end
end
@@ -676,23 +801,32 @@ class TeXUtil
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)
+ Register.flush(@@registers[s],handle,@@sorter[s])
# report("register #{@@registers[s].class}: #{@@registers[s].@nofentries} entries and #{@@registers[s].@nofpages} pages")
end
end
end
def MyRegisters::processor(logger)
- @@sorter.preset(eval("MyKeys").shortcuts,eval("MyKeys").expansions,eval("MyKeys").reductions,eval("MyKeys").divisions)
- @@sorter.prepare
@@registers.keys.each do |s|
+ @@sorter[s] = Sorter.new
+ @@sorter[s].preset(
+ eval("MyKeys").shortcuts,
+ eval("MyKeys").expansions,
+ eval("MyKeys").reductions,
+ eval("MyKeys").divisions,
+ @@languages[s] || '')
+ @@sorter[s].prepare
@@registers[s].each_index do |i|
- @@registers[s][i].build(@@sorter)
+ @@registers[s][i].build(@@sorter[s])
end
@@registers[s] = @@registers[s].sort
end
end
+ def MyRegisters::finalizer(logger)
+ end
+
end
end
@@ -730,6 +864,10 @@ class TeXUtil
@@plugins.processors if @@plugins
end
+ def MyPlugins::finalizer(logger)
+ @@plugins.finalizers if @@plugins
+ end
+
end
end
@@ -764,7 +902,7 @@ class TeXUtil
def MyKeys::reader(logger,data)
key = data.shift
- grp = data.shift # language code, todo
+ # grp = data.shift # language code, todo
case key
when 's' then @@shortcuts.push(data)
when 'e' then @@expansions.push(data)
@@ -783,6 +921,9 @@ class TeXUtil
logger.report("divisions : #{@@divisions.size}") # logger.report(@@divisions.inspect)
end
+ def MyKeys::finalizer(logger)
+ end
+
end
end
@@ -816,24 +957,31 @@ class TeXUtil
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}")
+ tuifile = File.suffixed(filename,'tui')
+ if FileTest.file?(tuifile) then
+ report("parsing file #{tuifile}")
+ if f = open(tuifile) then
+ f.each do |line|
+ case line.chomp
+ when /^f (.*)$/o then @plugins.reader('MyFiles', $1.splitdata)
+ when /^c (.*)$/o then @plugins.reader('MyCommands', [$1])
+ when /^e (.*)$/o then @plugins.reader('MyExtras', $1.splitdata)
+ when /^s (.*)$/o then @plugins.reader('MySynonyms', $1.splitdata)
+ when /^r (.*)$/o then @plugins.reader('MyRegisters',$1.splitdata)
+ when /^p (.*)$/o then @plugins.reader('MyPlugins', $1.splitdata)
+ when /^x (.*)$/o then @plugins.reader('MyKeys', $1.splitdata)
+ when /^r (.*)$/o then # nothing, not handled here
+ else
+ # report("unknown entry #{line[0,1]} in line #{line.chomp}")
+ end
end
+ f.close
end
- f.close
+ else
+ report("unable to locate #{tuifile}")
end
rescue
- report("fatal error in parsing #{filename}")
+ report("fatal error in parsing #{tuifile}")
@filename = 'texutil'
else
@filename = filename
@@ -848,19 +996,27 @@ class TeXUtil
def saved(filename=@filename)
if @fatalerror then
report("fatal error, no tuo file saved")
+ return false
else
- begin
+ begin
if f = File.open(File.suffixed(filename,'tuo'),'w') then
@plugins.writers(f)
f.close
end
rescue
report("fatal error when saving file (#{$!})")
+ return false
else
report("tuo file saved")
+ return true
end
end
+ end
+
+ def finalized
+ @plugins.finalizers
@plugins.resets
+ return true # for the moment
end
def reset
diff --git a/Master/texmf-dist/scripts/context/ruby/base/tool.rb b/Master/texmf-dist/scripts/context/ruby/base/tool.rb
index 7f40e773d0c..9f120ca117a 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/tool.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/tool.rb
@@ -132,7 +132,7 @@ module Tool
return old unless test(?f,old)
new = old.downcase
- new.gsub!(/[^A-Za-z0-9\-\.\\\/]/o) do # funny chars
+ new.gsub!(/[^A-Za-z0-9\_\-\.\\\/]/o) do # funny chars
'-'
end
if old =~ /[a-zA-Z]\:/o
diff --git a/Master/texmf-dist/scripts/context/ruby/base/variables.rb b/Master/texmf-dist/scripts/context/ruby/base/variables.rb
index 5cbc5ba538e..403b57716f4 100644
--- a/Master/texmf-dist/scripts/context/ruby/base/variables.rb
+++ b/Master/texmf-dist/scripts/context/ruby/base/variables.rb
@@ -12,6 +12,89 @@
require 'base/tool'
+class Hash
+
+ def nothing?(id)
+ ! self[id] || self[id].empty?
+ end
+
+ def subset(pattern)
+ h = Hash.new
+ p = pattern.gsub(/([\.\:\-])/) do "\\#{$1}" end
+ r = /^#{p}/
+ self.keys.each do |k|
+ h[k] = self[k].dup if k =~ r
+ end
+ return h
+ end
+
+end
+
+class ExtendedHash < Hash
+
+ @@re_var_a = /\%(.*?)\%/
+ @@re_var_b = /\$\((.*?)\)/
+
+ def set(key,value='',resolve=true)
+ if value then
+ self[key] = if resolve then resolved(value.to_s) else value.to_s end
+ else
+ self[key] = ''
+ end
+ end
+
+ def replace(key,value='')
+ self[key] = value if self?(key)
+ end
+
+ def get(key,default='')
+ if self.key?(key) then self[key] else default end
+ end
+
+ def true?(key)
+ self[key] =~ /^(yes|on|true|enable|enabled|y|start)$/io rescue false
+ end
+
+ def resolved(str)
+ begin
+ str.to_s.gsub(@@re_var_a) do
+ self[$1] || ''
+ end.gsub(@@re_var_b) do
+ self[$1] || ''
+ end
+ rescue
+ str.to_s rescue ''
+ end
+ end
+
+ def check(key,default='')
+ if self.key?(key) then
+ if self[key].empty? then self[key] = (default || '') end
+ else
+ self[key] = (default || '')
+ end
+ end
+
+ def checked(key,default='')
+ if self.key?(key) then
+ if self[key].empty? then default else self[key] end
+ else
+ default
+ end
+ end
+
+ def empty?(key)
+ self[key].empty?
+ end
+
+ # def downcase(key)
+ # self[key].downcase!
+ # end
+
+end
+
+# the next one is obsolete so we need to replace things
+
module Variables
def setvariable(key,value='')
@@ -26,6 +109,10 @@ module Variables
if @variables.key?(key) then @variables[key] else default end
end
+ def truevariable(key)
+ @variables[key] =~ /^(yes|on|true)$/io rescue false
+ end
+
def checkedvariable(str,default='')
if @variables.key?(key) then
if @variables[key].empty? then default else @variables[key] end
diff --git a/Master/texmf-dist/scripts/context/ruby/ctxtools.rb b/Master/texmf-dist/scripts/context/ruby/ctxtools.rb
index 94e6e735a20..0cf96e10d70 100644
--- a/Master/texmf-dist/scripts/context/ruby/ctxtools.rb
+++ b/Master/texmf-dist/scripts/context/ruby/ctxtools.rb
@@ -15,9 +15,38 @@
# todo: move scite here
#
-# todo: move kpse call to kpse class/module
+# todo: move kpse call to kpse class/module, faster and better
-banner = ['CtxTools', 'version 1.2.2', '2004/2005', 'PRAGMA ADE/POD']
+# Taco Hoekwater on patterns and lig building (see 'agr'):
+#
+# Any direct use of a ligature (as accessed by \char or through active
+# characters) is wrong and will create faulty hyphenation. Normally,
+# when TeX sees "office", it has six tokens, and it knows from the
+# patterns that it can hyphenate between the "ff". It will build an
+# internal list of four nodes, like this:
+#
+# [char, o , ffi ]
+# [lig , ffi, c ,[f,f,i]]
+# [char, c , e ]
+# [char, e , NULL]
+#
+# as you can see from the ffi line, it has remembered the original
+# characters. While hyphenating, it temporarily changes back to
+# that, then re-instates the ligature afterwards.
+#
+# If you feed it the ligature directly, like so:
+#
+# [char, o , ffi ]
+# [char, ffi , c ]
+# [char, c , e ]
+# [char, e , NULL]
+#
+# it cannot do that (it tries to hyphenate as if the "ffi" was a
+# character), and the result is wrong hyphenation.
+
+banner = ['CtxTools', 'version 1.3.3', '2004/2006', 'PRAGMA ADE/POD']
+
+# todo dirname
unless defined? ownpath
ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
@@ -29,6 +58,7 @@ require 'base/logger'
require 'base/system'
require 'rexml/document'
+require 'net/http'
require 'ftools'
require 'kconv'
@@ -84,24 +114,32 @@ class Commands
end
end
unless maincontextfile.empty? then
+ nextcontextfile = maincontextfile.sub(/context\.tex$/,"cont-new.tex")
case action
- when 1 then touchfile(maincontextfile)
- when 2 then reportversion(maincontextfile)
+ when 1 then
+ touchfile(maincontextfile)
+ touchfile(nextcontextfile,@@newcontextversion)
+ when 2 then
+ reportversion(maincontextfile)
+ reportversion(nextcontextfile,@@newcontextversion)
end
end
end
- def touchfile(filename)
+ @@contextversion = "\\\\contextversion"
+ @@newcontextversion = "\\\\newcontextversion"
+
+ def touchfile(filename,command=@@contextversion)
if FileTest.file?(filename) then
if data = IO.read(filename) then
- timestamp = Time.now.strftime('%Y.%m.%d')
+ timestamp = Time.now.strftime('%Y.%m.%d %H:%M')
prevstamp = ''
begin
- data.gsub!(/\\contextversion\{(\d+\.\d+\.\d+)\}/) do
+ data.gsub!(/#{command}\{(\d+\.\d+\.\d+.*?)\}/) do
prevstamp = $1
- "\\contextversion{#{timestamp}}"
+ "#{command.sub(/(\\)+/,"\\")}{#{timestamp}}"
end
rescue
else
@@ -133,11 +171,11 @@ class Commands
end
- def reportversion(filename)
+ def reportversion(filename,command=@@contextversion)
version = 'unknown'
begin
- if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+)\}/) then
+ if FileTest.file?(filename) && IO.read(filename).match(/#{command}\{(\d+\.\d+\.\d+.*?)\}/) then
version = $1
end
rescue
@@ -145,7 +183,7 @@ class Commands
if @commandline.option("pipe") then
print version
else
- report("context version: #{version}")
+ report("context version: #{version} (#{filename})")
end
end
@@ -183,7 +221,7 @@ class Commands
interfaces = @commandline.arguments
if interfaces.empty? then
- interfaces = ['en', 'cz','de','it','nl','ro']
+ interfaces = ['en','cz','de','it','nl','ro','fr']
end
interfaces.each do |interface|
@@ -281,7 +319,7 @@ class Commands
interfaces = @commandline.arguments
if interfaces.empty? then
- interfaces = ['cz','de','it','nl','ro']
+ interfaces = ['cz','de','it','nl','ro','fr']
else
interfaces.delete('en')
end
@@ -353,6 +391,10 @@ class Commands
data.i_translate('cd:inherit' , 'name' , strings)
# data.i_translate('cd:command' , 'name' , strings)
+ data.gsub!(/(\<cd\:interface[^\>]*?language=")en(")/) do
+ $1 + interface + $2
+ end
+
report("saving #{outfile}")
begin
@@ -375,13 +417,19 @@ class Commands
public
- def purgefiles(all=false)
+ def purgefiles
pattern = @commandline.arguments
- purgeall = @commandline.option("all") || all
+ purgeall = @commandline.option("all")
recurse = @commandline.option("recurse")
$dontaskprefixes.push(Dir.glob("mpx-*"))
+
+ if purgeall then
+ $dontaskprefixes.push(Dir.glob("*.tex.prep"))
+ $dontaskprefixes.push(Dir.glob("*.xml.prep"))
+ end
+
$dontaskprefixes.flatten!
$dontaskprefixes.sort!
@@ -394,7 +442,7 @@ class Commands
if ! pattern || pattern.empty? then
globbed = if recurse then "**/*.*" else "*.*" end
files = Dir.glob(globbed)
- report("purging files : #{globbed}")
+ report("purging#{if purgeall then ' all' end} temporary files : #{globbed}")
else
pattern.each do |pat|
globbed = if recurse then "**/#{pat}-*.*" else "#{pat}-*.*" end
@@ -402,7 +450,7 @@ class Commands
globbed = if recurse then "**/#{pat}.*" else "#{pat}.*" end
files.push(Dir.glob(globbed))
end
- report("purging files : #{pattern.join(' ')}")
+ report("purging#{if purgeall then ' all' end} temporary files : #{pattern.join(' ')}")
end
files.flatten!
files.sort!
@@ -428,6 +476,9 @@ class Commands
end
end
end
+ $dummyfiles.each do |file|
+ (File.delete(file) if (FileTest.size?(file) rescue 10) < 10) rescue false
+ end
$texnonesuffixes.each do |suffix|
files.each do |file|
if file =~ /(.*)\.#{suffix}$/i then
@@ -461,10 +512,6 @@ class Commands
end
- def purgeallfiles
- purgefiles(true) # for old times sake
- end
-
private
$removedfiles = 0
@@ -486,7 +533,9 @@ class Commands
$forsuresuffixes = [
"tui", "tup", "ted", "tes", "top",
"log", "tmp", "run", "bck", "rlg",
- "mpt", "mpx", "mpd", "mpo"
+ "mpt", "mpx", "mpd", "mpo", "mpb",
+ "ctl",
+ "tmp.md5", "tmp.out"
]
$texonlysuffixes = [
"dvi", "ps", "pdf"
@@ -494,6 +543,9 @@ class Commands
$texnonesuffixes = [
"tuo", "tub", "top"
]
+ $dummyfiles = [
+ "mpgraph"
+ ]
def removecontextfile (filename)
if filename && FileTest.file?(filename) then
@@ -607,7 +659,7 @@ class Commands
str.chomp!
str.sub!(/\s*$/o, '')
case str
- when /^[%\#]D/io then
+ when /^[%\#]D($| )/io then
if skiplevel == 0 then
someline = if str.length < 3 then "" else str[3,str.length-1] end
if indocument then
@@ -625,7 +677,7 @@ class Commands
nofdocuments += 1
end
end
- when /^[%\#]M/io then
+ 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")
@@ -642,20 +694,25 @@ class Commands
else
if skiplevel == 0 then
inlocaldocument = indocument
+ inlocaldocument = false # else first line skipped when not empty
someline = str
if indocument then
ted.puts("\\stopdocumentation\n")
indocument = false
end
- if someline.empty? && indefinition then
- ted.puts("\\stopdefinition\n")
- indefinition = false
- elsif indefinition then
- ted.puts("#{someline}\n")
+ if indefinition then
+ if someline.empty? then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ else
+ ted.puts("#{someline}\n")
+ end
elsif ! someline.empty? then
ted.puts("\n\\startdefinition\n")
indefinition = true
- unless inlocaldocument then
+ if inlocaldocument then
+ # nothing
+ else
nofdefinitions += 1
ted.puts("#{someline}\n")
end
@@ -742,6 +799,56 @@ end
#
# Maybe I'll make this hyptools.tex
+class String
+
+ def markbraces
+ level = 0
+ self.gsub(/([\{\}])/o) do |chr|
+ if chr == '{' then
+ level = level + 1
+ chr = "((+#{level}))"
+ elsif chr == '}' then
+ chr = "((-#{level}))"
+ level = level - 1
+ end
+ chr
+ end
+ end
+
+ def unmarkbraces
+ self.gsub(/\(\(\+\d+?\)\)/o) do
+ "{"
+ end .gsub(/\(\(\-\d+?\)\)/o) do
+ "}"
+ end
+ end
+
+ def getargument(pattern)
+ if self =~ /(#{pattern})\s*\(\(\+(\d+)\)\)(.*?)\(\(\-\2\)\)/m then # no /o
+ return $3
+ else
+ return ""
+ end
+ end
+
+ def withargument(pattern, &block)
+ if self.markbraces =~ /^(.*)(#{pattern}\s*)\(\(\+(\d+)\)\)(.*?)\(\(\-\3\)\)(.*)$/m then # no /o
+ "#{$1.unmarkbraces}#{$2}{#{yield($4.unmarkbraces)}}#{$5.unmarkbraces}"
+ else
+ self
+ end
+ end
+
+ def filterargument(pattern, &block)
+ if self.markbraces =~ /^(.*)(#{pattern}\s*)\(\(\+(\d+)\)\)(.*?)\(\(\-\3\)\)(.*)$/m then # no /o
+ yield($4.unmarkbraces)
+ else
+ self
+ end
+ end
+
+end
+
class Language
include CommandBase
@@ -751,6 +858,7 @@ class Language
@language = language
@filenames = filenames
@remapping = Array.new
+ @demapping = Array.new
@unicode = Hash.new
@encoding = encoding
@data = ''
@@ -758,8 +866,11 @@ class Language
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')
+ when 't1', 'ec', 'cork' then preload_vector('ec', 'enco-ec.tex')
+ when 'y', 'texnansi' then preload_vector('texnansi', 'enco-ans.tex')
+ when 'agr', 'agreek' then preload_vector('agr', 'enco-agr.tex')
+ when 't2a' then preload_vector('t2a', 'enco-cyr.tex')
+ when 'cyr' then preload_vector() # somehow loading t2a does not work out well
end
end
@@ -774,6 +885,9 @@ class Language
def remap(from, to)
@remapping.push([from,to])
end
+ def demap(from, to)
+ @demapping.push([from,to])
+ end
def load(filenames=@filenames)
begin
@@ -783,10 +897,11 @@ class Language
begin
if fname = located(filename) then
data = IO.read(fname)
- @data += data.gsub(/\%.*$/, '')
+ @data += data.gsub(/\%.*$/, '').gsub(/\\message\{.*?\}/, '')
data.gsub!(/(\\patterns|\\hyphenation)\s*\{.*/mo) do '' end
@read += "\n% preamble of file #{fname}\n\n#{data}\n"
report("file #{fname} is loaded")
+ @data.gsub!(/^[\s\n]+$/mois, '')
break # next fileset
end
rescue
@@ -806,11 +921,45 @@ class Language
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]
+ if true then
+ report("")
+ ["\\patterns","\\hyphenation"].each do |what|
+ @data = @data.withargument(what) do |content|
+ report("converting #{what}")
+ report("")
+ @demapping.each_index do |i|
+ content.gsub!(@demapping[i][0], @demapping[i][1])
+ end
+ content.gsub!(/\\delete\{.*?\}/o) do '' end
+ content.gsub!(/\\keep\{(.*?)\}/o) do $1 end
+ done = false
+ @remapping.each_index do |i|
+ from, to, m = @remapping[i][0], @remapping[i][1], 0
+ content.gsub!(from) do
+ done = true
+ m += 1
+ "[#{i}]"
+ end
+ report("#{m.to_s.rjust(5)} entries remapped to #{to}") unless m == 0
+ n += m
+ end
+ content.gsub!(/\[(\d+)\]/o) do
+ @remapping[$1.to_i][1]
+ end
+ report(" nothing remapped") unless done
+ report("")
+ content.to_s
+ end
+ end
+ else
+ @remapping.each do |k|
+ from, to, m = k[0], k[1], 0
+ @data.gsub!(from) do
+ m += 1
+ to
+ end
+ report("#{m.to_s.rjust(5)} entries remapped to #{to}") unless m == 0
+ n += m
end
end
report("#{n} changes in patterns and exceptions")
@@ -865,6 +1014,7 @@ class Language
end
def triggerunicode
+ return
if @commandline.option('utf8') then
"% xetex needs utf8 encoded patterns and for patterns\n" +
"% coded as such we need to enable this regime when\n" +
@@ -963,9 +1113,9 @@ class Language
begin
if f = File.open(patname,'w') then
data = ''
- @data.scan(/\\patterns\s*\{\s*(.*?)\s*\}/m) do
+ @data.filterargument('\\patterns') do |content|
report("merging patterns")
- data += $1 + "\n"
+ data += content.strip
end
data.gsub!(/(\s*\n\s*)+/mo, "\n")
@@ -989,9 +1139,9 @@ class Language
begin
if f = File.open(hypname,'w') then
data = ''
- @data.scan(/\\hyphenation\s*\{\s*(.*?)\s*\}/m) do
+ @data.filterargument('\\hyphenation') do |content|
report("merging exceptions")
- data += $1 + "\n"
+ data += content.strip
end
data.gsub!(/(\s*\n\s*)+/mo, "\n")
f << banner
@@ -1094,7 +1244,7 @@ class Language
end
- def preload_vector(encoding='')
+ def preload_vector(encoding='', filename='')
# funny polish
@@ -1117,41 +1267,285 @@ class Language
remap(/X/, "[aeligature]")
remap(/Y/, "[ostroke]")
remap(/Z/, "[aring]")
+ when 'hu' then
+ # nothing
when 'ca' then
- remap(/\\c\{.*?\}/, "")
+ demap(/\\c\{/, "\\delete{")
when 'de', 'deo' then
- remap(/\\c\{.*?\}/, "")
- remap(/\\n\{\}/, "")
+ demap(/\\c\{/, "\\delete{")
+ demap(/\\n\{/, "\\keep{")
remap(/\\3/, "[ssharp]")
remap(/\\9/, "[ssharp]")
remap(/\"a/, "[adiaeresis]")
remap(/\"o/, "[odiaeresis]")
remap(/\"u/, "[udiaeresis]")
when 'fr' then
+ demap(/\\n\{/, "\\keep{")
remap(/\\ae/, "[adiaeresis]")
remap(/\\oe/, "[odiaeresis]")
when 'la' then
# \lccode`'=`' somewhere else, todo
- remap(/\\c\{.*?\}/, "")
+ demap(/\\c\{/, "\\delete{")
remap(/\\a\s*/, "[aeligature]")
remap(/\\o\s*/, "[oeligature]")
+ when 'agr' then
+ remap(/\<\'a\|/, "[greekalphaiotasubdasiatonos]")
+ # remap(/\<\'a\|/, "[greekdasiatonos][greekAlpha][greekiota]")
+ remap(/\>\'a\|/, "[greekalphaiotasubpsilitonos]")
+ remap(/\<\`a\|/, "[greekalphaiotasubdasiavaria]")
+ remap(/\>\`a\|/, "[greekalphaiotasubpsilivaria]")
+ remap(/\<\~a\|/, "[greekalphaiotasubdasiaperispomeni]")
+ remap(/\>\~a\|/, "[greekalphaiotasubpsiliperispomeni]")
+ remap(/\'a\|/, "[greekalphaiotasubtonos]")
+ remap(/\`a\|/, "[greekalphaiotasubvaria]")
+ remap(/\~a\|/, "[greekalphaiotasubperispomeni]")
+ remap(/\<a\|/, "[greekalphaiotasubdasia]")
+ remap(/\>a\|/, "[greekalphaiotasubpsili]")
+ remap(/a\|/, "[greekalphaiotasub]")
+ remap(/\<\'h\|/, "[greeketaiotasubdasiatonos]")
+ remap(/\>\'h\|/, "[greeketaiotasubpsilitonos]")
+ remap(/\<\`h\|/, "[greeketaiotasubdasiavaria]")
+ remap(/\>\`h\|/, "[greeketaiotasubpsilivaria]")
+ remap(/\<\~h\|/, "[greeketaiotasubdasiaperispomeni]")
+ remap(/\>\~h\|/, "[greeketaiotasubpsiliperispomeni]")
+ remap(/\'h\|/, "[greeketaiotasubtonos]")
+ remap(/\`h\|/, "[greeketaiotasubvaria]")
+ remap(/\~h\|/, "[greeketaiotasubperispomeni]")
+ remap(/\<h\|/, "[greeketaiotasubdasia]")
+ remap(/\>h\|/, "[greeketaiotasubpsili]")
+ remap(/h\|/, "[greeketaiotasub]")
+ remap(/\<'w\|/, "[greekomegaiotasubdasiatonos]")
+ remap(/\>'w\|/, "[greekomegaiotasubpsilitonos]")
+ remap(/\<`w\|/, "[greekomegaiotasubdasiavaria]")
+ remap(/\>`w\|/, "[greekomegaiotasubpsilivaria]")
+ remap(/\<~w\|/, "[greekomegaiotasubdasiaperispomeni]")
+ remap(/\>~w\|/, "[greekomegaiotasubpsiliperispomeni]")
+ remap(/\<w\|/, "[greekomegaiotasubdasia]")
+ remap(/\>w\|/, "[greekomegaiotasubpsili]")
+ remap(/\'w\|/, "[greekomegaiotasubtonos]")
+ remap(/\`w\|/, "[greekomegaiotasubvaria]")
+ remap(/\~w\|/, "[greekomegaiotasubperispomeni]")
+ remap(/w\|/, "[greekomegaiotasub]")
+ remap(/\<\'i/, "[greekiotadasiatonos]")
+ remap(/\>\'i/, "[greekiotapsilitonos]")
+ remap(/\<\`i/, "[greekiotadasiavaria]")
+ remap(/\>\`i/, "[greekiotapsilivaria]")
+ remap(/\<\~i/, "[greekiotadasiaperispomeni]")
+ remap(/\>\~i/, "[greekiotapsiliperispomeni]")
+ remap(/\"\'i/, "[greekiotadialytikatonos]")
+ remap(/\"\`i/, "[greekiotadialytikavaria]")
+ remap(/\"\~i/, "[greekiotadialytikaperispomeni]")
+ remap(/\<i/, "[greekiotadasia]")
+ remap(/\>i/, "[greekiotapsili]")
+ remap(/\'i/, "[greekiotaoxia]")
+ remap(/\`i/, "[greekiotavaria]")
+ remap(/\~i/, "[greekiotaperispomeni]")
+ remap(/\"i/, "[greekiotadialytika]")
+ remap(/\>\~e/, "[greekepsilonpsiliperispomeni]")
+ remap(/\<\~e/, "[greekepsilondasiaperispomeni]")
+ remap(/\<\'e/, "[greekepsilondasiatonos]")
+ remap(/\>\'e/, "[greekepsilonpsilitonos]")
+ remap(/\<\`e/, "[greekepsilondasiavaria]")
+ remap(/\>\`e/, "[greekepsilonpsilivaria]")
+ remap(/\<e/, "[greekepsilondasia]")
+ remap(/\>e/, "[greekepsilonpsili]")
+ remap(/\'e/, "[greekepsilonoxia]")
+ remap(/\`e/, "[greekepsilonvaria]")
+ remap(/\~e/, "[greekepsilonperispomeni]")
+ remap(/\<\'a/, "[greekalphadasiatonos]")
+ remap(/\>\'a/, "[greekalphapsilitonos]")
+ remap(/\<\`a/, "[greekalphadasiavaria]")
+ remap(/\>\`a/, "[greekalphapsilivaria]")
+ remap(/\<\~a/, "[greekalphadasiaperispomeni]")
+ remap(/\>\~a/, "[greekalphapsiliperispomeni]")
+ remap(/\<a/, "[greekalphadasia]")
+ remap(/\>a/, "[greekalphapsili]")
+ remap(/\'a/, "[greekalphaoxia]")
+ remap(/\`a/, "[greekalphavaria]")
+ remap(/\~a/, "[greekalphaperispomeni]")
+ remap(/\<\'h/, "[greeketadasiatonos]")
+ remap(/\>\'h/, "[greeketapsilitonos]")
+ remap(/\<\`h/, "[greeketadasiavaria]")
+ remap(/\>\`h/, "[greeketapsilivaria]")
+ remap(/\<\~h/, "[greeketadasiaperispomeni]")
+ remap(/\>\~h/, "[greeketapsiliperispomeni]")
+ remap(/\<h/, "[greeketadasia]")
+ remap(/\>h/, "[greeketapsili]")
+ remap(/\'h/, "[greeketaoxia]")
+ remap(/\`h/, "[greeketavaria]")
+ remap(/\~h/, "[greeketaperispomeni]")
+ remap(/\<\~o/, "[greekomicrondasiaperispomeni]")
+ remap(/\>\~o/, "[greekomicronpsiliperispomeni]")
+ remap(/\<\'o/, "[greekomicrondasiatonos]")
+ remap(/\>\'o/, "[greekomicronpsilitonos]")
+ remap(/\<\`o/, "[greekomicrondasiavaria]")
+ remap(/\>\`o/, "[greekomicronpsilivaria]")
+ remap(/\<o/, "[greekomicrondasia]")
+ remap(/\>o/, "[greekomicronpsili]")
+ remap(/\'o/, "[greekomicronoxia]")
+ remap(/\`o/, "[greekomicronvaria]")
+ remap(/\~o/, "[greekomicronperispomeni]")
+ remap(/\<\'u/, "[greekupsilondasiatonos]")
+ remap(/\>\'u/, "[greekupsilonpsilitonos]")
+ remap(/\<\`u/, "[greekupsilondasiavaria]")
+ remap(/\>\'u/, "[greekupsilonpsilivaria]")
+ remap(/\<\~u/, "[greekupsilondasiaperispomeni]")
+ remap(/\>\~u/, "[greekupsilonpsiliperispomeni]")
+ remap(/\"\'u/, "[greekupsilondialytikatonos]")
+ remap(/\"\`u/, "[greekupsilondialytikavaria]")
+ remap(/\"\~u/, "[greekupsilondialytikaperispomeni]")
+ remap(/\<u/, "[greekupsilondasia]")
+ remap(/\>u/, "[greekupsilonpsili]")
+ remap(/\'u/, "[greekupsilonoxia]")
+ remap(/\`u/, "[greekupsilonvaria]")
+ remap(/\~u/, "[greekupsilonperispomeni]")
+ remap(/\"u/, "[greekupsilondiaeresis]")
+ remap(/\<\'w/, "[greekomegadasiatonos]")
+ remap(/\>\'w/, "[greekomegapsilitonos]")
+ remap(/\<\`w/, "[greekomegadasiavaria]")
+ remap(/\>\`w/, "[greekomegapsilivaria]")
+ remap(/\<\~w/, "[greekomegadasiaperispomeni]")
+ remap(/\>\~w/, "[greekomegapsiliperispomeni]")
+ remap(/\<w/, "[greekomegadasia]")
+ remap(/\>w/, "[greekomegapsili]")
+ remap(/\'w/, "[greekomegaoxia]")
+ remap(/\`w/, "[greekomegavaria]")
+ remap(/\~w/, "[greekomegaperispomeni]")
+ remap(/\<r/, "[greekrhodasia]")
+ remap(/\>r/, "[greekrhopsili]")
+ remap(/\<\~/, "[greekdasiaperispomeni]")
+ remap(/\>\~/, "[greekpsiliperispomeni]")
+ remap(/\<\'/, "[greekdasiatonos]")
+ remap(/\>\'/, "[greekpsilitonos]")
+ remap(/\<\`/, "[greekdasiavaria]")
+ remap(/\>\`/, "[greekpsilivaria]")
+ remap(/\"\'/, "[greekdialytikatonos]")
+ remap(/\"\`/, "[greekdialytikavaria]")
+ remap(/\"\~/, "[greekdialytikaperispomeni]")
+ remap(/\</, "[dasia]")
+ remap(/\>/, "[psili]")
+ remap(/\'/, "[oxia]")
+ remap(/\`/, "[greekvaria]")
+ remap(/\~/, "[perispomeni]")
+ remap(/\"/, "[dialytika]")
+ # unknown
+ remap(/\|/, "[greekIotadialytika]")
+ # next
+ remap(/A/, "[greekAlpha]")
+ remap(/B/, "[greekBeta]")
+ remap(/D/, "[greekDelta]")
+ remap(/E/, "[greekEpsilon]")
+ remap(/F/, "[greekPhi]")
+ remap(/G/, "[greekGamma]")
+ remap(/H/, "[greekEta]")
+ remap(/I/, "[greekIota]")
+ remap(/J/, "[greekTheta]")
+ remap(/K/, "[greekKappa]")
+ remap(/L/, "[greekLambda]")
+ remap(/M/, "[greekMu]")
+ remap(/N/, "[greekNu]")
+ remap(/O/, "[greekOmicron]")
+ remap(/P/, "[greekPi]")
+ remap(/Q/, "[greekChi]")
+ remap(/R/, "[greekRho]")
+ remap(/S/, "[greekSigma]")
+ remap(/T/, "[greekTau]")
+ remap(/U/, "[greekUpsilon]")
+ remap(/W/, "[greekOmega]")
+ remap(/X/, "[greekXi]")
+ remap(/Y/, "[greekPsi]")
+ remap(/Z/, "[greekZeta]")
+ remap(/a/, "[greekalpha]")
+ remap(/b/, "[greekbeta]")
+ remap(/c/, "[greekfinalsigma]")
+ remap(/d/, "[greekdelta]")
+ remap(/e/, "[greekepsilon]")
+ remap(/f/, "[greekphi]")
+ remap(/g/, "[greekgamma]")
+ remap(/h/, "[greeketa]")
+ remap(/i/, "[greekiota]")
+ remap(/j/, "[greektheta]")
+ remap(/k/, "[greekkappa]")
+ remap(/l/, "[greeklambda]")
+ remap(/m/, "[greekmu]")
+ remap(/n/, "[greeknu]")
+ remap(/o/, "[greekomicron]")
+ remap(/p/, "[greekpi]")
+ remap(/q/, "[greekchi]")
+ remap(/r/, "[greekrho]")
+ remap(/s/, "[greeksigma]")
+ remap(/t/, "[greektau]")
+ remap(/u/, "[greekupsilon]")
+ remap(/w/, "[greekomega]")
+ remap(/x/, "[greekxi]")
+ remap(/y/, "[greekpsi]")
+ remap(/z/, "[greekzeta]")
+ when 'ru' then
+ remap(/\xC1/, "[cyrillica]")
+ remap(/\xC2/, "[cyrillicb]")
+ remap(/\xD7/, "[cyrillicv]")
+ remap(/\xC7/, "[cyrillicg]")
+ remap(/\xC4/, "[cyrillicd]")
+ remap(/\xC5/, "[cyrillice]")
+ remap(/\xD6/, "[cyrilliczh]")
+ remap(/\xDA/, "[cyrillicz]")
+ remap(/\xC9/, "[cyrillici]")
+ remap(/\xCA/, "[cyrillicishrt]")
+ remap(/\xCB/, "[cyrillick]")
+ remap(/\xCC/, "[cyrillicl]")
+ remap(/\xCD/, "[cyrillicm]")
+ remap(/\xCE/, "[cyrillicn]")
+ remap(/\xCF/, "[cyrillico]")
+ remap(/\xD0/, "[cyrillicp]")
+ remap(/\xD2/, "[cyrillicr]")
+ remap(/\xD3/, "[cyrillics]")
+ remap(/\xD4/, "[cyrillict]")
+ remap(/\xD5/, "[cyrillicu]")
+ remap(/\xC6/, "[cyrillicf]")
+ remap(/\xC8/, "[cyrillich]")
+ remap(/\xC3/, "[cyrillicc]")
+ remap(/\xDE/, "[cyrillicch]")
+ remap(/\xDB/, "[cyrillicsh]")
+ remap(/\xDD/, "[cyrillicshch]")
+ remap(/\xDF/, "[cyrillichrdsn]")
+ remap(/\xD9/, "[cyrillicery]")
+ remap(/\xD8/, "[cyrillicsftsn]")
+ remap(/\xDC/, "[cyrillicerev]")
+ remap(/\xC0/, "[cyrillicyu]")
+ remap(/\xD1/, "[cyrillicya]")
+ remap(/\xA3/, "[cyrillicyo]")
else
end
if ! encoding.empty? then
begin
- filename = `kpsewhich -progname=context enco-#{encoding}.tex`
- if data = IO.read(filename.chomp) then
+ filename = `kpsewhich -progname=context #{filename}`
+ if data = IO.readlines(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}]")
+ accept = false
+ data.each do |line|
+ case line.chomp
+ when /\\start(en|)coding\s*\[(.*?)\]/io then
+ enc = $2
+ if accept = (enc == encoding) then
+ report("accepting vector #{enc}")
+ else
+ report("skipping vector #{enc}")
+ end
+ when /\\stop(en|)coding/io then
+ accept = false
+ when accept && /\\definecharacter\s*([a-zA-Z]+)\s*(\d+)\s*/o then
+ name, number = $1, $2
+ remap(/\^\^#{sprintf("%02x",number)}/, "[#{name}]")
+ if number.to_i > 127 then
+ remap(/#{sprintf("\\%03o",number)}/, "[#{name}]")
+ end
+ end
end
end
rescue
end
end
-
end
end
@@ -1185,7 +1579,7 @@ class Commands
# 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
+ # when interpreting the raw patterns. The second entry is a list of
# filesets (string|aray), each first match of a set is taken.
@@languagedata['ba' ] = [ 'ec' , ['bahyph.tex'] ]
@@ -1204,6 +1598,7 @@ class Commands
@@languagedata['hr' ] = [ 'ec' , ['hrhyph.tex'] ]
@@languagedata['hu' ] = [ 'ec' , ['huhyphn.tex'] ]
@@languagedata['en' ] = [ 'default' , [['ushyphmax.tex','ushyph.tex','hyphen.tex']] ]
+ @@languagedata['us' ] = [ 'default' , [['ushyphmax.tex','ushyph.tex','hyphen.tex']] ]
# inhyph.tex
@@languagedata['is' ] = [ 'ec' , ['ishyph.tex'] ]
@@languagedata['it' ] = [ 'ec' , ['ithyph.tex'] ]
@@ -1211,7 +1606,7 @@ class Commands
# mnhyph
@@languagedata['nl' ] = [ 'ec' , ['nehyph96.tex'] ]
@@languagedata['no' ] = [ 'ec' , ['nohyph.tex'] ]
- # oldgrhyph.tex
+ @@languagedata['agr'] = [ 'agr' , [['grahyph4.tex','oldgrhyph.tex']] ] # new, todo
@@languagedata['pl' ] = [ 'ec' , ['plhyph.tex'] ]
@@languagedata['pt' ] = [ 'ec' , ['pthyph.tex'] ]
@@languagedata['ro' ] = [ 'ec' , ['rohyph.tex'] ]
@@ -1222,7 +1617,8 @@ class Commands
@@languagedata['sv' ] = [ 'ec' , ['svhyph.tex'] ]
@@languagedata['tr' ] = [ 'ec' , ['tkhyph.tex'] ]
@@languagedata['uk' ] = [ 'default' , [['ukhyphen.tex','ukhyph.tex']] ]
-
+ # @@languagedata['ru' ] = [ 't2a' , ['ruhyphal.tex'] ] # t2a does not work
+ @@languagedata['ru' ] = [ 'cyr' , ['ruhyphal.tex'] ]
end
class Commands
@@ -1235,10 +1631,11 @@ class Commands
texmfroot = @commandline.argument('first')
texmfroot = '.' if texmfroot.empty?
- maproot = "#{texmfroot}/fonts/map/pdftex/context"
+ maproot = "#{texmfroot.gsub(/\\/,'/')}/fonts/map/pdftex/context"
if File.directory?(maproot) then
- if files = Dir.glob("#{maproot}/*.map") and files.size > 0 then
+ files = Dir.glob("#{maproot}/*.map")
+ if files.size > 0 then
files.each do |pdffile|
next if File.basename(pdffile) == 'pdftex.map'
pdffile = File.expand_path(pdffile)
@@ -1267,6 +1664,12 @@ class Commands
data.delete_if do |line|
line.gsub(/\s+/,'').empty?
end
+ data.collect! do |line|
+ # remove line with "name name" lines
+ line.gsub(/^(\S+)\s+\1\s*$/) do
+ $1
+ end
+ end
begin
if force then
if n > 0 then
@@ -1279,8 +1682,12 @@ class Commands
report("? #{File.basename(dpxfile)} - dvipdfm(x)")
end
else
- report("- #{File.basename(dpxfile)} - dvipdfm(x)")
- begin File.delete(dpxname) ; rescue ; end
+ report("- #{File.basename(dpxfile)} - dvipdfm(x) - no entries")
+ # begin File.delete(dpxname) ; rescue ; end
+ if f = File.open(dpxfile,'w') then
+ f.puts("% no map entries")
+ f.close
+ end
end
else
report(". #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
@@ -1314,6 +1721,118 @@ class Commands
end
+class Array
+
+ def add_shebang(filename,program)
+ unless self[0] =~ /^\#/ then
+ self.insert(0,"\#!/usr/env #{program}")
+ end
+ unless self[2] =~ /^\#.*?copyright\=/ then
+ self.insert(1,"\#")
+ self.insert(2,"\# copyright=pragma-ade readme=readme.pdf licence=cc-gpl")
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ self[2].gsub!(/ +/, ' ')
+ return true
+ else
+ return false
+ end
+ end
+
+ def add_directive(filename,program)
+ unless self[0] =~ /^\%/ then
+ self.insert(0,"\% content=#{program}")
+ end
+ unless self[2] =~ /^\%.*?copyright\=/ then
+ self.insert(1,"\%")
+ if File.expand_path(filename) =~ /[\\\/](doc|manuals)[\\\/]/ then
+ self.insert(2,"\% copyright=pragma-ade readme=readme.pdf licence=cc-by-nc-sa")
+ else
+ self.insert(2,"\% copyright=pragma-ade readme=readme.pdf licence=cc-gpl")
+ end
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ self[0].gsub!(/ +/, ' ')
+ return true
+ else
+ return false
+ end
+ end
+
+ def add_comment(filename)
+ if self[0] =~ /<\?xml.*?\?>/ && self[2] !~ /^<\!\-\-.*?copyright\=.*?\-\->/ then
+ self.insert(1,"")
+ if File.expand_path(filename) =~ /[\\\/](doc|manuals)[\\\/]/ then
+ self.insert(2,"<!-- copyright='pragma-ade' readme='readme.pdf' licence='cc-by-nc-sa' -->")
+ else
+ self.insert(2,"<!-- copyright='pragma-ade' readme='readme.pdf' licence='cc-gpl' -->")
+ end
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ return true
+ else
+ return false
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def brandfiles
+
+ force = @commandline.option("force")
+ files = @commandline.arguments # Dir.glob("**/*.*")
+ done = false
+
+ files.each do |filename|
+ if FileTest.file?(filename) then
+ ok = false
+ begin
+ data = IO.readlines(filename)
+ case filename
+ when /\.rb$/ then
+ ok = data.add_shebang(filename,'ruby')
+ when /\.pl$/ then
+ ok = data.add_shebang(filename,'perl')
+ when /\.py$/ then
+ ok = data.add_shebang(filename,'python')
+ when /\.lua$/ then
+ ok = data.add_shebang(filename,'lua')
+ when /\.tex$/ then
+ ok = data.add_directive(filename,'tex')
+ when /\.mp$/ then
+ ok = data.add_directive(filename,'metapost')
+ when /\.mf$/ then
+ ok = data.add_directive(filename,'metafont')
+ when /\.(xml|xsl|fo|fx|rlx|rng|exa)$/ then
+ ok = data.add_comment(filename)
+ end
+ rescue
+ report("fatal error in processing #{filename}") # maybe this catches the mac problem taco reported
+ else
+ if ok then
+ report()
+ report(filename)
+ report()
+ for i in 0..4 do
+ report(' ' + data[i].chomp)
+ end
+ if force && f = File.open(filename,'w') then
+ f.puts data
+ f.close
+ end
+ done = true
+ end
+ end
+ else
+ report("no file named #{filename}")
+ end
+ end
+ report() if done
+ end
+
+end
+
class Commands
include CommandBase
@@ -1322,68 +1841,609 @@ class Commands
# 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
+ if doctype then
+ tab = "\t"
+ handle << "<?xml version='1.0' encoding='utf-8'?>\n\n"
+ handle << "<!-- !DOCTYPE entities SYSTEM 'entities.xml' -->\n\n"
+ handle << "<!DOCTYPE #{doctype} [\n"
+ else
+ tab = ""
+ end
entities.keys.sort.each do |k|
- handle.puts("#{tab}<!ENTITY #{k} \"\&\##{entities[k]};\">")
+ handle << "#{tab}<!ENTITY #{k} \"\&\#x#{entities[k]};\">\n"
+ end
+ if doctype then
+ handle << "]>\n"
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
+ filenames = ['enco-uc.tex','contextnames.txt']
outputname = @commandline.argument('first')
+ doctype = @commandline.option('doctype')
+ entities = Hash.new
+
+ filenames.each do |filename|
+ # filename = `texmfstart tmftools.rb --progname=context #{filename}`.chomp
+ filename = `kpsewhich --progname=context #{filename}`.chomp
+ if filename and not filename.empty? and FileTest.file?(filename) then
+ report("loading #{filename.gsub(/\\/,'/')}") unless outputname.empty?
+ IO.readlines(filename).each do |line|
+ case line
+ when /^[\#\%]/io then
+ # skip comment line
+ when /\\definecharacter\s+([a-z]+)\s+\{\\uchar\{*(\d+)\}*\{(\d+)\}\}/io then
+ name, code = $1, ($2.to_i*256 + $3.to_i).to_s
+ entities[name] = code.rjust(4,'0') unless entities.key?(name)
+ when /^([A-F0-9]+)\;([a-z][a-z]+)\;(.*?)\;(.*?)\s*$/io then
+ code, name, adobe, comment = $1, $2, $3, $4
+ entities[name] = code.rjust(4,'0') unless entities.key?(name)
+ end
+ end
+ end
+ end
+ if outputname and not outputname.empty? then
+ if f = File.open(outputname,'w') then
+ report("saving #{entities.size} entities in #{outputname}")
+ flushentities(f,entities,doctype)
+ f.close
+ else
+ flushentities($stdout,entities,doctype)
+ end
+ else
+ flushentities($stdout,entities,doctype)
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def platformize
+
+ pattern = if @commandline.arguments.empty? then "*.{rb,pl,py}" else @commandline.arguments end
+ recurse = @commandline.option("recurse")
+ force = @commandline.option("force")
+ pattern = "#{if recurse then '**/' else '' end}#{pattern}"
+ Dir.glob(pattern).each do |file|
+ if File.file?(file) then
+ size = File.size(file)
+ data = IO.readlines(file)
+ if force then
+ if f = File.open(file,'w')
+ data.each do |line|
+ f.puts(line.chomp)
+ end
+ f.close
+ end
+ if File.size(file) == size then # not robust
+ report("file '#{file}' is unchanged")
+ else
+ report("file '#{file}' is platformized")
+ end
+ else
+ report("file '#{file}' is a candidate")
+ end
+ end
+ end
+ end
- 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
+
+class TexDeps
+
+ @@cs_tex = %q/
+ above abovedisplayshortskip abovedisplayskip
+ abovewithdelims accent adjdemerits advance afterassignment
+ aftergroup atop atopwithdelims
+ badness baselineskip batchmode begingroup
+ belowdisplayshortskip belowdisplayskip binoppenalty botmark
+ box boxmaxdepth brokenpenalty
+ catcode char chardef cleaders closein closeout clubpenalty
+ copy count countdef cr crcr csname
+ day deadcycles def defaulthyphenchar defaultskewchar
+ delcode delimiter delimiterfactor delimeters
+ delimitershortfall delimeters dimen dimendef discretionary
+ displayindent displaylimits displaystyle
+ displaywidowpenalty displaywidth divide
+ doublehyphendemerits dp dump
+ edef else emergencystretch end endcsname endgroup endinput
+ endlinechar eqno errhelp errmessage errorcontextlines
+ errorstopmode escapechar everycr everydisplay everyhbox
+ everyjob everymath everypar everyvbox exhyphenpenalty
+ expandafter
+ fam fi finalhyphendemerits firstmark floatingpenalty font
+ fontdimen fontname futurelet
+ gdef global group globaldefs
+ halign hangafter hangindent hbadness hbox hfil horizontal
+ hfill horizontal hfilneg hfuzz hoffset holdinginserts hrule
+ hsize hskip hss horizontal ht hyphenation hyphenchar
+ hyphenpenalty hyphen
+ if ifcase ifcat ifdim ifeof iffalse ifhbox ifhmode ifinner
+ ifmmode ifnum ifodd iftrue ifvbox ifvmode ifvoid ifx
+ ignorespaces immediate indent input inputlineno input
+ insert insertpenalties interlinepenalty
+ jobname
+ kern
+ language lastbox lastkern lastpenalty lastskip lccode
+ leaders left lefthyphenmin leftskip leqno let limits
+ linepenalty line lineskip lineskiplimit long looseness
+ lower lowercase
+ mag mark mathaccent mathbin mathchar mathchardef mathchoice
+ mathclose mathcode mathinner mathop mathopen mathord
+ mathpunct mathrel mathsurround maxdeadcycles maxdepth
+ meaning medmuskip message mkern month moveleft moveright
+ mskip multiply muskip muskipdef
+ newlinechar noalign noboundary noexpand noindent nolimits
+ nonscript scriptscript nonstopmode nulldelimiterspace
+ nullfont number
+ omit openin openout or outer output outputpenalty over
+ overfullrule overline overwithdelims
+ pagedepth pagefilllstretch pagefillstretch pagefilstretch
+ pagegoal pageshrink pagestretch pagetotal par parfillskip
+ parindent parshape parskip patterns pausing penalty
+ postdisplaypenalty predisplaypenalty predisplaysize
+ pretolerance prevdepth prevgraf
+ radical raise read relax relpenalty right righthyphenmin
+ rightskip romannumeral
+ scriptfont scriptscriptfont scriptscriptstyle scriptspace
+ scriptstyle scrollmode setbox setlanguage sfcode shipout
+ show showbox showboxbreadth showboxdepth showlists showthe
+ skewchar skip skipdef spacefactor spaceskip span special
+ splitbotmark splitfirstmark splitmaxdepth splittopskip
+ string
+ tabskip textfont textstyle the thickmuskip thinmuskip time
+ toks toksdef tolerance topmark topskip tracingcommands
+ tracinglostchars tracingmacros tracingonline tracingoutput
+ tracingpages tracingparagraphs tracingrestores tracingstats
+ uccode uchyph underline unhbox unhcopy unkern unpenalty
+ unskip unvbox unvcopy uppercase
+ vadjust valign vbadness vbox vcenter vfil vfill vfilneg
+ vfuzz voffset vrule vsize vskip vsplit vss vtop
+ wd widowpenalty write
+ xdef xleaders xspaceskip
+ year
+ /.split
+
+ @@cs_etex = %q/
+ beginL beginR botmarks
+ clubpenalties currentgrouplevel currentgrouptype
+ currentifbranch currentiflevel currentiftype
+ detokenize dimexpr displaywidowpenalties
+ endL endR eTeXrevision eTeXversion everyeof
+ firstmarks fontchardp fontcharht fontcharic fontcharwd
+ glueexpr glueshrink glueshrinkorder gluestretch
+ gluestretchorder gluetomu
+ ifcsname ifdefined iffontchar interactionmode
+ interactionmode interlinepenalties
+ lastlinefit lastnodetype
+ marks topmarks middle muexpr mutoglue
+ numexpr
+ pagediscards parshapedimen parshapeindent parshapelength
+ predisplaydirection
+ savinghyphcodes savingvdiscards scantokens showgroups
+ showifs showtokens splitdiscards splitfirstmarks
+ TeXXeTstate tracingassigns tracinggroups tracingifs
+ tracingnesting tracingscantokens
+ unexpanded unless
+ widowpenalties
+ /.split
+
+ @@cs_pdftex = %q/
+ pdfadjustspacing pdfannot pdfavoidoverfull
+ pdfcatalog pdfcompresslevel
+ pdfdecimaldigits pdfdest pdfdestmargin
+ pdfendlink pdfendthread
+ pdffontattr pdffontexpand pdffontname pdffontobjnum pdffontsize
+ pdfhorigin
+ pdfimageresolution pdfincludechars pdfinfo
+ pdflastannot pdflastdemerits pdflastobj
+ pdflastvbreakpenalty pdflastxform pdflastximage
+ pdflastximagepages pdflastxpos pdflastypos
+ pdflinesnapx pdflinesnapy pdflinkmargin pdfliteral
+ pdfmapfile pdfmaxpenalty pdfminpenalty pdfmovechars
+ pdfnames
+ pdfobj pdfoptionpdfminorversion pdfoutline pdfoutput
+ pdfpageattr pdfpageheight pdfpageresources pdfpagesattr
+ pdfpagewidth pdfpkresolution pdfprotrudechars
+ pdfrefobj pdfrefxform pdfrefximage
+ pdfsavepos pdfsnaprefpoint pdfsnapx pdfsnapy pdfstartlink
+ pdfstartthread
+ pdftexrevision pdftexversion pdfthread pdfthreadmargin
+ pdfuniqueresname
+ pdfvorigin
+ pdfxform pdfximage
+ /.split
+
+ @@cs_omega = %q/
+ odelimiter omathaccent omathchar oradical omathchardef omathcode odelcode
+ leftghost rightghost
+ charwd charht chardp charit
+ localleftbox localrightbox
+ localinterlinepenalty localbrokenpenalty
+ pagedir bodydir pardir textdir mathdir
+ boxdir nextfakemath
+ pagewidth pageheight pagerightoffset pagebottomoffset
+ nullocp nullocplist ocp externalocp ocplist pushocplist popocplist clearocplists ocptracelevel
+ addbeforeocplist addafterocplist removebeforeocplist removeafterocplist
+ OmegaVersion
+ InputTranslation OutputTranslation DefaultInputTranslation DefaultOutputTranslation
+ noInputTranslation noOutputTranslation
+ InputMode OutputMode DefaultInputMode DefaultOutputMode
+ noInputMode noOutputMode noDefaultInputMode noDefaultOutputMode
+ /.split
+
+ @@cs_plain = %q/
+ TeX
+ bgroup egroup endgraf space empty null
+ newcount newdimen newskip newmuskip newbox newtoks newhelp newread newwrite newfam newlanguage newinsert newif
+ maxdimen magstephalf magstep
+ frenchspacing nonfrenchspacing normalbaselines obeylines obeyspaces raggedright ttraggedright
+ thinspace negthinspace enspace enskip quad qquad
+ smallskip medskip bigskip removelastskip topglue vglue hglue
+ break nobreak allowbreak filbreak goodbreak smallbreak medbreak bigbreak
+ line leftline rightline centerline rlap llap underbar strutbox strut
+ cases matrix pmatrix bordermatrix eqalign displaylines eqalignno leqalignno
+ pageno folio tracingall showhyphens fmtname fmtversion
+ hphantom vphantom phantom smash
+ /.split
+
+ @@cs_eplain = %q/
+ eTeX
+ newmarks grouptype interactionmode nodetype iftype
+ tracingall loggingall tracingnone
+ /.split
+
+ # let's ignore \dimendef etc
+
+ @@primitives_def = "def|edef|xdef|gdef|let|newcount|newdimen|newskip|newbox|newtoks|newmarks|chardef|mathchardef|newconditional"
+
+ @@cs_global = [@@cs_tex,@@cs_etex,@@cs_pdftex,@@cs_omega].sort.flatten
+ @@types = [['invalid','*'],['okay','='],['forward','>'],['backward','<'],['unknown','?']]
+
+ def initialize(logger=nil)
+ @cs_local = Hash.new
+ @cs_new = Hash.new
+ @cs_defd = Hash.new
+ @cs_used = Hash.new
+ @filename = 'context.tex'
+ @files = Array.new # keep load order !
+ @compact = false
+ @logger = logger
+ end
+
+ def load(filename='context.tex',omitlist=['mult-com.tex'])
+ begin
+ @filename = filename
+ File.open(filename) do |f|
+ f.each do |line|
+ if line =~ /^\\input\s+(\S+)\s*/o then
+ @files.push($1) unless omitlist.include?(File.basename($1))
+ end
end
end
- if outputname and not outputname.empty? then
- if f = File.open(outputname,'w') then
- flushentities(f,entities)
- f.close
+ rescue
+ @files = Array.new
+ end
+ end
+
+ def analyze
+ @files.each do |filename|
+ if f = File.open(filename) then
+ @logger.report("loading #{filename}") if @logger
+ defs, uses, n = 0, 0, 0
+ f.each do |line|
+ n += 1
+ case line
+ when /^%/
+ # skip
+ when /\\newif\s*\\if([a-zA-Z@\?\!]+)/ then
+ pushdef(filename,n,"if:#{$1}")
+ when /\\([a-zA-Z@\?\!]+)(true|false)/ then
+ pushuse(filename,n,"if:#{$1}")
+ when /^\s*\\(#{@primitives_def})\\([a-zA-Z@\?\!]{3,})/o
+ pushdef(filename,n,$2)
+ when /\\([a-zA-Z@\?\!]{3,})/o
+ pushuse(filename,n,$1)
+ end
+ end
+ f.close
+ end
+ end
+ end
+
+ def feedback(compact=false)
+ begin
+ outputfile = File.basename(@filename).sub(/\.tex$/,'')+'.dep'
+ File.open(outputfile,'w') do |f|
+ @compact = compact
+ @logger.report("saving analysis in #{outputfile}") if @logger
+ list, len = @cs_local.keys.sort, 0
+ if @compact then
+ list.each do |cs|
+ if cs.length > len then len = cs.length end
+ end
+ len += 1
else
- flushentities($stdout,entities)
+ f.puts "<?xml version='1.0'?>\n"
+ f.puts "<dependencies xmlns='http://www.pragma-ade.com/schemas/texdeps.rng' rootfile='#{@filename}'>\n"
+ end
+ list.each do |cs|
+ if @cs_new.key?(cs) then
+ if @cs_new[cs] == @cs_local[cs] then
+ f.puts some_struc(cs,len,1,some_str(@cs_new,@cs_defd,cs))
+ elsif @cs_new[cs].first == @cs_local[cs].first then
+ f.puts some_struc(cs,len,2,some_str(@cs_new,@cs_defd,cs),some_str(@cs_local,@cs_used,cs))
+ else
+ f.puts some_struc(cs,len,3,some_str(@cs_new,@cs_defd,cs),some_str(@cs_local,@cs_used,cs))
+ end
+ else
+ f.puts some_struc(cs,len,4,some_str(@cs_local,@cs_used,cs))
+ end
+ end
+ if @compact then
+ # nothing
+ else
+ "</dependencies>\n" unless @compact
end
- else
- flushentities($stdout,entities)
end
+ rescue
+ end
+ end
+
+ private
+
+ def some_struc(cs,len,type=1,defstr='',usestr='')
+ if @compact then
+ "#{cs.ljust(len)} #{@@types[type][1]} #{defstr} #{usestr}"
+ else
+ "<macro name='#{cs}' type='#{type}'>\n" +
+ if defstr.empty? then " <defined/>\n" else " <defined>\n#{defstr} <\defined>\n" end +
+ if usestr.empty? then " <used/>\n" else " <used>#{usestr}\n <\used>\n" end +
+ "</macro>\n"
end
+ end
+ def some_str(files, lines, cs)
+ return '' unless files[cs]
+ if @compact then
+ str = '[ '
+ files[cs].each do |c|
+ str += c
+ str += " (#{lines[cs][c].join(' ')}) " if lines[cs][c]
+ str += ' '
+ end
+ str += ']'
+ str.gsub(/ +/, ' ')
+ else
+ str = ''
+ files[cs].each do |c|
+ if lines[cs][c] then
+ str += " <file name='#{c}'>\n"
+ str += " "
+ lines[cs][c].each do |l|
+ # str += " <line n='#{l}'/>\n"
+ str += "<line n='#{l}'/>"
+ end
+ str += "\n"
+ str += " </file>\n"
+ else
+ str += " <file name='#{c}'/>\n"
+ end
+ end
+ str
+ end
+ end
+
+ def pushdef(filename,n,cs)
+ unless @cs_new.key?(cs) then
+ @cs_new[cs] = Array.new
+ @cs_defd[cs] = Hash.new unless @cs_defd.key?(cs)
+ end
+ @cs_defd[cs][filename] = Array.new unless @cs_defd[cs][filename]
+ @cs_new[cs].push(filename) unless @cs_new[cs].include?(filename)
+ @cs_defd[cs][filename] << n
+ end
+
+ def pushuse(filename,n,cs)
+ unless @@cs_global.include?(cs.to_s) then
+ unless @cs_local[cs] then
+ @cs_local[cs] = Array.new
+ @cs_used[cs] = Hash.new unless @cs_used.key?(cs)
+ end
+ @cs_used[cs][filename] = Array.new unless @cs_used[cs][filename]
+ @cs_local[cs].push(filename) unless @cs_local[cs].include?(filename)
+ @cs_used[cs][filename] << n
+ end
end
end
+class Commands
-logger = Logger.new(banner.shift)
-commandline = CommandLine.new
+ include CommandBase
-commandline.registeraction('touchcontextfile', 'update context version')
-commandline.registeraction('contextversion', 'report context version')
+ def dependencies
-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]')
+ filename = if @commandline.arguments.empty? then 'context.tex' else @commandline.arguments.first end
+ compact = @commandline.option('compact')
-commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]')
-commandline.registeraction('purgefiles', 'remove temporary files [--all --recurse] [basename]')
+ ['progname=context',''].each do |progname|
+ unless FileTest.file?(filename) then
+ name = `kpsewhich #{progname} #{filename}`.chomp
+ if FileTest.file?(name) then
+ filename = name
+ break
+ end
+ end
+ end
-commandline.registeraction('documentation', 'generate documentation [--type=] [filename]')
+ if FileTest.file?(filename) && deps = TexDeps.new(logger) then
+ deps.load
+ deps.analyze
+ deps.feedback(compact)
+ else
+ report("unknown file #{filename}")
+ end
-commandline.registeraction('filterpages') # no help, hidden temporary feature
-commandline.registeraction('purgeallfiles') # no help, compatibility feature
+ end
-commandline.registeraction('patternfiles', 'generate pattern files [--all --xml --utf8] [languagecode]')
+end
+
+class Commands
+
+ @@re_utf_bom = /^\357\273\277/o # just utf-8
+
+ def disarmutfbom
+
+ if @commandline.arguments.empty? then
+ report("provide filename")
+ else
+ @commandline.arguments.each do |filename|
+ report("checking '#{filename}'")
+ if FileTest.file?(filename) then
+ begin
+ data = IO.read(filename)
+ if data.sub!(@@re_utf_bom,'') then
+ if @commandline.option('force') then
+ if f = File.open(filename,'wb') then
+ f << data
+ f.close
+ report("bom found and removed")
+ else
+ report("bom found and removed, but saving file fails")
+ end
+ else
+ report("bom found, use '--force' to remove it")
+ end
+ else
+ report("no bom found")
+ end
+ rescue
+ report("bom found, but removing it fails")
+ end
+ else
+ report("provide valid filename")
+ end
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def updatecontext
+
+ def fetchfile(site, name, target=nil)
+ begin
+ http = Net::HTTP.new(site)
+ resp, data = http.get(name.gsub(/^\/*/, '/'))
+ rescue
+ return false
+ else
+ begin
+ if data then
+ name = File.basename(name)
+ File.open(target || name, 'wb') do |f|
+ f << data
+ end
+ else
+ return false
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+ end
+
+ def locatedlocaltree
+ tree = `kpsewhich --expand-path $TEXMFLOCAL`.chomp rescue nil
+ unless tree && FileTest.directory?(tree) then
+ tree = `kpsewhich --expand-path $TEXMF`.chomp rescue nil
+ end
+ return tree
+ end
-commandline.registeraction('dpxmapfiles', 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]')
-commandline.registeraction('listentities', 'create doctype entity definition from enco-uc.tex')
+ def extractarchive(archive)
+ unless FileTest.file?(archive) then
+ report("fatal error, '#{archive}' has not been downloaded")
+ return false
+ end
+ unless system("unzip -uo #{archive}") then
+ report("fatal error, make sure that you have 'unzip' in your path")
+ return false
+ end
+ stubs = "scripts/context/stubs/unix/*"
+ if System.unix? and not system("chmod +x #{stubs}") then
+ report("change x-permissions of '#{stubs}' manually")
+ end
+ return true
+ end
+
+ def remakeformats
+ return system("texmfstart texexec --make --all")
+ end
+
+ if localtree = locatedlocaltree then
+ report("updating #{localtree}")
+ begin
+ Dir.chdir(localtree)
+ rescue
+ report("unable to change to #{localtree}")
+ else
+ archive = 'cont-tmf.zip'
+ report("fetching #{archive}")
+ unless fetchfile("www.pragma-ade.com","/context/latest/#{archive}") then
+ report("unable to fetch #{archive}")
+ return
+ end
+ report("extracting #{archive}")
+ unless extractarchive(archive) then
+ report("unable to extract #{archive}")
+ return
+ end
+ report("remaking formats")
+ unless remakeformats then
+ report("unable to remak formats")
+ end
+ end
+ else
+ report("unable to locate local tree")
+ end
+
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('touchcontextfile' , 'update context version')
+commandline.registeraction('contextversion' , 'report context version')
+commandline.registeraction('jeditinterface' , 'generate jedit syntax files [--pipe]')
+commandline.registeraction('bbeditinterface' , 'generate bbedit syntax files [--pipe]')
+commandline.registeraction('sciteinterface' , 'generate scite syntax files [--pipe]')
+commandline.registeraction('rawinterface' , 'generate raw syntax files [--pipe]')
+commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]')
+commandline.registeraction('purgefiles' , 'remove temporary files [--all --recurse] [basename]')
+commandline.registeraction('documentation' , 'generate documentation [--type=] [filename]')
+commandline.registeraction('filterpages' ) # no help, hidden temporary feature
+commandline.registeraction('patternfiles' , 'generate pattern files [--all --xml --utf8] [languagecode]')
+commandline.registeraction('dpxmapfiles' , 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]')
+commandline.registeraction('listentities' , 'create doctype entity definition from enco-uc.tex')
+commandline.registeraction('brandfiles' , 'add context copyright notice [--force]')
+commandline.registeraction('platformize' , 'replace line-endings [--recurse --force] [pattern]')
+commandline.registeraction('dependencies' , 'analyze depedencies witin context [--compact] [rootfile]')
+commandline.registeraction('updatecontext' , 'download latest version and remake formats')
+commandline.registeraction('disarmutfbom' , 'remove utf bom [--force]')
commandline.registervalue('type','')
@@ -1394,6 +2454,7 @@ commandline.registerflag('all')
commandline.registerflag('xml')
commandline.registerflag('log')
commandline.registerflag('utf8')
+commandline.registerflag('doctype')
# general
diff --git a/Master/texmf-dist/scripts/context/ruby/fcd_start.rb b/Master/texmf-dist/scripts/context/ruby/fcd_start.rb
index 8ac48f79e8c..348ac75ba83 100644
--- a/Master/texmf-dist/scripts/context/ruby/fcd_start.rb
+++ b/Master/texmf-dist/scripts/context/ruby/fcd_start.rb
@@ -256,6 +256,8 @@ class FastCD
@result = @list.grep(/\/#{@pattern}[^\/]*$/i)
end
end
+ else
+ puts(Dir.pwd.gsub(/\\/o, '/'))
end
rescue
end
@@ -403,31 +405,37 @@ class FastCD
end
+$stdout.sync = true
+
verbose, action, args = false, :find, Array.new
-usage = "fcd [--make|add|show|find] [--verbose] [pattern]"
+usage = "fcd [--add|clear|find|list|make|show|stub] [--verbose] [pattern]"
+version = "1.0.2"
+
+def quit(message)
+ puts(message)
+ exit
+end
ARGV.each do |a|
case a
- when '-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 '-c', '--clear' then action = :clear
when '-f', '--find' then action = :find
+ when '-l', '--list' then action = :show
+ when '-m', '--make' then action = :make
+ when '-s', '--show' then action = :show
when '--stub' then action = :stub
- when '-h', '--help' then puts "usage: #{usage}" ; exit
- when /^\-\-.*/ then puts "unknown switch: #{a}" + "\n" + "usage: #{usage}" ; exit
+ when '-v', '--verbose' then verbose = true
+ when '--version' then quit("version: #{version}")
+ when '-h', '--help' then quit("usage: #{usage}")
+ when /^\-\-.*/ then quit("error: unknown switch #{a}, try --help")
else args << a
end
end
-$stdout.sync = true
-
fcd = FastCD.new(verbose)
-
-fcd.report("Fast Change Dir / version 1.0")
+fcd.report("Fast Change Dir / version #{version}")
case action
when :make then
diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb
index 807cad10ccd..2c1079d1a6b 100644
--- a/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb
+++ b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb
@@ -13,11 +13,14 @@
require 'base/variables'
require 'base/system'
+require 'ftools'
class GhostScript
include Variables
+ @@pdftrimwhite = 'pdftrimwhite.pl'
+
@@pstopdfoptions = [
'AntiAliasColorImages',
'AntiAliasGrayImages',
@@ -213,7 +216,7 @@ class GhostScript
else report("invalid conversion method #{gsmethod}")
end
rescue
- report("job aborted due to some error #{$!}")
+ report("job aborted due to some error: #{$!}")
begin
File.delete(resultfile) if test(?e,resultfile)
rescue
@@ -338,9 +341,12 @@ class GhostScript
end
- def convertbounded (inpfile, outfile)
-
+ def convertbounded(inpfile, outfile)
report("converting #{inpfile} bounded")
+ do_convertbounded(inpfile, outfile)
+ end
+
+ def do_convertbounded(inpfile, outfile)
begin
return false if FileTest.file?(outfile) && (! File.delete(outfile))
@@ -350,7 +356,7 @@ class GhostScript
arguments = ''
arguments << "\@gsprofile.ini "
- arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER"
+ arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER "
arguments << "#{gscolorswitch} "
arguments << "#{gsdefaults} "
arguments << "-sOutputFile=#{outfile} #{gsstream} -c quit "
@@ -426,7 +432,7 @@ class GhostScript
# -dEPSFitPage and -dEPSCrop behave weird (don't work)
- arguments = "-sDEVICE=bbox -dSAFER -dNOPAUSE -dBATCH #{inpfile}"
+ arguments = "-sDEVICE=bbox -dSAFER -dNOPAUSE -dBATCH #{inpfile} "
debug("ghostscript: #{arguments}")
@@ -456,13 +462,14 @@ class GhostScript
report("converting #{inpfile} cropped")
- convertbounded(inpfile, @@pdftempfile)
+ do_convertbounded(inpfile, @@pdftempfile)
return unless test(?e,@@pdftempfile)
arguments = " --offset=#{@offset} #{@@pdftempfile} #{outfile}"
- unless ok = System.run('cropcrap',arguments) then
+ report("calling #{@@pdftrimwhite}")
+ unless ok = System.run(@@pdftrimwhite,arguments) then
report('cropping failed')
begin
File.delete(outfile)
diff --git a/Master/texmf-dist/scripts/context/ruby/imgtopdf.rb b/Master/texmf-dist/scripts/context/ruby/imgtopdf.rb
new file mode 100644
index 00000000000..98a36c17fd9
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/imgtopdf.rb
@@ -0,0 +1,86 @@
+#!/usr/bin/env ruby
+
+# program : newimgtopdf
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+
+require 'graphics/magick'
+
+banner = ['ImgToPdf', 'version 1.1.2', '2002-2006', 'PRAGMA ADE/POD']
+
+class Commands
+
+ include CommandBase
+
+ # nowadays we would force a directive, but
+ # for old times sake we handle default usage
+
+ def main
+ filename = @commandline.argument('first')
+
+ if filename.empty? then
+ help
+ else
+ convert
+ end
+ end
+
+ # actions
+
+ def convert
+
+ magick = Magick.new(session)
+
+ ['compression','depth','colorspace','quality','inputpath','outputpath'].each do |v|
+ magick.setvariable(v,@commandline.option(v))
+ end
+
+ @commandline.arguments.each do |fullname|
+ magick.setvariable('inputfile',fullname)
+ magick.setvariable('outputfile',fullname.gsub(/(\..*?$)/io, '.pdf'))
+ if @commandline.option('auto') then
+ magick.autoconvert
+ else
+ magick.convert
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('auto')
+
+commandline.registervalue('compression')
+commandline.registervalue('depth')
+commandline.registervalue('colorspace')
+commandline.registervalue('quality')
+
+commandline.registervalue('inputpath')
+commandline.registervalue('outputpath')
+
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registeraction('convert', 'convert image into pdf')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/mpstools.rb b/Master/texmf-dist/scripts/context/ruby/mpstools.rb
index 6dc0c35ad0c..534bfb95bcf 100644
--- a/Master/texmf-dist/scripts/context/ruby/mpstools.rb
+++ b/Master/texmf-dist/scripts/context/ruby/mpstools.rb
@@ -1,4 +1,6 @@
# todo
+#
+# this script will replace mptopdf and makempy
puts("This program is yet unfinished, for the moment it just calls 'mptopdf'.\n\n")
diff --git a/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb b/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb
index 3c1636cf2bc..563ae5b80a0 100644
--- a/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb
+++ b/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb
@@ -1,86 +1,8 @@
#!/usr/bin/env ruby
-# program : newimgtopdf
+# program : imgtopdf
# 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')
+load(File.join(File.expand_path(File.dirname($0)),'imgtopdf.rb'))
diff --git a/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb b/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb
index 898f987cbce..a45b4cab896 100644
--- a/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb
+++ b/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb
@@ -4,527 +4,5 @@
# 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')
+load(File.join(File.expand_path(File.dirname($0)),'pstopdf.rb'))
diff --git a/Master/texmf-dist/scripts/context/ruby/newtexexec.rb b/Master/texmf-dist/scripts/context/ruby/newtexexec.rb
index 27f8eeed96f..6b4db52dc51 100644
--- a/Master/texmf-dist/scripts/context/ruby/newtexexec.rb
+++ b/Master/texmf-dist/scripts/context/ruby/newtexexec.rb
@@ -1,609 +1,8 @@
-banner = ['TeXExec', 'version 6.1.0', '1997-2005', 'PRAGMA ADE/POD']
+#!/usr/bin/env ruby
-unless defined? ownpath
- ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
- $: << ownpath
-end
+# program : texexec
+# copyright : PRAGMA Advanced Document Engineering
+# version : 1997-2006
+# author : Hans Hagen
-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')
+load(File.join(File.expand_path(File.dirname($0)),'texexec.rb'))
diff --git a/Master/texmf-dist/scripts/context/ruby/newtexutil.rb b/Master/texmf-dist/scripts/context/ruby/newtexutil.rb
index af003a8e7b2..d6dd06a71ee 100644
--- a/Master/texmf-dist/scripts/context/ruby/newtexutil.rb
+++ b/Master/texmf-dist/scripts/context/ruby/newtexutil.rb
@@ -1,96 +1,8 @@
-banner = ['TeXUtil ', 'version 9.1.0', '1997-2005', 'PRAGMA ADE/POD']
+#!/usr/bin/env ruby
-unless defined? ownpath
- ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
- $: << ownpath
-end
+# program : texexec
+# copyright : PRAGMA Advanced Document Engineering
+# version : 1997-2006
+# author : Hans Hagen
-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')
+load(File.join(File.expand_path(File.dirname($0)),'texutil.rb'))
diff --git a/Master/texmf-dist/scripts/context/ruby/pdftools.rb b/Master/texmf-dist/scripts/context/ruby/pdftools.rb
new file mode 100644
index 00000000000..8a62a048728
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/pdftools.rb
@@ -0,0 +1,816 @@
+#!/usr/bin/env ruby
+
+# program : pdftools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2003-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on tex
+# related files.
+
+banner = ['PDFTools', 'version 1.2.1', '2003/2005', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'base/switch'
+require 'base/logger'
+
+require 'ftools'
+
+class File
+
+ def File.deletefiles(*filenames)
+ filenames.flatten.each do |filename|
+ begin
+ delete(filename) if FileTest.file?(filename)
+ rescue
+ end
+ end
+ end
+
+ def File.needsupdate(oldname,newname)
+ begin
+ return File.stat(oldname).mtime != File.stat(newname).mtime
+ rescue
+ return true
+ end
+ end
+
+ def File.syncmtimes(oldname,newname)
+ begin
+ t = Time.now # i'm not sure if the time is frozen, so we do it here
+ File.utime(0,t,oldname,newname)
+ rescue
+ end
+ end
+
+ def File.replacesuffix(oldname,subpath='')
+ newname = File.expand_path(oldname.sub(/\.\w+?$/,'.pdf'))
+ File.join(File.dirname(newname),subpath,File.basename(newname))
+ end
+
+end
+
+class ImageMagick
+
+ def initialize
+
+ begin
+ version = `convert -version`
+ rescue
+ @binary = nil
+ ensure
+ if (version) && (! version.empty?) && (version =~ /ImageMagick/mo) && (version =~ /version/mio) then
+ @binary = 'convert'
+ else
+ @binary = 'imagemagick'
+ end
+ end
+
+ end
+
+ def process(arguments)
+ begin
+ @binary && system("#{@binary} #{arguments}")
+ rescue
+ false
+ end
+ end
+
+end
+
+class TexExec
+
+ def initialize
+ @binary = 'texmfstart texexec.pl --pdf --batch --silent --purge'
+ end
+
+ def process(arguments,once=true)
+ begin
+ if once then
+ @binary && system("#{@binary} --once #{arguments}")
+ else
+ @binary && system("#{@binary} #{arguments}")
+ end
+ rescue
+ false
+ end
+ end
+
+end
+
+class PdfImages
+
+ def initialize
+ @binary = "pdfimages"
+ end
+
+ def process(arguments)
+ begin
+ @binary && system("#{@binary} #{arguments}")
+ rescue
+ false
+ end
+ end
+
+end
+
+class ConvertImage
+
+ def initialize(command=nil)
+ @command = command
+ end
+
+ def convertimage(filename)
+
+ return if filename =~ /\.(pdf)$/io
+
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ if filename =~ /\s/ then
+ @command.report("skipping strange filename '#{filename}'")
+ else
+ newname = File.replacesuffix(filename,subpath)
+ # newname.gsub!(s/[^a-zA-Z0-9\_-\.]/o, '-')
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ imagemagick = ImageMagick.new
+ if imagemagick then
+ ok = imagemagick.process("-compress zip -quality 99 #{filename} #{newname}")
+ File.syncmtimes(oldname,newname) if retain
+ end
+ end
+ end
+ end
+
+end
+
+class DownsampleImage
+
+ def initialize(command=nil)
+ @command = command
+ end
+
+ def convertimage(filename)
+
+ return if filename =~ /\.(pdf)$/io
+
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ if @command.option('lowres') then
+ method = '4'
+ elsif @command.option('medres') || @command.option('normal') then
+ method = '5'
+ else
+ method = '4'
+ end
+
+ if filename =~ /\s/ then
+ @command.report("skipping strange filename '#{filename}'")
+ else
+ newname = File.replacesuffix(filename,subpath)
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ ok = system("texmfstart pstopdf.rb --method=#{method} #{filename} #{newname}")
+ File.syncmtimes(oldname,newname) if retain
+ end
+ end
+ end
+
+end
+
+class ColorImage
+
+ def initialize(command=nil,tmpname='pdftools')
+ @command = command
+ @tmpname = tmpname
+ @colorname = nil
+ @colorspec = nil
+ @colorspace = nil
+ end
+
+ def registercolor(spec='.5',name='c')
+ name = name || 'c'
+ spec = spec.split(',')
+ case spec.length
+ when 4
+ @colorname, @colorspec, @colorspace = name, spec.join('/'), 'cmyk'
+ when 3
+ @colorname, @colorspec, @colorspace = name, spec.join('/'), 'rgb'
+ when 1
+ @colorname, @colorspec, @colorspace = name, spec.join('/'), 'gray'
+ else
+ @colorname, @colorspec, @colorspace = nil, nil, nil
+ end
+ end
+
+ def convertimage(filename)
+
+ invert = @command.option('invert')
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ subpath += '/' unless subpath.empty?
+
+ if @colorname && ! @colorname.empty? && @colorspec && ! @colorspec.empty? then
+ basename = filename.sub(/\.\w+?$/,'')
+ oldname = filename
+ ppmname = @tmpname + '-000.ppm'
+ jpgname = @tmpname + '-000.jpg'
+ newname = File.expand_path(oldname)
+ newname = File.dirname(newname) + '/' + subpath + @colorname + '-' + File.basename(newname)
+ newname.sub!(/\.\w+?$/, '.pdf')
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ pdfimages = PdfImages.new
+ imagemagick = ImageMagick.new
+ if pdfimages && imagemagick then
+ File.deletefiles(ppmname,jpgname,newname)
+ if filename =~ /\.(pdf)$/io then
+ ok = pdfimages.process("-j -f 1 -l 1 #{filename} #{@tmpname}")
+ if ok then
+ if FileTest.file?(ppmname) then
+ inpname = ppmname
+ elsif FileTest.file?(jpgname) then
+ inpname = jpgname
+ else
+ ok = false
+ end
+ if ok then
+ switch = if ! invert then '-negate' else '' end
+ # make sure that we keep the format
+ tmpname = File.basename(inpname)
+ tmpname = tmpname.sub(/(.*)\..*?$/,@tmpname) # somehow sub! fails here
+ ok = imagemagick.process("-colorspace gray #{switch} #{inpname} #{tmpname}")
+ if ! ok || ! FileTest.file?(tmpname) then
+ # problems
+ else
+ ok = imagemagick.process("-colorspace #{switch} #{@colorspace} -colorize #{@colorspec} -compress zip #{tmpname} #{newname}")
+ if ! ok || ! FileTest.file?(newname) then
+ # unable to colorize image
+ else
+ # conversion done
+ end
+ end
+ end
+ end
+ else
+ # make sure that we keep the format
+ tmpname = File.basename(basename)
+ tmpname = tmpname.sub(/(.*)\..*?$/,@tmpname) # somehow sub! fails here
+ ok = imagemagick.process("-colorspace gray #{oldname} #{tmpname}")
+ if ! ok || ! FileTest.file?(tmpname) then
+ # unable to convert color to gray
+ else
+ ok = imagemagick.process("-colorspace #{@colorspace} -colorize #{@colorspec} -compress zip #{tmpname} #{newname}")
+ if ! ok || ! FileTest.file?(newname) then
+ # unable to colorize image
+ else
+ # conversion done
+ end
+ end
+ end
+ File.deletefiles(ppmname,jpgname,tmpname)
+ File.syncmtimes(filename,newname) if retain
+ end
+ end
+ end
+ end
+
+end
+
+class SpotColorImage
+
+ def initialize(command=nil, tmpname='pdftools')
+ @command = command
+ @tmpname = tmpname
+ @colorname = nil
+ @colorspec = nil
+ @colorspace = nil
+ @colorfile = nil
+ end
+
+ def registercolor(spec='.5',name='unknown')
+ name = name || 'unknown'
+ if spec =~ /^[\d\.\,]+$/ then
+ spec = spec.split(',')
+ case spec.length
+ when 4
+ @colorname, @colorspec, @colorspace = name, ["c=#{spec[0]}","m=#{spec[1]}","y=#{spec[2]}","k=#{spec[3]}"].join(','), 'cmyk'
+ when 3
+ @colorname, @colorspec, @colorspace = name, ["r=#{spec[0]}","g=#{spec[1]}","b=#{spec[2]}"].join(','), 'rgb'
+ when 1
+ @colorname, @colorspec, @colorspace = name, ["s=#{spec[0]}"].join(','), 'gray'
+ else
+ @colorname, @colorspec, @colorspace = nil, nil, nil
+ end
+ else
+ @colorname, @colorfile = name, spec
+ end
+ end
+
+ def convertgrayimage(filename)
+
+ invert = @command.option('invert')
+ retain = @command.option('retain')
+ subpath = @command.option('subpath')
+
+ subpath += '/' unless subpath.empty?
+
+ if @colorname && ! @colorname.empty? && ((@colorspec && ! @colorspec.empty?) || (@colorfile && ! @colorfile.empty?)) then
+ basename = filename.sub(/\.\w+?$/,'')
+ oldname = filename # png jpg pdf
+ newname = File.expand_path(oldname)
+ ppmname = @tmpname + '-000.ppm'
+ jpgname = @tmpname + '-000.jpg'
+ outname = @tmpname + '-000.pdf'
+ texname = @tmpname + '-temp.tex'
+ pdfname = @tmpname + '-temp.pdf'
+ newname = File.dirname(newname) + '/' + subpath + @colorname + '-' + File.basename(newname)
+ newname.sub!(/\.\w+?$/, '.pdf')
+ begin
+ File.makedirs(File.dirname(newname))
+ rescue
+ end
+ if ! retain || File.needsupdate(filename,newname) then
+ pdfimages = PdfImages.new
+ imagemagick = ImageMagick.new
+ texexec = TexExec.new
+ if pdfimages && imagemagick && texexec then
+ if filename =~ /\.(jpg|png|pdf)$/io then
+ @command.report("processing #{basename}")
+ File.deletefiles(ppmname,jpgname,newname)
+ switch = if ! invert then '-negate' else '' end
+ if filename =~ /\.(pdf)$/io then
+ ok = pdfimages.process("-j -f 1 -l 1 #{oldname} #{@tmpname}")
+ if ok then
+ if FileTest.file?(ppmname) then
+ inpname = ppmname
+ elsif FileTest.file?(jpgname) then
+ inpname = jpgname
+ else
+ ok = false
+ end
+ if ok then
+ ok = imagemagick.process("-colorspace gray #{switch} -compress zip #{inpname} #{outname}")
+ end
+ end
+ else
+ ok = imagemagick.process("-colorspace gray #{switch} -compress zip #{oldname} #{outname}")
+ end
+ if ok then
+ ok = false unless FileTest.file?(outname)
+ end
+ if ok then
+ if f = File.open(texname, 'w') then
+ f.puts(conversionfile(filename,outname,newname))
+ f.close
+ ok = texexec.process(texname)
+ else
+ ok = false
+ end
+ @command.report("error in processing #{newname}") unless ok
+ if FileTest.file?(pdfname) then
+ if f = File.open(pdfname,'r') then
+ f.binmode
+ begin
+ if g = File.open(newname,'w') then
+ g.binmode
+ data = f.read
+ # pdftex (direct) & imagemagick (indirect)
+ if data =~ /(\d+)\s+0\s+obj\s+\[\/Separation\s+\/#{@colorname}/mos then
+ @command.report("replacing separation color")
+ object = $1
+ data.gsub!(/(\/Type\s+\/XObject.*?)(\/ColorSpace\s*(\/DeviceGray|\/DeviceCMYK|\/DeviceRGB|\d+\s+\d+\s+R))/mois) do
+ $1 + "/ColorSpace #{object} 0 R".ljust($2.length)
+ end
+ elsif data =~ /(\d+)\s+0\s+obj\s+\[\/Indexed\s*\[/mos then
+ @command.report("replacing indexed color")
+ # todo: more precise check on color
+ object = $1
+ data.gsub!(/(\/Type\s+\/XObject.*?)(\/ColorSpace\s*(\/DeviceGray|\/DeviceCMYK|\/DeviceRGB|\d+\s+\d+\s+R))/mois) do
+ $1 + "/ColorSpace #{object} 0 R".ljust($2.length)
+ end
+ elsif data =~ /(\d+)\s+0\s+obj\s+\[\/Separation/mos then
+ @command.report("replacing separation color")
+ object = $1
+ data.gsub!(/(\/Type\s+\/XObject.*?)(\/ColorSpace\s*(\/DeviceGray|\/DeviceCMYK|\/DeviceRGB|\d+\s+\d+\s+R))/mois) do
+ $1 + "/ColorSpace #{object} 0 R".ljust($2.length)
+ end
+ end
+ g.write(data)
+ g.close
+ end
+ rescue
+ @command.report("error in converting #{newname}")
+ else
+ @command.report("#{newname} is converted")
+ end
+ f.close
+ end
+ else
+ @command.report("error in writing #{newname}")
+ end
+ else
+ @command.report("error in producing #{newname}")
+ end
+ File.deletefiles(ppmname,jpgname,outname)
+ # File.deletefiles(texname,pdfname)
+ File.syncmtimes(filename,newname) if retain
+ end
+ else
+ @command.report("error in locating binaries")
+ end
+ else
+ @command.report("#{newname} is not changed")
+ end
+ end
+ end
+
+ private
+
+ # % example colorfile:
+ #
+ # \definecolor [darkblue] [c=1,m=.38,y=0,k=.64] % pantone pms 2965 uncoated m
+ # \definecolor [darkyellow] [c=0,m=.28,y=1,k=.06] % pantone pms 124 uncoated m
+ #
+ # % \definecolor [darkblue-100] [darkblue] [p=1]
+ # % \definecolor [darkyellow-100] [darkyellow] [p=1]
+ #
+ # \definecolorcombination [pdftoolscolor] [darkblue=.12,darkyellow=.28] [c=.1,m=.1,y=.3,k=.1]
+
+ def conversionfile(originalname,filename,finalname)
+ tex = "\\setupcolors[state=start]\n"
+ if @colorfile then
+ tex += "\\readfile{#{@colorfile}}{}{}\n"
+ tex += "\\starttext\n"
+ # tex += "\\predefineindexcolor[pdftoolscolor]\n"
+ tex += "\\startTEXpage\n"
+ tex += "\\pdfimage{#{filename}}\n"
+ tex += "\\stopTEXpage\n"
+ tex += "\\stoptext\n"
+ else
+ tex += "\\definecolor[#{@colorname}][#{@colorspec}]\n"
+ tex += "\\definecolor[pdftoolscolor][#{@colorname}][p=1]\n"
+ tex += "\\starttext\n"
+ tex += "\\startTEXpage\n"
+ tex += "\\hbox{\\color[pdftoolscolor]{\\pdfimage{#{filename}}}}\n"
+ tex += "\\stopTEXpage\n"
+ tex += "\\stoptext\n"
+ end
+ tex += "\n"
+ tex += "% old: #{originalname}\n"
+ tex += "% new: #{finalname}\n"
+ return tex
+ end
+
+end
+
+module XML
+
+ def XML::version
+ "<?xml version='1.0'?>"
+ end
+
+ def XML::start(element, attributes='')
+ if attributes.empty? then
+ "<#{element}>"
+ else
+ "<#{element} #{attributes}>"
+ end
+ end
+
+ def XML::end(element)
+ "</#{element}>"
+ end
+
+ def XML::empty(element, attributes='')
+ if attributes && attributes.empty? then
+ "<#{element}/>"
+ else
+ "<#{element} #{attributes}/>"
+ end
+ end
+
+ def XML::element(element, attributes='', content='')
+ if content && ! content.empty? then
+ XML::start(element,attributes) + content + XML::end(element)
+ else
+ XML::empty(element,attributes)
+ end
+ end
+
+ def XML::box(tag, rect, type=1)
+ case type
+ when 1
+ if rect && ! rect.empty? then
+ rect = rect.split(' ')
+ XML::element("#{tag}box", '',
+ XML::element("llx", '', rect[0]) +
+ XML::element("lly", '', rect[1]) +
+ XML::element("ulx", '', rect[2]) +
+ XML::element("uly", '', rect[3]) )
+ else
+ XML::empty("#{tag}box")
+ end
+ when 2
+ if rect && ! rect.empty? then
+ rect = rect.split(' ')
+ XML::element("box", "type='#{tag}'",
+ XML::element("llx", '', rect[0]) +
+ XML::element("lly", '', rect[1]) +
+ XML::element("ulx", '', rect[2]) +
+ XML::element("uly", '', rect[3]) )
+ else
+ XML::empty("box", "type='#{tag}'")
+ end
+ when 3
+ if rect && ! rect.empty? then
+ rect = rect.split(' ')
+ XML::element("box", "type='#{tag}' llx='#{rect[0]}' lly='#{rect[1]}' ulx='#{rect[2]}' uly='#{rect[3]}'")
+ else
+ XML::empty("box", "type='#{tag}'")
+ end
+ else
+ ''
+ end
+ end
+
+ def XML::crlf
+ "\n"
+ end
+
+ def XML::skip(n=1)
+ ' '*n
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ # alias savedhelp :help
+
+ # def help
+ # savedhelp
+ # report("under construction (still separate tools)")
+ # end
+
+ # filename.pdf --spotimage --colorname=darkblue --colorspec=1,0.38,0,0.64
+
+ def spotimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ colorname = @commandline.option('colorname')
+ colorspec = @commandline.option('colorspec')
+ if colorname && ! colorname.empty? && colorspec && ! colorspec.empty? then
+ files.each do |filename|
+ s = SpotColorImage.new(self)
+ s.registercolor(colorspec,colorname)
+ s.convertgrayimage(filename)
+ end
+ else
+ report("provide --colorname=somename --colorspec=c,m,y,k")
+ end
+ else
+ report("provide filename (png, jpg, pdf)")
+ end
+
+ end
+
+ def colorimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ colorname = @commandline.option('colorname')
+ colorspec = @commandline.option('colorspec')
+ if colorspec && ! colorspec.empty? then
+ files.each do |filename|
+ s = ColorImage.new(self)
+ s.registercolor(colorspec,colorname) # name optional
+ s.convertimage(filename)
+ end
+ else
+ report("provide --colorspec=c,m,y,k")
+ end
+ else
+ report("provide filename")
+ end
+
+ end
+
+ def convertimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ files.each do |filename|
+ s = ConvertImage.new(self)
+ s.convertimage(filename)
+ end
+ else
+ report("provide filename")
+ end
+
+ end
+
+ def downsampleimage
+
+ if ! @commandline.argument('first').empty? && files = findfiles() then
+ files.each do |filename|
+ s = DownsampleImage.new(self)
+ s.convertimage(filename)
+ end
+ else
+ report("provide filename")
+ end
+
+ end
+
+ def info
+
+ if files = findfiles() then
+
+ print(XML.version + XML.crlf)
+ print(XML.start('pdfinfo', "xmlns='http://www.pragma-ade.com/schemas/pdfinfo.rng'") + XML.crlf)
+
+ files.each do |filename|
+
+ if filename =~ /\.pdf$/io then
+
+ begin
+ data = `pdfinfo -box #{filename}`.chomp.split("\n")
+ rescue
+ data = nil
+ end
+
+ if data then
+
+ pairs = Hash.new
+
+ data.each do |d|
+ if (d =~ /^\s*(.*?)\s*\:\s*(.*?)\s*$/mois) then
+ key, val = $1, $2
+ pairs[key.downcase.sub(/ /,'')] = val
+ end
+ end
+
+ print(XML.skip(1) + XML.start('pdffile', "filename='#{filename}'") + XML.crlf)
+
+ print(XML.skip(2) + XML.element('path', '', File.expand_path(filename)) + XML.crlf)
+
+ if pairs.key?('error') then
+
+ print(XML.skip(2) + XML.element('comment', '', pairs['error']) + XML.crlf)
+
+ else
+
+ print(XML.skip(2) + XML.element('version', '', pairs['pdfversion']) + XML.crlf)
+ print(XML.skip(2) + XML.element('pages', '', pairs['pages' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('title', '', pairs['title' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('subject', '', pairs['subject' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('author', '', pairs['author' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('producer', '', pairs['producer' ]) + XML.crlf)
+
+ if pairs.key?('creationdate') then
+ pairs['creationdate'].sub!(/(\d\d)\/(\d\d)\/(\d\d)/) do
+ '20' + $3 + '-' + $1 + '-' +$2
+ end
+ pairs['creationdate'].sub!(/(\d\d)\/(\d\d)\/(\d\d\d\d)/) do
+ $3 + '-' + $1 + '-' + $2
+ end
+ print(XML.skip(2) + XML.element('creationdate', '', pairs['creationdate']) + XML.crlf)
+ end
+
+ if pairs.key?('moddate') then
+ if pairs['moddate'] =~ /(\d\d\d\d)(\d\d)(\d\d)/ then
+ pairs['moddate'] = "#{$1}-#{$2}-#{$3}"
+ end
+ print(XML.skip(2) + XML.element('modificationdate', '', pairs['moddate']) + XML.crlf)
+ end
+
+ print(XML.skip(2) + XML.element('tagged', '', pairs['tagged' ]) + XML.crlf)
+ print(XML.skip(2) + XML.element('encrypted', '', pairs['encrypted']) + XML.crlf)
+ print(XML.skip(2) + XML.element('optimized', '', pairs['optimized']) + XML.crlf)
+
+ if pairs.key?('PageSize') then
+ print(XML.skip(2) + XML.element('width', '', pairs['pagesize'].sub(/\s*(.*?)\s+(.*?)\s+.*/, $1)) + XML.crlf)
+ print(XML.skip(2) + XML.element('height', '', pairs['pagesize'].sub(/\s*(.*?)\s+(.*?)\s+.*/, $2)) + XML.crlf)
+ end
+
+ if pairs.key?('FileSize') then
+ print(XML.skip(2) + XML.element('size', '', pairs['filesize'].sub(/\s*(.*?)\s+.*/, $1)) + XML.crlf)
+ end
+
+ print(XML.skip(2) + XML.box('media', pairs['mediabox']) + XML.crlf)
+ print(XML.skip(2) + XML.box('crop' , pairs['cropbox' ]) + XML.crlf)
+ print(XML.skip(2) + XML.box('bleed', pairs['bleedbox']) + XML.crlf)
+ print(XML.skip(2) + XML.box('trim' , pairs['trimBox' ]) + XML.crlf)
+ print(XML.skip(2) + XML.box('art' , pairs['artbox' ]) + XML.crlf)
+
+ end
+
+ print(XML.skip(1) + XML.end('pdffile') + XML.crlf)
+
+ end
+
+ end
+
+ end
+
+ print(XML.end('pdfinfo') + XML.crlf)
+
+ end
+
+ end
+
+ def countpages
+ filenames, n = findfiles('pdf'), 0
+ filenames.each do |filename|
+ if `pdfinfo #{filename}`.chomp =~ /^pages\s*\:\s*(\d+)/mois then
+ report("#{$1.rjust(4)} pages found in #{filename}")
+ n += $1.to_i
+ end
+ end
+ report("#{n.to_s.rjust(4)} pages in total")
+ end
+
+ def analyzefile
+ # needs an update
+ filenames = @commandline.arguments
+ filenames.each do |filename|
+ if filename && FileTest.file?(filename) && filename =~ /\.pdf/io then
+ filesize = FileTest.size(filename)
+ report("analyzing file : #{filename}")
+ report("file size : #{filesize}")
+ if pdf = File.open(filename) then
+ pdf.binmode
+ nofobject, nofxform, nofannot, noflink, nofwidget, nofnamed, nofscript, nofcross = 0, 0, 0, 0, 0, 0, 0, 0
+ while data = pdf.gets do
+ data.scan(/\d+\s+\d+\s+obj/o) do nofobject += 1 end
+ data.scan(/\/Type\s*\/XObject/o) do nofxform += 1 end
+ data.scan(/\/Type\s*\/Annot/o) do nofannot += 1 end
+ data.scan(/\/GoToR\s*\/F/o) do nofcross += 1 end
+ data.scan(/\/Subtype\s*\/Link/o) do noflink += 1 end
+ data.scan(/\/Subtype\s*\/Widget/o) do nofwidget += 1 end
+ data.scan(/\/S\s*\/Named/o) do nofnamed += 1 end
+ data.scan(/\/S\s*\/JavaScript/o) do nofscript += 1 end
+ end
+ pdf.close
+ report("objects : #{nofobject}")
+ report("xforms : #{nofxform}")
+ report("annotations : #{nofannot}")
+ report("links : #{noflink} (#{nofnamed} named / #{nofscript} scripts / #{nofcross} files)")
+ report("widgets : #{nofwidget}")
+ end
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('spotimage' , 'filename --colorspec= --colorname= [--retain --invert --subpath=]')
+commandline.registeraction('colorimage', 'filename --colorspec= [--retain --invert --colorname= ]')
+commandline.registeraction('convertimage', 'filename [--retain --subpath]')
+commandline.registeraction('downsampleimage', 'filename [--retain --subpath --lowres --normal]')
+commandline.registeraction('info', 'filename')
+commandline.registeraction('countpages', 'pattern')
+
+commandline.registeraction('analyzefile' , 'filename')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registervalue('colorname')
+commandline.registervalue('colorspec')
+commandline.registervalue('subpath')
+
+commandline.registerflag('lowres')
+commandline.registerflag('medres')
+commandline.registerflag('normal')
+commandline.registerflag('invert')
+commandline.registerflag('retain')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')
diff --git a/Master/texmf-dist/scripts/context/ruby/pstopdf.rb b/Master/texmf-dist/scripts/context/ruby/pstopdf.rb
new file mode 100644
index 00000000000..197f83961ff
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/pstopdf.rb
@@ -0,0 +1,527 @@
+#!/usr/bin/env ruby
+
+# program : pstopdf
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2002-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+banner = ['PsToPdf', 'version 2.0.1', '2002-2006', 'PRAGMA ADE/POD']
+
+$: << File.expand_path(File.dirname($0))
+
+# 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/rlxtools.rb b/Master/texmf-dist/scripts/context/ruby/rlxtools.rb
index 7962474eb10..1225dedb301 100644
--- a/Master/texmf-dist/scripts/context/ruby/rlxtools.rb
+++ b/Master/texmf-dist/scripts/context/ruby/rlxtools.rb
@@ -97,20 +97,20 @@ class Commands
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 suffix = variables['suffix'].downcase 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}'"
+ pattern = "@name='#{conversion}' and @suffix='#{suffix.downcase}'"
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])
+ substititute(value,variables[name.to_s] || '')
end
end
end
@@ -176,7 +176,7 @@ class Commands
end
end
rescue
- report('error in manipulating files')
+ report("error in manipulating files: #{$!}")
end
begin
logname = "#{filename}.log"
@@ -202,7 +202,7 @@ class Commands
return str.strip
end
- def substititute(value,str)
+ def substititute(value,str='')
if str then
begin
if value.attributes.key?('method') then
@@ -247,10 +247,83 @@ class Commands
end
+class Commands
+
+ include CommandBase
+
+ def identify
+ @commandline.arguments.each do |filename|
+ if state = do_identify(filename) then
+ begin
+ File.open(filename+'.rli','w') do |f|
+ f << state
+ end
+ rescue
+ report("error in identifying #{filename}")
+ else
+ report("#{filename} is identified")
+ end
+ end
+ end
+ end
+
+ private
+
+ def do_identify(filename)
+ begin
+ str = nil
+ if FileTest.file?(filename) then
+ result = `identify -format \"x=%x,y=%y,w=%w,h=%h,b=%b\" #{filename}`.chomp.split(',')
+ tags = Hash.new
+ result.each do |r|
+ if rr = r.split("=") then
+ tags[rr[0]] = rr[1]
+ end
+ end
+ size = (tags['b']||0).to_i
+ width = unified(tags['w']||0,tags['x']||'1')
+ height = unified(tags['h']||0,tags['y']||'1')
+ if size > 0 then
+ str = ''
+ str << "<?xml version='1.0' standalone='yes'?>\n"
+ str << "<rl:identify name='#{File.basename(filename)}'>\n"
+ str << " <rl:size>#{size}</rl:size>\n"
+ str << " <rl:path>#{File.dirname(filename).sub(/\\/o,'/')}</rl:path>\n"
+ str << " <rl:width>#{width}</rl:width>\n"
+ str << " <rl:height>#{height}</rl:height>\n"
+ str << "</rl:identify>\n"
+ end
+ else
+ str = nil
+ end
+ rescue
+ str = nil
+ end
+ return str
+ end
+
+ def unified(dim,res)
+ case res
+ when /([\d\.]+)\s*PixelsPerInch/io then
+ sprintf("%.4fin",dim.to_f/$1.to_f)
+ when /([\d\.]+)\s*PixelsPerCentimeter/io then
+ sprintf("%.4fcm",dim.to_f/$1.to_f)
+ when /([\d\.]+)\s*PixelsPerMillimeter/io then
+ sprintf("%.4fmm",dim.to_f/$1.to_f)
+ when /([\d\.]+)\s*PixelsPerPoint/io then
+ sprintf("%.4fbp",dim.to_f/$1.to_f)
+ else
+ sprintf("%.4fbp",dim.to_f)
+ end
+ end
+
+end
+
logger = Logger.new(banner.shift)
commandline = CommandLine.new
commandline.registeraction('manipulate', ' [--test] manipulatorfile resourselog')
+commandline.registeraction('identify', 'filename')
commandline.registeraction('help')
commandline.registeraction('version')
diff --git a/Master/texmf-dist/scripts/context/ruby/rscortool.rb b/Master/texmf-dist/scripts/context/ruby/rscortool.rb
new file mode 100644
index 00000000000..c656fed8547
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/rscortool.rb
@@ -0,0 +1,63 @@
+# program : rscortool
+# copyright : PRAGMA Publishing On Demand
+# version : 1.00 - 2002
+# author : Hans Hagen
+#
+# project : eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+require 'rexml/document.rb'
+
+class Array
+
+ def downcase
+ self.collect { |l| l.to_s.downcase }
+ end
+
+end
+
+class SortedXML
+
+ def initialize (filename)
+ return nil if not filename or filename.empty? or not test(?e,filename)
+ @data = REXML::Document.new(File.new(filename),
+ {:ignore_whitespace_nodes => :all,
+ :compress_whitespace => :all})
+ end
+
+ def save (filename)
+ # filename += '.xml' unless filename.match(/\..*?$/)
+ filename += '.xml' unless filename =~ /\..*?$/
+ if not filename.empty? and f = open(filename,'w')
+ @data.write(f,0)
+ f.close
+ end
+ end
+
+ def sort
+ keys = REXML::XPath.match(@data.root,"/contacts/contact/@label")
+ return unless keys
+ keys = keys.downcase
+ records = @data.elements.to_a("/contacts/contact")
+ @data.elements.delete_all("/contacts/contact")
+ keys = keys.collect do |l| # prepare numbers
+ l.gsub(/(\d+)/) do |d| sprintf('%05d', d) end
+ end
+ keys.sort.each do |s|
+ @data.root.add_element(records[keys.index(s)])
+ end
+ end
+
+end
+
+def sortfile (filename)
+ c = SortedXML.new(filename)
+ c.sort
+ c.save('test.xml')
+end
+
+exit if ARGV[0] == nil or ARGV[0].empty?
+
+sortfile(ARGV[0])
diff --git a/Master/texmf-dist/scripts/context/ruby/rsfiltool.rb b/Master/texmf-dist/scripts/context/ruby/rsfiltool.rb
new file mode 100644
index 00000000000..f3abfdfc703
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/rsfiltool.rb
@@ -0,0 +1,340 @@
+# program : rsfiltool
+# copyright : PRAGMA Publishing On Demand
+# version : 1.01 - 2002
+# author : Hans Hagen
+#
+# project : eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+# --name=a,b,c.xml wordt names [a.xml, b.xml, c.xml]
+# --path=x/y/z/a,b,c.xml wordt [x/y/z/a.xml, x/y/z/b.xml, x/y/z/c.xml]
+
+# todo : split session stuff from xmpl/base into an xmpl/session module and "include xmpl/session" into base and here and ...
+
+require 'ftools'
+require 'xmpl/base'
+require 'xmpl/switch'
+require 'xmpl/request'
+
+session = Example.new('rsfiltool', '1.01', 'PRAGMA POD')
+
+filterprefix = 'rsfil-'
+
+commandline = CommandLine.new
+
+commandline.registerflag('submit')
+commandline.registerflag('fetch')
+commandline.registerflag('report')
+#commandline.registerflag('split')
+commandline.registerflag('stamp')
+commandline.registerflag('silent')
+commandline.registerflag('request')
+commandline.registerflag('nobackup')
+
+commandline.registervalue('filter')
+
+commandline.registervalue('root')
+commandline.registervalue('path')
+commandline.registervalue('name')
+
+commandline.expand
+
+session.set('log.silent',true) if commandline.option('silent')
+
+session.inherit(commandline)
+
+session.identify
+
+# session.exit unless session.loadenvironment
+
+def prepare (session)
+
+ # Normally the system provides the file, but a user can provide the rest; in
+ # order to prevent problems with keying in names, we force lowercase names.
+
+ session.set('option.file',session.get('argument.first')) if session.get('option.file').empty?
+
+ root = session.get('option.root').downcase
+ path = session.get('option.path').downcase
+ name = session.get('option.name').downcase
+ file = session.get('option.file').downcase
+
+ session.error('provide file') if file.empty?
+ session.error('provide root') if root.empty?
+
+ filter = session.get('option.filter').downcase
+ trash = session.get('option.trash').downcase
+
+ trash = '' unless FileTest.directory?(trash)
+
+ if not filter.empty? then
+ begin
+ require filter
+ rescue Exception
+ begin
+ require filterprefix + filter
+ rescue Exception
+ session.error('invalid filter')
+ end
+ end
+ begin
+ if RSFIL::valid?(file) then
+ split = RSFIL::split(file,name)
+ path = if split[0].downcase then split[0] else '' end
+ file = if split[1].downcase then split[1] else '' end
+ name = if split[2].downcase then split[2] else '' end
+ session.report('split result',split.inspect)
+ session.error('unable to split off path') if path.empty?
+ session.error('unable to split off file') if file.empty?
+ session.error('unable to split off name') if name.empty?
+ session.set('option.path',path) if path
+ session.set('option.file',file) if file
+ session.set('option.name',name) if name
+ else
+ session.error('invalid filename', file)
+ unless trash.empty? then
+ File.copy(file,trash + '/' + file)
+ end
+ end
+ rescue
+ session.error('unable to split',file,'with filter',filter)
+ end
+ end
+
+ session.error('provide path') if path.empty?
+
+ session.error('invalid root') unless test(?d,root)
+
+ exit if session.error?
+
+ session.set('fb.filename',file)
+
+ path.gsub!(/\\/o, '/')
+ path.gsub!(/\s/o, '')
+
+ path = root + '/' + path
+
+ # multiple paths
+
+ if path =~ /^(.*)\/(.*?)$/o then
+ prepath = $1
+ postpath = $2
+ paths = postpath.split(/\,/)
+ paths.collect! do |p|
+ prepath + '/' + p
+ end
+ else
+ paths = Array.new
+ paths.push(path)
+ end
+
+ paths.collect! do |p|
+ p.gsub(/[^a-zA-Z0-9\s\-\_\/\.\:]/o, '-')
+ end
+
+ file.gsub!(/\\/o, '/')
+ file.gsub!(/[^a-zA-Z0-9\s\-\_\/\.\:]/o, '-')
+
+# if session.get('option.split')
+# if file =~ /(.*)\.(.*?)$/o
+# path = path + '/' + $1
+# else
+# session.error('nothing to split in filename')
+# end
+# end
+
+ paths.each do |p|
+ begin
+ session.report('creating path', p)
+ File.makedirs(p)
+ rescue
+ session.error('unable to create path', p)
+ end
+ end
+
+ name.gsub!(/\s+/,'')
+
+ # can be a,b,c.exa.saved => a.exa.saved,b.exa.saved,c.exa.saved
+
+ if name =~ /(.*?)\.(.*)$/
+ name = $1
+ suffix = $2
+ names = name.split(/\,/)
+ names.collect! do |n|
+ n + '.' + suffix
+ end
+ name = names.join(',')
+ else
+ names = name.split(/\,/)
+ end
+
+ session.set('fb.path',path)
+ session.set('fb.paths',paths)
+ session.set('fb.name',name)
+ session.set('fb.names',names)
+
+end
+
+def thefullname(path,file,name='')
+
+ filename = file.gsub(/.*?\//, '')
+
+ if name.empty?
+ path + '/' + filename
+ else
+ unless name =~ /\..+$/o # unless name.match(/\..+$/o)
+ if filename =~ /(\..+)$/o # if file.match(/(\..+)$/o)
+ name = name + $1
+ end
+ end
+ path + '/' + name
+ end
+
+end
+
+def submitfile (session)
+
+ filename = session.get('fb.filename')
+ paths = session.get('fb.paths')
+ names = session.get('fb.names')
+
+ paths.each do |path|
+ session.report('submitting path',path)
+ names.each do |name|
+ session.report('submitting file',filename,'to',name)
+ submit(session,path,filename,name)
+ end
+ end
+
+end
+
+def submitlist (session)
+
+ requestname = session.get('fb.filename')
+ paths = session.get('fb.paths')
+
+ if test(?e,requestname)
+ session.report('loading request file', requestname)
+ if request = ExaRequest.new(requestname)
+ filelist = request.files
+ if filelist && (filelist.size > 0)
+ filelist.each do |filename|
+ paths.each do |path|
+ session.report('submitting file from list', filename)
+ submit(session,path,filename,request.naturalname(filename))
+ end
+ end
+ else
+ session.warning('no filelist in', requestname)
+ end
+ else
+ session.warning('unable to load', requestname)
+ end
+ else
+ session.warning('no file', requestname)
+ end
+
+end
+
+def submit (session, path, filename, newname)
+
+ fullname = thefullname(path,newname)
+
+ unless test(?e,filename)
+ session.warning('no file to submit', filename)
+ return
+ end
+
+ begin
+ File.copy(fullname,fullname+'.old') if ! session.get('nobackup') && test(?e,fullname)
+ if test(?e,filename)
+ File.copy(filename,fullname)
+ session.report('submit', filename, 'in', fullname)
+ if session.get('option.stamp')
+ f = open(fullname+'.tim','w')
+ f.puts(Time.now.gmtime.strftime("%a %b %d %H:%M:%S %Y"))
+ f.close
+ end
+ else
+ session.error('unable to locate', filename)
+ end
+ rescue
+ session.error('unable to move', filename, 'to', fullname)
+ end
+
+end
+
+def fetch (session)
+
+ filename = session.get('fb.filename')
+ paths = session.get('fb.paths')
+ name = session.get('fb.name')
+
+ begin
+ File.copy(filename,filename+'.old') if ! session.get('nobackup') && test(?e,filename)
+ paths.each do |path|
+ # fullname = thefullname(path,request.naturalname(filename))
+ # fullname = thefullname(path,filename)
+ fullname = thefullname(path,name)
+ if test(?e,fullname)
+ File.copy(fullname,filename)
+ session.report('fetch', filename, 'from', fullname)
+ return
+ else
+ session.report('file',fullname, 'is not present')
+ end
+ end
+ rescue
+ session.error('unable to fetch file from path')
+ end
+ session.error('no file',filename, 'fetched') unless test(?e,filename)
+
+end
+
+def report (session)
+
+ filename = session.get('fb.filename')
+ paths = session.get('fb.paths')
+
+ paths.each do |path|
+ fullname = thefullname(path,request.naturalname(filename))
+ if test(?e,fullname)
+ begin
+ session.report('file', fullname)
+ session.report('size', test(?s,fullname))
+ if test(?e,fullname+'.tim')
+ str = IO.readlines(fullname+'.tim')
+ # str = IO.read(fullname+'.tim')
+ session.report('time', str)
+ end
+ rescue
+ session.error('unable to report about', fullname)
+ end
+ end
+ end
+
+end
+
+if session.get('option.submit')
+ prepare(session)
+ if session.get('option.request')
+ submitlist(session)
+ else
+ submitfile(session)
+ end
+elsif session.get('option.fetch')
+ prepare(session)
+ fetch(session)
+elsif session.get('option.report')
+ prepare(session)
+ report(session)
+else
+ session.report('provide action')
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/rslibtool.rb b/Master/texmf-dist/scripts/context/ruby/rslibtool.rb
new file mode 100644
index 00000000000..7ae5efb647e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/rslibtool.rb
@@ -0,0 +1,114 @@
+# program : rslibtool
+# copyright : PRAGMA Publishing On Demand
+# version : 1.00 - 2002
+# author : Hans Hagen
+#
+# project : eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-pod.com / www.pragma-ade.com
+
+# --add --base=filename --path=directory pattern
+# --remove --base=filename --path=directory label
+# --sort --base=filename --path=directory
+# --purge --base=filename --path=directory
+# --dummy --base=filename
+# --namespace
+
+# rewrite
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'rslb/base'
+require 'xmpl/base'
+require 'xmpl/switch'
+
+session = Example.new('rslbtool', '1.0', 'PRAGMA POD')
+
+session.identify
+
+commandline = CommandLine.new
+
+commandline.registerflag('add')
+commandline.registerflag('remove')
+commandline.registerflag('delete')
+commandline.registerflag('sort')
+commandline.registerflag('purge')
+commandline.registerflag('dummy')
+commandline.registerflag('process')
+commandline.registerflag('namespace')
+
+commandline.registervalue('prefix')
+commandline.registervalue('base')
+commandline.registervalue('path')
+commandline.registervalue('result')
+commandline.registervalue('texexec')
+commandline.registervalue('zipalso')
+
+commandline.expand
+
+session.inherit(commandline)
+
+base = session.get('option.base')
+path = session.get('option.path')
+
+base = 'rslbtool.xml' if base.empty?
+
+# when path is given, assume that arg list is list of
+# suffixes, else assume it is a list of globbed filespec
+
+if path.empty?
+ base += '.xml' unless base =~ /\..+$/
+ list = commandline.arguments
+else
+ Dir.chdir(File.dirname(path))
+ list = Dir.glob("*.{#{commandline.arguments.join(',')}}")
+end
+
+begin
+ reslib = Resource.new(base,session.get('option.namespace'))
+ reslib.load(base)
+rescue
+ session.error('problems with loading base')
+ exit
+end
+
+unless session.get('option.texexec').empty?
+ reslib.set_texexec(session.get('option.texexec'))
+end
+
+if session.get('option.add')
+
+ session.report('adding records', list)
+ reslib.add_figures(list,session.get('option.prefix'))
+
+elsif session.get('option.remove') or session.get('option.delete')
+
+ session.report('removing records')
+ reslib.delete_figures(list)
+
+elsif session.get('option.sort')
+
+ session.report('sorting records')
+ reslib.sort_figures()
+
+elsif session.get('option.purge')
+
+ session.report('purging records')
+ reslib.purge_figures()
+
+elsif session.get('option.dummy')
+
+ session.report('creating dummy records')
+ reslib.create_dummies(session.get('option.process'),session.get('option.result'),session.get('option.zipalso'))
+
+else
+
+ session.warning('provide action')
+
+end
+
+reslib.save(base)
diff --git a/Master/texmf-dist/scripts/context/ruby/runtools.rb b/Master/texmf-dist/scripts/context/ruby/runtools.rb
new file mode 100644
index 00000000000..9c504845ab1
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/runtools.rb
@@ -0,0 +1,505 @@
+require 'timeout'
+require 'ftools'
+require 'rbconfig'
+
+class File
+
+ # we don't want a/b//c
+ #
+ # puts File.join('a','b','c')
+ # puts File.join('/a','b','c')
+ # puts File.join('a:','b','c')
+ # puts File.join('a/','/b/','c')
+ # puts File.join('/a','/b/','c')
+ # puts File.join('//a/','/b/','c')
+
+ def File.join(*list)
+ path, prefix = [*list].flatten.join(File::SEPARATOR), ''
+ path.sub!(/^([\/]+)/) do
+ prefix = $1
+ ''
+ end
+ path.gsub!(/([\\\/]+)/) do
+ File::SEPARATOR
+ end
+ prefix + path
+ end
+
+end
+
+
+class Job
+
+ $ownfile, $ownpath = '', ''
+
+ def Job::set_own_path(file)
+ $ownfile, $ownpath = File.basename(file), File.expand_path(File.dirname(file))
+ $: << $ownpath
+ end
+
+ def Job::ownfile
+ $ownfile
+ end
+
+ def Job::ownpath
+ $ownpath
+ end
+
+end
+
+class Job
+
+ def initialize
+ @startuppath = Dir.getwd
+ @log = Array.new
+ @testmode = false
+ @ownpath = $ownpath
+ @paths = Array.new
+ end
+
+ def exit(showlog=false)
+ Dir.chdir(@startuppath)
+ show_log if showlog
+ Kernel::exit
+ end
+
+ def platform
+ case Config::CONFIG['host_os']
+ when /mswin/ then :windows
+ else :unix
+ end
+ end
+
+ def path(*components)
+ File.join([*components].flatten)
+ end
+
+ def found(name)
+ FileTest.file?(path(name)) || FileTest.directory?(path(name))
+ end
+
+ def binary(name)
+ if platform == :windows then
+ name.sub(/\.[^\/]+$/o,'') + '.exe'
+ else
+ name
+ end
+ end
+
+ def suffixed(name,suffix)
+ if name =~ /\.[^\/]+$/o then
+ name
+ else
+ name + '.' + suffix
+ end
+ end
+
+ def expanded(*name)
+ File.expand_path(File.join(*name))
+ end
+
+ def argument(n,default=nil)
+ ARGV[n] || default
+ end
+
+ def variable(name,default='')
+ ENV[name] || default
+ end
+
+ def change_dir(*dir)
+ dir, old = expanded(path(*dir)), expanded(Dir.getwd)
+ unless old == dir then
+ begin
+ Dir.chdir(dir)
+ rescue
+ error("unable to change to path #{dir}")
+ else
+ if old == dir then
+ error("error in changing to path #{dir}")
+ else
+ message("changed to path #{dir}")
+ end
+ end
+ end
+ # return File.expand_path(Dir.getwd)
+ end
+
+ def delete_dir(*dir)
+ begin
+ dir = path(*dir)
+ pattern = "#{dir}/**/*"
+ puts("analyzing dir #{pattern}")
+ files = Dir.glob(pattern).sort.reverse
+ files.each do |f|
+ begin
+ # FileTest.file?(f) fails on .whatever files
+ File.delete(f)
+ rescue
+ # probably directory
+ else
+ puts("deleting file #{f}")
+ end
+ end
+ files.each do |f|
+ begin
+ Dir.rmdir(f)
+ rescue
+ # strange
+ else
+ message("deleting path #{f}")
+ end
+ end
+ begin
+ Dir.rmdir(dir)
+ rescue
+ # strange
+ else
+ message("deleting parent #{dir}")
+ end
+ Dir.glob(pattern).sort.each do |f|
+ warning("unable to delete #{f}")
+ end
+ rescue
+ warning("unable to delete path #{File.expand_path(dir)} (#{$!})")
+ else
+ message("path #{File.expand_path(dir)} removed")
+ end
+ end
+
+
+ def create_dir(*dir)
+ begin
+ dir = path(*dir)
+ unless FileTest.directory?(dir) then
+ File.makedirs(dir)
+ else
+ return
+ end
+ rescue
+ error("unable to create path #{File.expand_path(dir)}")
+ else
+ message("path #{File.expand_path(dir)} created")
+ end
+ end
+
+ def show_dir(delay=0)
+ _puts_("\n")
+ print Dir.getwd + ' '
+ begin
+ timeout(delay) do
+ loop do
+ print '.'
+ sleep(1)
+ end
+ end
+ rescue TimeoutError
+ # ok
+ end
+ _puts_("\n\n")
+ end
+
+ def copy_file(from,to='.',exclude=[])
+ to, ex = path(to), [exclude].flatten
+ Dir.glob(path(from)).each do |file|
+ tofile = to.sub(/[\.\*]$/o) do File.basename(file) end
+ _do_copy_(file,tofile) unless ex.include?(File.extname(file))
+ end
+ end
+
+ def clone_file(from,to)
+ if from and to then
+ to = File.join(File.basename(from),to) if File.basename(to).empty?
+ _do_copy_(from,to)
+ end
+ end
+
+ def copy_dir(from,to,pattern='*',exclude=[]) # recursive
+ pattern = '*' if ! pattern or pattern.empty?
+ if from and to and File.expand_path(from) != File.expand_path(to) then
+ ex = [exclude].flatten
+ Dir.glob("#{from}/**/#{pattern}").each do |file|
+ unless ex.include?(File.extname(file)) then
+ _do_copy_(file,File.join(to,file.sub(/^#{from}/, '')))
+ end
+ end
+ end
+ end
+
+ def copy_path(from,to,pattern='*',exclude=[]) # non-recursive
+ pattern = '*' if ! pattern or pattern.empty?
+ if from and to and File.expand_path(from) != File.expand_path(to) then
+ ex = [exclude].flatten
+ Dir.glob("#{from}/#{pattern}").each do |file|
+ unless ex.include?(File.extname(file)) then
+ _do_copy_(file,File.join(to,file.sub(/^#{from}/, '')))
+ end
+ end
+ end
+ end
+
+ def _do_copy_(file,tofile)
+ if FileTest.file?(file) and File.expand_path(file) != File.expand_path(tofile) then
+ begin
+ create_dir(File.dirname(tofile))
+ File.copy(file,tofile)
+ rescue
+ error("unable to copy #{file} to #{tofile}")
+ else
+ message("file #{file} copied to #{tofile}")
+ end
+ else
+ puts("file #{file} is not copied")
+ end
+ end
+
+ def rename_file(from,to)
+ from, to = path(from), path(to)
+ begin
+ File.move(from,to)
+ rescue
+ error("unable to rename #{from} to #{to}")
+ else
+ message("#{from} renamed to #{to}")
+ end
+ end
+
+ def delete_file(pattern)
+ Dir.glob(path(pattern)).each do |file|
+ _do_delete_(file)
+ end
+ end
+
+ def delete_files(*files)
+ [*files].flatten.each do |file|
+ _do_delete_(file)
+ end
+ end
+
+ def _do_delete_(file)
+ if FileTest.file?(file) then
+ begin
+ File.delete(file)
+ rescue
+ error("unable to delete file #{file}")
+ else
+ message("file #{file} deleted")
+ end
+ else
+ message("no file #{File.expand_path(file)}")
+ end
+ end
+
+ def show_log(filename=nil)
+ if filename then
+ begin
+ if f = File.open(filename,'w') then
+ @log.each do |line|
+ f.puts(line)
+ end
+ f.close
+ end
+ message("log data written to #{filename}")
+ rescue
+ error("unable to write log to #{filename}")
+ end
+ else
+ @log.each do |line|
+ _puts_(line)
+ end
+ end
+ end
+
+ def _puts_(str)
+ begin
+ STDOUT.puts( str)
+ rescue
+ STDERR.puts("error while writing '#{str}' to terminal")
+ end
+ end
+
+ def puts(message)
+ @log << message
+ _puts_(message)
+ end
+
+ def error(message)
+ puts("! #{message}")
+ exit
+ end
+
+ def warning(message)
+ puts("- #{message}")
+ end
+
+ def message(message)
+ puts("+ #{message}")
+ end
+
+ def export_variable(variable,value)
+ value = path(value) if value.class == Array
+ ENV[variable] = value
+ message("environment variable #{variable} set to #{value}")
+ return value
+ end
+
+ def execute_command(*command)
+ begin
+ command = [*command].flatten.join(' ')
+ message("running '#{command}'")
+ _puts_("\n")
+ ok = system(command)
+ _puts_("\n")
+ if true then # ok then
+ message("finished '#{command}'")
+ else
+ error("error in running #{command}")
+ end
+ rescue
+ error("unable to run #{command}")
+ end
+ end
+
+ def pipe_command(*command)
+ begin
+ command = [*command].flatten.join(' ')
+ message("running '#{command}'")
+ result = `#{command}`
+ _puts_("\n")
+ _puts_(result)
+ _puts_("\n")
+ rescue
+ error("unable to run #{command}")
+ end
+ end
+
+ def execute_script(script)
+ script = suffixed(script,'rb')
+ script = path(script_path,File.basename(script)) unless found(script)
+ if found(script) then
+ begin
+ message("loading script #{script}")
+ load(script)
+ rescue
+ error("error in loading script #{script} (#{$!})")
+ else
+ message("script #{script} finished")
+ end
+ else
+ warning("no script #{script}")
+ end
+ end
+
+ def execute_binary(*command)
+ command = [*command].flatten.join(' ').split(' ')
+ command[0] = binary(command[0])
+ execute_command(command)
+ end
+
+ def extend_path(pth)
+ export_variable('PATH',"#{path(pth)}#{File::PATH_SEPARATOR}#{ENV['PATH']}")
+ end
+
+ def startup_path
+ @startuppath
+ end
+
+ def current_path
+ Dir.getwd
+ end
+
+ def script_path
+ @ownpath
+ end
+
+ def push_path(newpath)
+ newpath = File.expand_path(newpath)
+ @paths.push(newpath)
+ change_dir(newpath)
+ end
+
+ def pop_path
+ change_dir(if @paths.length > 0 then @paths.pop else @startuppath end)
+ end
+
+ # runner = Runner.new
+ # runner.texmfstart('texexec','--help')
+
+ def texmfstart(name,args,verbose=false)
+ command = ['texmfstart',"#{'--verbose' if verbose}",name,args].flatten.join(' ')
+ system(command)
+ end
+
+end
+
+class Job
+
+ # copied from texmfstart and patched (message/error), different name
+
+ def use_tree(tree)
+ unless tree.empty? then
+ begin
+ setuptex = File.join(tree,'setuptex.tmf')
+ if FileTest.file?(setuptex) then
+ message("tex tree : #{setuptex}")
+ ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/'
+ ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME']
+ case RUBY_PLATFORM
+ when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin'
+ when /(linux)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux'
+ when /(darwin|rhapsody|nextstep)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx'
+ # when /(netbsd|unix)/i then # todo
+ else # todo
+ end
+ ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}"
+ message("preset : TEXPATH => #{ENV['TEXPATH']}")
+ message("preset : TEXOS => #{ENV['TEXOS']}")
+ message("preset : TEXMFOS => #{ENV['TEXMFOS']}")
+ message("preset : TMP => #{ENV['TMP']}")
+ IO.readlines(File.join(tree,'setuptex.tmf')).each do |line|
+ case line
+ when /^[\#\%]/ then
+ # comment
+ when /^(.*?)\s+\=\s+(.*)\s*$/ then
+ k, v = $1, $2
+ ENV[k] = v.gsub(/\%(.*?)\%/) do
+ ENV[$1] || ''
+ end
+ message("user set : #{k} => #{ENV[k]}")
+ end
+ end
+ else
+ warning("no setup file '#{setuptex}', tree not initialized") # no error
+ end
+ rescue
+ warning("error in setup: #{$!}")
+ end
+ end
+ end
+
+end
+
+Job::set_own_path($0)
+
+if Job::ownfile == 'runtools.rb' then
+
+ begin
+ script = ARGV.shift
+ if script then
+ script += '.rb' if File.extname(script).empty?
+ fullname = File.expand_path(script)
+ fullname = File.join(Job::ownpath,script) unless FileTest.file?(fullname)
+ if FileTest.file?(fullname) then
+ puts("loading script #{fullname}")
+ Job::set_own_path(fullname)
+ load(fullname)
+ else
+ puts("unknown script #{fullname}")
+ end
+ else
+ puts("provide script name")
+ end
+ rescue
+ puts("fatal error: #{$!}")
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/texexec.rb b/Master/texmf-dist/scripts/context/ruby/texexec.rb
new file mode 100644
index 00000000000..1a7175f230e
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/texexec.rb
@@ -0,0 +1,708 @@
+banner = ['TeXExec', 'version 6.2.0', '1997-2006', 'PRAGMA ADE/POD']
+
+unless defined? ownpath
+ ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ $: << ownpath
+end
+
+require 'ftools' # needed ?
+
+require 'base/switch'
+require 'base/logger'
+require 'base/variables'
+require 'base/system'
+
+require 'base/state' # needed ?
+require 'base/file' # needed ?
+
+require 'base/tex'
+require 'base/texutil'
+require 'base/kpse'
+
+class Commands
+
+ include CommandBase
+
+ def make
+ if job = TEX.new(logger) then
+ prepare(job)
+ # bonus, overloads language switch !
+ job.setvariable('language','all') if @commandline.option('all')
+ if @commandline.arguments.length > 0 then
+ if @commandline.arguments.first == 'all' then
+ job.setvariable('texformats',job.defaulttexformats)
+ job.setvariable('mpsformats',job.defaultmpsformats)
+ else
+ job.setvariable('texformats',@commandline.arguments)
+ job.setvariable('mpsformats',@commandline.arguments)
+ end
+ end
+ job.makeformats
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ 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')
+ exit 1 if job.error?
+ end
+ end
+
+ def mptex
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmptex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ exit 1 if job.error?
+ end
+ end
+
+ def mpxtex
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmpxtex
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ exit 1 if job.error?
+ end
+ end
+
+ def mpgraphic
+ if job = TEX.new(logger) then
+ job.setvariable('files',@commandline.arguments)
+ prepare(job)
+ job.processmpgraphic
+ job.inspect && Kpse.inspect if @commandline.option('verbose')
+ exit 1 if job.error?
+ end
+ end
+
+ # hard coded goodies # to be redone as s-ctx-.. with vars passed as such
+
+ def listing
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @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
+ # will be replaced, does not work any more
+ Kpse.runscript('texutil.pl',files.join(' '),'--figures')
+ 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
+ Kpse.runscript('ctxtools',ffname,'--document')
+ 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 arrangeoutput
+ 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 << " [#{emptypages}]\n" unless emptypages.empty?
+ f << " [width=#{textwidth}]\n"
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ job.setvariable('arrange',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to arrange')
+ end
+ else
+ report('no files to arrange')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ def selectoutput
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @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 copyoutput
+ copyortrim(false,'copy')
+ end
+
+ def trimoutput
+ copyortrim(true,'trim')
+ end
+
+ def copyortrim(trim=false,what='unknown')
+ if job = TEX.new(logger) then
+ prepare(job)
+ job.cleanuptemprunfiles
+ files = @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.to_i < 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
+
+ # todo: make this styles
+
+ def combineoutput
+ 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('paperformat', 'A4*A4').split(/[\*x]/o)
+ bannerheight = @commandline.checkedoption('bannerheight', '')
+ nx, ny = combination[0] || '2', combination[1] || combination[0] || '2'
+ from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4'
+ f << "\\setuppapersize[#{from}][#{to}]\n"
+ f << "\\setuplayout\n"
+ f << " [topspace=#{paperoffset},backspace=#{paperoffset},\n"
+ f << " header=0pt,footer=0pt,\n"
+ f << " width=middle,height=middle]\n"
+ if bannerheight.empty? then
+ f << "\\setuplayout[footer=1cm]\n"
+ else
+ f << "\\definelayer[page][width=\\paperwidth,height=\\paperheight]\n"
+ f << "\\setupbackgrounds[page][background=page]\n"
+ end
+ if @commandline.option('nobanner') then
+ f << "\\setuplayout[footer=0cm]\n"
+ f << "\\setupbackgrounds[page][background=]\n"
+ end
+ f << "\\setupexternalfigures\n"
+ f << " [directory=]\n"
+ f << "\\starttext\n"
+ files.each do |filename|
+ result = @commandline.checkedoption('result','texexec')
+ if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then
+ report("combination file: #{filename}")
+ cleanname = cleantexfilename(filename).downcase
+ bannerstring = "\\tttf #{cleanname}\\quad\\quad\\currentdate\\quad\\quad\\pagenumber"
+ if bannerheight.empty? then
+ f << "\\setupfootertexts\n"
+ f << " [#{bannerstring}]\n"
+ else
+ # for the moment we lack a better hook
+ f << "\\setuptexttexts\n"
+ f << " [{\\setlayerframed[page][preset=middlebottom][frame=off,height=#{bannerheight}]{#{bannerstring}}}]\n"
+ end
+ f << "\\combinepages[#{filename}][nx=#{nx},ny=#{ny}]\n"
+ f << "\\page\n"
+ end
+ end
+ f << "\\stoptext\n"
+ f.close
+ job.setvariable('interface','english')
+ job.setvariable('simplerun',true)
+ # job.setvariable('nooptionfile',true)
+ job.setvariable('files',[job.tempfilename])
+ job.processtex
+ else
+ report('no files to list')
+ end
+ else
+ report('no files to list')
+ end
+ job.cleanuptemprunfiles
+ end
+ end
+
+ private
+
+ def prepare(job)
+
+ job.booleanvars.each do |k|
+ job.setvariable(k,@commandline.option(k))
+ end
+ job.stringvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+ job.standardvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+ job.knownvars.each do |k|
+ job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty?
+ end
+
+ 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')
+ elsif @commandline.oneof('luatex') then
+ job.setvariable('texengine','luatex')
+ else
+ job.setvariable('texengine','standard')
+ end
+
+ if (str = @commandline.option('backend')) && ! str.standard? && ! str.empty? then
+ job.setvariable('backend',str)
+ elsif @commandline.oneof('pdfetex','pdftex','pdf','luatex') then
+ job.setvariable('backend','pdftex')
+ elsif @commandline.oneof('dvipdfmx','dvipdfm','dpx','dpm') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('xetex','xtx') then
+ job.setvariable('backend','xetex')
+ elsif @commandline.oneof('aleph') then
+ job.setvariable('backend','dvipdfmx')
+ elsif @commandline.oneof('dvips','ps') then
+ job.setvariable('backend','dvips')
+ elsif @commandline.oneof('xdv') then
+ job.setvariable('backend','xdv')
+ else
+ case job.getvariable('texengine')
+ when 'pdfetex' then job.setvariable('backend','pdftex')
+ when 'luatex' then job.setvariable('backend','pdftex')
+ when 'xetex' then job.setvariable('backend','xetex')
+ when 'aleph' then job.setvariable('backend','dvipdfmx')
+ else
+ job.setvariable('backend','standard')
+ end
+ end
+
+ if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then
+ job.setvariable('mpsengine',@commandline.option('engine'))
+ else
+ job.setvariable('mpsengine','standard')
+ end
+
+ end
+
+ def cleantexfilename(filename)
+ filename.gsub(/([\$\_\#])/) do "\\$1" end.gsub(/([\~])/) do "\\string$1" end
+ end
+
+end
+
+# we will make this pluggable, i.e. load plugins from base/tex that
+# extend the class and may even add switches
+#
+# commandline.load_plugins('base/tex')
+#
+# maybe it's too slow so for a while keep the --pdf* in here
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('make', 'make formats')
+commandline.registeraction('check', 'check versions')
+commandline.registeraction('process', 'process file')
+commandline.registeraction('mptex', 'process mp file')
+commandline.registeraction('mpxtex', 'process mpx file')
+commandline.registeraction('mpgraphic', 'process mp file to stand-alone graphics')
+
+commandline.registeraction('listing', 'list of file content')
+commandline.registeraction('figures', 'generate overview of figures')
+commandline.registeraction('modules', 'generate module documentation')
+commandline.registeraction('pdfarrange', 'impose pages (booklets)')
+commandline.registeraction('pdfselect', 'select pages from file(s)')
+commandline.registeraction('pdfcopy', 'copy pages from file(s)')
+commandline.registeraction('pdftrim', 'trim pages from file(s)')
+commandline.registeraction('pdfcombine', 'combine multiple pages')
+
+# compatibility switch
+
+class Commands
+
+ include CommandBase
+
+ alias pdfarrange :arrangeoutput
+ alias pdfselect :selectoutput
+ alias pdfcopy :copyoutput
+ alias pdftrim :trimoutput
+ alias pdfcombine :combineoutput
+
+end
+
+# so far for compatibility, will move to tex
+
+@@extrastringvars = [
+ 'pages', 'background', 'backspace', 'topspace', 'boxtype', 'tempdir','bannerheight',
+ 'printformat', 'method', 'scale', 'selection',
+ 'combination', 'textwidth', 'addempty', 'logfile',
+ 'startline', 'endline', 'startcolumn', 'endcolumn', 'scale'
+]
+
+@@extrabooleanvars = [
+ 'centerpage', 'noduplex', 'color', 'pretty',
+ 'fullscreen', 'screensaver', 'markings'
+]
+
+if job = TEX.new(logger) then
+
+ job.setextrastringvars(@@extrastringvars)
+ job.setextrabooleanvars(@@extrabooleanvars)
+
+ job.booleanvars.each do |k|
+ commandline.registerflag(k)
+ end
+ job.stringvars.each do |k|
+ commandline.registervalue(k,'')
+ end
+ job.standardvars.each do |k|
+ commandline.registervalue(k,'standard')
+ end
+ job.knownvars.each do |k|
+ commandline.registervalue(k,'')
+ end
+
+end
+
+class Commands
+
+ alias saved_help help
+
+ def wrap_help(title, vars)
+ report("")
+ report(title)
+ report("")
+ r, n = '', 0
+ vars.sort.each do |s|
+ if n == 5 then
+ report(r)
+ r, n = '', 1
+ else
+ n += 1
+ end
+ r << ' ' + s
+ end
+ report(r) unless r.empty?
+ end
+
+ def help
+ saved_help
+ if @commandline.option('all') then
+ if job = TEX.new(logger) then
+ wrap_help("boolean switches:", job.allbooleanvars)
+ wrap_help("string switches:", job.allstringvars)
+ end
+ else
+ report('')
+ report('--help --all shows all switches')
+ end
+ end
+
+end
+
+# todo: register flags -> first one true
+
+commandline.registerflag('pdf')
+commandline.registerflag('pdftex')
+commandline.registerflag('pdfetex')
+commandline.registerflag('luatex')
+
+commandline.registerflag('dvipdfmx')
+commandline.registerflag('dvipdfm')
+commandline.registerflag('dpx')
+commandline.registerflag('dpm')
+
+commandline.registerflag('dvips')
+commandline.registerflag('ps')
+
+commandline.registerflag('xetex')
+commandline.registerflag('xtx')
+commandline.registerflag('xdv')
+
+commandline.registerflag('aleph')
+
+commandline.registerflag('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/texmfstart.rb b/Master/texmf-dist/scripts/context/ruby/texmfstart.rb
index dc166bf92c3..411cb924873 100644
--- a/Master/texmf-dist/scripts/context/ruby/texmfstart.rb
+++ b/Master/texmf-dist/scripts/context/ruby/texmfstart.rb
@@ -2,7 +2,7 @@
# program : texmfstart
# copyright : PRAGMA Advanced Document Engineering
-# version : 1.5.5 - 2003/2005
+# version : 1.9.0 - 2003/2006
# author : Hans Hagen
#
# project : ConTeXt / eXaMpLe
@@ -23,6 +23,7 @@
# --locate => provides location
# --exec => exec instead of system
# --iftouched=a,b => only if timestamp a<>b
+# --ifchanged=a,b => only if checksum changed
#
# file: path: bin:
@@ -32,20 +33,1410 @@
$ownpath = File.expand_path(File.dirname($0)) unless defined? $ownpath
+$: << $ownpath
+
require "rbconfig"
+require "md5"
+
+# kpse_merge_done: require 'base/kpseremote'
+# kpse_merge_done: require 'base/kpsedirect'
+# kpse_merge_done: require 'base/kpsefast'
+# kpse_merge_done: require 'base/merge'
+
+# kpse_merge_start
+
+# kpse_merge_file: 'C:/data/develop/context/ruby/base/kpseremote.rb'
+
+# kpse_merge_done: require 'base/kpsefast'
+
+case ENV['KPSEMETHOD']
+ when /soap/o then # kpse_merge_done: require 'base/kpse/soap'
+ when /drb/o then # kpse_merge_done: require 'base/kpse/drb'
+ else # kpse_merge_done: require 'base/kpse/drb'
+end
+
+class KpseRemote
+
+ @@port = ENV['KPSEPORT'] || 7000
+ @@method = ENV['KPSEMETHOD'] || 'drb'
+
+ def KpseRemote::available?
+ @@method && @@port
+ end
+
+ def KpseRemote::start_server(port=nil)
+ kpse = KpseServer.new(port || @@port)
+ kpse.start
+ end
+
+ def KpseRemote::start_client(port=nil) # keeps object in server
+ kpseclient = KpseClient.new(port || @@port)
+ kpseclient.start
+ kpse = kpseclient.object
+ tree = kpse.choose(KpseUtil::identify, KpseUtil::environment)
+ [kpse, tree]
+ end
+
+ def KpseRemote::fetch(port=nil) # no need for defining methods but slower, send whole object
+ kpseclient = KpseClient.new(port || @@port)
+ kpseclient.start
+ kpseclient.object.fetch(KpseUtil::identify, KpseUtil::environment) rescue nil
+ end
+
+ def initialize(port=nil)
+ if KpseRemote::available? then
+ begin
+ @kpse, @tree = KpseRemote::start_client(port)
+ rescue
+ @kpse, @tree = nil, nil
+ end
+ else
+ @kpse, @tree = nil, nil
+ end
+ end
+
+ def progname=(value)
+ @kpse.set(@tree,'progname',value)
+ end
+ def format=(value)
+ @kpse.set(@tree,'format',value)
+ end
+ def engine=(value)
+ @kpse.set(@tree,'engine',value)
+ end
+
+ def progname
+ @kpse.get(@tree,'progname')
+ end
+ def format
+ @kpse.get(@tree,'format')
+ end
+ def engine
+ @kpse.get(@tree,'engine')
+ end
+
+ def load
+ @kpse.load(KpseUtil::identify, KpseUtil::environment)
+ end
+ def okay?
+ @kpse && @tree
+ end
+ def set(key,value)
+ @kpse.set(@tree,key,value)
+ end
+ def load_cnf
+ @kpse.load_cnf(@tree)
+ end
+ def load_lsr
+ @kpse.load_lsr(@tree)
+ end
+ def expand_variables
+ @kpse.expand_variables(@tree)
+ end
+ def expand_braces(str)
+ clean_name(@kpse.expand_braces(@tree,str))
+ end
+ def expand_path(str)
+ clean_name(@kpse.expand_path(@tree,str))
+ end
+ def expand_var(str)
+ clean_name(@kpse.expand_var(@tree,str))
+ end
+ def show_path(str)
+ clean_name(@kpse.show_path(@tree,str))
+ end
+ def var_value(str)
+ clean_name(@kpse.var_value(@tree,str))
+ end
+ def find_file(filename)
+ clean_name(@kpse.find_file(@tree,filename))
+ end
+ def find_files(filename,first=false)
+ # dodo: each filename
+ @kpse.find_files(@tree,filename,first)
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
+
+
+# kpse_merge_file: 'C:/data/develop/context/ruby/base/kpsefast.rb'
+
+# module : base/kpsefast
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+
+
+class String
+
+ def split_path
+ if self =~ /\;/o || self =~ /^[a-z]\:/io then
+ self.split(";")
+ else
+ self.split(":")
+ end
+ end
+
+end
+
+class Array
+
+ def join_path
+ self.join(File::PATH_SEPARATOR)
+ end
+
+end
+
+class File
+
+ def File.locate_file(path,name)
+ begin
+ files = Dir.entries(path)
+ if files.include?(name) then
+ fullname = File.join(path,name)
+ return fullname if FileTest.file?(fullname)
+ end
+ files.each do |p|
+ fullname = File.join(path,p)
+ if p != '.' and p != '..' and FileTest.directory?(fullname) and result = locate_file(fullname,name) then
+ return result
+ end
+ end
+ rescue
+ # bad path
+ end
+ return nil
+ end
+
+ def File.glob_file(pattern)
+ return Dir.glob(pattern).first
+ end
+
+end
+
+module KpseUtil
+
+ # to be adapted, see loading cnf file
+
+ @@texmftrees = ['texmf-local','texmf.local','../..','texmf'] # '../..' is for gwtex
+ @@texmfcnf = 'texmf.cnf'
+
+ def KpseUtil::identify
+ # we mainly need to identify the local tex stuff and wse assume that
+ # the texmfcnf variable is set; otherwise we need to expand the
+ # TEXMF variable and that takes time since it may involve more
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ ENV['SELFAUTOPARENT'] = ownpath
+ else
+ ENV['SELFAUTOPARENT'] = '.' # fall back
+ # may be too tricky:
+ #
+ # (ENV['PATH'] ||'').split_path.each do |p|
+ # if p.gsub!(/texmf.*?$/o, '') then
+ # ENV['SELFAUTOPARENT'] = p
+ # break
+ # end
+ # end
+ end
+ filenames = Array.new
+ if ENV['TEXMFCNF'] && ! ENV['TEXMFCNF'].empty? then
+ ENV['TEXMFCNF'].to_s.split_path.each do |path|
+ filenames << File.join(path,@@texmfcnf)
+ end
+ elsif ENV['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.',@@texmfcnf)
+ else
+ @@texmftrees.each do |tree|
+ filenames << File.join(ENV['SELFAUTOPARENT'],tree,'web2c',@@texmfcnf)
+ end
+ end
+ loop do
+ busy = false
+ filenames.collect! do |f|
+ f.gsub(/\$([a-zA-Z0-9\_\-]+)/o) do
+ if (! ENV[$1]) || (ENV[$1] == $1) then
+ "$#{$1}"
+ else
+ busy = true
+ ENV[$1]
+ end
+ end
+ end
+ break unless busy
+ end
+ filenames.delete_if do |f|
+ ! FileTest.file?(f)
+ end
+ return filenames
+ end
+
+ def KpseUtil::environment
+ Hash.new.merge(ENV)
+ end
+
+end
+
+class KpseFast
+
+ # formats are an incredible inconsistent mess
+
+ @@suffixes = Hash.new
+ @@formats = Hash.new
+ @@suffixmap = Hash.new
+
+ @@texmfcnf = 'texmf.cnf'
+
+ @@suffixes['gf'] = ['.<resolution>gf'] # todo
+ @@suffixes['pk'] = ['.<resolution>pk'] # todo
+ @@suffixes['tfm'] = ['.tfm']
+ @@suffixes['afm'] = ['.afm']
+ @@suffixes['base'] = ['.base']
+ @@suffixes['bib'] = ['.bib']
+ @@suffixes['bst'] = ['.bst']
+ @@suffixes['cnf'] = ['.cnf']
+ @@suffixes['ls-R'] = ['ls-R', 'ls-r']
+ @@suffixes['fmt'] = ['.fmt', '.efmt', '.efm', '.ofmt', '.ofm', '.oft', '.eofmt', '.eoft', '.eof', '.pfmt', '.pfm', '.epfmt', '.epf', '.xpfmt', '.xpf', '.afmt', '.afm']
+ @@suffixes['map'] = ['.map']
+ @@suffixes['mem'] = ['.mem']
+ @@suffixes['mf'] = ['.mf']
+ @@suffixes['mfpool'] = ['.pool']
+ @@suffixes['mft'] = ['.mft']
+ @@suffixes['mp'] = ['.mp']
+ @@suffixes['mppool'] = ['.pool']
+ @@suffixes['ocp'] = ['.ocp']
+ @@suffixes['ofm'] = ['.ofm', '.tfm']
+ @@suffixes['opl'] = ['.opl']
+ @@suffixes['otp'] = ['.otp']
+ @@suffixes['ovf'] = ['.ovf']
+ @@suffixes['ovp'] = ['.ovp']
+ @@suffixes['graphic/figure'] = ['.eps', '.epsi']
+ @@suffixes['tex'] = ['.tex']
+ @@suffixes['texpool'] = ['.pool']
+ @@suffixes['PostScript header'] = ['.pro']
+ @@suffixes['type1 fonts'] = ['.pfa', '.pfb']
+ @@suffixes['vf'] = ['.vf']
+ @@suffixes['ist'] = ['.ist']
+ @@suffixes['truetype fonts'] = ['.ttf', '.ttc']
+ @@suffixes['web'] = ['.web', '.ch']
+ @@suffixes['cweb'] = ['.w', '.web', '.ch']
+ @@suffixes['enc files'] = ['.enc']
+ @@suffixes['cmap files'] = ['.cmap']
+ @@suffixes['subfont definition files'] = ['.sfd']
+ @@suffixes['lig files'] = ['.lig']
+ @@suffixes['bitmap font'] = []
+ @@suffixes['MetaPost support'] = []
+ @@suffixes['TeX system documentation'] = []
+ @@suffixes['TeX system sources'] = []
+ @@suffixes['Troff fonts'] = []
+ @@suffixes['dvips config'] = []
+ @@suffixes['type42 fonts'] = []
+ @@suffixes['web2c files'] = []
+ @@suffixes['other text files'] = []
+ @@suffixes['other binary files'] = []
+ @@suffixes['misc fonts'] = []
+ @@suffixes['opentype fonts'] = []
+ @@suffixes['pdftex config'] = []
+ @@suffixes['texmfscripts'] = []
+
+ # replacements
+
+ @@suffixes['fmt'] = ['.fmt']
+ @@suffixes['type1 fonts'] = ['.pfa', '.pfb', '.pfm']
+ @@suffixes['tex'] = ['.tex', '.xml']
+ @@suffixes['texmfscripts'] = ['rb','lua','py','pl']
+
+ @@suffixes.keys.each do |k| @@suffixes[k].each do |s| @@suffixmap[s] = k end end
+
+ # TTF2TFMINPUTS
+ # MISCFONTS
+ # TEXCONFIG
+ # DVIPDFMINPUTS
+ # OTFFONTS
+
+ @@formats['gf'] = ''
+ @@formats['pk'] = ''
+ @@formats['tfm'] = 'TFMFONTS'
+ @@formats['afm'] = 'AFMFONTS'
+ @@formats['base'] = 'MFBASES'
+ @@formats['bib'] = ''
+ @@formats['bst'] = ''
+ @@formats['cnf'] = ''
+ @@formats['ls-R'] = ''
+ @@formats['fmt'] = 'TEXFORMATS'
+ @@formats['map'] = 'TEXFONTMAPS'
+ @@formats['mem'] = 'MPMEMS'
+ @@formats['mf'] = 'MFINPUTS'
+ @@formats['mfpool'] = 'MFPOOL'
+ @@formats['mft'] = ''
+ @@formats['mp'] = 'MPINPUTS'
+ @@formats['mppool'] = 'MPPOOL'
+ @@formats['ocp'] = 'OCPINPUTS'
+ @@formats['ofm'] = 'OFMFONTS'
+ @@formats['opl'] = 'OPLFONTS'
+ @@formats['otp'] = 'OTPINPUTS'
+ @@formats['ovf'] = 'OVFFONTS'
+ @@formats['ovp'] = 'OVPFONTS'
+ @@formats['graphic/figure'] = ''
+ @@formats['tex'] = 'TEXINPUTS'
+ @@formats['texpool'] = 'TEXPOOL'
+ @@formats['PostScript header'] = 'TEXPSHEADERS'
+ @@formats['type1 fonts'] = 'T1FONTS'
+ @@formats['vf'] = 'VFFONTS'
+ @@formats['ist'] = ''
+ @@formats['truetype fonts'] = 'TTFONTS'
+ @@formats['web'] = ''
+ @@formats['cweb'] = ''
+ @@formats['enc files'] = 'ENCFONTS'
+ @@formats['cmap files'] = 'CMAPFONTS'
+ @@formats['subfont definition files'] = 'SFDFONTS'
+ @@formats['lig files'] = 'LIGFONTS'
+ @@formats['bitmap font'] = ''
+ @@formats['MetaPost support'] = ''
+ @@formats['TeX system documentation'] = ''
+ @@formats['TeX system sources'] = ''
+ @@formats['Troff fonts'] = ''
+ @@formats['dvips config'] = ''
+ @@formats['type42 fonts'] = 'T42FONTS'
+ @@formats['web2c files'] = 'WEB2C'
+ @@formats['other text files'] = ''
+ @@formats['other binary files'] = ''
+ @@formats['misc fonts'] = ''
+ @@formats['opentype fonts'] = 'OPENTYPEFONTS'
+ @@formats['pdftex config'] = 'PDFTEXCONFIG'
+ @@formats['texmfscripts'] = 'TEXMFSCRIPTS'
+
+ attr_accessor :progname, :engine, :format, :rootpath, :treepath,
+ :verbose, :remember, :scandisk, :diskcache, :renewcache
+
+ @@cacheversion = '1'
+
+ def initialize
+ @rootpath = ''
+ @treepath = ''
+ @progname = 'kpsewhich'
+ @engine = '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'
+
+ @environment = ENV
+ end
+
+ def set(key,value)
+ case key
+ when 'progname' then @progname = value
+ when 'engine' then @engine = value
+ when 'format' then @format = value
+ end
+ end
+
+ def push_environment(env)
+ @environment = env
+ end
+
+ # {$SELFAUTOLOC,$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c}
+ #
+ # $SELFAUTOLOC : /usr/tex/bin/platform
+ # $SELFAUTODIR : /usr/tex/bin
+ # $SELFAUTOPARENT : /usr/tex
+ #
+ # since we live in scriptpath we need a slightly different method
+
+ def load_cnf(filenames=nil)
+ unless filenames then
+ ownpath = File.expand_path($0)
+ if ownpath.gsub!(/texmf.*?$/o, '') then
+ @environment['SELFAUTOPARENT'] = ownpath
+ else
+ @environment['SELFAUTOPARENT'] = '.'
+ end
+ unless @treepath.empty? then
+ unless @rootpath.empty? then
+ @treepath = @treepath.split(',').collect do |p| File.join(@rootpath,p) end.join(',')
+ end
+ @environment['TEXMF'] = @treepath
+ # only the first one
+ @environment['TEXMFCNF'] = File.join(@treepath.split(',').first,'texmf/web2c')
+ end
+ unless @rootpath.empty? then
+ @environment['TEXMFCNF'] = File.join(@rootpath,'texmf/web2c')
+ @environment['SELFAUTOPARENT'] = @rootpath
+ @isolate = true
+ end
+ filenames = Array.new
+ if @environment['TEXMFCNF'] and not @environment['TEXMFCNF'].empty? then
+ @environment['TEXMFCNF'].to_s.split_path.each do |path|
+ filenames << File.join(path,@@texmfcnf)
+ end
+ elsif @environment['SELFAUTOPARENT'] == '.' then
+ filenames << File.join('.',@@texmfcnf)
+ else
+ ['texmf-local','texmf'].each do |tree|
+ filenames << File.join(@environment['SELFAUTOPARENT'],tree,'web2c',@@texmfcnf)
+ end
+ end
+ end
+ # <root>/texmf/web2c/texmf.cnf
+ filenames = _expanded_path_(filenames)
+ @rootpath = filenames.first
+ 3.times do
+ @rootpath = File.dirname(@rootpath)
+ end
+ filenames.collect! do |f|
+ f.gsub("\\", '/')
+ end
+ filenames.each do |fname|
+ if FileTest.file?(fname) and f = File.open(fname) then
+ @cnffiles << fname
+ while line = f.gets do
+ loop do
+ # concatenate lines ending with \
+ break unless line.sub!(/\\\s*$/o) do
+ f.gets || ''
+ end
+ end
+ case line
+ when /^[\%\#]/o then
+ # comment
+ when /^\s*(.*?)\s*\=\s*(.*?)\s*$/o then
+ key, value = $1, $2
+ unless @variables.key?(key) then
+ value.sub!(/\%.*$/,'')
+ value.sub!(/\~/, "$HOME")
+ @variables[key] = value
+ end
+ @kpsevars[key] = true
+ end
+ end
+ f.close
+ end
+ end
+ end
+
+ def load_lsr
+ @lsrfiles = []
+ simplified_list(expansion('TEXMF')).each do |p|
+ ['ls-R','ls-r'].each do |f|
+ filename = File.join(p,f)
+ if FileTest.file?(filename) then
+ @lsrfiles << [filename,File.size(filename)]
+ break
+ end
+ end
+ end
+ @files = Hash.new
+ if @diskcache then
+ ['HOME','TEMP','TMP','TMPDIR'].each do |key|
+ if @environment[key] then
+ if FileTest.directory?(@environment[key]) then
+ @cachepath = @environment[key]
+ @cachefile = [@rootpath.gsub(/[^A-Z0-9]/io, '-').gsub(/\-+/,'-'),File.basename(@cachefile)].join('-')
+ break
+ end
+ end
+ end
+ if @cachepath and not @renewcache and FileTest.file?(File.join(@cachepath,@cachefile)) then
+ begin
+ if f = File.open(File.join(@cachepath,@cachefile)) then
+ cacheversion = Marshal.load(f)
+ if cacheversion == @@cacheversion then
+ lsrfiles = Marshal.load(f)
+ if lsrfiles == @lsrfiles then
+ @files = Marshal.load(f)
+ end
+ end
+ f.close
+ end
+ rescue
+ @files = Hash.new
+ end
+ end
+ end
+ return if @files.size > 0
+ @lsrfiles.each do |filedata|
+ filename, filesize = filedata
+ filepath = File.dirname(filename)
+ begin
+ path = '.'
+ data = IO.readlines(filename)
+ if data[0].chomp =~ /% ls\-R \-\- filename database for kpathsea\; do not change this line\./io then
+ data.each do |line|
+ case line
+ when /^[a-zA-Z0-9]/o then
+ line.chomp!
+ if @files[line] then
+ @files[line] << path
+ else
+ @files[line] = [path]
+ end
+ when /^\.\/(.*?)\:$/o then
+ path = File.join(filepath,$1)
+ end
+ end
+ end
+ rescue
+ # sorry
+ end
+ end
+ if @diskcache and @cachepath and f = File.open(File.join(@cachepath,@cachefile),'wb') then
+ f << Marshal.dump(@@cacheversion)
+ f << Marshal.dump(@lsrfiles)
+ f << Marshal.dump(@files)
+ f.close
+ end
+ end
+
+ def expand_variables
+ @expansions = Hash.new
+ if @isolate then
+ @variables['TEXMFCNF'] = @environment['TEXMFCNF'].dup
+ @variables['SELFAUTOPARENT'] = @environment['SELFAUTOPARENT'].dup
+ else
+ @environment.keys.each do |e|
+ if e =~ /^([a-zA-Z]+)\_(.*)\s*$/o then
+ @expansions["#{$1}.#{$2}"] = (@environment[e] ||'').dup
+ else
+ @expansions[e] = (@environment[e] ||'').dup
+ end
+ end
+ end
+ @variables.keys.each do |k|
+ @expansions[k] = @variables[k].dup unless @expansions[k]
+ end
+ loop do
+ busy = false
+ @expansions.keys.each do |k|
+ @expansions[k].gsub!(/\$([a-zA-Z0-9\_\-]*)/o) do
+ busy = true
+ @expansions[$1] || ''
+ end
+ @expansions[k].gsub!(/\$\{([a-zA-Z0-9\_\-]*)\}/o) do
+ busy = true
+ @expansions[$1] || ''
+ end
+ end
+ break unless busy
+ end
+ @expansions.keys.each do |k|
+ @expansions[k] = @expansions[k].gsub("\\", '/')
+ end
+ end
+
+ def variable(name='')
+ (name and not name.empty? and @variables[name.sub('$','')]) or ''
+ end
+
+ def expansion(name='')
+ (name and not name.empty? and @expansions[name.sub('$','')]) or ''
+ end
+
+ def variable?(name='')
+ name and not name.empty? and @variables.key?(name.sub('$',''))
+ end
+
+ def expansion?(name='')
+ name and not name.empty? and @expansions.key?(name.sub('$',''))
+ end
+
+ def simplified_list(str)
+ lst = str.gsub(/^\{/o,'').gsub(/\}$/o,'').split(",")
+ lst.collect do |l|
+ l.sub(/^[\!]*/,'').sub(/[\/\\]*$/o,'')
+ end
+ end
+
+ def original_variable(variable)
+ if variable?("#{@progname}.#{variable}") then
+ variable("#{@progname}.#{variable}")
+ elsif variable?(variable) then
+ variable(variable)
+ else
+ ''
+ end
+ end
+
+ def expanded_variable(variable)
+ if expansion?("#{variable}.#{@progname}") then
+ expansion("#{variable}.#{@progname}")
+ elsif expansion?(variable) then
+ expansion(variable)
+ else
+ ''
+ end
+ end
+
+ def original_path(filename='')
+ _expanded_path_(original_variable(var_of_format_or_suffix(filename)).split(";"))
+ end
+
+ def expanded_path(filename='')
+ _expanded_path_(expanded_variable(var_of_format_or_suffix(filename)).split(";"))
+ end
+
+ def _expanded_path_(pathlist)
+ i, n = 0, 0
+ pathlist.collect! do |mainpath|
+ mainpath.gsub(/([\{\}])/o) do
+ if $1 == "{" then
+ i += 1 ; n = i if i > n ; "<#{i}>"
+ else
+ i -= 1 ; "</#{i+1}>"
+ end
+ end
+ end
+ n.times do |i|
+ loop do
+ more = false
+ newlist = []
+ pathlist.each do |path|
+ unless path.sub!(/^(.*?)<(#{n-i})>(.*?)<\/\2>(.*?)$/) do
+ pre, mid, post = $1, $3, $4
+ mid.gsub!(/\,$/,',.')
+ mid.split(',').each do |m|
+ more = true
+ if m == '.' then
+ newlist << "#{pre}#{post}"
+ else
+ newlist << "#{pre}#{m}#{post}"
+ end
+ end
+ end then
+ newlist << path
+ end
+ end
+ if more then
+ pathlist = [newlist].flatten # copy -)
+ else
+ break
+ end
+ end
+ end
+ pathlist = pathlist.uniq.collect do |path|
+ p = path
+ # p.gsub(/^\/+/o) do '' end
+ # p.gsub!(/(.)\/\/(.)/o) do "#{$1}/#{$2}" end
+ # p.gsub!(/\/\/+$/o) do '//' end
+ p.gsub!(/\/\/+/o) do '//' end
+ p
+ end
+ pathlist
+ end
+
+ # todo: ignore case
+
+ def var_of_format(str)
+ @@formats[str] || ''
+ end
+
+ def var_of_suffix(str) # includes .
+ if @@suffixmap.key?(str) then @@formats[@@suffixmap[str]] else '' end
+ end
+
+ def var_of_format_or_suffix(str)
+ if @@formats.key?(str) then
+ @@formats[str]
+ elsif @@suffixmap.key?(File.extname(str)) then # extname includes .
+ @@formats[@@suffixmap[File.extname(str)]] # extname includes .
+ else
+ ''
+ end
+ end
+
+end
+
+class KpseFast
+
+ # test things
+
+ def list_variables(kpseonly=true)
+ @variables.keys.sort.each do |k|
+ if kpseonly then
+ puts("#{k} = #{@variables[k]}") if @kpsevars[k]
+ else
+ puts("#{if @kpsevars[k] then 'K' else 'E' end} #{k} = #{@variables[k]}")
+ end
+ end
+ end
+
+ def list_expansions(kpseonly=true)
+ @expansions.keys.sort.each do |k|
+ if kpseonly then
+ puts("#{k} = #{@expansions[k]}") if @kpsevars[k]
+ else
+ puts("#{if @kpsevars[k] then 'K' else 'E' end} #{k} = #{@expansions[k]}")
+ end
+ end
+ end
+
+ def list_lsr
+ puts("files = #{@files.size}")
+ end
+
+ def set_test_patterns
+ @variables["KPSE_TEST_PATTERN_A"] = "foo/{1,2}/bar//"
+ @variables["KPSE_TEST_PATTERN_B"] = "!!x{A,B{1,2}}y"
+ @variables["KPSE_TEST_PATTERN_C"] = "x{A,B//{1,2}}y"
+ @variables["KPSE_TEST_PATTERN_D"] = "x{A,B//{1,2,}}//y"
+ end
+
+ def show_test_patterns
+ ['A','B','D'].each do |i|
+ puts ""
+ puts @variables ["KPSE_TEST_PATTERN_#{i}"]
+ puts ""
+ puts expand_path("KPSE_TEST_PATTERN_#{i}").split_path
+ puts ""
+ end
+ end
+
+end
+
+class KpseFast
+
+ # kpse stuff
+
+ def expand_braces(str) # output variable and brace expansion of STRING.
+ _expanded_path_(original_variable(str).split_path).join_path
+ end
+
+ def expand_path(str) # output complete path expansion of STRING.
+ _expanded_path_(expanded_variable(str).split_path).join_path
+ end
+
+ def expand_var(str) # output variable expansion of STRING.
+ expanded_variable(str)
+ end
+
+ def show_path(str) # output search path for file type NAME
+ expanded_path(str).join_path
+ end
+
+ def var_value(str) # output the value of variable $STRING.
+ original_variable(str)
+ end
+
+end
+
+class KpseFast
+
+ def _is_cnf_?(filename)
+ filename == File.basename((@cnffiles.first rescue @@texmfcnf) || @@texmfcnf)
+ end
+
+ def find_file(filename)
+ if _is_cnf_?(filename) then
+ @cnffiles.first rescue ''
+ else
+ [find_files(filename,true)].flatten.first || ''
+ end
+ end
+
+ def find_files(filename,first=false)
+ if _is_cnf_?(filename) then
+ result = @cnffiles.dup
+ else
+ if @remember then
+ # stamp = "#{filename}--#{@format}--#{@engine}--#{@progname}"
+ stamp = "#{filename}--#{@engine}--#{@progname}"
+ return @found[stamp] if @found.key?(stamp)
+ end
+ pathlist = expanded_path(filename)
+ result = []
+ filelist = if @files.key?(filename) then @files[filename].uniq else nil end
+ done = false
+ if pathlist.size == 0 then
+ if FileTest.file?(filename) then
+ done = true
+ result << '.'
+ end
+ else
+ pathlist.each do |path|
+ doscan = if path =~ /^\!\!/o then false else true end
+ recurse = if path =~ /\/\/$/o then true else false end
+ pathname = path.dup
+ pathname.gsub!(/^\!+/o, '')
+ done = false
+ if not done and filelist then
+ # checking for exact match
+ if filelist.include?(pathname) then
+ result << pathname
+ done = true
+ end
+ if not done and recurse then
+ # checking for fuzzy //
+ pathname.gsub!(/\/+$/o, '/.*')
+ # pathname.gsub!(/\/\//o,'/[\/]*/')
+ pathname.gsub!(/\/\//o,'/.*?/')
+ re = /^#{pathname}/
+ filelist.each do |f|
+ if re =~ f then
+ result << f # duplicates will be filtered later
+ done = true
+ end
+ break if done
+ end
+ end
+ end
+ if not done and doscan then
+ # checking for path itself
+ pname = pathname.sub(/\.\*$/,'')
+ if not pname =~ /\*/o and FileTest.file?(File.join(pname,filename)) then
+ result << pname
+ done = true
+ end
+ end
+ break if done and first
+ end
+ end
+ if not done and @scandisk then
+ pathlist.each do |path|
+ pathname = path.dup
+ unless pathname.gsub!(/^\!+/o, '') then # !! prevents scan
+ recurse = pathname.gsub!(/\/+$/o, '')
+ complex = pathname.gsub!(/\/\//o,'/*/')
+ if recurse then
+ if complex then
+ if ok = File.glob_file("#{pathname}/**/#{filename}") then
+ result << File.dirname(ok)
+ done = true
+ end
+ elsif ok = File.locate_file(pathname,filename) then
+ result << File.dirname(ok)
+ done = true
+ end
+ elsif complex then
+ if ok = File.glob_file("#{pathname}/#{filename}") then
+ result << File.dirname(ok)
+ done = true
+ end
+ elsif FileTest.file?(File.join(pathname,filename)) then
+ result << pathname
+ done = true
+ end
+ break if done and first
+ end
+ end
+ end
+ result = result.uniq.collect do |pathname|
+ File.join(pathname,filename)
+ end
+ @found[stamp] = result if @remember
+ end
+ return result # redundant
+ end
+
+end
+
+class KpseFast
+
+ class FileData
+ attr_accessor :tag, :name, :size, :date
+ def initialize(tag=0,name=nil,size=nil,date=nil)
+ @tag, @name, @size, @date = tag, name, size, date
+ end
+ def FileData.sizes(a)
+ a.collect do |aa|
+ aa.size
+ end
+ end
+ def report
+ case @tag
+ when 1 then "deleted | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 2 then "present | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}"
+ when 3 then "obsolete | #{' '*8} | #{' '*16} | #{@name}"
+ end
+ end
+ end
+
+ def analyze_files(filter='',strict=false,sort='',delete=false)
+ puts("command line = #{ARGV.join(' ')}")
+ puts("number of files = #{@files.size}")
+ puts("filter pattern = #{filter}")
+ puts("loaded cnf files = #{@cnffiles.join(' ')}")
+ puts('')
+ if filter.gsub!(/^not:/,'') then
+ def the_same(filter,filename)
+ not filter or filter.empty? or /#{filter}/ !~ filename
+ end
+ else
+ def the_same(filter,filename)
+ not filter or filter.empty? or /#{filter}/ =~ filename
+ end
+ end
+ @files.keys.each do |name|
+ if @files[name].size > 1 then
+ data = Array.new
+ @files[name].each do |path|
+ filename = File.join(path,name)
+ # if not filter or filter.empty? or /#{filter}/ =~ filename then
+ if the_same(filter,filename) then
+ if FileTest.file?(filename) then
+ if delete then
+ data << FileData.new(1,filename,File.size(filename),File.mtime(filename))
+ begin
+ File.delete(filename) if delete
+ rescue
+ end
+ else
+ data << FileData.new(2,filename,File.size(filename),File.mtime(filename))
+ end
+ else
+ # data << FileData.new(3,filename)
+ end
+ end
+ end
+ if data.length > 1 then
+ if strict then
+ # if data.collect do |d| d.size end.uniq! then
+ # data.sort! do |a,b| b.size <=> a.size end
+ # data.each do |d| puts d.report end
+ # puts ''
+ # end
+ data.sort! do |a,b|
+ if a.size and b.size then
+ b.size <=> a.size
+ else
+ 0
+ end
+ end
+ bunch = Array.new
+ done = false
+ data.each do |d|
+ if bunch.size == 0 then
+ bunch << d
+ elsif bunch[0].size == d.size then
+ bunch << d
+ else
+ if bunch.size > 1 then
+ bunch.each do |b|
+ puts b.report
+ end
+ done = true
+ end
+ bunch = [d]
+ end
+ end
+ puts '' if done
+ else
+ case sort
+ when 'size' then data.sort! do |a,b| a.size <=> b.size end
+ when 'revsize' then data.sort! do |a,b| b.size <=> a.size end
+ when 'date' then data.sort! do |a,b| a.date <=> b.date end
+ when 'revdate' then data.sort! do |a,b| b.date <=> a.date end
+ end
+ data.each do |d| puts d.report end
+ puts ''
+ end
+ end
+ end
+ end
+ end
+
+end
+
+
+ # 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
+
+
+
+# kpse_merge_file: 'C:/data/develop/context/ruby/base/kpse/drb.rb'
+
+require 'drb'
+# kpse_merge_done: require 'base/kpse/trees'
+
+class KpseServer
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ end
+
+ def start
+ puts "starting drb service at port #{@port}"
+ DRb.start_service("druby://localhost:#{@port}", KpseTrees.new)
+ trap(:INT) do
+ DRb.stop_service
+ end
+ DRb.thread.join
+ end
+
+ def stop
+ # todo
+ end
+
+end
+
+class KpseClient
+
+ attr_accessor :port
+
+ def initialize(port=7000)
+ @port = port
+ @kpse = nil
+ end
+
+ def start
+ # only needed when callbacks are used / slow, due to Socket::getaddrinfo
+ # DRb.start_service
+ end
+
+ def object
+ @kpse = DRbObject.new(nil,"druby://localhost:#{@port}")
+ end
+
+end
+
+
+# SERVER_URI="druby://localhost:8787"
+#
+# # Start a local DRbServer to handle callbacks.
+# #
+# # Not necessary for this small example, but will be required
+# # as soon as we pass a non-marshallable object as an argument
+# # to a dRuby call.
+# DRb.start_service
+#
+
+
+# kpse_merge_file: 'C:/data/develop/context/ruby/base/kpse/trees.rb'
+
+require 'monitor'
+# kpse_merge_done: require 'base/kpsefast'
+
+class KpseTrees < Monitor
+
+ def initialize
+ @trees = Hash.new
+ end
+
+ def pattern(filenames)
+ filenames.join('|').gsub(/\\+/o,'/').downcase
+ end
+
+ def choose(filenames,environment)
+ current = pattern(filenames)
+ load(filenames,environment) unless @trees[current]
+ puts "enabling tree #{current}"
+ current
+ end
+
+ def fetch(filenames,environment) # will send whole object !
+ current = pattern(filenames)
+ load(filenames,environment) unless @trees[current]
+ puts "fetching tree #{current}"
+ @trees[current]
+ end
+
+ def load(filenames,environment)
+ current = pattern(filenames)
+ puts "loading tree #{current}"
+ @trees[current] = KpseFast.new
+ @trees[current].push_environment(environment)
+ @trees[current].load_cnf(filenames)
+ @trees[current].expand_variables
+ @trees[current].load_lsr
+ end
+
+ def set(tree,key,value)
+ case key
+ when 'progname' then @trees[tree].progname = value
+ when 'engine' then @trees[tree].engine = value
+ when 'format' then @trees[tree].format = value
+ end
+ end
+ def get(tree,key)
+ case key
+ when 'progname' then @trees[tree].progname
+ when 'engine' then @trees[tree].engine
+ when 'format' then @trees[tree].format
+ end
+ end
+
+ def load_cnf(tree)
+ @trees[tree].load_cnf
+ end
+ def load_lsr(tree)
+ @trees[tree].load_lsr
+ end
+ def expand_variables(tree)
+ @trees[tree].expand_variables
+ end
+ def expand_braces(tree,str)
+ @trees[tree].expand_braces(str)
+ end
+ def expand_path(tree,str)
+ @trees[tree].expand_path(str)
+ end
+ def expand_var(tree,str)
+ @trees[tree].expand_var(str)
+ end
+ def show_path(tree,str)
+ @trees[tree].show_path(str)
+ end
+ def var_value(tree,str)
+ @trees[tree].var_value(str)
+ end
+ def find_file(tree,filename)
+ @trees[tree].find_file(filename)
+ end
+ def find_files(tree,filename,first)
+ @trees[tree].find_files(filename,first)
+ end
+
+end
+
+
+# kpse_merge_file: 'C:/data/develop/context/ruby/base/kpsedirect.rb'
+
+class KpseDirect
+
+ attr_accessor :progname, :format, :engine
+
+ def initialize
+ @progname, @format, @engine = '', '', ''
+ end
+
+ def expand_path(str)
+ clean_name(`kpsewhich -expand-path=#{str}`.chomp)
+ end
+
+ def expand_var(str)
+ clean_name(`kpsewhich -expand-var=#{str}`.chomp)
+ end
+
+ def find_file(str)
+ clean_name(`kpsewhich #{_progname_} #{_format_} #{str}`.chomp)
+ end
+
+ def _progname_
+ if @progname.empty? then '' else "-progname=#{@progname}" end
+ end
+ def _format_
+ if @format.empty? then '' else "-format=\"#{@format}\"" end
+ end
+
+ private
+
+ def clean_name(str)
+ str.gsub(/\\/,'/')
+ end
+
+end
+
+
+# kpse_merge_file: 'C:/data/develop/context/ruby/base/merge.rb'
+
+# module : base/merge
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2006
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+
+
+# this module will package all the used modules in the file itself
+# so that we can relocate the file at wish, usage:
+#
+# merge:
+#
+# unless SelfMerge::ok? && SelfMerge::merge then
+# puts("merging should happen on the path were the base inserts reside")
+# end
+#
+# cleanup:
+#
+# unless SelfMerge::cleanup then
+# puts("merging should happen on the path were the base inserts reside")
+
+module SelfMerge
+
+ @@kpsemergestart = "\# kpse_merge_start"
+ @@kpsemergestop = "\# kpse_merge_stop"
+ @@kpsemergefile = "\# kpse_merge_file: "
+ @@kpsemergedone = "\# kpse_merge_done: "
+
+ @@filename = File.basename($0)
+ @@ownpath = File.expand_path(File.dirname($0))
+ @@modroot = '(base|graphics|rslb|www)' # needed in regex in order not to mess up SelfMerge
+ @@modules = $".collect do |file| File.expand_path(file) end
+
+ @@modules.delete_if do |file|
+ file !~ /^#{@@ownpath}\/#{@@modroot}.*$/
+ end
+
+ def SelfMerge::ok?
+ begin
+ @@modules.each do |file|
+ return false unless FileTest.file?(file)
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::merge
+ begin
+ if SelfMerge::ok? && rbfile = IO.read(@@filename) then
+ begin
+ inserts = "#{@@kpsemergestart}\n\n"
+ @@modules.each do |file|
+ inserts << "#{@@kpsemergefile}'#{file}'\n\n"
+ inserts << IO.read(file).gsub(/^#.*?\n$/,'')
+ inserts << "\n\n"
+ end
+ inserts << "#{@@kpsemergestop}\n\n"
+ # no gsub! else we end up in SelfMerge
+ rbfile.sub!(/#{@@kpsemergestart}\s*#{@@kpsemergestop}/mois) do
+ inserts
+ end
+ rbfile.gsub!(/^(.*)(require [\"\'].*?#{@@modroot}.*)$/) do
+ pre, post = $1, $2
+ if pre =~ /#{@@kpsemergedone}/ then
+ "#{pre}#{post}"
+ else
+ "#{pre}#{@@kpsemergedone}#{post}"
+ end
+ end
+ rescue
+ return false
+ else
+ begin
+ File.open(@@filename,'w') do |f|
+ f << rbfile
+ end
+ rescue
+ return false
+ end
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::cleanup
+ begin
+ if rbfile = IO.read(@@filename) then
+ begin
+ rbfile.sub!(/#{@@kpsemergestart}(.*)#{@@kpsemergestop}\s*/mois) do
+ "#{@@kpsemergestart}\n\n#{@@kpsemergestop}\n\n"
+ end
+ rbfile.gsub!(/^(.*#{@@kpsemergedone}.*)$/) do
+ str = $1
+ if str =~ /require [\"\']/ then
+ str.gsub(/#{@@kpsemergedone}/, '')
+ else
+ str
+ end
+ end
+ rescue
+ return false
+ else
+ begin
+ File.open(@@filename,'w') do |f|
+ f << rbfile
+ end
+ rescue
+ return false
+ end
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def SelfMerge::replace
+ if SelfMerge::ok? then
+ SelfMerge::cleanup
+ SelfMerge::merge
+ end
+ end
+
+end
+
+
+# kpse_merge_stop
+
+
$mswindows = Config::CONFIG['host_os'] =~ /mswin/
$separator = File::PATH_SEPARATOR
-$version = "1.6.2"
+$version = "2.0.0"
if $mswindows then
-
require "win32ole"
require "Win32API"
-
end
-exit if defined?(REQUIRE2LIB)
+# exit if defined?(REQUIRE2LIB)
$stdout.sync = true
$stderr.sync = true
@@ -61,16 +1452,19 @@ $suffixinputs['lua'] = 'LUAINPUTS'
$suffixinputs['jar'] = 'JAVAINPUTS'
$suffixinputs['pdf'] = 'PDFINPUTS'
-$predefined['texexec'] = 'texexec.pl'
-$predefined['texutil'] = 'texutil.pl'
+$predefined['texexec'] = 'texexec.rb'
+$predefined['texutil'] = 'texutil.rb'
$predefined['texfont'] = 'texfont.pl'
+$predefined['texshow'] = 'texshow.pl'
+$predefined['makempy'] = 'makempy.pl'
$predefined['mptopdf'] = 'mptopdf.pl'
$predefined['pstopdf'] = 'pstopdf.rb'
$predefined['examplex'] = 'examplex.rb'
$predefined['concheck'] = 'concheck.rb'
+$predefined['runtools'] = 'runtools.rb'
$predefined['textools'] = 'textools.rb'
$predefined['tmftools'] = 'tmftools.rb'
$predefined['ctxtools'] = 'ctxtools.rb'
@@ -80,16 +1474,42 @@ $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
+$predefined['newpstopdf'] = 'pstopdf.rb'
+$predefined['newtexexec'] = 'texexec.rb'
+$predefined['pdftrimwhite'] = 'pdftrimwhite.pl'
+
+$makelist = [
+ # context
+ 'texexec',
+ 'texutil',
+ 'texfont',
+ # mp/ps
+ 'pstopdf',
+ 'mptopdf',
+ 'makempy',
+ # misc
+ 'ctxtools',
+ 'pdftools',
+ 'xmltools',
+ 'textools',
+ 'mpstools',
+ 'tmftools',
+ 'exatools',
+ 'runtools',
+ 'rlxtools',
+ #
+ # no, 'texmfstart'
+]
+
+# 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'
+$editor = ENV['TEXMFSTART_EDITOR'] || ENV['EDITOR'] || ENV['editor'] || 'scite'
$crossover = true # to other tex tools, else only local
@@ -113,6 +1533,31 @@ end
$applications['htm'] = $applications['html']
$applications['eps'] = $applications['ps']
+$kpse = nil
+
+def check_kpse
+ if $kpse then
+ # already done
+ else
+ begin
+ if KpseRemote::available? then
+ $kpse = KpseRemote.new
+ if $kpse.okay? then
+ puts("kpse : remote") if $verbose
+ else
+ $kpse = KpseDirect.new
+ puts("kpse : direct (forced)") if $verbose
+ end
+ else
+ $kpse = KpseDirect.new
+ puts("kpse : direct") if $verbose
+ end
+ rescue
+ puts("kpse : direct (fallback)") if $verbose
+ end
+ end
+end
+
if $mswindows then
GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', ['P','P','N'], 'N')
@@ -170,30 +1615,43 @@ 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
+
+ @@update_eps = 1
+
def File.needsupdate(oldname,newname)
begin
- if $mswindows then
- return File.stat(oldname).mtime > File.stat(newname).mtime
+ oldtime = File.stat(oldname).mtime.to_i
+ newtime = File.stat(newname).mtime.to_i
+ if newtime >= oldtime then
+ return false
+ elsif oldtime-newtime < @@update_eps then
+ return false
else
- return File.stat(oldname).mtime != File.stat(newname).mtime
+ return true
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)
+ return
begin
if $mswindows then
- # does not work (yet)
+ # does not work (yet) / gives future timestamp
+ # t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
+ # File.utime(0,t,oldname,newname)
else
t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here
File.utime(0,t,oldname,newname)
@@ -202,6 +1660,14 @@ class File
end
end
+ def File.timestamp(name)
+ begin
+ "#{File.stat(name).mtime}"
+ rescue
+ return 'unknown'
+ end
+ end
+
end
def hashed (arr=[])
@@ -242,6 +1708,10 @@ def launch(filename)
end
end
+# env|environment
+# rel|relative
+# loc|locate|kpse|path|file
+
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, ''
@@ -252,12 +1722,27 @@ def expanded(arg) # no "other text files", too restricted
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
+ end . gsub(/(rel|relative)\:([a-zA-Z\-\_\.0-9]+)/o) do
+ method, original, resolved = $1, $2, ''
+ ['.','..','../..'].each do |r|
+ if FileTest.file?(File.join(r,original)) then
+ resolved = File.join(r,original)
+ break
+ end
+ end
+ if resolved.empty? then
+ original
+ else
+ resolved
+ end
+ end . gsub(/(kpse|loc|locate|file|path)\:([a-zA-Z\-\_\.0-9]+)/o) do
method, original, resolved = $1, $2, ''
if $program && ! $program.empty? then
- pstrings = ["-progname=#{$program}"]
+ # pstrings = ["-progname=#{$program}"]
+ pstrings = [$program]
else
- pstrings = ['','progname=context']
+ # pstrings = ['','-progname=context']
+ pstrings = ['','context']
end
# auto suffix with texinputs as fall back
if ENV["_CTX_K_V_#{original}_"] then
@@ -265,22 +1750,31 @@ def expanded(arg) # no "other text files", too restricted
report("environment provides #{original} as #{resolved}") unless $report
resolved
else
+ check_kpse
pstrings.each do |pstr|
if resolved.empty? then
- command = "kpsewhich #{pstr} #{original}"
- report("running #{command}")
+ # command = "kpsewhich #{pstr} #{original}"
+ # report("running #{command}")
+ report("locating '#{original}' in program space '#{pstr}'")
begin
- resolved = `#{command}`.chomp
+ # resolved = `#{command}`.chomp
+ $kpse.progname = pstr
+ $kpse.format = ''
+ resolved = $kpse.find_file(original).gsub(/\\/,'/')
rescue
resolved = ''
end
end
# elsewhere in the tree
if resolved.empty? then
- command = "kpsewhich #{pstr} -format=\"other text files\" #{original}"
- report("running #{command}")
+ # command = "kpsewhich #{pstr} -format=\"other text files\" #{original}"
+ # report("running #{command}")
+ report("locating '#{original}' in program space '#{pstr}' using format 'other text files'")
begin
- resolved = `#{command}`.chomp
+ # resolved = `#{command}`.chomp
+ $kpse.progname = pstr
+ $kpse.format = 'other text files'
+ resolved = $kpse.find_file(original).gsub(/\\/,'/')
rescue
resolved = ''
end
@@ -339,8 +1833,11 @@ def runoneof(application,fullname,browserpermitted)
else
report("starting #{$filename}") unless $report
output("\n") if $report && $verbose
- applications = $applications[application]
- if applications.class == Array then
+ applications = $applications[application.downcase]
+ if ! applications then
+ output("problems with determining application type")
+ return true
+ elsif applications.class == Array then
if $report then
output([fullname,expanded($arguments)].join(' '))
return true
@@ -369,21 +1866,22 @@ def runoneof(application,fullname,browserpermitted)
end
def report(str)
- $stderr.puts(str) if $verbose
+ $stdout.puts(str) if $verbose
end
def output(str)
- $stderr.puts(str)
+ $stdout.puts(str)
end
def usage
- print "version : #{$version} - 2003/2005 - www.pragma-ade.com\n"
+ print "version : #{$version} - 2003/2006 - www.pragma-ade.com\n"
print("\n")
print("usage : texmfstart [switches] filename [optional arguments]\n")
print("\n")
- print("switches : --verbose --report --browser --direct --execute --locate --iftouched\n")
+ print("switches : --verbose --report --browser --direct --execute --locate --iftouched --ifchanged\n")
print(" --program --file --page --arguments --batch --edit --report --clear\n")
print(" --make --lmake --wmake --path --stubpath --indirect --before --after\n")
+ print(" --tree --autotree --environment --showenv\n")
print("\n")
print("example : texmfstart pstopdf.rb cow.eps\n")
print(" texmfstart --locate examplex.rb\n")
@@ -391,13 +1889,22 @@ def usage
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 --program=yourtex yourscript.rb arg-1 arg-2\n")
print(" texmfstart --direct xsltproc kpse:somefile.xsl somefile.xml\n")
+ print(" texmfstart --direct ruby rel:wn-cleanup-1.rb oldfile.xml newfile.xml\n")
print(" texmfstart bin:xsltproc env:somepreset path:somefile.xsl somefile.xml\n")
print(" texmfstart --iftouched=normal,lowres downsample.rb normal lowres\n")
- print(" texmfstart texmfstart bin:scite kpse:texmf.cnf\n")
- print(" texmfstart texmfstart --exec bin:scite *.tex\n")
- print(" texmfstart texmfstart --edit texmf.cnf\n")
+ print(" texmfstart --ifchanged=somefile.dat --direct processit somefile.dat\n")
+ print(" texmfstart bin:scite kpse:texmf.cnf\n")
+ print(" texmfstart --exec bin:scite *.tex\n")
+ print(" texmfstart --edit texmf.cnf\n")
+ print(" texmfstart --edit kpse:texmf.cnf\n")
+ print(" texmfstart --serve\n")
+ print("\n")
+ print(" texmfstart --stubpath=/usr/local/bin [--make --remove] --verbose all\n")
+ print(" texmfstart --stubpath=auto [--make --remove] all\n")
+ print("\n")
+ check_kpse
end
# somehow registration does not work out (at least not under windows)
@@ -411,7 +1918,7 @@ def registered?(filename)
end
def registered(filename)
- return ENV[tag(filename)]
+ return ENV[tag(filename)] || 'unknown'
end
def register(filename,fullname)
@@ -425,204 +1932,220 @@ def register(filename,fullname)
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
+ begin
+ filename = filename.sub(/script:/o, '') # so we have bin: and script: and nothing
+ if $predefined.key?(filename) then
+ report("expanding '#{filename}' to '#{$predefined[filename]}'")
+ filename = $predefined[filename]
end
- 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
+ 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|
- 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")
+ fullname = filename+'.'+suffix
+ if FileTest.file?(fullname) && register(filename,fullname)
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]}")
+ 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? && ! (p[0] == 'unknown') then
+ suffixlist.each do |suffix|
+ fname = "#{filename}.#{suffix}"
+ fullname = File.expand_path(File.join(p[0],fname))
+ report("locating '#{fname}' in #{p[1]} path '#{p[0]}'")
+ if FileTest.file?(fullname) && register(filename,fullname) then
+ report("'#{fname}' located in #{p[1]} path")
+ return shortpathname(fullname)
end
end
- 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
+ end
+ end
+ # now we consult environment settings
+ fullname = nil
+ check_kpse
+ $kpse.progname = program
+ suffixlist.each do |suffix|
+ begin
+ break unless $suffixinputs[suffix]
+ environment = ENV[$suffixinputs[suffix]] || ENV[$suffixinputs[suffix]+".#{$program}"]
+ if ! environment || environment.empty? then
+ begin
+ # environment = `kpsewhich -expand-path=\$#{$suffixinputs[suffix]}`.chomp
+ environment = $kpse.expand_path("\$#{$suffixinputs[suffix]}")
+ rescue
+ environment = nil
else
- fullname = nil
+ 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
- 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
+ return shortpathname(fullname) if register(filename,fullname)
+ # then we fall back on kpsewhich
+ suffixlist.each do |suffix|
+ # TDS script scripts location as per 2004
+ if suffix =~ /(#{$scriptlist})/ then
+ begin
+ report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (1)")
+ # fullname = `kpsewhich -progname=#{program} -format=texmfscripts #{filename}.#{suffix}`.chomp
+ $kpse.format = 'texmfscripts'
+ fullname = $kpse.find_file("#{filename}.#{suffix}").gsub(/\\/,'/')
+ rescue
+ report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (1)")
+ fullname = nil
+ else
+ return shortpathname(fullname) if register(filename,fullname)
+ end
+ end
+ # old TDS location: .../texmf/context/...
begin
- report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (1)")
- fullname = `kpsewhich -progname=#{program} -format=texmfscripts #{filename}.#{suffix}`.chomp
+ report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (2)")
+ # fullname = `kpsewhich -progname=#{program} -format="other text files" #{filename}.#{suffix}`.chomp
+ $kpse.format = 'other text files'
+ fullname = $kpse.find_file("#{filename}.#{suffix}").gsub(/\\/,'/')
rescue
- report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (1)")
+ report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (2)")
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)
+ return shortpathname(fullname) if register(filename,fullname)
+ # let's take a look at the path
+ paths = ENV['PATH'].split($separator)
+ suffixlist.each do |s|
+ paths.each do |p|
+ suffixedname = "#{filename}.#{s}"
+ report("checking #{p} for #{filename}")
+ if FileTest.file?(File.join(p,suffixedname)) then
+ fullname = File.join(p,suffixedname)
+ return shortpathname(fullname) if register(filename,fullname)
+ end
end
end
- 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(',')
+ # 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
- 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
+ # texroot = `kpsewhich -expand-var=$SELFAUTOPARENT`.chomp
+ texroot = $kpse.expand_var("$SELFAUTOPARENT")
rescue
- report("locating '#{filename}.#{suffix}' in tree '#{texroot}' aborted")
+ texroot = ''
+ else
+ texroot.sub!(/[\\\/][^\\\/]*?$/, '')
+ end
+ if not texroot.empty? then
+ sffxlst = suffixlist.join(',')
+ begin
+ report("locating '#{filename}' in document tree '#{texroot}/doc*'")
+ if (result = Dir.glob("#{texroot}/doc*/**/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then
+ fullname = result[0]
+ end
+ rescue
+ report("locating '#{filename}.#{suffixlist.join('|')}' in tree '#{texroot}' aborted")
+ end
end
+ return shortpathname(fullname) if register(filename,fullname)
end
- 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 = ['**']
+ report("aggressively locating '#{filename}' in tex trees")
+ begin
+ # textrees = `kpsewhich -expand-var=$TEXMF`.chomp
+ textrees = $kpse.expand_var("$TEXMF")
+ rescue
+ textrees = ''
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
+ 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
- rescue
- report("locating '#{filename}' in tree '#{tt}' aborted")
- next
end
end
+ break if fullname && ! fullname.empty?
end
- break if fullname && ! fullname.empty?
end
- end
- if register(filename,fullname) then
- return shortpathname(fullname)
- else
- return ''
+ if register(filename,fullname) then
+ return shortpathname(fullname)
+ else
+ return ''
+ end
+ rescue
+ # error, trace = $!, $@.join("\n")
+ # report("fatal error: #{error}\n#{trace}")
+ report("fatal error")
end
end
def run(fullname)
if ! fullname || fullname.empty? then
- report("the file '#{$filename}' is not found")
+ output("the file '#{$filename}' is not found")
elsif FileTest.file?(fullname) then
begin
case fullname
- when /\.(#{$scriptlist})$/ then
+ when /\.(#{$scriptlist})$/i then
return runoneof($1,fullname,false)
- when /\.(#{$documentlist})$/ then
+ when /\.(#{$documentlist})$/i then
return runoneof($1,fullname,true)
else
return runoneof('unknown',fullname,false)
end
rescue
- report("starting '#{$filename}' in program space '#{$program}' fails")
+ report("starting '#{$filename}' in program space '#{$program}' fails (#{$!})")
end
else
report("the file '#{$filename}' in program space '#{$program}' is not accessible")
@@ -646,13 +2169,33 @@ def edit(filename)
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')
+def make(filename,windows=false,linux=false,remove=false)
+ basename = File.basename(filename).gsub(/\.[^.]+?$/, '')
+ if $stubpath == 'auto' then
+ basename = File.dirname($0) + '/' + basename
+ else
+ basename = $stubpath + '/' + basename unless $stubpath.empty?
+ end
+ if filename == 'texmfstart' then
+ program = 'ruby'
+ command = 'kpsewhich --format=texmfscripts --progname=context texmfstart.rb'
+ filename = `#{command}`.chomp.gsub(/\\/, '/')
+ if filename.empty? then
+ report("failure: #{command}")
+ return
+ elsif not remove then
+ if windows then
+ ['bat','exe'].each do |suffix|
+ if FileTest.file?("#{basename}.#{suffix}") then
+ report("windows stub '#{basename}.#{suffix}' skipped (already present)")
+ return
+ end
+ end
+ elsif linux && FileTest.file?(basename) then
+ report("unix stub '#{basename}' skipped (already present)")
+ return
+ end
+ end
else
program = nil
if filename =~ /[\\\/]/ && filename =~ /\.(#{$scriptlist})$/ then
@@ -660,31 +2203,39 @@ def make(filename,windows=false,linux=false)
end
filename = "\"#{filename}\"" if filename =~ /\s/
program = 'texmfstart' if $indirect || ! program || program.empty?
- begin
+ end
+ begin
+ callname = $predefined[filename.sub(/\.*?$/,'')] || filename
+ if remove then
+ if windows && (File.delete(basename+'.bat') rescue false) then
+ report("windows stub '#{basename}.bat' removed (calls #{callname})")
+ elsif linux && (File.delete(basename) rescue false) then
+ report("unix stub '#{basename}' removed (calls #{callname})")
+ end
+ else
if windows && f = open(basename+'.bat','w') then
f.binmode
f.write("@echo off\015\012")
- f.write("#{program} #{filename} %*\015\012")
+ f.write("#{program} #{callname} %*\015\012")
f.close
- report("windows stub '#{basename}.bat' made")
+ report("windows stub '#{basename}.bat' made (calls #{callname})")
elsif linux && f = open(basename,'w') then
f.binmode
f.write("#!/bin/sh\012")
- f.write("#{program} #{filename} $@\012")
+ f.write("#{program} #{callname} \"$@\"\012")
f.close
- report("unix stub '#{basename}' made")
+ report("unix stub '#{basename}' made (calls #{callname})")
end
- rescue
- report("failed to make stub '#{basename}'")
- else
- return true
end
+ rescue
+ report("failed to make stub '#{basename}' #{$!}")
+ return false
+ else
+ return true
end
- return false
end
def process(&block)
-
if $iftouched then
files = $directives['iftouched'].split(',')
oldname, newname = files[0], files[1]
@@ -697,52 +2248,145 @@ def process(&block)
else
report("file #{oldname} is untouched")
end
+ elsif $ifchanged then
+ filename = $directives['ifchanged']
+ checkname = filename + ".md5"
+ oldchecksum, newchecksum = "old", "new"
+ begin
+ newchecksum = MD5.new(IO.read(filename)).hexdigest.upcase
+ rescue
+ newchecksum = "new"
+ else
+ begin
+ oldchecksum = IO.read(checkname).chomp
+ rescue
+ oldchecksum = "old"
+ end
+ end
+ if $verbose then
+ report("old checksum #{filename}: #{oldchecksum}")
+ report("new checksum #{filename}: #{newchecksum}")
+ end
+ if oldchecksum != newchecksum then
+ report("file is changed, processing started")
+ begin
+ File.open(checkname,'w') do |f|
+ f << newchecksum
+ end
+ rescue
+ end
+ yield
+ else
+ report("file #{filename} is unchanged")
+ end
else
yield
end
+end
+def checkenvironment(tree)
+ report('')
+ ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME']
+ case RUBY_PLATFORM
+ when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin'
+ when /(linux)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux'
+ when /(darwin|rhapsody|nextstep)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx'
+ # when /(netbsd|unix)/i then # todo
+ else # todo
+ end
+ ENV['TEXOS'] = "#{ENV['TEXOS'].sub(/^[\\\/]*/, '').sub(/[\\\/]*$/, '')}"
+ ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/'
+ ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}"
+ report('')
+ report("preset : TEXPATH => #{ENV['TEXPATH']}")
+ report("preset : TEXOS => #{ENV['TEXOS']}")
+ report("preset : TEXMFOS => #{ENV['TEXMFOS']}")
+ report("preset : TMP => #{ENV['TMP']}")
+ report('')
end
-def 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]}")
+def loadfile(filename)
+ begin
+ IO.readlines(filename).each do |line|
+ case line.chomp
+ when /^[\#\%]/ then
+ # comment
+ when /^(.*?)\s*(\>|\=|\<)\s*(.*)\s*$/ then
+ # = assign | > prepend | < append
+ key, how, value = $1, $2, $3
+ begin
+ # $SAFE = 0
+ value.gsub!(/\%(.*?)\%/) do
+ ENV[$1] || ''
+ end
+ # value.gsub!(/\;/,$separator) if key =~ /PATH/i then
+ case how
+ when '=', '<<' then ENV[key] = value
+ when '?', '??' then ENV[key] = ENV[key] || value
+ when '<', '+=' then ENV[key] = (ENV[key] || '') + $separator + value
+ when '>', '=+' then ENV[key] = value + $separator + (ENV[key] ||'')
+ end
+ rescue
+ report("user set failed : #{key} (#{$!})")
+ else
+ report("user set : #{key} => #{ENV[key]}")
end
- end
+ end
+ end
+ rescue
+ report("error in reading file '#{filename}'")
+ end
+end
+
+def loadtree(tree)
+ begin
+ unless tree.empty? then
+ if File.directory?(tree) then
+ setuptex = File.join(tree,'setuptex.tmf')
+ else
+ setuptex = tree.dup
+ end
+ if FileTest.file?(setuptex) then
+ report("tex tree definition: #{setuptex}")
+ checkenvironment(File.dirname(setuptex))
+ loadfile(setuptex)
else
report("no setup file '#{setuptex}'")
end
- rescue
end
+ rescue
+ # maybe tree is empty or boolean (no arg given)
+ end
+end
+
+def loadenvironment(environment)
+ begin
+ unless environment.empty? then
+ filename = if $path.empty? then environment else File.expand_path(File.join($path,environment)) end
+ if FileTest.file?(filename) then
+ report("environment : #{environment}")
+ loadfile(filename)
+ else
+ report("no environment file '#{environment}'")
+ end
+ end
+ rescue
+ report("problem while loading '#{environment}'")
+ end
+end
+
+def show_environment
+ if $showenv then
+ keys = ENV.keys.sort
+ size = 0
+ keys.each do |k|
+ size = k.size if k.size > size
+ end
+ report('')
+ keys.each do |k|
+ report("#{k.rjust(size)} => #{ENV[k]}")
+ end
+ report('')
end
end
@@ -752,38 +2396,54 @@ def execute(arguments)
$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'] || ''
+ $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'] || false
+ $arguments = $directives['arguments'] || ''
+ $execute = $directives['execute'] || $directives['exec'] || false
+ $locate = $directives['locate'] || false
+
+ $autotree = if $directives['autotree'] then (ENV['TEXMFSTART_TREE'] || ENV['TEXMFSTARTTREE'] || '') else '' end
+
+ $path = $directives['path'] || ''
+ $tree = $directives['tree'] || $autotree || ''
+ $environment = $directives['environment'] || ''
+
+ $make = $directives['make'] || false
+ $remove = $directives['remove'] || $directives['delete'] || false
+ $unix = $directives['unix'] || false
+ $windows = $directives['windows'] || $directives['mswin'] || false
+ $stubpath = $directives['stubpath'] || ''
+ $indirect = $directives['indirect'] || false
+
+ $before = $directives['before'] || ''
+ $after = $directives['after'] || ''
+
+ $iftouched = $directives['iftouched'] || false
+ $ifchanged = $directives['ifchanged'] || false
+
+ $openoffice = $directives['oo'] || false
+ $crossover = false if $directives['clear']
- $make = $directives['make'] || false
- $unix = $directives['unix'] || false
- $windows = $directives['windows'] || false
- $stubpath = $directives['stubpath'] || ''
- $indirect = $directives['indirect'] || false
+ $showenv = $directives['showenv'] || false
+ $verbose = true if $showenv
- $before = $directives['before'] || ''
- $after = $directives['after'] || ''
+ $serve = $directives['serve'] || false
- $iftouched = $directives['iftouched'] || false
+ $verbose = true if (ENV['_CTX_VERBOSE_'] =~ /(y|yes|t|true|on)/io) && ! $locate && ! $report
- $openoffice = $directives['oo'] || false
+ # private:
- $crossover = false if $directives['clear']
+ $selfmerge = $directives['selfmerge'] || false
+ $selfcleanup = $directives['selfclean'] || $directives['selfcleanup'] || false
ENV['_CTX_VERBOSE_'] = 'yes' if $verbose
@@ -805,21 +2465,49 @@ def execute(arguments)
end
end
- if $help || ! $filename || $filename.empty? then
+ if $selfmerge then
+ output("ruby libraries are cleaned up") if SelfMerge::cleanup
+ output("ruby libraries are merged") if SelfMerge::merge
+ elsif $selfcleanup then
+ output("ruby libraries are cleaned up") if SelfMerge::cleanup
+ elsif $serve then
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ # # kpse_merge_done: require 'base/kpseremote'
+ begin
+ KpseRemote::start_server
+ rescue
+ end
+ else
+ usage
+ puts("")
+ puts("message : set 'KPSEMETHOD' and 'KPSEPORT' variables")
+ end
+ elsif $help || ! $filename || $filename.empty? then
usage
- checktree($tree)
+ loadtree($tree)
+ loadenvironment($environment)
+ show_environment()
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)
+ loadtree($tree)
+ loadenvironment($environment)
+ show_environment()
+ if $make || $remove then
+ if $filename == 'all' then
+ makelist = $makelist
else
- make($filename,$mswindows,!$mswindows)
+ makelist = [$filename]
+ end
+ makelist.each do |filename|
+ if $windows then
+ make(filename,true,false,$remove)
+ elsif $unix then
+ make(filename,false,true,$remove)
+ else
+ make(filename,$mswindows,!$mswindows,$remove)
+ end
end
elsif $browser && $filename =~ /^http\:\/\// then
launch($filename)
@@ -832,8 +2520,12 @@ def execute(arguments)
edit($filename)
else # script: or no prefix
command = find(shortpathname($filename),$program)
- register("THREAD",File.dirname(File.expand_path(command)))
- run(command)
+ if command then
+ register("THREAD",File.dirname(File.expand_path(command)))
+ run(command)
+ else
+ report('unable to locate program')
+ end
end
end
rescue
@@ -845,3 +2537,5 @@ def execute(arguments)
end
execute(ARGV)
+
+exit (if ($?.to_i rescue 0) > 0 then 1 else 0 end)
diff --git a/Master/texmf-dist/scripts/context/ruby/textools.rb b/Master/texmf-dist/scripts/context/ruby/textools.rb
index 78982f175a1..99338834736 100644
--- a/Master/texmf-dist/scripts/context/ruby/textools.rb
+++ b/Master/texmf-dist/scripts/context/ruby/textools.rb
@@ -13,7 +13,7 @@
# This script will harbor some handy manipulations on tex
# related files.
-banner = ['TeXTools', 'version 1.2.2', '2002/2005', 'PRAGMA ADE/POD']
+banner = ['TeXTools', 'version 1.3.1', '2002/2006', 'PRAGMA ADE/POD']
unless defined? ownpath
ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
@@ -41,6 +41,69 @@ class Commands
include CommandBase
+ def tpmmake
+ if filename = @commandline.argument('first') then
+ filename = File.join('tpm',filename) unless filename =~ /^tpm[\/\\]/
+ filename += '.tpm' unless filename =~ /\.tpm$/
+ if FileTest.file?(filename) then
+ data = IO.read(filename) rescue ''
+ data, fn, n = calculate_tpm(data,"TPM:RunFiles")
+ data, fm, m = calculate_tpm(data,"TPM:DocFiles")
+ data = replace_tpm(data,"TPM:Size",n+m)
+ report("total size #{n+m}")
+ begin
+ File.open(filename, 'w') do |f|
+ f << data
+ end
+ rescue
+ report("unable to save '#{filename}'")
+ else
+ report("file '#{filename}' is updated")
+ filename = File.basename(filename).sub(/\..*$/,'')
+ zipname = sprintf("%s-%04i.%02i.%02i%s",filename,Time.now.year,Time.now.month,Time.now.day,'.zip')
+ File.delete(zipname) rescue true
+ report("zipping file '#{zipname}'")
+ system("zip -r -9 -q #{zipname} #{[fn,fm].flatten.join(' ')}")
+ end
+ else
+ report("no file '#{filename}'")
+ end
+ end
+ end
+
+ def calculate_tpm(data, tag='')
+ size, ok = 0, Array.new
+ data.gsub!(/<#{tag}.*>(.*?)<\/#{tag}>/m) do
+ content = $1
+ files = content.split(/\s+/)
+ files.each do |file|
+ unless file =~ /^\s*$/ then
+ if FileTest.file?(file) then
+ report("found file #{file}")
+ size += FileTest.size(file) rescue 0
+ ok << file
+ else
+ report("missing file #{file}")
+ end
+ end
+ end
+ "<#{tag} size=\"#{size}\">#{content}</#{tag}>"
+ end
+ [data, ok, size]
+ end
+
+ def replace_tpm(data, tag='', txt='')
+ data.gsub(/(<#{tag}.*>)(.*?)(<\/#{tag}>)/m) do
+ $1 + txt.to_s + $3
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
def hidemapnames
report('hiding FontNames in map files')
xidemapnames(true)
@@ -871,6 +934,8 @@ commandline.registeraction('downcasefilenames', '[--recurse] [--force]') # not y
commandline.registeraction('stripformfeeds' , '[--recurse] [--force]') # not yet documented
commandline.registeraction('showfont' , 'filename')
+commandline.registeraction('tpmmake' , 'tpm file (run in texmf root)')
+
commandline.registeraction('help')
commandline.registeraction('version')
diff --git a/Master/texmf-dist/scripts/context/ruby/texutil.rb b/Master/texmf-dist/scripts/context/ruby/texutil.rb
new file mode 100644
index 00000000000..da9a192054d
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/texutil.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.arguments.join(' ')}")
+ end
+
+ def purgeallfiles
+ system("texmfstart ctxtools --purge --all #{@commandline.arguments.join(' ')}")
+ end
+
+ def documentation
+ system("texmfstart ctxtools --document #{@commandline.arguments.join(' ')}")
+ end
+
+ def analyzefile
+ system("texmfstart pdftools --analyze #{@commandline.arguments.join(' ')}")
+ end
+
+ def filterpages # obsolete
+ system("texmfstart ctxtools --purge #{@commandline.arguments.join(' ')}")
+ end
+
+ def figures
+ report("this code is not yet converted from perl to ruby")
+ end
+
+ def logfile
+ report("this code is not yet converted from perl to ruby")
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+# main feature
+
+commandline.registeraction('references', 'convert tui file into tuo file')
+
+# todo features
+
+commandline.registeraction('figures', 'generate figure dimensions file')
+commandline.registeraction('logfile', 'filter essential log messages')
+
+# backward compatibility features
+
+commandline.registeraction('purgefiles', 'remove most temporary files')
+commandline.registeraction('purgeallfiles', 'remove all temporary files')
+commandline.registeraction('documentation', 'generate documentation file from source')
+commandline.registeraction('analyzefile', 'analyze pdf file')
+
+# old feature, not needed any longer due to extension of pdftex
+
+commandline.registeraction('filterpages')
+
+# generic features
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.registerflag('verbose')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/tmftools.rb b/Master/texmf-dist/scripts/context/ruby/tmftools.rb
index d125c5cae69..074e949d139 100644
--- a/Master/texmf-dist/scripts/context/ruby/tmftools.rb
+++ b/Master/texmf-dist/scripts/context/ruby/tmftools.rb
@@ -21,10 +21,11 @@
# tmftools --analyze > kpsewhat.log
# tmftools --analyze --strict > kpsewhat.log
# tmftools --analyze --delete --force "texmf-local/fonts/.*/somename"
+# tmftools --serve
# the real thing
-banner = ['TMFTools', 'version 1.0.0 (experimental, no help yet)', '2005', 'PRAGMA ADE/POD']
+banner = ['TMFTools', 'version 1.1.0 (experimental, no help yet)', '2005/2006', 'PRAGMA ADE/POD']
unless defined? ownpath
ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
@@ -33,27 +34,58 @@ 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
+ # require 'base/kpseremote'
+ # if KpseRemote::available? then
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ require 'base/kpseremote'
+ k = KpseRemote.new
+ else
+ k = nil
+ end
+ if k && k.okay? then
+ k.progname = @commandline.option('progname')
+ k.engine = @commandline.option('engine')
+ k.format = @commandline.option('format')
+ else
+ require 'base/kpsefast'
+ k = KpseFast.new
+ k.rootpath = @commandline.option('rootpath')
+ k.treepath = @commandline.option('treepath')
+ k.progname = @commandline.option('progname')
+ k.engine = @commandline.option('engine')
+ k.format = @commandline.option('format')
+ k.diskcache = @commandline.option('diskcache')
+ k.renewcache = @commandline.option('renewcache')
+ k.load_cnf
+ k.expand_variables
+ k.load_lsr
+ end
return k
end
+ def serve
+ if ENV['KPSEMETHOD'] && ENV['KPSEPORT'] then
+ require 'base/kpseremote'
+ begin
+ KpseRemote::start_server
+ rescue
+ end
+ end
+ end
+
+ def reload
+ begin
+ init_kpse.load
+ rescue
+ end
+ end
+
def main
if option = @commandline.option('expand-braces') and not option.empty? then
puts init_kpse.expand_braces(option)
@@ -128,6 +160,9 @@ commandline.registerflag('verbose')
commandline.registeraction('help')
commandline.registeraction('version')
+commandline.registeraction('reload', 'reload file database')
+commandline.registeraction('serve', 'act as kpse server')
+
commandline.expand
Commands.new(commandline,logger,banner).send(commandline.action || 'main')
diff --git a/Master/texmf-dist/scripts/context/ruby/www/admin.rb b/Master/texmf-dist/scripts/context/ruby/www/admin.rb
new file mode 100644
index 00000000000..8983ca2b77c
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/www/admin.rb
@@ -0,0 +1,215 @@
+require 'fileutils'
+
+require 'www/lib'
+require 'www/dir'
+require 'www/common'
+
+class WWW
+
+ include Common
+
+ # klopt nog niet, twee keer task met een verschillend doel
+
+ def handle_exatask
+ # case @session.check('task', request_variable('task'))
+ task, options, option = @session.get('task'), @session.get('option').split(@@re_bar), request_variable('option')
+ option = (options.first || '') if option.empty?
+ case task
+ when 'exaadmin'
+ @session.set('status', 'admin') # admin: status|dir
+ touch_session(@session.get('id'))
+ if options.include?(option) then
+ case option
+ when 'status' then handle_exaadmin_status
+ when 'dir' then handle_exaadmin_dir
+ else handle_exaadmin_status
+ end
+ elsif option.empty? then
+ message('Status', "unknown option")
+ else
+ message('Status', "option '#{option}' not permitted #{options.inspect}")
+ end
+ else
+ message('Status', "unknown task '#{task}'")
+ end
+ end
+
+ def handle_exaadmin
+ if id = valid_session() then
+ handle_exatask
+ else
+ message('Status', 'no login')
+ end
+ end
+
+ def handle_exaadmin_dir
+ check_template_file('exalogin','exalogin-template.htm')
+ @interface.set('path:docroot', work_root)
+ @interface.set('dir:uri', 'exaadmin') # forces the dir handler into cgi mode
+ @interface.set('dir:task', 'exaadmin') # forces the dir handler into cgi mode
+ @interface.set('dir:option', 'dir') # forces the dir handler into cgi mode
+ filename = "#{@@session_prefix}#{request_variable('path')}"
+ fullname = File.join(work_root,filename)
+ if request_variable('path').empty? then
+ handle_exaadmin_status
+ elsif FileTest.directory?(fullname) then
+ handle_dir(filename, [], false)
+ elsif File.zero?(fullname) then
+ message('Error', "The file '#{filename}' is empty")
+ elsif File.size?(fullname) > (4 * 1024 * 1024) then
+ if FileTest.file?(File.expand_path(File.join(cache_root,filename))) then
+ str = "<br/><br/>Cached alternative: <a href=\"#{File.join('cache',filename)}\">#{File.basename(filename)}</a>"
+ else
+ str = ''
+ end
+ message('Error', "The file '#{filename}' is too big to serve over cgi." + str)
+ else
+ send_file(fullname)
+ end
+ end
+
+ def handle_exaadmin_status
+ check_template_file('exalogin','exalogin-template.htm')
+ begin
+ n, str, lines, list, start, most, least, cached = 0, '', '', Hash.new, Time.now, 0, 0, false
+ filename = File.join(tmp_path(dirname),'sessions.rbd')
+ begin
+ File.open(filename) do |f|
+ list = Marshal.load(f)
+ end
+ rescue
+ cached, list = false, Hash.new
+ else
+ cached = true
+ end
+ files = Dir.glob("{#{work_roots.join(',')}}/#{@@session_prefix}*.ses")
+ list.keys.each do |l|
+ list.delete(l) unless files.include?(l) # slow
+ end
+ files.each do |f|
+ ctime = File.ctime(f)
+ stime = list[f][0] == ctime rescue 0
+ unless ctime == stime then
+ begin
+ hash = load_session_file(f)
+ rescue
+ else
+ list[f] = [ctime,hash]
+ end
+ end
+ end
+ begin
+ File.open(filename,'w') do |f|
+ f << Marshal.dump(list)
+ end
+ rescue
+ # no save
+ end
+ begin
+ keys = list.keys.sort do |a,b|
+ case list[b][0] <=> list[a][0]
+ when -1 then -1
+ when +1 then +1
+ else
+ a <=> b
+ end
+ end
+ rescue
+ keys = list.keys.sort
+ end
+ totaltime, totaldone = 0.0, 0
+ if keys.length > 0 then
+ keys.each do |entry|
+ s, t, session = entry, list[entry][0], list[entry][1]
+ status = session['status'] || ''
+ runtime = (session['runtime'] || '').to_f rescue 0
+ starttime = (start.to_i-session['starttime'].to_i).to_s rescue ''
+ requesttime = session['endtime'].to_i-session['starttime'].to_i rescue 0
+ requesttime = if requesttime > 0 then requesttime.to_s else '' end
+ if runtime > 0.0 then
+ totaltime += runtime
+ totaldone += 1
+ if least > 0 then
+ if runtime < least then least = runtime end
+ else
+ least = runtime
+ end
+ if most > 0 then
+ if runtime > most then most = runtime end
+ else
+ most = runtime
+ end
+ end
+ if status.empty? then
+ # skip, garbage
+ elsif status =~ /^(|exa)admin/o then
+ # skip, useless
+ else
+ begin
+ lines << "<tr>\n"
+ lines << td("<a href=\"exaadmin?option=dir&path=#{session['id']}.dir\">#{session['id']}</a>")
+ lines << td(status)
+ lines << td(session['timeout'])
+ lines << td(starttime)
+ lines << td(session['runtime'])
+ lines << td(requesttime)
+ lines << td(t.strftime("%H:%M:%S %Y-%m-%d"))
+ lines << td(session['domain'])
+ lines << td(session['project'])
+ lines << td(session['username'])
+ lines << td(File.basename(File.dirname(s)))
+ lines << "</tr>\n"
+ rescue
+ else
+ n += 1
+ end
+ end
+ end
+ if n > 0 then
+ str = "<table cellpadding='0'>\n"
+ str << "<tr>\n"
+ str << th('session identifier')
+ str << th('status')
+ str << th('timeout')
+ str << th('time')
+ str << th('runtime')
+ str << th('total')
+ str << th('modification&nbsp;time')
+ str << th('domain')
+ str << th('project')
+ str << th('username')
+ str << th('process')
+ str << "</tr>\n"
+ str << lines
+ str << "</table>\n"
+ end
+ end
+ rescue
+ message('Status', "#{$!} There is currently no status available.", false, @@admin_refresh, 'exaadmin')
+ else
+ if n > 0 then
+ # r = if n > 100 then 60 else @@admin_refresh.to_i end # scanning takes long
+ r = @@admin_refresh
+ average = "average = #{if totaldone > 0 then sprintf('%.02f',totaltime/totaldone) else '0' end} (#{sprintf('%.02f',least)} .. #{sprintf('%.02f',most)})"
+ sessions = "sessions = #{n}"
+ refresh = "refresh = #{r.to_s} sec"
+ loadtime = "loadtime = #{sprintf('%.04f',Time.now-start)} sec"
+ cached = if cached then "cached" else "not cached" end
+ message("Status | #{sessions} | #{refresh} | #{loadtime} - #{cached} | #{average} |", str, false, r, 'exaadmin')
+ else
+ message('Status', "There are no sessions registered.", false, @@admin_refresh, 'exaadmin')
+ end
+ end
+ end
+
+ private
+
+ def th(str)
+ "<th align='left'>#{str}&nbsp;&nbsp;&nbsp;</th>\n"
+ end
+
+ def td(str)
+ "<td><code>#{str || ''}&nbsp;&nbsp;&nbsp</code></td>\n"
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/www/common.rb b/Master/texmf-dist/scripts/context/ruby/www/common.rb
new file mode 100644
index 00000000000..9c383229456
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/www/common.rb
@@ -0,0 +1,80 @@
+# We cannot chdir in threads because it is something
+# process wide, so we will run into problems with the
+# other threads. The same is true for the global ENV
+# pseudo hash, so we cannot communicate the runpath
+# via an anvironment either. This leaves texmfstart
+# in combination with a path directive and an tmf file.
+
+module Common # can be a mixin
+
+ # we assume that the hash.subset method is defined
+
+ @@re_texmfstart = /^(texmfstart|ruby\s*texmfstart.rb)\s*(.*)$/
+ @@re_texmfpath = /^\-\-path\=/
+
+ def command_string(path,command,log='')
+ runner = "texmfstart --path=#{File.expand_path(path)}"
+ if command =~ @@re_texmfstart then
+ cmd, arg = $1, $2
+ if arg =~ @@re_texmfpath then
+ # there is already an --path (first switch)
+ else
+ command = "#{runner} #{arg}"
+ end
+ else
+ command = "#{runner} bin:#{command}"
+ end
+ if log && ! log.empty? then
+ return "#{command} 2>&1 > #{File.expand_path(File.join(path,log))}"
+ else
+ return command
+ end
+ end
+
+ def set_os_vars
+ begin
+ ENV['TEXOS'] = ENV['TEXOS'] || platform
+ rescue
+ ENV['TEXOS'] = 'texmf-linux'
+ else
+ ENV['TEXOS'] = 'texmf-' + ENV['TEXOS'] unless ENV['TEXOS'] =~ /^texmf\-/
+ ensure
+ ENV['EXA:TEXOS'] = ENV['TEXOS']
+ end
+ end
+
+ def set_environment(hash)
+ set_os_vars
+ paths = ENV['PATH'].split(File::PATH_SEPARATOR)
+ hash.subset('binpath:').keys.each do |key|
+ begin
+ paths << File.expand_path(hash[key])
+ rescue
+ end
+ end
+ ENV['PATH'] = paths.uniq.join(File::PATH_SEPARATOR)
+ hash.subset('path:').keys.each do |path|
+ key, value = "EXA:#{path.upcase}", File.expand_path(hash[path])
+ ENV[key] = value
+ end
+ end
+
+ def save_environment(hash,path,filename='request.tmf')
+ begin
+ File.open(File.join(path,filename),'w') do |f|
+ set_os_vars
+ ['EXA:TEXOS','TEXOS'].each do |key|
+ f.puts("#{key} = #{ENV[key]}")
+ end
+ hash.subset('binpath:').keys.each do |key|
+ f.puts("PATH < #{File.expand_path(@interface.get(key))}")
+ end
+ hash.subset('path:').keys.each do |path|
+ f.puts("EXA:#{path.upcase} = #{File.expand_path(@interface.get(path))}")
+ end
+ end
+ rescue
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/www/dir.rb b/Master/texmf-dist/scripts/context/ruby/www/dir.rb
new file mode 100644
index 00000000000..09e088d772d
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/www/dir.rb
@@ -0,0 +1,155 @@
+require 'www/lib'
+
+# dir handling
+
+class WWW
+
+ # borrowed code from webrick demo, patched
+
+ @@dir_name_width = 25
+
+ def handle_dir(dirpath=@variables.get('path'),hidden=[],showdirs=true)
+ check_template_file('dir','text-template.htm')
+ docroot = @interface.get('path:docroot')
+ dirpath = dirpath || ''
+ hidden = [] unless hidden
+ local_path = dirpath.dup
+ title, str = "Index of #{escaped(dirpath)}", ''
+ begin
+ local_path.gsub!(/[\/\\]+/,'/')
+ local_path.gsub!(/\/$/, '')
+ if local_path !~ /^(\.|\.\.|\/|[a-zA-Z]\:)$/io then # maybe also /...
+ full_path = File.join(docroot,local_path)
+ @interface.set('log:dir', full_path)
+ begin
+ list = Dir::entries(full_path)
+ rescue
+ str << "unable to parse #{local_path}"
+ else
+ if list then
+ list.collect! do |name|
+ if name =~ /^\.+/o then
+ nil # no . and ..
+ else
+ st = (File::stat(File.join(docroot,local_path,name)) rescue nil)
+ if st.nil? then
+ [name, nil, -1, false]
+ elsif st.directory? then
+ if showdirs then [name + "/", st.mtime, -1, true] else nil end
+ elsif hidden.length > 0 then
+ if hidden.include?(name) then nil else [name, st.mtime, st.size, false] end
+ else
+ [name, st.mtime, st.size, false]
+ end
+ end
+ end
+ list.compact!
+ n, m, s = @variables.get('n'), @variables.get('m'), @variables.get('s')
+ if ! n.empty? then
+ idx, d0 = 0, n
+ elsif ! m.empty? then
+ idx, d0 = 1, m
+ elsif ! s.empty? then
+ idx, d0 = 2, s
+ else
+ idx, d0 = 0, 'a'
+ end
+ d1 = if d0 == 'a' then 'd' else 'a' end
+ if d0 == 'a' then
+ list.sort! do |a,b| a[idx] <=> b[idx] end
+ else
+ list.sort! do |a,b| b[idx] <=> a[idx] end
+ end
+ u = dir_uri(@variables.get('path') || '.')
+ str << "<div class='dir-view'>\n<pre>\n"
+ str << "<a href=\"#{u}&n=#{d1}\">name</A>".ljust(49+u.length)
+ str << "<a href=\"#{u}&m=#{d1}\">last modified</A>".ljust(41+u.length)
+ str << "<a href=\"#{u}&s=#{d1}\">size</A>".rjust(31+u.length) << "\n" << "\n"
+ # parent path
+ if showdirs && ! hidden.include?('..') then
+ dname = "parent directory"
+ fname = "#{File.dirname(dirpath)}"
+ time = File::mtime(File.join(docroot,local_path,"/.."))
+ str << dir_entry(fname,dname,time,-1,true)
+ str << "\n"
+ end
+ # directories
+ done = false
+ list.each do |name, time, size, dir|
+ if dir then
+ if name.size > @@dir_name_width then
+ dname = name.sub(/^(.#{@@dir_name_width-2})(.*)/) do $1 + ".." end
+ else
+ dname = name
+ end
+ fname = "#{escaped(dirpath)}/#{escaped(name)}"
+ str << dir_entry(fname,dname,time,size,dir)
+ done = true
+ end
+ end
+ str << "\n" if done
+ # files
+ list.each do |name, time, size, dir|
+ unless dir then
+ if name.size > @@dir_name_width then
+ dname = name.sub(/^(.#{@@dir_name_width-2})(.*)/) do $1 + ".." end
+ else
+ dname = name
+ end
+ fname = "#{escaped(dirpath)}/#{escaped(name)}"
+ str << dir_entry(fname,dname,time,size,dir)
+ end
+ end
+ str << "\n"
+ str << '</pre></div>'
+ else
+ str << 'no info'
+ end
+ end
+ else
+ str << 'no access'
+ end
+ rescue
+ str << "error #{$!}<br/><pre>"
+ str << $@.join("\n")
+ str << "</pre>"
+ end
+ message(title,str)
+ end
+ def dir_uri(f='.')
+ u, t, o = @interface.get('dir:uri'), @interface.get('dir:task'), @interface.get('dir:option') # takes precedence, in case we run under cgi control
+ if u.empty? then
+ u, t, o = @interface.get('process:uri'), '', ''
+ elsif ! t.empty? then
+ t = "task=#{t}&"
+ o = "option=#{o}&"
+ end
+ if u && ! u.empty? then
+ u = u.sub(/\?.*$/,'') # frozen string
+ if f =~ /^\.+$/ then
+ "#{u}?#{t}#{o}path="
+ else
+ "#{u}?#{t}#{o}path=#{f}"
+ end
+ else
+ ''
+ end
+ end
+
+ def dir_entry(fname,dname,time,size,dir=false)
+ if dir then
+ f = fname.sub(/\/+$/,'').sub(/^\/+/,'')
+ s = "<a href=\"#{dir_uri(f)}\">#{dname}</a>"
+ elsif ! @interface.get('dir:uri').empty? then # takes precedence, in case we run under cgi control
+ s = "<a href=\"#{dir_uri(fname.gsub(/\/+/,'/'))}\">#{dname}</a>"
+ else
+ s = "<a href=\"#{fname.gsub(/\/+/,'/')}\">#{dname}</a>"
+ end
+ # s << " " * (30 - dname.size)
+ s << " " * (@@dir_name_width + 5 - dname.size)
+ s << (time ? time.strftime("%Y/%m/%d %H:%M ") : " " * 22)
+ s << (size >= 0 ? size.to_s : "-").rjust(12) << "\n"
+ return s
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/www/exa.rb b/Master/texmf-dist/scripts/context/ruby/www/exa.rb
new file mode 100644
index 00000000000..c6d59d86434
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/www/exa.rb
@@ -0,0 +1,386 @@
+require 'fileutils'
+require 'www/lib'
+require 'www/dir'
+require 'www/common'
+require 'www/admin'
+
+class WWW
+
+ include Common
+
+ def handle_exadefault
+ check_template_file('exalogin','exalogin-template.htm')
+ if id = logged_in_session(true) then
+ finish_login
+ else
+ message('Error', 'No default login permitted.')
+ end
+ end
+
+ def handle_exalogin
+ check_template_file('exalogin','exalogin-template.htm')
+ if id = logged_in_session(false) then
+ finish_login
+ else
+ message('Error', 'No default login permitted.')
+ end
+ end
+
+ def finish_login
+ get_gui()
+ filename, path, task = @session.get('gui'), @session.checked('path','.'), @session.get('task')
+ if ! task.empty? then
+ save_session
+ handle_exatask
+ elsif filename and not filename.empty? then
+ save_session
+ fullname = filename.gsub(/\.\./,'')
+ fullname = File.join(path,filename) unless FileTest.file?(fullname)
+ fullname = File.join(@interface.get('path:interfaces'), filename) unless FileTest.file?(fullname)
+ fullname = File.join(@interface.get('path:interfaces'), path, filename) unless FileTest.file?(fullname)
+ if FileTest.file?(fullname) then
+ send_file(fullname,true)
+ else
+ message('Interface', 'Invalid interface request, no valid interface file.' )
+ end
+ else
+ message('Interface', 'Invalid interface request, no default interface file.')
+ end
+ end
+
+ def handle_exainterface()
+ check_template_file('text','exalogin-template.htm')
+ if id = valid_session() then
+ filename = @interface.get('process:uri').to_s # kind of dup
+ if ! filename.empty? && filename.sub!(/^.*\//,'') then
+ path = @session.checked('path', '.')
+ fullname = filename.gsub(/\.\./,'')
+ fullname = File.join(path,filename) unless FileTest.file?(fullname)
+ fullname = File.join(@interface.get('path:interfaces'),filename) unless FileTest.file?(fullname)
+ fullname = File.join(@interface.get('path:interfaces'),path,filename) unless FileTest.file?(fullname)
+ if FileTest.file?(fullname) then
+ save_session
+ send_file(fullname,true)
+ else
+ get_file(filename)
+ filename, path = @session.get('gui'), @session.checked('path','.')
+ if filename and not filename.empty? then
+ save_session
+ fullname = filename.gsub(/\.\./,'')
+ fullname = File.join(path,filename) unless FileTest.file?(fullname)
+ fullname = File.join(@interface.get('path:interfaces'),filename) unless FileTest.file?(fullname)
+ fullname = File.join(@interface.get('path:interfaces'),path,filename) unless FileTest.file?(fullname)
+ send_file(fullname,true) if FileTest.file?(fullname)
+ else
+ message('Interface', 'Invalid interface request, no interface file.')
+ end
+ end
+ else
+ message('Interface', 'Invalid interface request, no resource file.')
+ end
+ else
+ message('Interface', 'Invalid interface request, no login.')
+ end
+ end
+
+ def handle_exarequest() # todo: check if request is 'command'
+ check_template_file('exalogin','exalogin-template.htm')
+ if id = client_session() then
+ client = true
+ @interface.set('log:kind', "remote client request: #{id}")
+ elsif id = valid_session() then
+ client = false
+ @interface.set('log:kind', "remote browser request: #{id}")
+ else
+ client, id = false, nil
+ @interface.set('log:kind', 'unknown kind of request')
+ end
+ if id then
+ dir, tmp = dirname, tmp_path(dirname)
+ requestname, replyname = 'request.exa', 'reply.exa'
+ requestfile, replyfile = File.join(tmp,requestname), File.join(tmp,replyname)
+ lockfile = File.join(dirname,lckname)
+ action, filename, command, url, req = '', '', '', '', ''
+ extract_sent_files(tmp)
+ @variables.each do |key, value|
+ case key
+ when 'exa:request' then
+ req = value.dup
+ when 'exa:action' then
+ action = value.dup
+ # when 'exa:command' then
+ # command = value.dup
+ # when 'exa:url' then
+ # url = value.dup
+ when 'exa:filename' then
+ filename = value.dup
+ when 'exa:threshold' then
+ @interface.set('process:threshold', value.dup)
+ when /^fakename/o then
+ @variables.set(key, File.basename(value))
+ when /^filename\-/o then
+ @variables.set(key, filename = File.basename(value))
+ when /^dataname\-/o then
+ @variables.set(key)
+ else # remove varname- prefix from value
+ @variables.set(key, @variables.get(key).sub(/#{key}\-/,''))
+ end
+ end
+ @variables.check('exa:filename', filename)
+ @variables.check('exa:action', action)
+ if @variables.empty?('exa:filename') then
+ @variables.set('exa:filename', @interface.get('log:attachments').split('|').first || '')
+ end
+ req.gsub!(/<exa:data\s*\/>/i, '')
+ dat = "<exa:data>\n"
+ @variables.each do |key, value|
+ if ['password','exa:request'].include?(key) then
+ # skip
+ elsif ! value || value.empty? then
+ dat << "<exa:variable label='#{key}'/>\n"
+ else # todo: escape 'm
+ dat << "<exa:variable label='#{key}'>#{value}</exa:variable>\n"
+ end
+ end
+ dat << "</exa:data>\n"
+ if req.empty? then
+ req << "<?xml version='1.0' ?>\n"
+ req << "<exa:request #{@@namespace}'>\n"
+ req << "<exa:application>\n"
+ req << "<exa:action>'#{action}</exa:action>\n" unless action.empty?
+ # req << "<exa:command>'#{command}</exa:command>\n" unless command.empty?
+ # req << "<exa:url>'#{url}</exa:url>\n" unless url.empty?
+ req << "<exa:application>\n"
+ req << "<exa:comment>constructed request</exa:comment>\n"
+ req << dat
+ req << "</exa:request>\n"
+ else
+ # better use rexml but slower
+ if req =~ /<exa:request[^>]*>.*?\s*<exa:threshold>\s*(.*?)\s*<\/exa:threshold>\s*.*?<\/exa:request>/mois then
+ threshold = $1
+ unless threshold.empty? then
+ @interface.set('process:threshold', threshold)
+ @session.set('threshold', threshold)
+ end
+ end
+ req.sub!(/(<exa:request[^>]*>.*?)\s*<exa:option>\s*\-\-action\=(.*?)\s*<\/exa:option>\s*(.*?<\/exa:request>)/mois) do
+ pre, act, pos = $1, $2, $3
+ action = act.sub(/\.exa$/,'') if action.empty?
+ str = "#{pre}<exa:action>#{action}</exa:action>#{pos}"
+ str.sub(/\s*<exa:command>.*?<\/exa:command>\s*/mois ,'')
+ end
+ req.sub!(/(<exa:request[^>]*>.*?)<exa:action>\s*(.*?)\s*<\/exa:action>(.*?<\/exa:request>)/mois) do
+ pre, act, pos = $1, $2, $3
+ action = act.sub(/\.exa$/,'') if action.empty?
+ str = "#{pre}<exa:action>#{action}</exa:action>#{pos}"
+ str.sub(/\s*<exa:command>.*?<\/exa:command>\s*/mois ,'')
+ end
+ unless req =~ /<exa:data>(.*?)<\/exa:data>/mois then
+ req.sub!(/(<\/exa:request>)/) do dat + $1 end
+ end
+ end
+ req.sub!(/<exa:filename>.*?<\/exa:filename>/mois, '')
+ unless @variables.empty?('exa:filename') then
+ req.sub!(/(<\/exa:application>)/mois) do
+ "<exa:filename>#{@variables.get('exa:filename')}<\/exa:filename>" + $1
+ end
+ end
+ @variables.set('exa:action', action)
+ @interface.set("log:#{requestname}", req)
+ begin
+ File.open(requestfile,'w') do |f|
+ f << req
+ end
+ rescue
+ message('Error', 'There is a problem in handling this request (working path access).')
+ return
+ end
+ File.delete(replyfile) rescue false
+ @interface.set('log:action',action)
+ get_command(action)
+ logdata = ''
+ begin
+ command = @session.get('command')
+ @interface.set('log:command',if command.empty? then '[no command]' else command end)
+ if ! command.empty? then
+ @session.set('starttime', Time.now.to_i.to_s) # can be variables and in save list
+ if @interface.true?('process:background') then
+ # background
+ @session.set('status', 'running: background')
+ @session.set('maxtime', @interface.get('process:timeout'))
+ @session.set('threshold', @interface.get('process:threshold'))
+ save_session
+ timeout(@@watch_delay) do
+ save_environment(@interface,tmp)
+ begin
+ starttime = File.mtime(@session_file)
+ # crap
+ loop do
+ sleep(1)
+ if starttime != File.mtime(@session_file) then
+ break unless FileTest.file?(lockfile)
+ end
+ end
+ rescue TimeoutError
+ if client then
+ send_reply()
+ else
+ message('Status', 'Processing your request takes a while',true,5,'exastatus')
+ end
+ return
+ rescue
+ end
+ end
+ if client then send_reply() else send_result() end
+ else
+ # foreground
+ status = 'running: foreground'
+ @session.set('status', status)
+ @session.set('maxtime', @interface.get('process:timeout'))
+ @session.set('threshold', @interface.get('process:threshold'))
+ save_session
+ timeout(@interface.get('process:timeout').to_i) do
+ begin
+ status = 'running: foreground'
+ set_environment(@interface)
+ save_environment(@interface,tmp)
+ command = command_string(tmp,command)
+ logdata = `#{command}`
+ rescue TimeoutError
+ status = 'running: timeout'
+ logdata = "timeout: #{@interface.get('process:timeout')} seconds"
+ rescue
+ status = 'running: aborted'
+ logdata = 'fatal runtime error'
+ else
+ @session.set('endtime', Time.now.to_i.to_s)
+ status = 'running: finished'
+ end
+ end
+ @session.set('status', status)
+ save_session
+ case @session.get('status')
+ when 'running: finished' then
+ if client then send_reply(logdata) else send_result(logdata) end
+ when 'running: timeout' then
+ message('Error', 'There is a problem in handling this request (timeout).')
+ when 'running: aborted' then
+ message('Error', 'There is a problem in handling this request (aborted).')
+ else
+ message('Error', 'There is a problem in handling this request (unknown).')
+ end
+ end
+ else
+ message('Error', 'There is a problem in handling this request (no runner).')
+ end
+ rescue
+ message('Error', 'There is a problem in handling this request (no run).' + $!)
+ end
+ else
+ message('Error', 'Invalid session.')
+ end
+ end
+
+ def handle_exacommand() # shares code with exarequest
+ check_template_file('exalogin','exalogin-template.htm')
+ if id = client_session() then
+ client = true
+ @interface.set('log:kind', "remote client request: #{id}")
+ elsif id = valid_session() then
+ client = false
+ @interface.set('log:kind', "remote browser request: #{id}")
+ else
+ client, id = false, nil
+ @interface.set('log:kind', 'unknown kind of request')
+ end
+ if id then
+ dir, tmp = dirname, tmp_path(dirname)
+ requestname, replyname = 'request.exa', 'reply.exa'
+ requestfile, replyfile = File.join(tmp,requestname), File.join(tmp,replyname)
+ req, command, url = '', '', ''
+ @variables.each do |key, value|
+ case key
+ when 'exa:request' then
+ req = value.dup
+ when 'exa:command' then
+ command = value.dup
+ when 'exa:threshold' then
+ @interface.set('process:threshold', value.dup)
+ when 'exa:url' then
+ url = value.dup
+ end
+ end
+ unless req.empty? then
+ # better use rexml but slower / reuse these : command = filter_from_request('exa:command')
+ if req =~ /<exa:request[^>]*>.*?\s*<exa:command>\s*(.*?)\s*<\/exa:command>\s*.*?<\/exa:request>/mois then
+ command = $1
+ end
+ if req =~ /<exa:request[^>]*>.*?\s*<exa:url>\s*(.*?)\s*<\/exa:url>\s*.*?<\/exa:request>/mois then
+ url = $1
+ end
+ if req =~ /<exa:request[^>]*>.*?\s*<exa:threshold>\s*(.*?)\s*<\/exa:threshold>\s*.*?<\/exa:request>/mois then
+ threshold = $1
+ unless threshold.empty? then
+ @interface.set('process:threshold', threshold)
+ @session.set('threshold', threshold)
+ end
+ end
+ end
+ @variables.check('exa:command', command)
+ @variables.check('exa:url', url)
+ File.delete(replyfile) rescue false
+ case @variables.get('exa:command')
+ when 'fetch' then
+ if @variables.empty?('exa:url') then
+ message('Error', "Problems with fetching, no file given")
+ else
+ # the action starts here
+ filename = @variables.get('exa:url').to_s # kind of dup
+ unless filename.empty? then
+ get_path(filename) # also registers filename as url
+ path = @session.checked('path', '')
+ fullname = filename.gsub(/\.\./,'')
+ fullname = File.join(path,fullname) unless path.empty?
+ if FileTest.file?(fullname) then
+ if client then
+ send_url(fullname)
+ else
+ send_file(fullname,true)
+ end
+ @session.set('threshold', @interface.get('process:threshold'))
+ @session.set('url',filename)
+ save_session
+ else
+ message('Error', "Problems with fetching, unknown file #{fullname}.")
+ # message('Error', "Problems with fetching, unknown file #{filename}.")
+ end
+ else
+ message('Error', "Problems with fetching, invalid file #{filename}.")
+ end
+ # and ends here
+ end
+ else
+ message('Error', "Invalid command #{command}.")
+ end
+ else
+ message('Error', 'Invalid session.')
+ end
+ end
+
+ def handle_exastatus
+ if request_variable('id').empty? then
+ if id = valid_session() then
+ send_result()
+ else
+ message('Error', 'Invalid session.')
+ end
+ else
+ if id = valid_session() then
+ send_reply()
+ else
+ send_reply('invalid session')
+ end
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/www/lib.rb b/Master/texmf-dist/scripts/context/ruby/www/lib.rb
new file mode 100644
index 00000000000..542c31166d3
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/www/lib.rb
@@ -0,0 +1,1399 @@
+#!/usr/bin/env ruby
+
+# This is just a simple environment for remote processing of context
+# files. It's not a framework, nor an example of how that should be done.
+# Nowadays there are environments like Rails or Nitro. Maybe some day I'll
+# give one of them a try.
+
+# <META Http-Equiv="Cache-Control" Content="no-cache">
+# <META Http-Equiv="Pragma" Content="no-cache">
+# <META Http-Equiv="Expires" Content="0">
+
+# we make limited use of cgi methods because we also need to handle webrick
+
+# %var% as well as $(var) are supported
+
+# paths need to be expanded before they enter apache, since .. is not
+# handled by default
+
+require 'base/variables'
+
+require 'ftools'
+require 'fileutils'
+require 'tempfile'
+require 'timeout'
+require 'md5'
+require 'digest/md5'
+require 'cgi' # we also need escaping for webrick (could move it here)
+
+# beware, namespaces have to match !
+
+module XML
+
+ def XML::element(tag,attributes=nil)
+ if attributes.class == Hash then
+ if block_given? then
+ XML::element(tag,XML::attributes(attributes)) do yield end
+ else
+ XML::element(tag,XML::attributes(attributes))
+ end
+ else
+ if block_given? then
+ "<#{tag}#{if attributes && ! attributes.empty? then ' ' + attributes end}>#{yield}</#{tag}>"
+ else
+ "<#{tag}#{if attributes && ! attributes.empty? then ' ' + attributes end}/>"
+ end
+ end
+ end
+
+ def XML::attributes(hash)
+ str = ''
+ hash.each do |k,v|
+ str << ' ' unless str.empty?
+ if v =~ /\'/ then
+ str << "#{k}=\"#{v}\""
+ else
+ str << "#{k}=\'#{v}\'"
+ end
+ end
+ return str
+ end
+
+ def XML::create(version='1.0')
+ "<?version='#{version}'?>#{yield || ''}"
+ end
+
+ def XML::line
+ "\n"
+ end
+
+end
+
+# str =
+ # XML::create do
+ # XML::element('test') do
+ # XML::element('test') do
+ # 'text a'
+ # end +
+ # XML::element('test',XML::attributes({'a'=>'b'})) do
+ # XML::element('nested',XML::attributes({'a'=>'b'})) do
+ # 'text b-1'
+ # end +
+ # XML::element('nested',XML::attributes({'a'=>'b'})) do
+ # 'text b-2'
+ # end
+ # end +
+ # XML::element('nested',{'a'=>'b'}) do
+ # 'text c'
+ # end
+ # end
+ # end
+
+class ExtendedHash
+
+ DEFAULT = 'default'
+
+ @@re_default = /^(default|)$/i
+
+ def default?(key)
+ self[key] =~ @@re_default rescue true # unset, empty or 'default'
+ end
+
+ def default(key)
+ self[key] = DEFAULT
+ end
+
+ def match?(key,value)
+ value == '*' || value == self[key]
+ end
+
+
+end
+
+class WWW
+
+ @@session_prefix = ''
+ @@data_file = 'example.cfg'
+ @@session_max_age = 60*60
+ @@watch_delay = 30
+ @@send_threshold = 2*1024*1024
+ @@admin_refresh = 10
+ @@namespace = "http://www.pragma-ade.com/schemas/example.rng"
+
+ @@re_bar = /\s*\|\s*/
+ @@re_lst = /\s*\,\s*/
+ @@re_var_a = /\%(.*?)\%/
+ @@re_var_b = /\$\((.*?)\)/
+
+ attr_reader :variables
+ attr_writer :variables
+
+ @@paths = [
+ 'configurations',
+ 'data',
+ 'distributions',
+ 'documents',
+ 'interfaces',
+ 'logs',
+ 'resources',
+ 'runners',
+ 'scripts',
+ 'templates',
+ 'work']
+
+ @@re_true = /^\s*(YES|ON|TRUE|1)\s*$/io
+ @@re_false = /^\s*(NO|OFF|FALSE|0)\s*$/io
+
+ def initialize(webrick_daemon=nil,webrick_request=nil,webrick_response=nil)
+ @session_id, @session_file = '', ''
+ @cgi, @cgi_cookie = nil, nil
+ @webrick_daemon, @webrick_request, @webrick_response = webrick_daemon, webrick_request, webrick_response
+
+ @interface = ExtendedHash.new
+ @variables = ExtendedHash.new
+ @session = ExtendedHash.new
+
+ @checked = false
+
+ analyze_request()
+ update_interface()
+
+ @interface.set('template:message' , 'exalogin-template.htm')
+ @interface.set('template:status' , 'exalogin-template.htm')
+ @interface.set('template:login' , 'exalogin.htm')
+ @interface.set('process:timeout' , @@session_max_age)
+ @interface.set('process:threshold' , @@send_threshold)
+ @interface.set('process:background', 'yes') # this demands a watchdog being active
+ @interface.set('process:indirect' , 'no') # indirect download, no direct feed
+ @interface.set('process:autologin' , 'yes') # provide default interface when applicable
+ @interface.set('process:exaurl' , '') # this one will be used as replacement in templates
+ @interface.set('trace:run' , 'no')
+ @interface.set('trace:errors' , 'no')
+ @interface.set('process:os' , platform)
+ @interface.set('process:texos' , 'texmf-' + platform)
+
+ @interface.set('trace:run' , 'yes') if (ENV['EXA:TRACE:RUN'] || '') =~ @@re_true
+ @interface.set('trace:errors' , 'yes') if (ENV['EXA:TRACE:ERRORS'] || '') =~ @@re_true
+
+ yield self if block_given?
+ end
+
+ def set(key,value)
+ @interface.set(key,value)
+ end
+ def get(key)
+ @interface.get(key,value)
+ end
+
+ def 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
+
+ def check_cgi
+ # when mod_ruby is used, we need to close
+ # the cgi session explicitly
+ unless @webrick_request then
+ unless @cgi then
+ @cgi = CGI.new('html4')
+ at_exit do
+ begin
+ @cgi.close
+ rescue
+ end
+ end
+ end
+ end
+ end
+
+ def request_variable(key)
+ begin
+ if @webrick_request then
+ [@webrick_request.query[key]].flatten.first.to_s
+ else
+ check_cgi
+ [@cgi.params[key]].flatten.first.to_s
+ end
+ rescue
+ ''
+ end
+ end
+
+ def request_cookie(key)
+ begin
+ if @cgi then
+ if str = @cgi.cookies[key] then
+ return str.first || ''
+ end
+ elsif @webrick_request then
+ @webrick_request.cookies.flatten.each do |cookie|
+ if cookie.name == key then
+ return cookie.value unless cookie.value.empty?
+ end
+ end
+ end
+ rescue
+ end
+ return ''
+ end
+
+ def analyze_request
+ if @webrick_request then
+ @interface.set('path:docroot', @webrick_daemon.config[:DocumentRoot] || './documents')
+ @interface.set('process:uri', @webrick_request.request_uri.to_s)
+ # @interface.set('process.url', [@webrick_request.host,@webrick_request.request_port].join(':'))
+ @cgi = nil
+ @webrick_request.query.each do |key, value|
+ # todo: filename
+ @variables.set(key, [value].flatten.first)
+ end
+ else
+ @interface.set('path:docroot', ENV['DOCUMENT_ROOT'] || './documents')
+ @interface.set('process:uri', ENV['REQUEST_URI'] || '')
+ # @interface.set('process.url', [ENV['SERVER_NAME'],ENV['SERVER:PORT']].join(':'))
+ ARGV[0] = '' # get rid of terminal mode
+ check_cgi
+ # quite fragile, due to changes between 1.6 and 1.8
+ @cgi.params.keys.each do |p|
+ if @cgi[p].respond_to?(:original_filename) then
+ @interface.set('log:method','post')
+ if @cgi[p].original_filename && ! @cgi[p].original_filename.empty? then
+ @variables.set(p, File.basename(@cgi[p].original_filename))
+ else
+ case @cgi.params[p].class
+ when StringIO.class then @variables.set(p, @cgi[p].read)
+ when Array.class then @variables.set(p, @cgi[p].first.to_s)
+ when String.class then @variables.set(p, @cgi[p])
+ when Tempfile.class then @variables.set(p, '[data blob]')
+ end
+ end
+ else
+ @interface.set('log:method','get') unless @interface.get('log:method') == 'post'
+ @variables.set(p, [@cgi.params[p]].flatten.first.to_s)
+ end
+ end
+ end
+ @interface.set('path:root', File.dirname(@interface.get('path:docroot')))
+ end
+
+ # name in calling script takes precedence
+ # one can set template:whatever as well
+ # todo: in config
+
+ def check_template_file(tag='',filename='exalogin-template.htm')
+ @interface.set('file:template', filename) if @interface.get('file:template').empty?
+ @interface.set('tag:template', tag)
+ @interface.set('file:template', @interface.get('tag:template')) unless @interface.get('tag:template').empty?
+ end
+
+ def update_interface()
+ root = @interface.get('path:docroot')
+ @interface.set('path:docroot', File.expand_path("#{root}"))
+ @@paths.each do |path|
+ @interface.set("path:#{path}", File.expand_path("#{root}/../#{path}"))
+ end
+ @interface.set('file:template', @interface.get('tag:template')) unless @interface.get('tag:template').empty?
+ end
+
+ def indirect?(result)
+ size = FileTest.size?(result) || 0
+ @interface.true?('trace:errors') || @interface.true?('trace:run') || @interface.true?('process:indirect') ||
+ ((! @interface.empty?('process:threshold')) && (size > @interface.get('process:threshold').to_i)) ||
+ ((! @session.empty?('threshold')) && (size > @session.get('threshold').to_i))
+ end
+
+end
+
+# files
+
+class WWW
+
+ def sesname
+ File.basename(@session_file)
+ end
+ def dirname
+ File.basename(@session_file.sub(/ses$/,'dir'))
+ end
+ def lckname
+ File.basename(@session_file.sub(/ses$/,'lck'))
+ end
+
+ def work_root(expand=true)
+ p = if expand then File.expand_path(@interface.get('path:work')) else @interface.get('path:work') end
+ if @interface.true?('process:background') then
+ File.join(@interface.get('path:work'),'watch')
+ else
+ File.join(@interface.get('path:work'),'direct')
+ end
+ end
+
+ def work_roots(expand=true)
+ p = if expand then File.expand_path(@interface.get('path:work')) else @interface.get('path:work') end
+ [File.join(@interface.get('path:work'),'watch'),File.join(@interface.get('path:work'),'direct')]
+ end
+
+ def cache_root(expand=true)
+ p = if expand then File.expand_path(@interface.get('path:work')) else @interface.get('path:work') end
+ File.join(@interface.get('path:work'),'cache')
+ end
+
+ def cleanup_path(dir)
+ FileUtils::rm_r(pth) rescue false
+ end
+
+ def tmp_path(dir)
+ @interface.set('path:templates', File.expand_path(@interface.get('path:templates'))) # to be sure; here ? ? ?
+ pth = File.join(work_root,dir)
+ File.makedirs(pth) rescue false
+ pth
+ end
+
+ def locked?(lck)
+ FileTest.file?(lck)
+ end
+
+end
+
+# sessions
+
+class WWW
+
+ @@session_tags = ['id','domain','project','username','password','gui','path','process','command','filename','action','status', 'starttime','endtime','runtime','task','option','threshold','url'].sort
+ @@session_keep = ['id','domain','project','username','password','process'].sort
+ @@session_reset = @@session_tags - @@session_keep
+
+ def new_session()
+ if @variables.empty?('exa:session') then
+ @session_id = new_session_id
+ else
+ @session_id = @variables.get('exa:session')
+ end
+ if @session_id == 'default' then # ???
+ @session_id = new_session_id
+ end
+ @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses")
+ register_session
+ return @session_id
+ end
+
+ def reset_session(all=false)
+ (if all then @@session_tags else @@session_reset end).each do |k|
+ @session.set(k)
+ end
+ end
+
+ def valid_session
+ @session_id = request_variable('id')
+ if @session_id.empty? then
+ begin
+ if @cgi then
+ if @session_id = @cgi.cookies['session_id'] then
+ @session_id = @session_id.first || ''
+ else
+ @session_id = ''
+ end
+ elsif @webrick_request then
+ @webrick_request.cookies.flatten.each do |cookie|
+ if cookie.name == 'session_id' then
+ unless cookie.value.empty? then
+ @session_id = cookie.value
+ # break
+ end
+ end
+ end
+ else
+ @session_id = ''
+ end
+ rescue
+ @interface.set('log:session',"[error in request #{$!}]")
+ return false
+ end
+ end
+ if @session_id.empty? then
+ @interface.set('log:session','[no id, check work dir permissions]')
+ return false
+ else
+ @interface.set('log:session',@session_id)
+ load_session
+ if ! @session.empty?('domain') && ! @session.empty?('project') && ! @session.empty?('username') then
+ register_session
+ return @session_id
+ else
+ return false
+ end
+ end
+ end
+
+ def touch_session(id=nil)
+ begin
+ t = Time.now
+ File.utime(t,t,File.join(work_root,"#{@@session_prefix}#{id || @session_id}.ses")) rescue false
+ rescue
+ false
+ end
+ end
+
+ def forced_session
+ @session_id = new_session
+ if @session_id.empty? then
+ @interface.set('log:session','[no id, check work dir permissions]')
+ return false
+ else
+ return check_session
+ end
+ end
+
+ def client_session
+ request, done = @variables.get('exa:request'), false
+ request.sub!(/(^.*<exa:request[^>]*>.*?)\s*<exa:client>\s*(.*)\s*<\/exa:client>\s*(.*?<\/exa:request>.*$)/mio) do
+ pre, client, post = $1, $2, $3
+ client.scan(/<exa:(domain|project|username|password)>(.*?)<\/exa:\1>/mio) do
+ @variables.set($1, $2)
+ end
+ done = true
+ pre + post
+ end
+ if done then
+ return forced_session
+ else
+ return nil
+ end
+ end
+
+ def register_session
+ if @cgi then
+ @cgi_cookie = CGI::Cookie::new(
+ 'name' => 'session_id',
+ 'value' => @session_id,
+ 'expires' => Time.now + @interface.get('process:timeout').to_i
+ )
+ # @cgi_cookie = CGI::Cookie::new('session_id',@session_id)
+ elsif @webrick_response then
+ if cookie = WEBrick::Cookie.new('session_id', @session_id) then
+ cookie.expires = Time.now + @interface.get('process:timeout').to_i
+ cookie.max_age = @interface.get('process:timeout').to_i
+ cookie.comment = 'exa identifier'
+ @webrick_response.cookies.clear
+ @webrick_response.cookies << cookie
+ end
+ end
+ end
+
+ def new_session_id # taken from cgi
+ md5 = Digest::MD5::new
+ now = Time::now
+ md5.update(now.to_s)
+ md5.update(String(now.usec))
+ md5.update(String(rand(0)))
+ md5.update(String($$))
+ md5.update('foobar')
+ @new_session = true
+ md5.hexdigest[0,32] # was 16
+ end
+
+ @@hide_passwords = true
+ HIDDEN = 'hidden'
+
+ def same_passwords(password) # password in cfg file
+ if @@hide_passwords && (@session.get('password') == HIDDEN) && (@session_id == @session.get('id')) then
+ # this condition is only true when a same session id is found and
+ # the password is checked once and set to HIDDEN
+ same = true
+ elsif password =~ /^MD5:/ then
+ # so, one cannot send a known encrypted password since it will be
+ # encrypted twice then
+ same = (password == "MD5:" + MD5.new(@session.get('password')).hexdigest.upcase)
+ else
+ if (@session.default?('domain') && @session.default?('project') && @session.default?('username')) then
+ @session.default('password') # is this safe enough?
+ end
+ same = (password == @session.get('password'))
+ end
+ if @@hide_passwords && same then
+ @session.set('password', HIDDEN)
+ save_session # next time this session is ok anyway
+ end
+ return same
+ end
+
+ @@session_line = /^\s*(?![\#\%])(.*?)\s*\=\s*(.*?)\s*$/o
+ @@session_begin = 'begin exa session'
+ @@session_end = 'end exa session'
+
+ def loaded_session_data(filename)
+ begin
+ if data = IO.readlines(filename) then
+ return data if (data.first =~ /^[\#\%]\s*#{@@session_begin}/o) && (data.last =~ /^[\#\%]\s*#{@@session_end}/o)
+ end
+ rescue
+ end
+ return nil
+ end
+
+ def load_session()
+ begin
+ @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses")
+ if data = loaded_session_data(@session_file) then
+ data.each do |line|
+ if line =~ @@session_line then
+ @session.set($1, $2 || '')
+ end
+ end
+ else
+ return false
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def load_session_file(filename)
+ begin
+ if data = loaded_session_data(filename) then
+ session = Hash.new
+ data.each do |line|
+ if line =~ @@session_line then
+ session[$1] = $2 || ''
+ end
+ end
+ else
+ Hash.new
+ end
+ rescue
+ Hash.new
+ else
+ session
+ end
+ end
+
+ def save_session
+ begin
+ unless @session_id.empty? then
+ @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses")
+ @session_file = File.join(work_root,"#{@@session_prefix}#{@session_id}.ses")
+ File.open(@session_file,'w') do |f|
+ f << "\# #{@@session_begin}\n"
+ @@session_tags.each do |tag|
+ if @session && @session.key?(tag) then
+ if ! @session.get(tag).empty? then # no one liner, fails
+ f << "#{tag}=#{@session.get(tag)}\n"
+ end
+ elsif @variables.key?(tag) && ! @variables.empty?(key) then
+ f << "#{tag}=#{@variables.get(tag)}\n"
+ end
+ end
+ @session.subset("ENV").keys.each do |tag|
+ f << "#{tag}=#{@session.get(tag)}\n"
+ end
+ f << "\# #{@@session_end}\n"
+ end
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+
+ def logged_in_session(force_default=false)
+ if force_default || (@variables.default?('domain') && @variables.default?('project') && @variables.default?('username')) then
+ id = default_session
+ else
+ id = check_session
+ end
+ end
+
+ def default_session
+ if @interface.true?('process:autologin') then
+ @variables.default('domain')
+ @variables.default('project')
+ @variables.default('username')
+ @variables.default('password')
+ check_session
+ else
+ @session_id = nil
+ end
+ end
+
+ def check_session
+ @session.set('domain', @variables.get('domain').downcase)
+ @session.set('project', @variables.get('project').downcase)
+ @session.set('username', @variables.get('username').downcase)
+ @session.set('password', @variables.get('password').downcase)
+ new_session
+ @session.set('id', @session_id)
+ save_session
+ return @session_id
+ end
+
+ def delete_session(id=nil)
+ File.delete(work_root,"#{@@session_prefix}#{id || @session_id}.ses") rescue false
+ end
+
+ def cleanup_sessions(max_age=nil)
+ begin
+ now, age = Time.now, (max_age||@interface.get('process:timeout')).to_i
+ Dir.glob("{#{work_root},#{cache_root}/#{@@session_prefix}*").each do |s|
+ begin
+ if (now - File.mtime(s)) > age then
+ if FileTest.directory?(s) then
+ FileUtils::rm_r(s)
+ else
+ File.delete(s)
+ end
+ end
+ rescue
+ # maybe purged in the meantime
+ end
+ end
+ rescue
+ # maybe another process is busy
+ end
+ end
+
+end
+
+# templates
+
+class WWW
+
+ def filled_template(title,text,showtime=false,refresh=0,refreshurl=nil)
+ template = @interface.get("template:#{@interface.get('tag:template')}")
+ template = @interface.get("template:status") if template.empty?
+ fullname = File.join(@interface.get('path:templates'),template)
+ @interface.set('log:templatename',template)
+ @interface.set('log:templatefile',fullname)
+ append_status(text)
+ htmreply = ''
+ if FileTest.file?(fullname) then
+ begin
+ htmreply = IO.read(fullname)
+ rescue
+ htmreply = ''
+ end
+ end
+ if refresh>0 then
+ if refreshurl then
+ metadata = "<meta http-equiv='refresh' content='#{refresh};#{refreshurl}'>"
+ else
+ metadata = "<meta http-equiv='refresh' content='#{refresh}'>"
+ end
+ else
+ metadata = ''
+ end
+ if ! htmreply || htmreply.empty? then
+ # in head: <link rel='stylesheet' href='/exaresource/exastyle.css'>
+ htmreply = <<-EOD
+ <html>
+ #{metadata}
+ <head>
+ <title>#{title}</title>
+ </head>
+ <body>
+ <h2>#{title}</h2>
+ <h4>#{Time.now}</h4>
+ #{text}
+ </body>
+ </html>
+ EOD
+ else
+ if showtime then
+ exa_template = "<h1>#{title}</h1>\n<h2>#{Time.now}</h2>\n#{text}\n"
+ else
+ exa_template = "<h1>#{title}</h1>#{text}\n"
+ end
+ htmreply = replace_template_placeholder(htmreply,exa_template,metadata)
+ end
+ htmreply
+ end
+
+ def message(title,str='',showtime=false,refresh=0,refreshurl=nil)
+ if @cgi then
+ @cgi.out("cookie"=>[@cgi_cookie]) do
+ filled_template(title,str,showtime,refresh,refreshurl)
+ end
+ elsif @webrick_response then
+ @webrick_response['content-type'] = 'text/html'
+ @webrick_response.body = filled_template(title,str,showtime,refresh,refreshurl)
+ else
+ filled_template(title,str,showtime,refresh,refreshurl)
+ end
+ end
+
+ def plaintext(str)
+ if @cgi then
+ @cgi.out('cookie'=>[@cgi_cookie],'content-type'=>'text/plain') do
+ str
+ end
+ elsif @webrick_response then
+ @webrick_response['content-type'] = 'text/plain'
+ @webrick_response.body = str
+ else
+ str
+ end
+ end
+
+ def exareply(status='',url='',size='',comment='')
+ exaurl = @interface.get('process:exaurl')
+ str = "<?xml version='1.0'?>\n\n"
+ str << "<exa:reply xmlns:exa='#{@@namespace}'>\n"
+ str << " <exa:session>#{@session_id}</exa:session>\n" unless @session_id.empty?
+ str << " <exa:status>#{status}</exa:status>\n" unless (status || '').empty?
+ str << " <exa:url>#{exaurl}/#{url}</exa:url>\n" unless (url || '').empty?
+ str << " <exa:size>#{size}</exa:size>\n" unless (size || '').empty?
+ str << " <exa:comment>#{comment}</exa:comment>\n" unless (comment|| '').empty?
+ str << "</exa:reply>\n"
+ return str
+ end
+
+ def append_status(str='')
+ if @interface.true?('trace:errors') then
+ if $! && $@ then
+ str << "<br/><br/><br/><em>Error:</em><br/><pre>#{$!}</pre><pre>"
+ str << $@.join("\n")
+ end
+ str << '<br/><br/><br/>'
+ str << status_data
+ str << '<em>Paths</em><br/>'
+ str << '<pre>'
+ @interface.subset('path:').each do |k,v|
+ if FileTest.directory?(v) then
+ if FileTest.writable?(v) then
+ str << "#{v} exists and is writable\n"
+ else
+ str << "#{v} is not writable\n"
+ end
+ else
+ str << "#{v} does not exist\n"
+ end
+ end
+ str << '</pre>'
+ end
+ str
+ end
+
+ def simpleurl(url)
+ if url then url.sub(/(:80|:443)$/,'') else '' end
+ end
+
+ def replace_exa_placeholders(data)
+ data.gsub(/([\"\'])\@exa\_([a-zA-Z0-9\-\_]+)\1/) do
+ quot, key, value = $1, $2, ''
+ begin
+ value = @variables.get(key)
+ rescue
+ value = ''
+ end
+ quot + value + quot
+ end
+ end
+
+ def replace_url_placeholder(data)
+ data.gsub!(/(http:\/\/|\/+)*\@exa\_main\_url/, @interface.get('process:exaurl'))
+ replace_exa_placeholders(data)
+ end
+
+ def replace_template_placeholder(data,template='',metadata='')
+ data.gsub!(/(http:\/\/|\/+)*\@exa\_main\_url/, @interface.get('process:exaurl'))
+ data.gsub!(/\@exa\_template/, template)
+ data.gsub!(/\@exa\_metadata/, metadata)
+ replace_exa_placeholders(data)
+ end
+
+ def escaped(str)
+ str
+ end
+
+end
+
+# send files
+
+class WWW
+
+ def send_file(filename,parse=false) # this can take a lot of memory, look for alternative (fastcgi ?)
+ begin
+ if filename =~ /\.pdf$/ then
+ mimetype, parse = 'application/pdf', false
+ elsif filename =~ /\.(html|htm)$/ then
+ mimetype, parse = 'text/html', true
+ else
+ mimetype, parse = 'text/plain', false
+ end
+ if FileTest.file?(filename) then
+ if @webrick_response then
+ begin
+ @webrick_response['content-type'] = mimetype
+ @webrick_response['content-length'] = FileTest.size?(filename)
+ if parse then
+ File.open(filename, 'rb') do |f|
+ @webrick_response.body = replace_url_placeholder(f.read)
+ end
+ else
+ @webrick_response.body = File.open(filename, 'rb')
+ end
+ rescue
+ else
+ return
+ end
+ elsif @cgi then
+ begin
+ # the following works ok, but stores the whole file in memory (see @cgi.out)
+ #
+ # File.open(filename, 'rb') do |f|
+ # @cgi.out('cookie'=>[@cgi_cookie],'connection'=>'close', 'length'=>File.size(filename), 'type'=>mimetype) do
+ # if parse then replace_url_placeholder(f.read) else f.read end
+ # end
+ # end
+ if parse then
+ File.open(filename, 'rb') do |f|
+ @cgi.out('cookie'=>[@cgi_cookie],'connection'=>'close', 'length'=>File.size(filename), 'type'=>mimetype) do
+ replace_url_placeholder(f.read)
+ end
+ end
+ else
+ @cgi.print(@cgi.header('cookie'=>[@cgi_cookie],'connection'=>'close', 'length'=>File.size(filename), 'type'=>mimetype))
+ File.open(filename, 'rb') do |f|
+ while str = f.gets do
+ @cgi.print(str)
+ end
+ end
+ end
+ rescue
+ else
+ return
+ end
+ end
+ end
+ rescue
+ end
+ message('Error', "There is a problem with sending file #{File.basename(filename)}.")
+ end
+
+ def send_htmlfile(filename,parse=false)
+ send_file(filename,parse)
+ end
+ def send_pdffile(filename) # this can take a lot of memory, look for alternative (fastcgi ?)
+ send_file(filename,false)
+ end
+
+end
+
+# tracing
+
+class WWW
+
+ def show_vars(a=@variables,title='')
+ if a && a.length > 0 then
+ if title.empty? then
+ str = ''
+ else
+ str = "<em>#{title}</em>"
+ end
+ str << "<br/><pre>\n"
+ a.keys.sort.each do |k|
+ if k && a[k] && ! a[k].empty? then
+ if k == 'password' then
+ val = if a[k] == 'default' then 'default' else '******' end
+ else
+ # str << "#{k} => #{a[k].sub(/^\s+/moi,'').sub(/\s+$/moi,'')}\n"
+ val = a[k].to_s.strip
+ val.gsub!("&","&amp;")
+ val.gsub!("<","&lt;")
+ val.gsub!(">","&gt;")
+ val.gsub!("\n","\n ")
+ end
+ str << "#{k} => #{val}\n"
+ end
+ end
+ str << "</pre><br/>\n"
+ return str
+ else
+ return ''
+ end
+ end
+
+ def status_data
+ show_vars(@session , 'Session' ) +
+ show_vars(@variables, 'Variables' ) +
+ show_vars(@interface, 'Interface' ) +
+ show_vars(ENV , 'Environment')
+ end
+
+ def report_status
+ check_template_file('status')
+ message('Status',status_data)
+ end
+
+end
+
+# attachments
+
+class WWW
+
+ def extract_sent_files(dir)
+ files = Array.new
+ if @cgi then
+ @cgi.params.keys.each do |tag|
+ begin
+ if filename = @cgi[tag].original_filename then
+ files << extract_file_content(dir,filename,@cgi[tag]) unless filename.empty?
+ end
+ rescue
+ end
+ end
+ elsif @webrick_request then
+ @webrick_request.query.keys.each do |tag|
+ begin
+ if filename = @webrick_request.query[tag].filename then
+ files << extract_file_content(dir,filename,@webrick_request.query[tag]) unless filename.empty?
+ end
+ rescue
+ end
+ end
+ end
+ @interface.set('log:attachments', files.compact.uniq.join('|'))
+ end
+
+ def extract_file_content(dir,filename,data)
+ filename = File.join(dir,File.basename(filename))
+ begin
+ @interface.set('log:attachclass', data.class.inspect)
+ if data.class == Tempfile then
+ begin
+ File.copy(data.path,filename)
+ rescue
+ begin
+ File.open(filename,'wb') do |f|
+ File.open(data.path,'rb') do |g|
+ while str = g.gets do
+ f.write(str)
+ end
+ end
+ end
+ rescue
+ @interface.set('log:attachstate', "saving tempfile #{filename} failed (#{$!})")
+ else
+ @interface.set('log:attachstate', "tempfile #{filename} has been saved")
+ end
+ else
+ @interface.set('log:attachstate', "#{data.path} copied to #{filename}")
+ end
+ elsif data.class == String then
+ begin
+ File.open(filename,'wb') do |f|
+ f.write(data)
+ end
+ rescue
+ @interface.set('log:attachstate', "saving string #{filename} failed (#{$!})")
+ else
+ @interface.set('log:attachstate', "string #{filename} has been saved")
+ end
+ elsif data.class == StringIO then
+ begin
+ File.open(filename,'wb') do |f|
+ f.write(data.read)
+ end
+ rescue
+ @interface.set('log:attachstate', "saving stringio #{filename} failed (#{$!})")
+ else
+ @interface.set('log:attachstate', "stringio #{filename} has been saved")
+ end
+ else
+ @interface.set('log:attachstate', "unknown attachment class #{data.class.to_s}")
+ end
+ rescue
+ begin File.delete(filename) ; rescue ; end
+ else
+ begin File.delete(filename) if FileTest.size(filename) == 0 ; rescue ; end
+ end
+ return File.basename(filename)
+ end
+
+end
+
+# configuration
+
+class WWW
+
+ def interface_base_name(str)
+ str.sub(/\.(pdf|htm|html)$/, '')
+ end
+
+ def located_interface_file(filename)
+ ['configurations', 'runners', 'scripts'].each do |tag|
+ datafile = File.join(@interface.get("path:#{tag}"),filename)
+ if FileTest.file?(datafile+'.encrypted') then
+ return datafile + '.encrypted'
+ elsif FileTest.file?(datafile) then
+ return datafile
+ end
+ end
+ return nil
+ end
+
+ def load_interface_file(filename=@@data_file)
+ reset_session() # no save yet
+ if datafile = located_interface_file(filename) then
+ nestedfiles = Array.new
+ begin
+ data = IO.read(datafile) || ''
+ unless data.empty? then
+ loop do # we need to load them recursively
+ done = false
+ data.gsub!(/^include\s*:\s*(.*?)\s*$/) do
+ includedname, done = $1, true
+ if nestedname = located_interface_file(includedname) then
+ begin
+ str = ("\n" + IO.read(nestedname) + "\n") || ''
+ rescue
+ nestedfiles << File.basename('-'+includedname)
+ ''
+ else
+ nestedfiles << File.basename('+'+includedname)
+ str
+ end
+ else
+ nestedfiles << File.basename('-'+includedname)
+ ''
+ end
+ end
+ break unless done
+ end
+ end
+ @interface.set('log:configurationfile', datafile + ' [' + nestedfiles.join(' ') + ']')
+ return data
+ rescue
+ end
+ end
+ @interface.set('log:configurationfile', filename + ' [not loaded]')
+ return nil
+ end
+
+ def fetch_session_interface_variables(data)
+ data.scan(/^variable\s*:\s*(.*?)\s*\=\s*(.*?)\s*$/) do
+ @interface.set($1, $2)
+ end
+ return true
+ end
+
+ def fetch_session_project_list(data)
+ projectlist, permitted = Array.new, false
+ data.scan(/^user\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*\,\s*(.*?)\s*$/) do
+ domain, username, password, projects = $1, $2, $3, $4
+ if @session.match?('domain',domain) && @session.match?('username',username) then
+ if same_passwords(password) then
+ projectlist, permitted = @interface.resolved(projects).split(@@re_bar), true
+ break
+ end
+ end
+ end
+ if permitted then
+ @interface.set('log:projectlist', '['+projectlist.join(' ')+']')
+ if projectlist.length == 0 then
+ return nil
+ else
+ return projectlist
+ end
+ else
+ @interface.set('log:projectlist', '[no projects]')
+ return nil
+ end
+ end
+
+ def fetch_session_command(data)
+ data.scan(/^process\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*$/) do
+ domain, process, command = $1, $2, $3
+ if @session.match?('domain',domain) && @session.match?('process',process) then
+ @session.set('command', @interface.resolved(command))
+ end
+ end
+ return @session.get('command')
+ end
+
+ def fetch_session_settings(data)
+ data.scan(/^setting\s*:\s*(.*?)\s*\,\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*$/) do
+ domain, process, variable, value = $1, $2, $3, $4
+ if @session.match?('domain',domain) && @session.match?('process',process) then
+ @interface.set(variable,value)
+ end
+ end
+ end
+
+ def get_command(action)
+ # @session.set('action', action)
+ # if @session.get('process') == 'none' then
+ # @interface.set('log:child','yes')
+ # @session.set('process', action)
+ # end
+ if data = load_interface_file() then
+ fetch_session_interface_variables(data)
+ if projectlist = fetch_session_project_list(data) then
+ data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do
+ domain, project, gui, path, process = $1, $2, $3, $4, $5
+ if @session.match?('domain',domain) then
+ if @session.match?('project',project) then
+ if projectlist.include?(project) then
+ @session.set('process', @interface.resolved(process))
+ # break # no, else we end up in the parent (e.g. examplap instead of impose)
+ end
+ elsif ! action.empty? && project == action then
+ if projectlist.include?(action) then
+ @session.set('process', @interface.resolved(process))
+ # break # no, else we end up in the parent (e.g. examplap instead of impose)
+ end
+ end
+ end
+ end
+ fetch_session_command(data)
+ fetch_session_settings(data)
+ end
+ end
+ return ! @session.nothing?('command')
+ end
+
+ def get_file(filename)
+ @session.set('filename', filename)
+ if data = load_interface_file() then
+ fetch_session_interface_variables(data)
+ if projectlist = fetch_session_project_list(data) then
+ data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do
+ domain, project, gui, path, process = $1, $2, $3, $4, $5
+ if @session.match?('domain',domain) then
+ guilist = @interface.resolved(gui).split(@@re_bar)
+ guilist.each do |g|
+ if /#{filename}$/ =~ g then
+ @session.set('gui', File.expand_path(@interface.resolved(g)))
+ @session.set('path', File.expand_path(@interface.resolved(path)))
+ @session.set('process', process)
+ break # take first matching interface
+ end
+ end
+ end
+ end
+ end
+ end
+ return ! (@session.nothing?('gui') && @session.nothing?('path') && @session.nothing?('process'))
+ end
+
+ def get_path(url='')
+ if data = load_interface_file() then
+ fetch_session_interface_variables(data)
+ if projectlist = fetch_session_project_list(data) then
+ data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do
+ domain, project, gui, path, process = $1, $2, $3, $4, $5
+ if @session.match?('domain',domain) && @session.match?('project',project) then
+ @session.set('url', url)
+ @session.set('gui', '')
+ @session.set('path', File.expand_path(@interface.resolved(path)))
+ @session.set('process', '')
+ end
+ end
+ end
+ end
+ return ! @session.nothing?('path')
+ end
+
+ def get_gui()
+ if data = load_interface_file() then
+ fetch_session_interface_variables(data)
+ if projectlist = fetch_session_project_list(data) then
+ data.scan(/^project\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*,\s*(.*?)\s*,\s*(.*?)\s*$/) do
+ domain, project, gui, path, process = $1, $2, $3, $4, $5
+ if @session.match?('domain',domain) && @session.match?('project',project) && projectlist.include?(project) then
+ @session.set('gui', File.expand_path(@interface.resolved(gui)))
+ @session.set('path', File.expand_path(@interface.resolved(path)))
+ @session.set('process', process) unless process == 'none'
+ break # take first matching interface
+ end
+ end
+ data.scan(/^admin\s*:\s*(.*?)\s*\,\s*(.*?)\s*\=\s*(.*?)\s*\,\s*(.*?)\s*$/) do
+ domain, project, task, option = $1, $2, $3, $4
+ if @session.match?('domain',domain) && @session.match?('project',project) && projectlist.include?(project) then
+ @session.set('task', task)
+ @session.set('option', option)
+ break # take first matching task
+ end
+ end
+ end
+ end
+ return ! (@session.nothing?('gui') && @session.nothing?('path') && @session.nothing?('process'))
+ end
+
+end
+
+class WWW
+
+ def send_reply(logdata='')
+ if @interface.true?('trace:run') then
+ send_result(logdata)
+ else
+ dir, tmp = dirname, tmp_path(dirname)
+ case @session.get('status')
+ when 'running: finished' then
+ resultname, replyname = 'result.pdf', 'reply.exa'
+ replyfile = File.join(tmp,replyname)
+ if FileTest.file?(replyfile) then
+ begin
+ data = IO.read(replyfile)
+ resultname = if data =~ /<exa:output>(.*?)<\/exa:output>/ then $1 else resultname end
+ rescue
+ plaintext(exareply('error in reply'))
+ return
+ end
+ end
+ resultfile = File.join(tmp,resultname)
+ if FileTest.file?(resultfile) then
+ if indirect?(resultfile) then
+ begin
+ File.makedirs(File.join(cache_root,dir))
+ FileUtils::mv(resultfile,File.join(cache_root,dir,resultname))
+ rescue
+ plaintext(exareply('unable to access cache'))
+ else
+ plaintext(exareply('big file', "cache/#{dir}/#{resultname}", "#{File.size?(resultfile)}"))
+ end
+ else
+ send_file(resultfile)
+ end
+ else
+ plaintext(exareply('no result'))
+ end
+ else # background, running, aborted
+ plaintext(exareply(@session.get('status')))
+ end
+ end
+ end
+
+ def send_url(fullname)
+ dir, tmp = dirname, tmp_path(dirname)
+ resultname, replyname = 'result.pdf', 'reply.exa'
+ replyfile = File.join(tmp,replyname)
+ resultfile = File.join(tmp,resultname)
+ targetname = File.join(cache_root,dir,resultname)
+ # make sure that there is no target left in case of an
+ # error; needed in case of given session name
+ if FileTest.directory?(File.join(cache_root,dir)) then
+ File.delete(targetname) rescue false
+ end
+ # now try to locate the file
+ if FileTest.file?(fullname) then
+ if indirect?(fullname) then
+ begin
+ # check if directory exists and (if so) delete left overs
+ File.makedirs(File.join(cache_root,dir))
+ File.delete(targetname) rescue false
+ File.symlink(fullname,targetname) rescue message('Status',$!)
+ unless FileTest.file?(targetname) then
+ FileUtils::cp(fullname,targetname) rescue false
+ end
+ rescue
+ plaintext(exareply('unable to access cache'))
+ else
+ plaintext(exareply('big file', "cache/#{dir}/#{resultname}", "#{File.size?(fullname)}"))
+ end
+ else
+ send_file(fullname)
+ end
+ else
+ message('Status', 'The file is not found')
+ end
+ end
+
+ def send_result(logdata='')
+ check_template_file('exalogin','exalogin-template.htm')
+ dir, tmp = dirname, tmp_path(dirname)
+ resultname, replyname, logname = 'result.pdf', 'reply.exa', 'log.htm'
+ case @session.get('status')
+ when 'running: background' then
+ if st = @session.get('starttime') then # fuzzy
+ st = Time.now.to_i if st.empty?
+ if (Time.now.to_i - st.to_i) > @interface.get('process:timeout').to_i then
+ message('Status', 'Your request has been aborted (timeout)',true)
+ else
+ message('Status', 'Your request is queued',true,5,'exastatus')
+ end
+ end
+ when 'running: busy' then
+ if st = @session.get('starttime') then # fuzzy
+ st = Time.now.to_i if st.empty?
+ if (Time.now.to_i - st.to_i) > @interface.get('process:timeout').to_i then
+ message('Status', 'Your request has been aborted (timeout)',true)
+ else
+ message('Status', 'Your request is being processed',true,5,'exastatus')
+ end
+ end
+ when 'running: aborted' then
+ message('Status', 'Your request has been aborted (timeout)',true)
+ when 'running: finished' then
+ if @interface.true?('trace:run') then
+ logfile = File.join(tmp,logname)
+ begin
+ if f = File.open(logname,'w') then
+ if logdata.empty? then
+ begin
+ logdata = IO.read('www-watch.out')
+ rescue
+ logdata = 'no log data'
+ end
+ end
+ f << filled_template('Log',"<pre>#{CGI::escapeHTML(logdata)}</pre>")
+ f.close
+ end
+ rescue
+ message('Error', '')
+ end
+ if FileTest.file?(logfile) then
+ begin
+ File.makedirs(File.join(cache_root,dir))
+ FileUtils::mv(logfile,File.join(cache_root,dir,logname))
+ rescue
+ logdata = "<br/><br/>unable to access cache</a>"
+ else
+ logdata = "<br/><br/><a href='/cache/#{dir}/#{logname}'>#{logname}</a>"
+ end
+ else
+ logdata = ''
+ end
+ else
+ logdata = ''
+ end
+ # todo: generate reply.exa if no reply
+ replyfile = File.join(tmp,replyname)
+ if FileTest.file?(replyfile) then
+ begin
+ data = IO.read(replyfile)
+ resultname = if data =~ /<exa:output>(.*?)<\/exa:output>/ then $1 else resultname end
+ rescue
+ message('Error','There is a problem in handling this request (invalid reply).')
+ return
+ end
+ end
+ resultfile = File.join(tmp,resultname)
+ if FileTest.file?(resultfile) then
+ if indirect?(resultfile) then
+ begin
+ File.makedirs(File.join(cache_root,dir))
+ FileUtils::mv(resultfile,File.join(cache_root,dir,resultname))
+ rescue
+ str = "<br/><br/>unable to access cache</a>"
+ else
+ str = "<br/><br/><a href='/cache/#{dir}/#{resultname}'>#{resultname}</a>&nbsp;&nbsp;(#{File.size?(resultname)} bytes)"
+ end
+ message('Result', 'You can pick up the result here:' + str + logdata)
+ else
+ send_file(resultfile)
+ end
+ else
+ message('Error', 'There is a problem in handling this request (no result file).' + logdata)
+ end
+ end
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/www/login.rb b/Master/texmf-dist/scripts/context/ruby/www/login.rb
new file mode 100644
index 00000000000..1c88a97e6ad
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/www/login.rb
@@ -0,0 +1,13 @@
+require 'www/lib'
+
+# basic login
+
+class WWW
+
+ def handle_login()
+ check_template_file('login','exalogin.htm')
+ set('password', '')
+ message('Login','')
+ end
+
+end
diff --git a/Master/texmf-dist/scripts/context/ruby/wwwclient.rb b/Master/texmf-dist/scripts/context/ruby/wwwclient.rb
new file mode 100644
index 00000000000..8f5451a8dcf
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/wwwclient.rb
@@ -0,0 +1,677 @@
+#!/usr/bin/env ruby
+
+# a direct request is just passed on
+#
+# exaclient --direct --request=somerequest.exa --result=somefile.pdf
+#
+# in an extended request the filename in the template file is replaced by the filename
+# given on the command line; templates are located on the current path and at parent
+# directories (two levels); the filename is expanded to a full path
+#
+# exaclient --extend --template=tmicare-l-h.exa --file=somefile.xml --result=somefile.pdf
+#
+# a constructed request is build out of the provided filename and action; the filename is
+# expanded to a full path
+#
+# exaclient --construct --action=tmicare-s-h.exa --file=somefile.xml --result=somefile.pdf
+#
+# in all cases, the result is either determined by a switch or taken from a reply file
+
+banner = ['WWWClient', 'version 1.0.0', '2003-2006', 'PRAGMA ADE/POD']
+
+$: << File.dirname(File.expand_path($0))
+
+require 'base/switch'
+require 'base/logger'
+
+require 'timeout'
+require 'thread'
+require 'rexml/document'
+require 'net/http'
+
+class File
+
+ def File.backtracked(filename,level=3)
+ if level > 0 && filename && ! filename.empty? then
+ if FileTest.file?(filename) then
+ filename
+ else
+ File.backtracked('../'+filename,level-1)
+ end
+ else
+ filename
+ end
+ end
+
+ def File.expanded(filename)
+ File.expand_path(filename)
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+end
+
+class Commands
+
+ @@namespace = "xmlns:exa='http://www.pragma-ade.com/schemas/example.rng'"
+ @@randchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "abcdefghijklmnopqrstuvwxyz"
+
+ def traceback
+ "(error: #{$!})" + "\n -- " + $@.join("\n >>")
+ end
+
+ def pdf(action,filename,enabled)
+ if enabled && FileTest.file?(filename) then
+ begin
+ report("pdf action #{action} on #{filename}")
+ case action
+ when 'close' then system("pdfclose --all")
+ when 'open' then system("pdfopen --file #{filename}")
+ end
+ rescue
+ # forget about it
+ end
+ end
+ end
+
+ def status(replyfile,str) # when block, then ok
+ begin
+ # def status(*whatever)
+ # end
+ File.open(replyfile,'w') do |f|
+ report("saving reply info in '#{replyfile}'")
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<exa:reply #{@@namespace}>\n")
+ if block_given? then
+ f.puts(" <exa:status>ok</exa:status>\n")
+ f.puts(" #{yield}\n")
+ else
+ f.puts(" <exa:status>error</exa:status>\n")
+ end
+ f.puts(" <exa:comment>" + str + "</exa:comment>\n")
+ f.puts("</exa:reply>\n")
+ f.close
+ report("saving status: #{str}")
+ end
+ rescue
+ report("saving reply info in '#{replyfile}' fails")
+ ensure
+ exit
+ end
+ exit # to be real sure
+ end
+
+
+ def boundary_string (length) # copied from webrick/utils
+ rand_max = @@randchars.size
+ ret = ""
+ length.times do
+ ret << @@randchars[rand(rand_max)]
+ end
+ ret.upcase
+ end
+
+end
+
+class Commands
+
+ @@connecttimeout = 10*60 # ten minutes
+ @@processtimeout = 60*60 # an hour
+ @@polldelay = 5 # 5 seconds
+
+ def main
+
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ dataaction = @commandline.option('action')
+
+ if ! datatemplate.empty? then
+ report("template '#{datatemplate}' specified without --construct")
+ report("aborting")
+ elsif ! dataaction.empty? then
+ report("action data '#{dataaction}' specified without --construct or --extend")
+ report("aborting")
+ elsif ! datafile.empty? then
+ report("action file '#{datafile}' specified without --construct or --extend")
+ report("aborting")
+ else
+ report("assuming --direct")
+ direct()
+ end
+
+ end
+
+ def construct
+
+ requestfile = @commandline.option('request')
+ replyfile = @commandline.option('reply')
+
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ dataaction = @commandline.option('action')
+
+ domain = @commandline.option('domain')
+ project = @commandline.option('project')
+ username = @commandline.option('username')
+ password = @commandline.option('password')
+
+ threshold = @commandline.option('threshold')
+
+ datablob = ''
+
+ begin
+ datablob = IO.read(datatemplate)
+ rescue
+ datablob = ''
+ else
+ begin
+ request = REXML::Document.new(datablob)
+ if e = REXML::XPath.match(request.root,"/exa:request/exa:data") then
+ datablob = e.to_s.chomp
+ end
+ rescue
+ datablob = ''
+ end
+ end
+
+ begin
+ File.open(requestfile,'w') do |f|
+ f.puts "<?xml version='1.0'?>\n"
+ f.puts "<exa:request #{@@namespace}>\n"
+ f.puts " <exa:application>\n"
+ f.puts " <exa:action>#{dataaction}</exa:action>\n" unless dataaction.empty?
+ f.puts " <exa:filename>#{datafile}</exa:filename>\n" unless datafile.empty?
+ f.puts " <exa:threshold>#{threshold}</exa:threshold>\n" unless threshold.empty?
+ f.puts " </exa:application>\n"
+ f.puts " <exa:client>\n"
+ f.puts " <exa:domain>#{domain}</exa:domain>\n"
+ f.puts " <exa:project>#{project}</exa:project>\n"
+ f.puts " <exa:username>#{username}</exa:username>\n"
+ f.puts " <exa:password>#{password}</exa:password>\n"
+ f.puts " </exa:client>\n"
+ if datablob.empty? then
+ f.puts " <exa:data/>\n"
+ else
+ f.puts " #{datablob.chomp}\n"
+ end
+ f.puts "</exa:request>"
+ end
+ rescue
+ status(replyfile,"unable to create '#{requestfile}'")
+ end
+
+ direct()
+
+ end
+
+ def extend
+
+ requestfile = @commandline.option('request')
+ replyfile = @commandline.option('reply')
+
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ dataaction = @commandline.option('action')
+
+ threshold = @commandline.option('threshold')
+
+ if datatemplate.empty? then
+ status(replyfile,"invalid data template '#{datatemplate}'")
+ else
+ begin
+ if FileTest.file?(datatemplate) && oldrequest = IO.read(datatemplate) then
+ request, done = REXML::Document.new(oldrequest), false
+ if ! threshold.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application/exa:threshold") then
+ e.text, done = threshold, true
+ end
+ if ! dataaction.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application/exa:action") then
+ e.text, done = dataaction, true
+ end
+ if ! datafile.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application/exa:filename") then
+ e.text, done = datafile, true
+ end
+ #
+ if ! threshold.empty? && e = REXML::XPath.match(request.root,"/exa:request/exa:application") then
+ e = e.add_element('exa:threshold')
+ e.add_text(threshold.to_s)
+ done = true
+ end
+ #
+ report("nothing replaced in template file") unless done
+ begin
+ File.open(requestfile,'w') do |f|
+ f.puts(newrequest.to_s)
+ end
+ rescue
+ status(replyfile,"unable to create '#{requestfile}'")
+ end
+ else
+ status(replyfile,"unable to read data template '#{datatemplate}'")
+ end
+ rescue
+ status(replyfile,"unable to handle data template '#{datatemplate}'")
+ end
+ end
+
+ direct()
+
+ end
+
+ def direct
+
+ requestpath = @commandline.option('path')
+ requestfile = @commandline.option('request')
+ replyfile = @commandline.option('reply')
+ resultfile = @commandline.option('result')
+ datatemplate = @commandline.option('template')
+ datafile = @commandline.option('file')
+ threshold = @commandline.option('threshold')
+ address = @commandline.option('address')
+ port = @commandline.option('port')
+ session_id = @commandline.option('session')
+ exaurl = @commandline.option('exaurl')
+
+ exaurl = "/#{exaurl}" unless exaurl =~ /^\//
+
+ address.sub!(/^http\:\/\//io) do
+ ''
+ end
+ address.sub!(/\:(\d+)$/io) do
+ port = $1
+ ''
+ end
+
+ autopdf = @commandline.option('autopdf')
+
+ dialogue = nil
+
+ resultfile.sub!(/\.[a-z]+?$/, '') # don't overwrite the source
+
+ unless requestpath.empty? then
+ begin
+ if FileTest.directory?(requestpath) then
+ if Dir.chdir(requestpath) then
+ report("gone to path '#{requestpath}'")
+ else
+ status(replyfile,"unable to go to path '#{requestpath}")
+ end
+ else
+ status(replyfile,"unable to locate '#{requestpath}'")
+ end
+ rescue
+ status(replyfile,"unable to handle '#{requestpath}'")
+ end
+ end
+
+ datafile = File.expand_path(datafile) unless datafile.empty?
+ datatemplate = File.backtracked(datatemplate,3) unless datatemplate.empty?
+
+ # request must be valid
+
+ status(replyfile,'no request file') if requestfile.empty?
+ status(replyfile,"invalid request file '#{requestfile}'") unless FileTest.file?(requestfile)
+
+ begin
+ request = IO.readlines(requestfile).join('')
+ request = REXML::Document.new(request)
+ status(replyfile,'invalid request (no request)') unless request.root.fully_expanded_name=='exa:request'
+ status(replyfile,'invalid request (no application block)') unless request.elements['exa:request'].elements['exa.application'] == nil # explicit nil test needed
+ rescue REXML::ParseException
+ status(replyfile,'invalid request (invalid xml file)')
+ rescue
+ status(replyfile,'invalid request (invalid file)')
+ else
+ report("using request file '#{requestfile}'")
+ end
+
+ # request can force session_id
+
+ if session_id && session_id.empty? then
+ begin
+ id = request.elements['exa:request'].elements['exa:application'].elements['exa:session'].text
+ rescue Exception
+ id = ''
+ ensure
+ if id && ! id.empty? then
+ session_id = id
+ end
+ end
+ end
+
+ # request can overload reply name
+
+ begin
+ rreplyfile = request.elements['exa:request'].elements['exa:application'].elements['exa:output'].text
+ rescue Exception
+ rreplyfile = nil
+ ensure
+ if rreplyfile && ! rreplyfile.empty? then
+ replyfile = rreplyfile
+ report("reply file '#{replyfile} set by request'")
+ else
+ report("using reply file '#{replyfile}'")
+ end
+ end
+
+ # request can overload result name
+
+ begin
+ rresultfile = request.elements['exa:request'].elements['exa:application'].elements['exa:result']
+ rescue Exception
+ rresultfile = nil
+ ensure
+ if rresultfile && ! rresultfile.empty? then
+ resultfile = rresultfile
+ report("result file '#{resultfile}' set by request")
+ else
+ report("using result file '#{resultfile}'")
+ end
+ end
+
+ # try to connect to server
+
+ start_time = Time.now
+
+ processtimeout = begin @commandline.option('timeout').to_i rescue @@processtimeout end
+ processtimeout = @@processtimeout if processtimeout == 0 # 'xx'.to_i => 0
+
+ dialogue = start_dialogue(address, port, processtimeout)
+
+ if dialogue then
+ # continue
+ else
+ status(replyfile,'no connection')
+ end
+
+ # post request
+
+ timeout (@@processtimeout-10) do # -10 so that we run into this one first
+ begin
+ report("posting request of type '#{exaurl}'")
+ report("using session id '#{session_id}'") if session_id && ! session_id.empty?
+ firstline, chunks, total = nil, 0, 0
+ body, boundary, crlf = '', boundary_string(32), "\x0d\x0a"
+ body << '--' + boundary + crlf
+ body << "Content-Disposition: form-data; name=\"exa:request\""
+ body << crlf
+ body << "Content-Type: text/plain"
+ body << crlf + crlf
+ body << request.to_s
+ body << crlf + '--' + boundary + crlf
+if session_id && ! session_id.empty? then
+ body << "Content-Disposition: form-data; name=\"exa:session\""
+ body << "Content-Type: text/plain"
+ body << crlf + crlf
+ body << session_id
+ body << crlf + '--' + boundary + crlf
+end
+ begin
+ File.open(datafile,'rb') do |df|
+ body << "Content-Disposition: form-data; name=\"filename\""
+ body << "Content-Type: text/plain"
+ body << crlf + crlf
+ body << datafile
+ body << crlf + '--' + boundary + crlf
+ body << "Content-Disposition: form-data; name=\"fakename\" ; filename=\"#{datafile}\""
+ body << "Content-Type: application/octetstream"
+ body << "Content-Transfer-Encoding: binary"
+ body << crlf + crlf
+ body << df.read
+ body << crlf + '--' + boundary + '--' + crlf
+ end
+ rescue
+ # skip
+ end
+ headers = Hash.new
+ headers['content-type'] = "multipart/form-data; boundary=#{boundary}"
+ headers['content-length'] = body.length.to_s
+ begin
+ File.open(resultfile,'wb') do |rf|
+ begin
+ # firstline is max 1024 but ok for reply
+ dialogue.post(exaurl,body,headers) do |str|
+ if ! firstline || firstline.empty? then
+ report('receiving result') if total == 0
+ firstline = str
+ end
+ total += 1
+ rf.write(str)
+ end
+ rescue
+ report("forced close #{traceback}")
+ end
+ end
+ rescue
+ status(replyfile,'cannot open file')
+ end
+ begin
+ File.delete(resultfile) if File.zero?(resultfile)
+ rescue
+ end
+ unless FileTest.file?(resultfile) then
+ report("deleting empty resultfile")
+ begin
+ File.delete(resultfile)
+ rescue
+ # nice try, an error anyway
+ end
+ status(replyfile,'empty file')
+ else
+ n, id, status = 0, '', ''
+ loop do
+ again = false
+ if ! dialogue then
+ again = true
+ elsif firstline =~ /(\<exa:reply)/moi then
+ begin
+ reply = REXML::Document.new(firstline)
+ id = (REXML::XPath.match(reply.root,"/exa:reply/exa:session/text()") || '').to_s
+ status = (REXML::XPath.match(reply.root,"/exa:reply/exa:status/text()") || '').to_s
+ rescue
+ report("error in parsing reply #{traceback}")
+ break
+ else
+ report("status: #{status}")
+ if (status =~ /^running\s*\:\s*(background|busy)$/i) && (! id.empty?) then
+ report("waiting for status reply (#{n*@@polldelay})")
+ again = true
+ end
+ end
+ end
+ if again then
+ n += 1
+ sleep(@@polldelay) # todo: duplicate when n > 1
+ unless dialogue then
+ report('reestablishing connection')
+ dialogue = start_dialogue(address, port, processtimeout)
+ end
+ if dialogue then
+ begin
+ File.open(resultfile,'wb') do |rf|
+ begin
+ body = "id=#{id}"
+ headers = Hash.new
+ headers['content-type'] = "application/x-www-form-urlencoded"
+ headers['content-length'] = body.length.to_s
+ total, firstline = 0, ''
+ dialogue.post("/exastatus",body,headers) do |str|
+ if ! firstline || firstline.empty? then
+ firstline = str
+ end
+ total += 1
+ rf.write(str)
+ end
+ rescue
+ report("forced close #{traceback}")
+ dialogue = nil
+ again = true
+ end
+ end
+ begin
+ File.delete(resultfile) if File.zero?(resultfile)
+ rescue
+ end
+ rescue
+ report("error in opening file #{traceback}")
+ status(replyfile,'cannot open file')
+ end
+ else
+ report("unable to make a connection")
+ status(replyfile,'unable to make a connection') # exit
+ end
+ else
+ break
+ end
+ end
+ case firstline
+ when /<\?xml\s*version=.*?\?>\s*<exa:reply/moi then
+ begin
+ File.delete(replyfile) if FileTest.file?(replyfile)
+ resultfile = replyfile if File.rename(resultfile,replyfile)
+ rescue
+ end
+ report("reply saved in '#{resultfile}'")
+ when /\%PDF\-/io then
+ report("done, file #{resultfile}, type pdf, #{total} chunks, #{File.size? rescue 0} bytes")
+ if resultfile =~ /\.pdf$/i then
+ report("file identified as 'pdf'")
+ elsif resultfile =~ /\..*$/o
+ report("result file suffix should be 'pdf'")
+ else
+ newresultfile = resultfile + '.pdf'
+ newresultfile.sub!(/\.pdf\.pdf/io, '.pdf')
+ pdf('close',newresultfile,autopdf)
+ begin
+ File.delete(newresultfile) if FileTest.file?(newresultfile)
+ resultfile = newresultfile if File.rename(resultfile,newresultfile)
+ rescue
+ report("adding 'pdf' suffix to result name failed")
+ else
+ report("'pdf' suffix added to result name")
+ end
+ end
+ report("result saved in '#{resultfile}'")
+ pdf('open',resultfile,autopdf)
+ status(replyfile,'ok') do
+ "<exa:filename>#{resultfile}</exa:filename>"
+ end
+ when /html/io then
+ report("done, file #{resultfile}, type html, #{total} chunks, #{File.size? rescue 0} bytes")
+ if resultfile =~ /\.(htm|html)$/i then
+ report("file identified as 'html'")
+ elsif resultfile =~ /\..*$/o
+ report("result file suffix should be 'htm'")
+ else
+ newresultfile = resultfile + '.htm'
+ begin
+ File.delete(newresultfile) if FileTest.file?(newresultfile)
+ resultfile = newresultfile if File.rename(resultfile,newresultfile)
+ rescue
+ report("adding 'htm' suffix to result name failed")
+ else
+ report("'htm' suffix added to result name")
+ end
+ end
+ report("result saved in '#{resultfile}'")
+ status(replyfile,'ok') do
+ "<exa:filename>#{resultfile}</exa:filename>"
+ end
+ else
+ report("no result file, first line #{firstline}")
+ status(replyfile,'no result file')
+ end
+ end
+ rescue TimeoutError
+ report("aborted due to time out")
+ status(replyfile,'time out')
+ rescue
+ report("aborted due to some problem #{traceback}")
+ status(replyfile,"no answer #{traceback}")
+ end
+ end
+
+ begin
+ report("run time: #{Time.now-start_time} seconds")
+ rescue
+ end
+
+ end
+
+ def start_dialogue(address, port, processtimeout)
+ timeout(@@connecttimeout) do
+ report("trying to connect to #{address}:#{port}")
+ begin
+ begin
+ if dialogue = Net::HTTP.new(address, port) then
+ # dialogue.set_debug_output $stderr
+ dialogue.read_timeout = processtimeout # set this before start
+ if dialogue.start then
+ report("connected to #{address}:#{port}, timeout: #{processtimeout}")
+ else
+ retry
+ end
+ else
+ retry
+ end
+ rescue
+ sleep(2)
+ retry
+ else
+ return dialogue
+ end
+ rescue TimeoutError
+ return nil
+ rescue
+ return nil
+ end
+ end
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registerflag('autopdf')
+
+commandline.registervalue('path' , '')
+
+commandline.registervalue('request' , 'request.exa')
+commandline.registervalue('reply' , 'reply.exa')
+commandline.registervalue('result' , 'result')
+
+commandline.registervalue('template' , '')
+commandline.registervalue('file' , '')
+commandline.registervalue('action' , '')
+commandline.registervalue('timeout' , '')
+
+commandline.registervalue('domain' , 'default')
+commandline.registervalue('project' , 'default')
+commandline.registervalue('username' , 'guest')
+commandline.registervalue('password' , 'anonymous')
+commandline.registervalue('exaurl' , 'exarequest')
+commandline.registervalue('threshold' , '0')
+commandline.registervalue('session' , '')
+
+commandline.registervalue('address' , 'localhost')
+commandline.registervalue('port' , '80')
+
+commandline.registeraction('direct' , '[--path --request --reply --result --autopdf]')
+commandline.registeraction('construct', '[--path --request --reply --result --autopdf] --file --action')
+commandline.registeraction('extend' , '[--path --request --reply --result --autopdf] --file --action --template')
+
+commandline.registeraction('direct')
+commandline.registeraction('construct')
+commandline.registeraction('extend')
+
+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/wwwserver.rb b/Master/texmf-dist/scripts/context/ruby/wwwserver.rb
new file mode 100644
index 00000000000..18ea57df1d2
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/wwwserver.rb
@@ -0,0 +1,293 @@
+#!/usr/env ruby
+
+banner = ['WWWServer', 'version 1.0.0', '2003-2006', 'PRAGMA ADE/POD']
+
+$: << File.dirname(File.expand_path($0))
+
+require 'base/switch'
+require 'base/logger'
+
+require 'monitor'
+
+# class WWW < Monitor
+# end
+# class Server < Monitor
+# end
+
+require 'www/lib'
+require 'www/dir'
+require 'www/login'
+require 'www/exa'
+
+require 'tempfile'
+require 'ftools'
+require 'webrick'
+
+class Server
+
+ attr_accessor :document_root, :work_path, :logs_path, :port_number, :exa_url, :verbose, :trace, :direct
+
+ def initialize(logger)
+ @httpd = nil
+ @document_root = ''
+ @work_path = ''
+ @logs_path = ''
+ @port_number = 8061
+ @exa_url = 'http://localhost:8061'
+ @logger = logger
+ @n_of_clients = 500
+ @request_timeout = 5*60
+ @verbose = false
+ @trace = false
+ @direct = false
+ end
+
+ def report(str)
+ @logger.report(str) if @logger
+ end
+
+ def setup
+ if @document_root.empty? then
+ rootpath = File.expand_path($0)
+ @document_root = File.expand_path(File.join(File.dirname(rootpath),'..','documents'))
+ unless FileTest.directory?(@document_root) then # todo: optional
+ loop do
+ prevpath = rootpath.dup
+ rootpath = File.dirname(rootpath)
+ if prevpath == rootpath then
+ break
+ else
+ checkpath = File.join(rootpath,'documents')
+ # report("locating: #{checkpath}")
+ if FileTest.directory?(checkpath) then
+ @document_root = checkpath
+ break
+ else
+ checkpath = File.join(rootpath,'docroot/documents')
+ # report("locating: #{checkpath}")
+ if FileTest.directory?(checkpath) then
+ @document_root = checkpath
+ break
+ end
+ end
+ end
+ end
+ end
+ end
+ @document_root = File.join(Dir.pwd, 'documents') unless FileTest.directory?(@document_root)
+ unless FileTest.directory?(@document_root) then
+ report("invalid document root: #{@document_root}")
+ exit
+ else
+ report("using document root: #{@document_root}")
+ end
+ #
+ @work_path = File.expand_path(File.join(@document_root,'..','work')) if @work_path.empty?
+ # begin File.makedirs(@work_path) ; rescue ; end # no, let's auto-temp
+ if ! FileTest.directory?(@work_path) || ! FileTest.writable?(@work_path) then
+ @work_path = File.expand_path(File.join(Dir.tmpdir,'exaserver','work'))
+ begin File.makedirs(@logs_path) ; rescue ; end
+ end
+ report("using work path: #{@work_path}")
+ #
+ @logs_path = File.expand_path(File.join(@document_root,'..','logs')) if @logs_path.empty?
+ # begin File.makedirs(@logs_path) ; rescue ; end # no, let's auto-temp
+ if ! FileTest.directory?(@logs_path) || ! FileTest.writable?(@logs_path) then
+ @logs_path = File.expand_path(File.join(Dir.tmpdir,'exaserver','logs'))
+ begin File.makedirs(@logs_path) ; rescue ; end
+ end
+ report("using log path: #{@logs_path}")
+ #
+ if @logs_path.empty? then
+ @logfile = $stderr
+ @accfile = $stderr
+ else
+ @logfile = File.join(@logs_path,'exa-info.log')
+ @accfile = File.join(@logs_path,'exa-access.log')
+ begin File.delete(@logfile) ; rescue ; end
+ begin File.delete(@accfile) ; rescue ; end
+ end
+ #
+ begin
+ @httpd = WEBrick::HTTPServer.new(
+ :DocumentRoot => @document_root,
+ :DocumentRootOptions => { :FancyIndexing => false },
+ :DirectoryIndex => ['index.html','index.htm','showcase.pdf'],
+ :Port => @port_number.to_i,
+ :Logger => WEBrick::Log.new(@logfile, WEBrick::Log::INFO), # DEBUG
+ :RequestTimeout => @request_timeout,
+ :MaxClients => @n_of_clients,
+ :AccessLog => [
+ [ @accfile, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
+ [ @accfile, WEBrick::AccessLog::REFERER_LOG_FORMAT ],
+ [ @accfile, WEBrick::AccessLog::AGENT_LOG_FORMAT ],
+ # :CGIPathEnv => ENV["PATH"] # PATH environment variable for CGI.
+ ]
+ )
+ rescue
+ report("starting server at port: #{@port_number} failed")
+ exit
+ else
+ report("running server at port: #{@port_number}")
+ end
+
+ begin
+ #
+ @httpd.mount_proc("/dir") do |request,reply|
+ report("accepting /dir") if @verbose
+ web_session(request,reply).handle_dir
+ end
+ @httpd.mount_proc("/login") do |request,reply|
+ report("accepting /login") if @verbose
+ web_session(request,reply).handle_login
+ end
+ @httpd.mount("/cache", WEBrick::HTTPServlet::FileHandler, File.join(@work_path,'cache'))
+ # @httpd.mount_proc("/cache") do |request,reply|
+ # WEBrick::HTTPServlet::FileHandler(@httpd,@work_path) # not ok
+ # end
+ @httpd.mount_proc("/exalogin") do |request,reply|
+ report("accepting /exalogin") if @verbose
+ web_session(request,reply).handle_exalogin
+ end
+ @httpd.mount_proc("/exadefault") do |request,reply|
+ report("accepting /exadefault") if @verbose
+ web_session(request,reply).handle_exadefault
+ end
+ @httpd.mount_proc("/exainterface") do |request,reply|
+ report("accepting /exainterface") if @verbose
+ web_session(request,reply).handle_exainterface
+ end
+ @httpd.mount_proc("/exarequest") do |request,reply|
+ report("accepting /exarequest") if @verbose
+ web_session(request,reply).handle_exarequest
+ end
+ @httpd.mount_proc("/exacommand") do |request,reply|
+ report("accepting /exacommand") if @verbose
+ web_session(request,reply).handle_exacommand
+ end
+ @httpd.mount_proc("/exastatus") do |request,reply|
+ report("accepting /exastatus") if @verbose
+ web_session(request,reply).handle_exastatus
+ end
+ @httpd.mount_proc("/exaadmin") do |request,reply|
+ report("accepting /exaadmin") if @verbose
+ web_session(request,reply).handle_exaadmin
+ end
+ #
+ rescue
+ report("problem in starting server: #{$!}")
+ end
+ [:INT, :TERM, :EXIT].each do |signal|
+ trap(signal) do
+ @httpd.shutdown
+ end
+ end
+ end
+
+ def start
+ unless @httpd then
+ setup
+ @httpd.start
+ end
+ end
+
+ def stop
+ @httpd.shutdown if @httpd
+ end
+
+ def restart
+ stop
+ start
+ end
+
+ private
+
+ def web_session(request,reply)
+ www = WWW.new(@httpd,request,reply)
+ www.set('path:work', @work_path)
+ www.set('path:logs', @logs_path)
+ www.set('path:root', File.dirname(@document_root))
+ www.set('process:exaurl', @exa_url)
+ www.set('trace:errors','yes') if @trace
+ www.set('process:background', 'no') if @direct
+ return www
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def start
+ if server = setup then server.start end
+ end
+
+ def stop
+ if server = setup then server.stop end
+ end
+
+ def restart
+ if server = setup then server.restart end
+ end
+
+ private
+
+ def setup
+ server = Server.new(logger)
+ server.document_root = @commandline.option('root')
+ server.verbose = @commandline.option('verbose')
+ if @commandline.option('forcetemp') then
+ server.work_path = Dir.tmpdir + '/exa/work'
+ server.logs_path = Dir.tmpdir + '/exa/logs'
+ [server.work_path,server.logs_path].each do |d|
+ begin
+ File.makedirs(d) unless FileTest.directory?(d)
+ rescue
+ report("unable to create #{d}")
+ exit
+ end
+ unless FileTest.writable?(d) then
+ report("unable to access #{d}")
+ exit
+ end
+ end
+ else
+ server.work_path = @commandline.option('work')
+ server.logs_path = @commandline.option('logs')
+ end
+ server.port_number = @commandline.option('port')
+ server.exa_url = @commandline.option('url')
+ server.trace = @commandline.option('trace')
+ server.direct = @commandline.option('direct')
+ return server
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registervalue('root' , '')
+commandline.registervalue('work' , '')
+commandline.registervalue('logs' , '')
+commandline.registervalue('address', 'localhost')
+commandline.registervalue('port' , '8061')
+commandline.registervalue('url' , 'http://localhost:8061')
+
+commandline.registeraction('start' , 'start the server [--root --forcetemp --work --logs --address --port --url]')
+commandline.registeraction('stop' , 'stop the server')
+commandline.registeraction('restart', 'restart the server')
+
+commandline.registerflag('forcetemp')
+commandline.registerflag('direct')
+commandline.registerflag('verbose')
+commandline.registerflag('trace')
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'start')
+
diff --git a/Master/texmf-dist/scripts/context/ruby/wwwwatch.rb b/Master/texmf-dist/scripts/context/ruby/wwwwatch.rb
new file mode 100644
index 00000000000..0afb9a39291
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/ruby/wwwwatch.rb
@@ -0,0 +1,483 @@
+#!/usr/bin/env ruby
+
+banner = ['WWWWatch', 'version 1.0.0', '2003-2006', 'PRAGMA ADE/POD']
+
+$: << File.dirname(File.expand_path($0))
+
+require 'base/switch'
+require 'base/logger'
+
+require 'www/common'
+
+require 'monitor'
+require 'fileutils'
+require 'ftools'
+require 'tempfile'
+require 'timeout'
+require 'thread'
+
+class Watch < Monitor
+
+ include Common
+
+ @@session_prefix = ''
+ @@check_factor = 4
+ @@process_timeout = 1*60*60
+ @@fast_wait_loop = false
+
+ @@session_line = /^\s*(?![\#\%])(.*?)\s*\=\s*(.*?)\s*$/o
+ @@session_begin = 'begin exa session'
+ @@session_end = 'end exa session'
+
+ attr_accessor :root_path, :work_path, :create, :cache_path, :delay, :max_threads, :max_age, :verbose
+
+ def initialize(logger) # we need to register all @vars here becase of the monitor
+ @threads = Hash.new
+ @files = Array.new
+ @stats = Hash.new
+ @skips = Hash.new
+ @root_path = ''
+ @work_path = Dir.tmpdir
+ @cache_path = @work_path
+ @last_action = Time.now
+ @delay = 1
+ @max_threads = 5
+ @max_age = @@process_timeout
+ @logger = logger
+ @verbose = false
+ @create = false
+ # [:INT, :TERM, :EXIT].each do |signal|
+ # trap(signal) do
+ # kill
+ # exit # rescue false
+ # end
+ # end
+ # at_exit do
+ # kill
+ # end
+ end
+
+ def trace
+ if @verbose && @logger then
+ @logger.report("exception: #{$!})")
+ $@.each do |t|
+ @logger.report(">> #{t}")
+ end
+ end
+ end
+
+ def report(str)
+ @logger.report(str) if @logger
+ end
+
+ def setup
+ @threads = Hash.new
+ @files = Array.new
+ @stats = Hash.new
+ @skips = Hash.new
+ @root_path = File.expand_path(File.join(File.dirname(Dir.pwd),'.')) if @root_path.empty?
+ @work_path = File.expand_path(File.join(@root_path,'work','watch')) if @work_path.empty?
+ # @cache_path = File.expand_path(File.join(@root_path,'work','cache')) if @cache_path.empty?
+ @cache_path = File.expand_path(File.join(File.dirname(@work_path),'cache')) if @cache_path.empty?
+ if @create then
+ begin File.makedirs(@work_path) ; rescue ; end
+ begin File.makedirs(@cache_path) ; rescue ; end
+ end
+ unless File.writable?(@work_path) then
+ @work_path = File.expand_path(File.join(Dir.tmpdir,'work','watch'))
+ if @create then
+ begin File.makedirs(@work_path) ; rescue ; end
+ end
+ end
+ unless File.writable?(@cache_path) then
+ @cache_path = File.expand_path(File.join(Dir.tmpdir,'work','cache'))
+ if @create then
+ begin File.makedirs(@cache_path) ; rescue ; end
+ end
+ end
+ unless File.writable?(@work_path) then
+ puts "no valid work path: #{@work_path}"
+ exit! rescue false # no checking, no at_exit done
+ end
+ unless File.writable?(@cache_path) then
+ puts "no valid work path: #{@work_path}" ; # no reason to exit
+ end
+ @last_action = Time.now
+ report("watching path #{@work_path}") if @verbose
+ end
+
+ def lock(lck)
+ begin
+ report("watchdog: locking #{lck}") if @verbose
+ File.open(lck,'w') do |f|
+ f << Time.now
+ end
+ rescue
+ trace
+ end
+ end
+
+ def unlock(lck)
+ begin
+ report("watchdog: unlocking #{lck}") if @verbose
+ File.delete(lck)
+ rescue
+ trace
+ end
+ end
+
+ def kill
+ @threads.each do |t|
+ t.kill rescue false
+ end
+ end
+
+ def restart
+ @files = Array.new
+ @skips = Hash.new
+ @stats = Hash.new
+ kill # threads
+ end
+
+ def collect
+ begin
+ @files = Array.new
+ Dir.glob("#{@work_path}/#{@@session_prefix}*.ses").each do |sessionfile|
+ sessionfile = File.expand_path(sessionfile)
+ begin
+ if @threads.key?(sessionfile) then
+ # leave alone
+ elsif (Time.now - File.mtime(sessionfile)) > @max_age.to_i then
+ # delete
+ FileUtils::rm_r(sessionfile) rescue false
+ FileUtils::rm_r(sessionfile.sub(/ses$/,'dir')) rescue false
+ FileUtils::rm_r(sessionfile.sub(/ses$/,'lck')) rescue false
+ begin
+ FileUtils::rm_r(File.join(@cache_path, File.basename(sessionfile.sub(/ses$/,'dir'))))
+ rescue
+ report("watchdog: problems in cache cleanup #{$!}") # if @verbose
+ end
+ @stats.delete(sessionfile) rescue false
+ @skips.delete(sessionfile) rescue false
+ report("watchdog: removing session #{sessionfile}") if @verbose
+ elsif ! @skips.key?(sessionfile) then
+ @files << sessionfile
+ report("watchdog: checking session #{sessionfile}") if @verbose
+ end
+ rescue
+ # maybe purged in the meantime
+ end
+ end
+ rescue
+ if File.directory?(@work_path) then
+ @files = Array.new
+ else
+ # maybe dir is deleted (manual cleanup)
+ restart
+ end
+ end
+ begin
+ Dir.glob("#{@cache_path}/*.dir").each do |dirname|
+ begin
+ if (Time.now - File.mtime(dirname)) > @max_age.to_i then
+ begin
+ FileUtils::rm_r(dirname)
+ rescue
+ report("watchdog: problems in cache cleanup #{$!}") # if @verbose
+ end
+ end
+ rescue
+ # maybe purged in the meantime
+ end
+ end
+ rescue
+ end
+ end
+
+ def purge
+ begin
+ Dir.glob("#{@work_path}/#{@@session_prefix}*").each do |sessionfile|
+ sessionfile = File.expand_path(sessionfile)
+ begin
+ if (Time.now - File.mtime(sessionfile)) > @max_age.to_i then
+ begin
+ if FileTest.directory?(sessionfile) then
+ FileUtils::rm_r(sessionfile)
+ else
+ File.delete(sessionfile)
+ end
+ rescue
+ end
+ begin
+ @stats.delete(sessionfile)
+ @skips.delete(sessionfile)
+ rescue
+ end
+ report("watchdog: purging session #{sessionfile}") if @verbose
+ end
+ rescue
+ # maybe purged in the meantime
+ end
+ end
+ rescue
+ end
+ end
+
+ def loaded_session_data(filename)
+ begin
+ if data = IO.readlines(filename) then
+ return data if (data.first =~ /^[\#\%]\s*#{@@session_begin}/o) && (data.last =~ /^[\#\%]\s*#{@@session_end}/o)
+ end
+ rescue
+ trace
+ end
+ return nil
+ end
+
+ def load(sessionfile)
+ # we assume that we get an exception when the file is locked
+ begin
+ if data = loaded_session_data(sessionfile) then
+ report("watchdog: loading session #{sessionfile}") if @verbose
+ vars = Hash.new
+ data.each do |line|
+ begin
+ if line.chomp =~ /^(.*?)\s*\=\s*(.*?)\s*$/o then
+ key, value = $1, $2
+ vars[key] = value
+ end
+ rescue
+ end
+ end
+ return vars
+ else
+ return nil
+ end
+ rescue
+ trace
+ return nil
+ end
+ end
+
+ def save(sessionfile, vars)
+ begin
+ report("watchdog: saving session #{sessionfile}") if @verbose
+ if @stats.key?(sessionfile) then
+ @stats[sessionfile] = File.mtime(sessionfile)
+ elsif @stats[sessionfile] == File.mtime(sessionfile) then
+ else
+ # construct data first
+ str = "\# #{@@session_begin}\n"
+ for k,v in vars do
+ str << "#{k}=#{v}\n"
+ end
+ str << "\# #{@@session_end}\n"
+ # save as fast as possible
+ File.open(sessionfile,'w') do |f|
+ f.puts(str)
+ end
+ end
+ rescue
+ report("watchdog: unable to save session #{sessionfile}") if @verbose
+ trace
+ return false
+ else
+ return true
+ end
+ end
+
+ def launch
+ begin
+ @files.each do |sessionfile|
+ if @threads.length < @max_threads then
+ begin
+ if ! @skips.key?(sessionfile) && (vars = load(sessionfile)) then
+ if (id = vars['id']) && vars['status'] then
+ if vars['status'] == 'running: background' then
+ @last_action = Time.now
+ @threads[sessionfile] = Thread.new(vars, sessionfile) do |vars, sessionfile|
+ begin
+ report("watchdog: starting thread #{sessionfile}") if @verbose
+ dir = File.expand_path(sessionfile.sub(/ses$/,'dir'))
+ lck = File.expand_path(sessionfile.sub(/ses$/,'lck'))
+ start_of_run = Time.now
+ start_of_job = start_of_run.dup
+ max_runtime = @max_age
+ begin
+ start_of_job = vars['starttime'].to_i || start_of_run
+ start_of_job = start_of_run if start_of_job == 0
+ rescue
+ start_of_job = Time.now
+ end
+ begin
+ max_runtime = vars['maxtime'].to_i || @max_age
+ max_runtime = @max_age if max_runtime == 0
+ max_runtime = max_runtime - (Time.now.to_i - start_of_job.to_i)
+ rescue
+ max_runtime = @max_age
+ end
+ lock(lck)
+ if max_runtime > 0 then
+ command = vars['command'] || ''
+ if ! command.empty? then
+ vars['status'] = 'running: busy'
+ vars['timeout'] = max_runtime.to_s
+ save(sessionfile,vars)
+ timeout(max_runtime) do
+ begin
+ command = command_string(dir,command,'process.log')
+ report("watchdog: #{command}") if @verbose
+ system(command)
+ rescue TimeoutError
+ vars['status'] = 'running: timeout'
+ rescue
+ trace
+ vars['status'] = 'running: aborted'
+ else
+ vars['status'] = 'running: finished'
+ vars['runtime'] = sprintf("%.02f",(Time.now - start_of_run))
+ vars['endtime'] = Time.now.to_i.to_s
+ end
+ end
+ else
+ vars['status'] = 'running: aborted' # no command
+ end
+ else
+ vars['status'] = 'running: aborted' # not enough time
+ end
+ save(sessionfile,vars)
+ unlock(lck)
+ report("watchdog: ending thread #{sessionfile}") if @verbose
+ @threads.delete(sessionfile)
+ rescue
+ trace
+ end
+ end
+ else
+ report("watchdog: skipping - id (#{vars['id']}) / status (#{vars['status']})") if @verbose
+ end
+ @skips[sessionfile] = true
+ else
+ # not yet ok
+ end
+ else
+ # maybe a lock
+ end
+ rescue
+ trace
+ end
+ else
+ break
+ end
+ end
+ rescue
+ trace
+ end
+ end
+
+ def wait
+ begin
+ # report(Time.now.to_s) if @verbose
+ loop do
+ if @threads.length == @max_threads then
+ if @delay > @max_threads then
+ sleep(@delay)
+ else
+ sleep(@max_threads)
+ end
+ break if @@fast_wait_loop
+ else
+ sleep(@delay)
+ break
+ end
+ end
+ rescue
+ trace
+ end
+ end
+
+ def check
+ begin
+ time = Time.now
+ if (time - @last_action) > @@check_factor*@max_age then
+ report("watchdog: cleanup") if @verbose
+ @stats = Hash.new
+ @last_action = time
+ kill
+ end
+ rescue
+ trace
+ end
+ end
+
+ def cycle
+ loop do
+ begin
+ collect
+ launch
+ wait
+ check
+ rescue
+ trace
+ report("watchdog: some problem, restarting loop")
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def watch
+ if watch = setup then
+ watch.cycle
+ else
+ report("provide valid work path")
+ end
+ end
+ def main
+ watch
+ end
+
+ private
+
+ def setup
+ if watch = Watch.new(logger) then
+ watch.root_path = @commandline.option('root')
+ watch.work_path = @commandline.option('work')
+ watch.cache_path = @commandline.option('cache')
+ watch.create = @commandline.option('create')
+ watch.verbose = @commandline.option('verbose')
+ begin
+ watch.max_threads = @commandline.option('threads').to_i
+ rescue
+ watch.max_threads = 5
+ end
+ watch.setup
+ end
+ return watch
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registervalue('root', '')
+commandline.registervalue('work', '')
+commandline.registervalue('cache', '')
+commandline.registervalue('threads', '5')
+
+commandline.registerflag('create')
+
+commandline.registeraction('watch', '[--work=path] [--root=path] [--create]')
+
+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
index a57f7445020..a10d34314aa 100644
--- a/Master/texmf-dist/scripts/context/ruby/xmltools.rb
+++ b/Master/texmf-dist/scripts/context/ruby/xmltools.rb
@@ -10,13 +10,16 @@
# info : j.hagen@xs4all.nl
# www : www.pragma-ade.com
+# todo : use kpse lib
+
# This script will harbor some handy manipulations on tex
# related files.
-banner = ['XMLTools', 'version 1.1.1', '2002/2005', 'PRAGMA ADE/POD']
+banner = ['XMLTools', 'version 1.2.0', '2002/2006', 'PRAGMA ADE/POD']
unless defined? ownpath
ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'')
+ # ownpath = File.dirname($0)
$: << ownpath
end
@@ -170,7 +173,7 @@ class Commands
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 system("texmfstart texexec --batch --pdf --once --result=#{long} --use=mmlpag #{style} #{modes} #{file}.xml") then
if eps then
if f = open("#{file}-mmlpages.txt") then
while line = f.gets do
@@ -226,7 +229,7 @@ class Commands
else
report("error in processing file #{file}")
end
- system("texmfstart texutil --purge")
+ system("texmfstart ctxtools --purge")
else
report("error in processing file #{file}")
end
@@ -235,77 +238,188 @@ class Commands
def analyze
- file = @commandline.argument('first')
- result = @commandline.option('output')
+ file = @commandline.argument('first')
+ result = @commandline.option('output')
+ utf = @commandline.option('utf')
+ process = @commandline.option('process')
if FileTest.file?(file) then
if data = IO.read(file) then
- 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
+ if data =~ /<?xml.*?version\=/ then
+ report("xml file #{file} loaded")
+ elements = Hash.new
+ attributes = Hash.new
+ entities = Hash.new
+ chars = Hash.new
+ unicodes = Hash.new
+ names = Hash.new
+ data.scan(/<([^>\s\/\!\?]+)([^>]*?)>/o) do
+ element, attributelist = $1, $2
+ if elements.key?(element) then
+ elements[element] += 1
else
- attributes[element][key][value] = 1
+ 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
- end
- data.scan(/\&([^\;]+)\;/o) do
- entity = $1
- if entities.key?(entity) then
- entities[entity] += 1
- else
- entities[entity] = 1
+ data.scan(/\&([^\;]+)\;/o) do
+ entity = $1
+ if entities.key?(entity) then
+ entities[entity] += 1
+ else
+ entities[entity] = 1
+ end
end
- end
- result = file.gsub(/\..*?$/, '') + '.xlg' if result.empty?
- if f = File.open(result,'w') then
- report("saving report in #{result}")
- f.puts "<?xml version='1.0'?>\n"
- f.puts "<document>\n"
- if entities.length>0 then
- f.puts " <entities>\n"
- entities.keys.asort.each do |entity|
- f.puts " <entity name=#{entity.xstring} n=#{entities[entity].to_s.xstring}/>\n"
+ if utf then
+ data.scan(/(\w)/u) do
+ chars[$1] = (chars[$1] || 0) + 1
+ end
+ if chars.size > 0 then
+ begin
+ # todo : use kpse lib
+ filename, ownpath, foundpath = 'contextnames.txt', File.dirname($0), ''
+ begin
+ foundpath = File.dirname(`kpsewhich -progname=context -format=\"other text files\" #{filename}`.chomp)
+ rescue
+ foundpath = '.'
+ else
+ foundpath = '.' if foundpath.empty?
+ end
+ [foundpath,ownpath,File.join(ownpath,'../../../context/data')].each do |path|
+ fullname = File.join(path,filename)
+ if FileTest.file?(fullname) then
+ report("loading '#{fullname}'")
+ # rough scan, we assume no valid lines after comments
+ IO.read(fullname).scan(/^([0-9A-F][0-9A-F][0-9A-F][0-9A-F])\s*\;\s*(.*?)\s*\;\s*(.*?)\s*\;\s*(.*?)\s*$/) do
+ names[$1.hex.to_i.to_s] = [$2,$3,$4]
+ end
+ break
+ end
+ end
+ rescue
+ end
end
- 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"
+ result = file.gsub(/\..*?$/, '') + '.xlg' if result.empty?
+ if f = File.open(result,'w') then
+ report("saving report in #{result}")
+ f.puts "<?xml version='1.0'?>\n"
+ f.puts "<document>\n"
+ if entities.length>0 then
+ total = 0
+ entities.each do |k,v|
+ total += v
+ end
+ f.puts " <entities n=#{total.to_s.xstring}>\n"
+ entities.keys.asort.each do |entity|
+ f.puts " <entity name=#{entity.xstring} n=#{entities[entity].to_s.xstring}/>\n"
+ end
+ f.puts " </entities>\n"
+ end
+ if utf && (chars.size > 0) then
+ total = 0
+ chars.each do |k,v|
+ total += v
+ end
+ f.puts " <characters n=#{total.to_s.xstring}>\n"
+ chars.each do |k,v|
+ if k.length > 1 then
+ begin
+ u = k.unpack('U')
+ unicodes[u] = (unicodes[u] || 0) + v
+ rescue
+ report("invalid utf codes")
+ end
+ end
+ end
+ unicodes.keys.sort.each do |u|
+ ustr = u.to_s
+ if names[ustr] then
+ f.puts " <character number=#{ustr.xstring} pname=#{names[ustr][0].xstring} cname=#{names[ustr][1].xstring} uname=#{names[ustr][2].xstring} n=#{unicodes[u].to_s.xstring}/>\n"
+ else
+ f.puts " <character number=#{ustr.xstring} n=#{unicodes[u].to_s.xstring}/>\n"
+ end
+ end
+ f.puts " </characters>\n"
+ end
+ if elements.length>0 then
+ f.puts " <elements>\n"
+ elements.keys.sort.each do |element|
if attributes.key?(element) then
- 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"
+ f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}>\n"
+ if attributes.key?(element) then
+ attributes[element].keys.asort.each do |attribute|
+ f.puts " <attribute name=#{attribute.xstring}>\n"
+ if attribute =~ /id$/o then
+ nn = 0
+ attributes[element][attribute].keys.asort.each do |value|
+ nn += attributes[element][attribute][value].to_i
+ end
+ f.puts " <instance value=#{"*".xstring} n=#{nn.to_s.xstring}/>\n"
+ else
+ attributes[element][attribute].keys.asort.each do |value|
+ f.puts " <instance value=#{value.xstring} n=#{attributes[element][attribute][value].to_s.xstring}/>\n"
+ end
+ end
+ f.puts " </attribute>\n"
end
- f.puts " </attribute>\n"
end
+ f.puts " </element>\n"
+ else
+ f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}/>\n"
end
- f.puts " </element>\n"
- else
- f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}/>\n"
end
+ f.puts " </elements>\n"
+ end
+ f.puts "</document>\n"
+ f.close
+ if process then
+ system("texmfstart texexec --purge --pdf --use=xml-analyze #{result}")
+ end
+ else
+ report("unable to open file '#{result}'")
+ end
+ else
+ report("invalid xml file '#{file}'")
+ end
+ else
+ report("unable to load file '#{file}'")
+ end
+ else
+ report("unknown file '#{file}'")
+ end
+ end
+
+ def cleanup # todo, share loading/saving with previous
+
+ file = @commandline.argument('first')
+ force = @commandline.option('force')
+ verbose = @commandline.option('verbose')
+
+ if FileTest.file?(file) then
+ if data = IO.read(file) then
+ if data =~ /<?xml.*?version\=/ then
+ data = doxmlcleanup(data,verbose)
+ result = if force then file else file.gsub(/\..*?$/, '') + '.xlg' end
+ begin
+ if f = File.open(result,'w') then
+ f << data
+ f.close
end
- f.puts " </elements>\n"
+ rescue
+ report("unable to open file '#{result}'")
end
- f.puts "</document>\n"
else
- report("unable to open file '#{result}'")
+ report("invalid xml file '#{file}'")
end
else
report("unable to load file '#{file}'")
@@ -313,8 +427,77 @@ class Commands
else
report("unknown file '#{file}'")
end
+
end
+ def doxmlreport(str,verbose=false)
+ if verbose then
+ result = str
+ report(result)
+ return result
+ else
+ return str
+ end
+ end
+
+ def doxmlcleanup(data="",verbose=false)
+
+ # remove funny spaces (looks cleaner)
+ #
+ # data = "<whatever ></whatever ><whatever />"
+
+ data.gsub!(/\<(\/*\w+)\s*(\/*)>/o) do
+ "<#{$1}#{$2}>"
+ end
+
+ # remove funny ampersands
+ #
+ # data = "<x> B&W </x>"
+
+ data.gsub!(/\&([^\<\>\&]*?)\;/mo) do
+ "<entity name='#{$1}'/>"
+ end
+ data.gsub!(/\&/o) do
+ doxmlreport("&amp;",verbose)
+ end
+ data.gsub!(/\<entity name=\'(.*?)\'\/\>/o) do
+ doxmlreport("&#{$1};",verbose)
+ end
+
+ # remove funny < >
+ #
+ # data = "<x> < 5% </x>"
+
+ data.gsub!(/<([^>].*?)>/o) do
+ tag = $1
+ case tag
+ when /^\//o then
+ "<#{tag}>" # funny tag but ok
+ when /\/$/o then
+ "<#{tag}>" # funny tag but ok
+ when /</o then
+ doxmlreport("&lt;#{tag}>",verbose)
+ else
+ "<#{tag}>"
+ end
+ end
+
+ # remove funny < >
+ #
+ # data = "<x> > 5% </x>"
+
+ data.gsub!(/<([^>].*?)>([^\>\<]*?)>/o) do
+ doxmlreport("<#{$1}>#{$2}&gt;",verbose)
+ end
+
+ return data
+ end
+
+ # puts doxmlcleanup("<whatever ></whatever ><whatever />")
+ # puts doxmlcleanup("<x> B&W </x>")
+ # puts doxmlcleanup("<x> < 5% </x>")
+ # puts doxmlcleanup("<x> > 5% </x>")
+
end
logger = Logger.new(banner.shift)
@@ -322,7 +505,8 @@ 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('analyze', 'report entities and elements [--utf --process]')
+commandline.registeraction('cleanup', 'cleanup xml file [--force]')
# commandline.registeraction('dir', 'filename --pattern= --output= [--recurse --stripname --longname --url --root]')
# commandline.registeraction('mmlpages','filename [--eps --jpg --png --style= --mode=]')
@@ -344,8 +528,11 @@ commandline.registervalue('root')
commandline.registerflag('eps')
commandline.registerflag('png')
commandline.registerflag('jpg')
+commandline.registerflag('utf')
+commandline.registerflag('process')
commandline.registervalue('style')
commandline.registervalue('modes')
+commandline.registervalue('verbose')
commandline.expand
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/ctxtools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/ctxtools.bat
new file mode 100644
index 00000000000..13614a1d98d
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/ctxtools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart ctxtools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/exatools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/exatools.bat
new file mode 100644
index 00000000000..36230a528f4
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/exatools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart exatools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/makempy.bat b/Master/texmf-dist/scripts/context/stubs/mswin/makempy.bat
new file mode 100644
index 00000000000..9622d96a5f3
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/makempy.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart makempy.pl %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/mpstools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/mpstools.bat
new file mode 100644
index 00000000000..10ec3f1d687
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/mpstools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart mpstools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/mptopdf.bat b/Master/texmf-dist/scripts/context/stubs/mswin/mptopdf.bat
new file mode 100644
index 00000000000..757ea58f9ce
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/mptopdf.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart mptopdf.pl %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/pdftools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/pdftools.bat
new file mode 100644
index 00000000000..71662d3f766
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/pdftools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart pdftools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/pstopdf.bat b/Master/texmf-dist/scripts/context/stubs/mswin/pstopdf.bat
new file mode 100644
index 00000000000..091b0d4d7bd
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/pstopdf.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart pstopdf.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/rlxtools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/rlxtools.bat
new file mode 100644
index 00000000000..e5cdca25009
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/rlxtools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart rlxtools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/runtools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/runtools.bat
new file mode 100644
index 00000000000..37df398ab37
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/runtools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart runtools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/texexec.bat b/Master/texmf-dist/scripts/context/stubs/mswin/texexec.bat
new file mode 100644
index 00000000000..44615dbd918
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/texexec.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart texexec.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/texfont.bat b/Master/texmf-dist/scripts/context/stubs/mswin/texfont.bat
new file mode 100644
index 00000000000..9dd50309ff3
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/texfont.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart texfont.pl %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/textools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/textools.bat
new file mode 100644
index 00000000000..4fc5c549255
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/textools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart textools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/texutil.bat b/Master/texmf-dist/scripts/context/stubs/mswin/texutil.bat
new file mode 100644
index 00000000000..61e90ca6509
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/texutil.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart texutil.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/tmftools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/tmftools.bat
new file mode 100644
index 00000000000..a7053043d22
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/tmftools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart tmftools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/mswin/xmltools.bat b/Master/texmf-dist/scripts/context/stubs/mswin/xmltools.bat
new file mode 100644
index 00000000000..9721095b298
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/mswin/xmltools.bat
@@ -0,0 +1,2 @@
+@echo off
+texmfstart xmltools.rb %*
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/ctxtools b/Master/texmf-dist/scripts/context/stubs/unix/ctxtools
new file mode 100644
index 00000000000..84e47bbee3c
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/ctxtools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart ctxtools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/exatools b/Master/texmf-dist/scripts/context/stubs/unix/exatools
new file mode 100644
index 00000000000..50ff0f07e46
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/exatools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart exatools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/makempy b/Master/texmf-dist/scripts/context/stubs/unix/makempy
new file mode 100644
index 00000000000..4bf7a1af230
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/makempy
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart makempy.pl "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/mpstools b/Master/texmf-dist/scripts/context/stubs/unix/mpstools
new file mode 100644
index 00000000000..b4c8f634548
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/mpstools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart mpstools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/mptopdf b/Master/texmf-dist/scripts/context/stubs/unix/mptopdf
new file mode 100644
index 00000000000..980a3123d1a
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/mptopdf
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart mptopdf.pl "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/pdftools b/Master/texmf-dist/scripts/context/stubs/unix/pdftools
new file mode 100644
index 00000000000..92ee803a868
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/pdftools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart pdftools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/pstopdf b/Master/texmf-dist/scripts/context/stubs/unix/pstopdf
new file mode 100644
index 00000000000..5b38ed426cb
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/pstopdf
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart pstopdf.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/rlxtools b/Master/texmf-dist/scripts/context/stubs/unix/rlxtools
new file mode 100644
index 00000000000..41cea40fc09
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/rlxtools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart rlxtools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/runtools b/Master/texmf-dist/scripts/context/stubs/unix/runtools
new file mode 100644
index 00000000000..ff9a333791b
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/runtools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart runtools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/texexec b/Master/texmf-dist/scripts/context/stubs/unix/texexec
new file mode 100644
index 00000000000..2158172906a
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/texexec
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart texexec.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/texfont b/Master/texmf-dist/scripts/context/stubs/unix/texfont
new file mode 100644
index 00000000000..a91f786e3f8
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/texfont
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart texfont.pl "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/textools b/Master/texmf-dist/scripts/context/stubs/unix/textools
new file mode 100644
index 00000000000..7445eac371c
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/textools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart textools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/texutil b/Master/texmf-dist/scripts/context/stubs/unix/texutil
new file mode 100644
index 00000000000..607154af059
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/texutil
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart texutil.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/tmftools b/Master/texmf-dist/scripts/context/stubs/unix/tmftools
new file mode 100644
index 00000000000..7531a966390
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/tmftools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart tmftools.rb "$@"
diff --git a/Master/texmf-dist/scripts/context/stubs/unix/xmltools b/Master/texmf-dist/scripts/context/stubs/unix/xmltools
new file mode 100644
index 00000000000..03086d0436b
--- /dev/null
+++ b/Master/texmf-dist/scripts/context/stubs/unix/xmltools
@@ -0,0 +1,2 @@
+#!/bin/sh
+texmfstart xmltools.rb "$@"