summaryrefslogtreecommitdiff
path: root/scripts/context/ruby/ctxtools.rb
diff options
context:
space:
mode:
authorMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
committerMarius <mariausol@gmail.com>2010-07-04 15:32:09 +0300
commit85b7bc695629926641c7cb752fd478adfdf374f3 (patch)
tree80293f5aaa7b95a500a78392c39688d8ee7a32fc /scripts/context/ruby/ctxtools.rb
downloadcontext-85b7bc695629926641c7cb752fd478adfdf374f3.tar.gz
stable 2010-05-24 13:10
Diffstat (limited to 'scripts/context/ruby/ctxtools.rb')
-rw-r--r--scripts/context/ruby/ctxtools.rb2785
1 files changed, 2785 insertions, 0 deletions
diff --git a/scripts/context/ruby/ctxtools.rb b/scripts/context/ruby/ctxtools.rb
new file mode 100644
index 000000000..c1cbc7d20
--- /dev/null
+++ b/scripts/context/ruby/ctxtools.rb
@@ -0,0 +1,2785 @@
+#!/usr/bin/env ruby
+#encoding: ASCII-8BIT
+
+# program : ctxtools
+# copyright : PRAGMA Advanced Document Engineering
+# version : 2004-2005
+# author : Hans Hagen
+#
+# project : ConTeXt / eXaMpLe
+# concept : Hans Hagen
+# info : j.hagen@xs4all.nl
+# www : www.pragma-ade.com
+
+# This script will harbor some handy manipulations on context
+# related files.
+
+# todo: move scite here
+#
+# todo: move kpse call to kpse class/module, faster and better
+
+# Taco Hoekwater on patterns and lig building (see 'agr'):
+#
+# Any direct use of a ligature (as accessed by \char or through active
+# characters) is wrong and will create faulty hyphenation. Normally,
+# when TeX sees "office", it has six tokens, and it knows from the
+# patterns that it can hyphenate between the "ff". It will build an
+# internal list of four nodes, like this:
+#
+# [char, o , ffi ]
+# [lig , ffi, c ,[f,f,i]]
+# [char, c , e ]
+# [char, e , NULL]
+#
+# as you can see from the ffi line, it has remembered the original
+# characters. While hyphenating, it temporarily changes back to
+# that, then re-instates the ligature afterwards.
+#
+# If you feed it the ligature directly, like so:
+#
+# [char, o , ffi ]
+# [char, ffi , c ]
+# [char, c , e ]
+# [char, e , NULL]
+#
+# it cannot do that (it tries to hyphenate as if the "ffi" was a
+# character), and the result is wrong hyphenation.
+
+banner = ['CtxTools', 'version 1.3.5', '2004/2008', 'PRAGMA ADE']
+
+$: << File.expand_path(File.dirname($0)) ; $: << File.join($:.last,'lib') ; $:.uniq!
+
+require 'base/switch'
+require 'base/logger'
+require 'base/system'
+require 'base/kpse'
+require 'base/file'
+
+require 'rexml/document'
+require 'net/http'
+require 'fileutils'
+# require 'ftools'
+require 'kconv'
+
+exit if defined?(REQUIRE2LIB)
+
+class String
+
+ def i_translate(element, attribute, category)
+ self.gsub!(/(<#{element}.*?#{attribute}=)([\"\'])(.*?)\2/) do
+ if category.key?($3) then
+ # puts "#{element} #{$3} -> #{category[$3]}\n" if element == 'cd:inherit'
+ # puts "#{element} #{$3} => #{category[$3]}\n" if element == 'cd:command'
+ "#{$1}#{$2}#{category[$3]}#{$2}"
+ else
+ # puts "#{element} #{$3} -> ?\n" if element == 'cd:inherit'
+ # puts "#{element} #{$3} => ?\n" if element == 'cd:command'
+ "#{$1}#{$2}#{$3}#{$2}" # unchanged
+ end
+ end
+ end
+
+ def i_load(element, category)
+ self.scan(/<#{element}.*?name=([\"\'])(.*?)\1.*?value=\1(.*?)\1/) do
+ category[$2] = $3
+ end
+ end
+
+ def nosuffix(suffix)
+ self.sub(/\.#{suffix}/,'') # no /o
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def touchcontextfile
+ dowithcontextfile(1)
+ end
+
+ def contextversion
+ dowithcontextfile(2)
+ end
+
+ private
+
+ def dowithcontextfile(action)
+ maincontextfile = 'context.tex'
+ unless FileTest.file?(maincontextfile) then
+ begin
+ maincontextfile = Kpse.found(maincontextfile,'context')
+ rescue
+ maincontextfile = ''
+ end
+ end
+ unless maincontextfile.empty? then
+ nextcontextfile = maincontextfile.sub(/context\.tex$/,"cont-new.tex")
+ case action
+ when 1 then
+ touchfile(maincontextfile)
+ touchfile(nextcontextfile,@@newcontextversion)
+ when 2 then
+ reportversion(maincontextfile)
+ reportversion(nextcontextfile,@@newcontextversion)
+ end
+ end
+
+ end
+
+ @@contextversion = "\\\\contextversion"
+ @@newcontextversion = "\\\\newcontextversion"
+
+ def touchfile(filename,command=@@contextversion)
+
+ if FileTest.file?(filename) then
+ if data = IO.read(filename) then
+ timestamp = Time.now.strftime('%Y.%m.%d %H:%M')
+ prevstamp = ''
+ begin
+ data.gsub!(/#{command}\{(\d+\.\d+\.\d+.*?)\}/) do
+ prevstamp = $1
+ "#{command.sub(/(\\)+/,"\\")}{#{timestamp}}"
+ end
+ rescue
+ else
+ begin
+ File.delete(filename+'.old')
+ rescue
+ end
+ begin
+ File.copy(filename,filename+'.old')
+ rescue
+ end
+ begin
+ if f = File.open(filename,'w') then
+ f.puts(data)
+ f.close
+ end
+ rescue
+ end
+ end
+ if prevstamp.empty? then
+ report("#{filename} is not updated, no timestamp found")
+ else
+ report("#{filename} is updated from #{prevstamp} to #{timestamp}")
+ end
+ end
+ else
+ report("#{filename} is not found")
+ end
+
+ end
+
+ def reportversion(filename,command=@@contextversion)
+
+ version = 'unknown'
+ begin
+ if FileTest.file?(filename) && IO.read(filename).match(/#{command}\{(\d+\.\d+\.\d+.*?)\}/) then
+ version = $1
+ end
+ rescue
+ end
+ if @commandline.option("pipe") then
+ print version
+ else
+ report("context version: #{version} (#{filename})")
+ end
+
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def jeditinterface
+ editinterface('jedit')
+ end
+
+ def bbeditinterface
+ editinterface('bbedit')
+ end
+
+ def sciteinterface
+ editinterface('scite')
+ end
+
+ def rawinterface
+ editinterface('raw')
+ end
+
+ private
+
+ def editinterface(type='raw')
+
+ return unless FileTest.file?("cont-en.xml")
+
+ interfaces = @commandline.arguments
+
+ if interfaces.empty? then
+ interfaces = ['en','cs','de','it','nl','ro','fr']
+ end
+
+ interfaces.each do |interface|
+ begin
+ collection = Hash.new
+ mappings = Hash.new
+ if f = open("keys-#{interface}.xml") then
+ while str = f.gets do
+ if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+value=\"(.*?)\".*?\>/o then
+ mappings[$1] = $2
+ end
+ end
+ f.close
+ if f = open("cont-en.xml") then
+ while str = f.gets do
+ if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+type=\"environment\".*?\>/o then
+ collection["start#{mappings[$1]}"] = ''
+ collection["stop#{mappings[$1]}"] = ''
+ elsif str =~ /\<cd\:command\s+name=\"(.*?)\".*?\>/o then
+ collection["#{mappings[$1]}"] = ''
+ end
+ end
+ f.close
+ case type
+ when 'jedit' then
+ if f = open("context-jedit-#{interface}.xml", 'w') then
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<!DOCTYPE MODE SYSTEM 'xmode.dtd'>\n\n")
+ f.puts("<MODE>\n")
+ f.puts(" <RULES>\n")
+ f.puts(" <KEYWORDS>\n")
+ collection.keys.sort.each do |name|
+ f.puts(" <KEYWORD2>\\#{name}</KEYWORD2>\n") unless name.empty?
+ end
+ f.puts(" </KEYWORDS>\n")
+ f.puts(" </RULES>\n")
+ f.puts("</MODE>\n")
+ f.close
+ end
+ when 'bbedit' then
+ if f = open("context-bbedit-#{interface}.xml", 'w') then
+ f.puts("<?xml version='1.0'?>\n\n")
+ f.puts("<key>BBLMKeywordList</key>\n")
+ f.puts("<array>\n")
+ collection.keys.sort.each do |name|
+ f.puts(" <string>\\#{name}</string>\n") unless name.empty?
+ end
+ f.puts("</array>\n")
+ f.close
+ end
+ when 'scite' then
+ if f = open("cont-#{interface}-scite.properties", 'w') then
+ i = 0
+ f.write("keywordclass.macros.context.#{interface}=")
+ collection.keys.sort.each do |name|
+ unless name.empty? then
+ if i==0 then
+ f.write("\\\n ")
+ i = 5
+ else
+ i = i - 1
+ end
+ f.write("#{name} ")
+ end
+ end
+ f.write("\n")
+ f.close
+ end
+ else # raw
+ collection.keys.sort.each do |name|
+ puts("\\#{name}\n") unless name.empty?
+ end
+ end
+ end
+ end
+ end
+ end
+
+ end
+
+end
+
+# class Commands
+#
+# include CommandBase
+#
+# public
+#
+# def translateinterface
+#
+# # since we know what kind of file we're dealing with,
+# # we do it quick and dirty instead of using rexml or
+# # xslt
+#
+# interfaces = @commandline.arguments
+#
+# if interfaces.empty? then
+# interfaces = ['cs','de','it','nl','ro','fr']
+# else
+# interfaces.delete('en')
+# end
+#
+# interfaces.flatten.each do |interface|
+#
+# variables, constants, strings, list, data = Hash.new, Hash.new, Hash.new, '', ''
+#
+# keyfile, intfile, outfile = "keys-#{interface}.xml", "cont-en.xml", "cont-#{interface}.xml"
+#
+# report("generating #{keyfile}")
+#
+# begin
+# one = "texexec --make --all #{interface}"
+# two = "texexec --batch --silent --interface=#{interface} x-set-01"
+# if @commandline.option("force") then
+# system(one)
+# system(two)
+# elsif not system(two) then
+# system(one)
+# system(two)
+# end
+# rescue
+# end
+#
+# unless File.file?(keyfile) then
+# report("no #{keyfile} generated")
+# next
+# end
+#
+# report("loading #{keyfile}")
+#
+# begin
+# list = IO.read(keyfile)
+# rescue
+# list = empty
+# end
+#
+# if list.empty? then
+# report("error in loading #{keyfile}")
+# next
+# end
+#
+# list.i_load('cd:variable', variables)
+# list.i_load('cd:constant', constants)
+# list.i_load('cd:command' , strings)
+# # list.i_load('cd:element' , strings)
+#
+# report("loading #{intfile}")
+#
+# begin
+# data = IO.read(intfile)
+# rescue
+# data = empty
+# end
+#
+# if data.empty? then
+# report("error in loading #{intfile}")
+# next
+# end
+#
+# report("translating interface en to #{interface}")
+#
+# data.i_translate('cd:string' , 'value', strings)
+# data.i_translate('cd:variable' , 'value', variables)
+# data.i_translate('cd:parameter', 'name' , constants)
+# data.i_translate('cd:constant' , 'type' , variables)
+# data.i_translate('cd:variable' , 'type' , variables)
+# data.i_translate('cd:inherit' , 'name' , strings)
+# # data.i_translate('cd:command' , 'name' , strings)
+#
+# data.gsub!(/(\<cd\:interface[^\>]*?language=")en(")/) do
+# $1 + interface + $2
+# end
+#
+# report("saving #{outfile}")
+#
+# begin
+# if f = File.open(outfile, 'w') then
+# f.write(data)
+# f.close
+# end
+# rescue
+# end
+#
+# end
+#
+# end
+#
+# end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ # faster is to glob the whole dir and regexp over that list
+
+ def purgefiles
+
+ pattern = @commandline.arguments
+ purgeall = @commandline.option("all")
+ recurse = @commandline.option("recurse")
+
+ $dontaskprefixes.push(Dir.glob("mpx-*"))
+
+ if purgeall then
+ $dontaskprefixes.push(Dir.glob("*.tex.prep"))
+ $dontaskprefixes.push(Dir.glob("*.xml.prep"))
+ end
+
+ $dontaskprefixes.flatten!
+ $dontaskprefixes.sort!
+
+ if purgeall then
+ $forsuresuffixes.push($texnonesuffixes)
+ $texnonesuffixes = []
+ $forsuresuffixes.flatten!
+ end
+
+ if ! pattern || pattern.empty? then
+ globbed = if recurse then "**/*.*" else "*.*" end
+ files = Dir.glob(globbed)
+ report("purging#{if purgeall then ' all' end} temporary files : #{globbed}")
+ else
+ report("purging#{if purgeall then ' all' end} temporary files : #{pattern.join(' ')}")
+ pattern.each do |pat|
+ nosuf = File.unsuffixed(pat)
+ globbed = if recurse then "**/#{nosuf}-*.*" else "#{nosuf}-*.*" end
+ report("checking files that match '#{globbed}'")
+ files = Dir.glob(globbed)
+ globbed = if recurse then "**/#{nosuf}.*" else "#{nosuf}.*" end
+ report("checking files that match '#{globbed}'")
+ files.push(Dir.glob(globbed))
+ end
+ end
+ files.flatten!
+ files.sort!
+
+ $dontaskprefixes.each do |file|
+ removecontextfile(file)
+ end
+ $dontasksuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /#{suffix}$/i
+ end
+ end
+ $forsuresuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /\.#{suffix}$/i
+ end
+ end
+ files.each do |file|
+ if file =~ /(.*?)\.\d+$/o then
+ basename = $1
+ if file =~ /mp(graph|run)/o || FileTest.file?("#{basename}.mp") then
+ removecontextfile($file)
+ end
+ end
+ end
+ $dummyfiles.each do |file|
+ (File.delete(file) if (FileTest.size?(file) rescue 10) < 10) rescue false
+ end
+ $texnonesuffixes.each do |suffix|
+ files.each do |file|
+ if file =~ /(.*)\.#{suffix}$/i then
+ if FileTest.file?("#{$1}.tex") || FileTest.file?("#{$1}.xml") || FileTest.file?("#{$1}.fo") then
+ keepcontextfile(file)
+ else
+ strippedname = $1.gsub(/\-[a-z]$/io, '')
+ if FileTest.file?("#{strippedname}.tex") || FileTest.file?("#{strippedname}.xml") then
+ keepcontextfile("#{file} (potential result file)")
+ else
+ removecontextfile(file)
+ end
+ end
+ end
+ end
+ end
+
+ files = Dir.glob("*.*")
+ $dontasksuffixes.each do |suffix|
+ files.each do |file|
+ removecontextfile(file) if file =~ /^#{suffix}$/i
+ end
+ end
+
+ if $removedfiles || $keptfiles || $persistentfiles then
+ report("removed files : #{$removedfiles}")
+ report("kept files : #{$keptfiles}")
+ report("persistent files : #{$persistentfiles}")
+ report("reclaimed bytes : #{$reclaimedbytes}")
+ end
+
+ end
+
+ private
+
+ $removedfiles = 0
+ $keptfiles = 0
+ $persistentfiles = 0
+ $reclaimedbytes = 0
+
+ $dontaskprefixes = [
+ # "tex-form.tex", "tex-edit.tex", "tex-temp.tex",
+ "texexec.tex", "texexec.tui", "texexec.tuo",
+ "texexec.tuc", "texexec.tua",
+ "texexec.ps", "texexec.pdf", "texexec.dvi",
+ "cont-opt.tex", "cont-opt.bak"
+ ]
+ $dontasksuffixes = [
+ "mp(graph|run)\\.mp", "mp(graph|run)\\.mpd", "mp(graph|run)\\.mpo", "mp(graph|run)\\.mpy",
+ "mp(graph|run)\\.\\d+", "mp(graph|run)\\.mp.keep",
+ "xlscript\\.xsl"
+ ]
+ $forsuresuffixes = [
+ "tui", "tua", "tup", "ted", "tes", "top",
+ "log", "tmp", "run", "bck", "rlg",
+ "mpt", "mpx", "mpd", "mpo", "mpb",
+ "ctl",
+ "pgf", "synctex.gz",
+ "tmp.md5", "tmp.out"
+ ]
+ $texonlysuffixes = [
+ "dvi", "ps", "pdf"
+ ]
+ $texnonesuffixes = [
+ "tuo", "tub", "top", "tuc"
+ ]
+ $dummyfiles = [
+ "mpgraph"
+ ]
+
+ def removecontextfile (filename)
+ if filename && FileTest.file?(filename) then
+ begin
+ filesize = FileTest.size(filename)
+ File.delete(filename)
+ rescue
+ report("problematic : #{filename}")
+ else
+ if FileTest.file?(filename) then
+ $persistentfiles += 1
+ report("persistent : #{filename}")
+ else
+ $removedfiles += 1
+ $reclaimedbytes += filesize
+ report("removed : #{filename}")
+ end
+ end
+ end
+ end
+
+ def keepcontextfile (filename)
+ if filename && FileTest.file?(filename) then
+ $keptfiles += 1
+ report("not removed : #{filename}")
+ end
+ end
+
+end
+
+#D Documentation can be woven into a source file. The next
+#D routine generates a new, \TEX\ ready file with the
+#D documentation and source fragments properly tagged. The
+#D documentation is included as comment:
+#D
+#D \starttypen
+#D %D ...... some kind of documentation
+#D %M ...... macros needed for documenation
+#D %S B begin skipping
+#D %S E end skipping
+#D \stoptypen
+#D
+#D The most important tag is \type {%D}. Both \TEX\ and \METAPOST\
+#D files use \type{%} as a comment chacacter, while \PERL, \RUBY\
+#D and alike use \type{#}. Therefore \type{#D} is also handled.
+#D
+#D The generated file gets the suffix \type{ted} and is
+#D structured as:
+#D
+#D \starttypen
+#D \startmodule[type=suffix]
+#D \startdocumentation
+#D \stopdocumentation
+#D \startdefinition
+#D \stopdefinition
+#D \stopmodule
+#D \stoptypen
+#D
+#D Macro definitions specific to the documentation are not
+#D surrounded by start||stop commands. The suffix specifaction
+#D can be overruled at runtime, but defaults to the file
+#D extension. This specification can be used for language
+#D depended verbatim typesetting.
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def documentation
+ files = @commandline.arguments
+ processtype = @commandline.option("type")
+ files.each do |fullname|
+ if fullname =~ /(.*)\.(.+?)$/o then
+ filename, filesuffix = $1, $2
+ else
+ filename, filesuffix = fullname, 'tex'
+ end
+ filesuffix = 'tex' if filesuffix.empty?
+ fullname, resultname = "#{filename}.#{filesuffix}", "#{filename}.ted"
+ if ! FileTest.file?(fullname)
+ report("empty input file #{fullname}")
+ elsif ! tex = File.open(fullname)
+ report("invalid input file #{fullname}")
+ elsif ! ted = File.open(resultname,'w') then
+ report("unable to openresult file #{resultname}")
+ else
+ report("input file : #{fullname}")
+ report("output file : #{resultname}")
+ nofdocuments, nofdefinitions, nofskips = 0, 0, 0
+ skiplevel, indocument, indefinition, skippingbang = 0, false, false, false
+ if processtype.empty? then
+ filetype = filesuffix.downcase.sub(/^mk.+$/,'tex') # make sure that mkii and mkiv files are handled
+ else
+ filetype = processtype.downcase
+ end
+ report("filetype : #{filetype}")
+ # we need to signal to texexec what interface to use
+ firstline = tex.gets
+ if firstline =~ /^\%.*interface\=/ then
+ ted.puts(firstline)
+ else
+ tex.rewind # seek(0)
+ end
+ ted.puts("\\startmodule[type=#{filetype}]\n")
+ while str = tex.gets do
+ if skippingbang then
+ skippingbang = false
+ else
+ str.chomp!
+ str.sub!(/\s*$/o, '')
+ case str
+ when /^[%\#]D($| )/io then
+ if skiplevel == 0 then
+ someline = if str.length < 3 then "" else str[3,str.length-1] end
+ if indocument then
+ ted.puts("#{someline}\n")
+ else
+ if indefinition then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ end
+ unless indocument then
+ ted.puts("\n\\startdocumentation\n")
+ end
+ ted.puts("#{someline}\n")
+ indocument = true
+ nofdocuments += 1
+ end
+ end
+ when /^[%\#]M($| )/io then
+ if skiplevel == 0 then
+ someline = if str.length < 3 then "" else str[3,str.length-1] end
+ ted.puts("#{someline}\n")
+ end
+ when /^[%\%]S B/io then
+ skiplevel += 1
+ nofskips += 1
+ when /^[%\%]S E/io then
+ skiplevel -= 1
+ when /^[%\#]/io then
+ #nothing
+ when /^eval \'\(exit \$\?0\)\' \&\& eval \'exec perl/o then
+ skippingbang = true
+ else
+ if skiplevel == 0 then
+ inlocaldocument = indocument
+ inlocaldocument = false # else first line skipped when not empty
+ someline = str
+ if indocument then
+ ted.puts("\\stopdocumentation\n")
+ indocument = false
+ end
+ if indefinition then
+ if someline.empty? then
+ ted.puts("\\stopdefinition\n")
+ indefinition = false
+ else
+ ted.puts("#{someline}\n")
+ end
+ elsif ! someline.empty? then
+ ted.puts("\n\\startdefinition\n")
+ indefinition = true
+ if inlocaldocument then
+ # nothing
+ else
+ nofdefinitions += 1
+ ted.puts("#{someline}\n")
+ end
+ end
+ end
+ end
+ end
+ end
+ if indocument then
+ ted.puts("\\stopdocumentation\n")
+ end
+ if indefinition then
+ ted.puts("\\stopdefinition\n")
+ end
+ ted.puts("\\stopmodule\n")
+ ted.close
+
+ if nofdocuments == 0 && nofdefinitions == 0 then
+ begin
+ File.delete(resultname)
+ rescue
+ end
+ end
+ report("documentation sections : #{nofdocuments}")
+ report("definition sections : #{nofdefinitions}")
+ report("skipped sections : #{nofskips}")
+ end
+ end
+ end
+
+end
+
+#D This feature was needed when \PDFTEX\ could not yet access page object
+#D numbers (versions prior to 1.11).
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ def filterpages # temp feature / no reporting
+ filename = @commandline.argument('first')
+ filename.sub!(/\.([a-z]+?)$/io,'')
+ pdffile = "#{filename}.pdf"
+ tuofile = "#{filename}.tuo"
+ if FileTest.file?(pdffile) then
+ begin
+ prevline, n = '', 0
+ if (pdf = File.open(pdffile)) && (tuo = File.open(tuofile,'a')) then
+ report('filtering page object numbers')
+ pdf.binmode
+ while line = pdf.gets do
+ line.chomp
+ # typical pdftex search
+ if (line =~ /\/Type \/Page/o) && (prevline =~ /^(\d+)\s+0\s+obj/o) then
+ p = $1
+ n += 1
+ tuo.puts("\\objectreference{PDFP}{#{n}}{#{p}}{#{n}}\n")
+ else
+ prevline = line
+ end
+ end
+ end
+ pdf.close
+ tuo.close
+ report("number of pages : #{n}")
+ rescue
+ report("fatal error in filtering pages")
+ end
+ end
+ end
+
+end
+
+# This script is used to generate hyphenation pattern files
+# that suit ConTeXt. One reason for independent files is that
+# over the years too many uncommunicated changes took place
+# as well that inconsistency in content, naming, and location
+# in the texmf tree takes more time than I'm willing to spend
+# on it. Pattern files are normally shipped for LaTeX (and
+# partially plain). A side effect of independent files is that
+# we can make them encoding independent.
+#
+# Maybe I'll make this hyptools.tex
+
+class String
+
+ def markbraces
+ level = 0
+ self.gsub(/([\{\}])/o) do |chr|
+ if chr == '{' then
+ level = level + 1
+ chr = "((+#{level}))"
+ elsif chr == '}' then
+ chr = "((-#{level}))"
+ level = level - 1
+ end
+ chr
+ end
+ end
+
+ def unmarkbraces
+ self.gsub(/\(\(\+\d+?\)\)/o) do
+ "{"
+ end .gsub(/\(\(\-\d+?\)\)/o) do
+ "}"
+ end
+ end
+
+ def getargument(pattern)
+ if self =~ /(#{pattern})\s*\(\(\+(\d+)\)\)(.*?)\(\(\-\2\)\)/m then # no /o
+ return $3
+ else
+ return ""
+ end
+ end
+
+ def withargument(pattern, &block)
+ if self.markbraces =~ /^(.*)(#{pattern}\s*)\(\(\+(\d+)\)\)(.*?)\(\(\-\3\)\)(.*)$/m then # no /o
+ "#{$1.unmarkbraces}#{$2}{#{yield($4.unmarkbraces)}}#{$5.unmarkbraces}"
+ else
+ self
+ end
+ end
+
+ def filterargument(pattern, &block)
+ if self.markbraces =~ /^(.*)(#{pattern}\s*)\(\(\+(\d+)\)\)(.*?)\(\(\-\3\)\)(.*)$/m then # no /o
+ yield($4.unmarkbraces)
+ else
+ self
+ end
+ end
+
+end
+
+class Language
+
+ include CommandBase
+
+ def initialize(commandline=nil, language='en', filenames=nil, encoding='ec')
+ @commandline= commandline
+ @language = language
+ @filenames = filenames
+ @remapping = Array.new
+ @demapping = Array.new
+ @cloning = Array.new
+ @unicode = Hash.new
+ @encoding = encoding
+ @data = ''
+ @read = ''
+ preload_accents()
+ preload_unicode() if @commandline.option('utf8')
+ case @encoding.downcase
+ when 't1', 'ec', 'cork' then preload_vector('ec', 'enco-ec.tex')
+ when 'y', 'texnansi' then preload_vector('texnansi', 'enco-ans.tex')
+ when 'agr', 'agreek' then preload_vector('agr', 'enco-agr.tex')
+ when 't2a' then preload_vector('t2a', 'enco-cyr.tex')
+ when 'cyr' then preload_vector() # somehow loading t2a does not work out well
+ end
+ end
+
+ def report(str)
+ if @commandline then
+ @commandline.report(str)
+ else
+ puts("#{str}\n")
+ end
+ end
+
+ def remap(from, to)
+ @remapping.push([from,to])
+ end
+ def demap(from, to)
+ @demapping.push([from,to])
+ end
+ def clone(from, to)
+ @cloning.push([from,to])
+ end
+
+ def load(filenames=@filenames)
+ found = false
+ begin
+ if filenames then
+ @filenames.each do |fileset|
+ [fileset].flatten.each do |filename|
+ begin
+ if fname = located(filename) then
+ data = IO.read(fname)
+ @data += data.gsub(/\%.*$/, '').gsub(/\\message\{.*?\}/, '')
+ data.gsub!(/(\\patterns|\\hyphenation)\s*\{.*/mo) do '' end
+ @read += "\n% preamble of file #{fname}\n\n#{data}\n"
+ @data.gsub!(/^[\s\n]+$/moi, '')
+ report("file #{fname} is loaded")
+ found = true
+ break # next fileset
+ end
+ rescue
+ report("file #{filename} is not readable")
+ end
+ end
+ end
+ end
+ rescue
+ end
+ return found
+ end
+
+ def valid?
+ ! @data.empty?
+ end
+
+ def convert
+ if @data then
+ n = 0
+ if true then
+ report("")
+ ["\\patterns","\\hyphenation"].each do |what|
+ @data = @data.withargument(what) do |content|
+ report("converting #{what}")
+ report("")
+ @demapping.each_index do |i|
+ content.gsub!(@demapping[i][0], @demapping[i][1])
+ end
+ content.gsub!(/\\delete\{.*?\}/o) do '' end
+ content.gsub!(/\\keep\{(.*?)\}/o) do $1 end
+ done = false
+ @remapping.each_index do |i|
+ from, to, m = @remapping[i][0], @remapping[i][1], 0
+ content.gsub!(from) do
+ done = true
+ m += 1
+ "[#{i}]"
+ end
+ report("#{m.to_s.rjust(5)} entries remapped to #{to}") unless m == 0
+ n += m
+ end
+ content.gsub!(/\[(\d+)\]/o) do
+ @remapping[$1.to_i][1]
+ end
+ report(" nothing remapped") unless done
+ @cloning.each_index do |i|
+ c = 0
+ f, s = @cloning[i][0], @cloning[i][1]
+ str = "#{f}|#{s}"
+ str.gsub!(/([\[\]])/) do "\\" + "#{$1}" end
+ reg = /(#{str})/
+ content.gsub!(/(\S*(#{str})\S*)/) do
+ a, b = $1, $1
+ a.gsub!(reg, f)
+ b.gsub!(reg, s)
+ c = c + 1
+ "#{a} #{b}"
+ end
+ report("#{c.to_s.rjust(5)} times #{f} cloned to #{s}")
+ n += c
+ end
+ report("")
+ content.to_s
+ end
+ end
+ else
+ @remapping.each do |k|
+ from, to, m = k[0], k[1], 0
+ @data.gsub!(from) do
+ m += 1
+ to
+ end
+ report("#{m.to_s.rjust(5)} entries remapped to #{to}") unless m == 0
+ n += m
+ end
+ end
+ report("#{n} changes in patterns and exceptions")
+ if @commandline.option('utf8') then
+ n = 0
+ @data.gsub!(/\[(.*?)\]/o) do
+ n += 1
+ @unicode[$1] || $1
+ end
+ report("#{n} unicode utf8 entries")
+ end
+ return true
+ else
+ return false
+ end
+ end
+
+ def comment(str)
+ str.gsub!(/^\n/o, '')
+ str.chomp!
+ if @commandline.option('xml') then
+ "<!-- #{str.strip} -->\n\n"
+ else
+ "% #{str.strip}\n\n"
+ end
+ end
+
+ def content(tag, str)
+ lst = str.split(/\s+/)
+ lst.collect! do |l|
+ l.strip
+ end
+ if lst.length>0 then
+ lst = "\n#{lst.join("\n")}\n"
+ else
+ lst = ""
+ end
+ if @commandline.option('xml') then
+ lst.gsub!(/\[(.*?)\]/o) do
+ "&#{$1};"
+ end
+ "<#{tag}>#{lst}</#{tag}>\n\n"
+ else
+ "\\#{tag} \{#{lst}\}\n\n"
+ end
+ end
+
+ def banner
+ if @commandline.option('xml') then
+ "<?xml version='1.0' standalone='yes' ?>\n\n"
+ end
+ end
+
+ def triggerunicode
+ return
+ if @commandline.option('utf8') then
+ "% xetex needs utf8 encoded patterns and for patterns\n" +
+ "% coded as such we need to enable this regime when\n" +
+ "% not in xetex; this code will be moved into context\n" +
+ "% as soon as we've spread the generic patterns\n" +
+ "\n" +
+ "\\ifx\\XeTeXversion\\undefined \\else\n" +
+ " \\ifx\\enableregime\\undefined \\else\n" +
+ " \\enableregime[utf]\n" +
+ " \\fi\n" +
+ "\\fi\n" +
+ "\n"
+ end
+ end
+
+ def save
+ xml = @commandline.option("xml")
+
+ patname = "lang-#{@language}.pat"
+ hypname = "lang-#{@language}.hyp"
+ rmename = "lang-#{@language}.rme"
+ logname = "lang-#{@language}.log"
+
+ desname = "lang-all.xml"
+
+ @data.gsub!(/\\[nc]\{(.+?)\}/) do $1 end
+ @data.gsub!(/\{\}/) do '' end
+ @data.gsub!(/\n+/mo) do "\n" end
+ @read.gsub!(/\n+/mo) do "\n" end
+
+ description = ''
+ commentfile = rmename.dup
+
+ begin
+ desfile = Kpse.found(desname,'context')
+ if f = File.new(desfile) then
+ if doc = REXML::Document.new(f) then
+ if e = REXML::XPath.first(doc.root,"/descriptions/description[@language='#{@language}']") then
+ description = e.to_s
+ end
+ end
+ end
+ rescue
+ description = ''
+ else
+ unless description.empty? then
+ commentfile = desname.dup
+ str = "<!-- copied from lang-all.xml\n\n"
+ str << "<?xml version='1.0' standalone='yes'?>\n\n"
+ str << description.chomp
+ str << "\n\nend of copy -->\n"
+ str.gsub!(/^/io, "% ") unless @commandline.option('xml')
+ description = comment("begin description data")
+ description << str + "\n"
+ description << comment("end description data")
+ report("description found for language #{@language}")
+ end
+ end
+
+ begin
+ if description.empty? || @commandline.option('log') then
+ if f = File.open(logname,'w') then
+ report("saving #{@remapping.length} remap patterns in #{logname}")
+ @remapping.each do |m|
+ f.puts("#{m[0].inspect} => #{m[1]}\n")
+ end
+ f.close
+ end
+ else
+ File.delete(logname) if FileTest.file?(logname)
+ end
+ rescue
+ end
+
+ begin
+ if description.empty? || @commandline.option('log') then
+ if f = File.open(rmename,'w') then
+ data = @read.dup
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+ f << comment("comment copied from public hyphenation files}")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << comment("begin original comment")
+ f << "#{data}\n"
+ f << comment("end original comment")
+ f.close
+ report("comment saved in file #{rmename}")
+ else
+ report("file #{rmename} is not writable")
+ end
+ else
+ File.delete(rmename) if FileTest.file?(rmename)
+ end
+ rescue
+ end
+
+ begin
+ if f = File.open(patname,'w') then
+ data = ''
+ @data.filterargument('\\patterns') do |content|
+ report("merging patterns")
+ data += content.strip
+ end
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+
+ f << banner
+ f << comment("context pattern file, see #{commentfile} for original comment")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << description
+ f << comment("begin pattern data")
+ f << triggerunicode
+ f << content('patterns', data)
+ f << comment("end pattern data")
+ f.close
+ report("patterns saved in file #{patname}")
+ else
+ report("file #{patname} is not writable")
+ end
+ rescue
+ report("problems with file #{patname}")
+ end
+
+ begin
+ if f = File.open(hypname,'w') then
+ data = ''
+ @data.filterargument('\\hyphenation') do |content|
+ report("merging exceptions")
+ data += content.strip
+ end
+ data.gsub!(/(\s*\n\s*)+/mo, "\n")
+ f << banner
+ f << comment("context hyphenation file, see #{commentfile} for original comment")
+ f << comment("source of data: #{@filenames.join(' ')}")
+ f << description
+ f << comment("begin hyphenation data")
+ f << triggerunicode
+ f << content('hyphenation', data)
+ f << comment("end hyphenation data")
+ f.close
+ report("exceptions saved in file #{hypname}")
+ else
+ report("file #{hypname} is not writable")
+ end
+ rescue
+ report("problems with file #{hypname}")
+ end
+ end
+
+ def process
+ load
+ if valid? then
+ convert
+ save
+ else
+ report("aborted due to missing files")
+ end
+ end
+
+ def Language::generate(commandline, language='', filenames='', encoding='ec')
+ if ! language.empty? && ! filenames.empty? then
+ commandline.report("processing language #{language}")
+ commandline.report("")
+ language = Language.new(commandline,language,filenames,encoding)
+ if language.load then
+ language.convert
+ language.save
+ commandline.report("")
+ end
+ end
+ end
+
+ private
+
+ def located(filename)
+ begin
+ ["context","plain","latex"].each do |name| # fallbacks needed for czech patterns
+ fname = Kpse.found(filename, name)
+ if FileTest.file?(fname) then
+ report("using file #{fname}")
+ return fname
+ end
+ end
+ report("file #{filename} is not present")
+ return nil
+ rescue
+ report("file #{filename} cannot be located using kpsewhich")
+ return nil
+ end
+ end
+
+ def preload_accents
+
+ begin
+ if filename = located("enco-acc.tex") then
+ if data = IO.read(filename) then
+ report("preloading accent conversions")
+ data.scan(/\\defineaccent\s*\\*(.+?)\s*\{*(.+?)\}*\s*\{\\(.+?)\}/o) do
+ one, two, three = $1, $2, $3
+ one.gsub!(/[\`\~\!\^\*\_\-\+\=\:\;\"\'\,\.\?]/o) do
+ "\\#{one}"
+ end
+ remap(/\\#{one} #{two}/, "[#{three}]")
+ remap(/\\#{one}#{two}/, "[#{three}]") unless one =~ /[a-zA-Z]/o
+ remap(/\\#{one}\{#{two}\}/, "[#{three}]")
+ end
+ end
+ end
+ rescue
+ end
+
+ end
+
+ def preload_unicode
+
+ # \definecharacter Agrave {\uchar0{192}}
+
+ begin
+ if filename = located("enco-uc.tex") then
+ if data = IO.read(filename) then
+ report("preloading unicode conversions")
+ data.scan(/\\definecharacter\s*(.+?)\s*\{\\uchar\{*(\d+)\}*\s*\{(\d+)\}/o) do
+ one, two, three = $1, $2.to_i, $3.to_i
+ @unicode[one] = [(two*256 + three)].pack("U")
+ end
+ end
+ end
+ rescue
+ report("error in loading unicode mapping (#{$!})")
+ end
+
+ end
+
+ def preload_vector(encoding='', filename='')
+
+ # funny polish
+
+ case @language
+ when 'pl' then
+ remap(/\/a/, "[aogonek]") ; remap(/\/A/, "[Aogonek]")
+ remap(/\/c/, "[cacute]") ; remap(/\/C/, "[Cacute]")
+ remap(/\/e/, "[eogonek]") ; remap(/\/E/, "[Eogonek]")
+ remap(/\/l/, "[lstroke]") ; remap(/\/L/, "[Lstroke]")
+ remap(/\/n/, "[nacute]") ; remap(/\/N/, "[Nacute]")
+ remap(/\/o/, "[oacute]") ; remap(/\/O/, "[Oacute]")
+ remap(/\/s/, "[sacute]") ; remap(/\/S/, "[Sacute]")
+ remap(/\/x/, "[zacute]") ; remap(/\/X/, "[Zacute]")
+ remap(/\/z/, "[zdotaccent]") ; remap(/\/Z/, "[Zdotaccent]")
+ when 'sl' then
+ remap(/\"c/,"[ccaron]") ; remap(/\"C/,"[Ccaron]")
+ remap(/\"s/,"[scaron]") ; remap(/\"S/,"[Scaron]")
+ remap(/\"z/,"[zcaron]") ; remap(/\"Z/,"[Zcaron]")
+ when 'da' then
+ remap(/X/, "[aeligature]")
+ remap(/Y/, "[ostroke]")
+ remap(/Z/, "[aring]")
+ when 'hu' then
+ # nothing
+ when 'ca' then
+ demap(/\\c\{/, "\\delete{")
+ when 'de', 'deo' then
+ demap(/\\c\{/, "\\delete{")
+ demap(/\\n\{/, "\\keep{")
+ remap(/\\3/, "[ssharp]")
+ remap(/\\9/, "[ssharp]")
+ remap(/\"a/, "[adiaeresis]")
+ remap(/\"o/, "[odiaeresis]")
+ remap(/\"u/, "[udiaeresis]")
+ when 'fr' then
+ demap(/\\n\{/, "\\delete{")
+ remap(/\\ae/, "[aeligature]")
+ remap(/\\oe/, "[oeligature]")
+ when 'la' then
+ # \lccode`'=`' somewhere else, todo
+ demap(/\\c\{/, "\\delete{")
+ remap(/\\a\s*/, "[aeligature]")
+ remap(/\\o\s*/, "[oeligature]")
+ when 'agr' then
+ # bug fix
+ remap("a2|", "[greekalphaiotasub]")
+ remap("h2|", "[greeketaiotasub]")
+ remap("w2|", "[greekomegaiotasub]")
+ remap(">2r1<2r", "[2ῤ1ῥ]")
+ remap(">a2n1wdu'", "[ἀ2ν1ωδύ]")
+ remap(">e3s2ou'", "[ἐ3σ2ού]")
+ # main conversion
+ remap(/\<\'a\|/, "[greekalphaiotasubdasiatonos]")
+ # remap(/\<\'a\|/, "[greekdasiatonos][greekAlpha][greekiota]")
+ remap(/\>\'a\|/, "[greekalphaiotasubpsilitonos]")
+ remap(/\<\`a\|/, "[greekalphaiotasubdasiavaria]")
+ remap(/\>\`a\|/, "[greekalphaiotasubpsilivaria]")
+ remap(/\<\~a\|/, "[greekalphaiotasubdasiaperispomeni]")
+ remap(/\>\~a\|/, "[greekalphaiotasubpsiliperispomeni]")
+ remap(/\'a\|/, "[greekalphaiotasubtonos]")
+ remap(/\`a\|/, "[greekalphaiotasubvaria]")
+ remap(/\~a\|/, "[greekalphaiotasubperispomeni]")
+ remap(/\<a\|/, "[greekalphaiotasubdasia]")
+ remap(/\>a\|/, "[greekalphaiotasubpsili]")
+ remap(/a\|/, "[greekalphaiotasub]")
+ remap(/\<\'h\|/, "[greeketaiotasubdasiatonos]")
+ remap(/\>\'h\|/, "[greeketaiotasubpsilitonos]")
+ remap(/\<\`h\|/, "[greeketaiotasubdasiavaria]")
+ remap(/\>\`h\|/, "[greeketaiotasubpsilivaria]")
+ remap(/\<\~h\|/, "[greeketaiotasubdasiaperispomeni]")
+ remap(/\>\~h\|/, "[greeketaiotasubpsiliperispomeni]")
+ remap(/\'h\|/, "[greeketaiotasubtonos]")
+ remap(/\`h\|/, "[greeketaiotasubvaria]")
+ remap(/\~h\|/, "[greeketaiotasubperispomeni]")
+ remap(/\<h\|/, "[greeketaiotasubdasia]")
+ remap(/\>h\|/, "[greeketaiotasubpsili]")
+ remap(/h\|/, "[greeketaiotasub]")
+ remap(/\<'w\|/, "[greekomegaiotasubdasiatonos]")
+ remap(/\>'w\|/, "[greekomegaiotasubpsilitonos]")
+ remap(/\<`w\|/, "[greekomegaiotasubdasiavaria]")
+ remap(/\>`w\|/, "[greekomegaiotasubpsilivaria]")
+ remap(/\<~w\|/, "[greekomegaiotasubdasiaperispomeni]")
+ remap(/\>~w\|/, "[greekomegaiotasubpsiliperispomeni]")
+ remap(/\<w\|/, "[greekomegaiotasubdasia]")
+ remap(/\>w\|/, "[greekomegaiotasubpsili]")
+ remap(/\'w\|/, "[greekomegaiotasubtonos]")
+ remap(/\`w\|/, "[greekomegaiotasubvaria]")
+ remap(/\~w\|/, "[greekomegaiotasubperispomeni]")
+ remap(/w\|/, "[greekomegaiotasub]")
+ remap(/\<\'i/, "[greekiotadasiatonos]")
+ remap(/\>\'i/, "[greekiotapsilitonos]")
+ remap(/\<\`i/, "[greekiotadasiavaria]")
+ remap(/\>\`i/, "[greekiotapsilivaria]")
+ remap(/\<\~i/, "[greekiotadasiaperispomeni]")
+ remap(/\>\~i/, "[greekiotapsiliperispomeni]")
+ remap(/\"\'i/, "[greekiotadialytikatonos]")
+ remap(/\"\`i/, "[greekiotadialytikavaria]")
+ remap(/\"\~i/, "[greekiotadialytikaperispomeni]")
+ remap(/\<i/, "[greekiotadasia]")
+ remap(/\>i/, "[greekiotapsili]")
+ remap(/\'i/, "[greekiotaoxia]")
+ remap(/\`i/, "[greekiotavaria]")
+ remap(/\~i/, "[greekiotaperispomeni]")
+ remap(/\"i/, "[greekiotadialytika]")
+ remap(/\>\~e/, "[greekepsilonpsiliperispomeni]")
+ remap(/\<\~e/, "[greekepsilondasiaperispomeni]")
+ remap(/\<\'e/, "[greekepsilondasiatonos]")
+ remap(/\>\'e/, "[greekepsilonpsilitonos]")
+ remap(/\<\`e/, "[greekepsilondasiavaria]")
+ remap(/\>\`e/, "[greekepsilonpsilivaria]")
+ remap(/\<e/, "[greekepsilondasia]")
+ remap(/\>e/, "[greekepsilonpsili]")
+ remap(/\'e/, "[greekepsilonoxia]")
+ remap(/\`e/, "[greekepsilonvaria]")
+ remap(/\~e/, "[greekepsilonperispomeni]")
+ remap(/\<\'a/, "[greekalphadasiatonos]")
+ remap(/\>\'a/, "[greekalphapsilitonos]")
+ remap(/\<\`a/, "[greekalphadasiavaria]")
+ remap(/\>\`a/, "[greekalphapsilivaria]")
+ remap(/\<\~a/, "[greekalphadasiaperispomeni]")
+ remap(/\>\~a/, "[greekalphapsiliperispomeni]")
+ remap(/\<a/, "[greekalphadasia]")
+ remap(/\>a/, "[greekalphapsili]")
+ remap(/\'a/, "[greekalphaoxia]")
+ remap(/\`a/, "[greekalphavaria]")
+ remap(/\~a/, "[greekalphaperispomeni]")
+ remap(/\<\'h/, "[greeketadasiatonos]")
+ remap(/\>\'h/, "[greeketapsilitonos]")
+ remap(/\<\`h/, "[greeketadasiavaria]")
+ remap(/\>\`h/, "[greeketapsilivaria]")
+ remap(/\<\~h/, "[greeketadasiaperispomeni]")
+ remap(/\>\~h/, "[greeketapsiliperispomeni]")
+ remap(/\<h/, "[greeketadasia]")
+ remap(/\>h/, "[greeketapsili]")
+ remap(/\'h/, "[greeketaoxia]")
+ remap(/\`h/, "[greeketavaria]")
+ remap(/\~h/, "[greeketaperispomeni]")
+ remap(/\<\~o/, "[greekomicrondasiaperispomeni]")
+ remap(/\>\~o/, "[greekomicronpsiliperispomeni]")
+ remap(/\<\'o/, "[greekomicrondasiatonos]")
+ remap(/\>\'o/, "[greekomicronpsilitonos]")
+ remap(/\<\`o/, "[greekomicrondasiavaria]")
+ remap(/\>\`o/, "[greekomicronpsilivaria]")
+ remap(/\<o/, "[greekomicrondasia]")
+ remap(/\>o/, "[greekomicronpsili]")
+ remap(/\'o/, "[greekomicronoxia]")
+ remap(/\`o/, "[greekomicronvaria]")
+ remap(/\~o/, "[greekomicronperispomeni]")
+ remap(/\<\'u/, "[greekupsilondasiatonos]")
+ remap(/\>\'u/, "[greekupsilonpsilitonos]")
+ remap(/\<\`u/, "[greekupsilondasiavaria]")
+ remap(/\>\`u/, "[greekupsilonpsilivaria]")
+ remap(/\<\~u/, "[greekupsilondasiaperispomeni]")
+ remap(/\>\~u/, "[greekupsilonpsiliperispomeni]")
+ remap(/\"\'u/, "[greekupsilondialytikatonos]")
+ remap(/\"\`u/, "[greekupsilondialytikavaria]")
+ remap(/\"\~u/, "[greekupsilondialytikaperispomeni]")
+ remap(/\<u/, "[greekupsilondasia]")
+ remap(/\>u/, "[greekupsilonpsili]")
+ remap(/\'u/, "[greekupsilonoxia]")
+ remap(/\`u/, "[greekupsilonvaria]")
+ remap(/\~u/, "[greekupsilonperispomeni]")
+ remap(/\"u/, "[greekupsilondiaeresis]")
+ remap(/\<\'w/, "[greekomegadasiatonos]")
+ remap(/\>\'w/, "[greekomegapsilitonos]")
+ remap(/\<\`w/, "[greekomegadasiavaria]")
+ remap(/\>\`w/, "[greekomegapsilivaria]")
+ remap(/\<\~w/, "[greekomegadasiaperispomeni]")
+ remap(/\>\~w/, "[greekomegapsiliperispomeni]")
+ remap(/\<w/, "[greekomegadasia]")
+ remap(/\>w/, "[greekomegapsili]")
+ remap(/\'w/, "[greekomegaoxia]")
+ remap(/\`w/, "[greekomegavaria]")
+ remap(/\~w/, "[greekomegaperispomeni]")
+ remap(/\<r/, "[greekrhodasia]")
+ remap(/\>r/, "[greekrhopsili]")
+ remap(/\<\~/, "[greekdasiaperispomeni]")
+ remap(/\>\~/, "[greekpsiliperispomeni]")
+ remap(/\<\'/, "[greekdasiatonos]")
+ remap(/\>\'/, "[greekpsilitonos]")
+ remap(/\<\`/, "[greekdasiavaria]")
+ remap(/\>\`/, "[greekpsilivaria]")
+ remap(/\"\'/, "[greekdialytikatonos]")
+ remap(/\"\`/, "[greekdialytikavaria]")
+ remap(/\"\~/, "[greekdialytikaperispomeni]")
+ remap(/\</, "[greekdasia]")
+ remap(/\>/, "[greekpsili]")
+ remap(/\d.{0,2}''/, "")
+ remap(/\'/, "[greekoxia]")
+ remap(/\`/, "[greekvaria]")
+ remap(/\~/, "[perispomeni]")
+ remap(/\"/, "[greekdialytika]")
+ # unknown
+ # remap(/\|/, "[greekIotadialytika]")
+ # next
+ remap(/A/, "[greekAlpha]")
+ remap(/B/, "[greekBeta]")
+ remap(/D/, "[greekDelta]")
+ remap(/E/, "[greekEpsilon]")
+ remap(/F/, "[greekPhi]")
+ remap(/G/, "[greekGamma]")
+ remap(/H/, "[greekEta]")
+ remap(/I/, "[greekIota]")
+ remap(/J/, "[greekTheta]")
+ remap(/K/, "[greekKappa]")
+ remap(/L/, "[greekLambda]")
+ remap(/M/, "[greekMu]")
+ remap(/N/, "[greekNu]")
+ remap(/O/, "[greekOmicron]")
+ remap(/P/, "[greekPi]")
+ remap(/Q/, "[greekChi]")
+ remap(/R/, "[greekRho]")
+ remap(/S/, "[greekSigma]")
+ remap(/T/, "[greekTau]")
+ remap(/U/, "[greekUpsilon]")
+ remap(/W/, "[greekOmega]")
+ remap(/X/, "[greekXi]")
+ remap(/Y/, "[greekPsi]")
+ remap(/Z/, "[greekZeta]")
+ remap(/a/, "[greekalpha]")
+ remap(/b/, "[greekbeta]")
+ remap(/c/, "[greekfinalsigma]")
+ remap(/d/, "[greekdelta]")
+ remap(/e/, "[greekepsilon]")
+ remap(/f/, "[greekphi]")
+ remap(/g/, "[greekgamma]")
+ remap(/h/, "[greeketa]")
+ remap(/i/, "[greekiota]")
+ remap(/j/, "[greektheta]")
+ remap(/k/, "[greekkappa]")
+ remap(/l/, "[greeklambda]")
+ remap(/m/, "[greekmu]")
+ remap(/n/, "[greeknu]")
+ remap(/o/, "[greekomicron]")
+ remap(/p/, "[greekpi]")
+ remap(/q/, "[greekchi]")
+ remap(/r/, "[greekrho]")
+ remap(/s/, "[greeksigma]")
+ remap(/t/, "[greektau]")
+ remap(/u/, "[greekupsilon]")
+ remap(/w/, "[greekomega]")
+ remap(/x/, "[greekxi]")
+ remap(/y/, "[greekpsi]")
+ remap(/z/, "[greekzeta]")
+ clone("[greekalphatonos]", "[greekalphaoxia]")
+ clone("[greekepsilontonos]", "[greekepsilonoxia]")
+ clone("[greeketatonos]", "[greeketaoxia]")
+ clone("[greekiotatonos]", "[greekiotaoxia]")
+ clone("[greekomicrontonos]", "[greekomicronoxia]")
+ clone("[greekupsilontonos]", "[greekupsilonoxia]")
+ clone("[greekomegatonos]", "[greekomegaoxia]")
+ when 'ru' then
+ remap(/\xC1/, "[cyrillica]")
+ remap(/\xC2/, "[cyrillicb]")
+ remap(/\xD7/, "[cyrillicv]")
+ remap(/\xC7/, "[cyrillicg]")
+ remap(/\xC4/, "[cyrillicd]")
+ remap(/\xC5/, "[cyrillice]")
+ remap(/\xD6/, "[cyrilliczh]")
+ remap(/\xDA/, "[cyrillicz]")
+ remap(/\xC9/, "[cyrillici]")
+ remap(/\xCA/, "[cyrillicishrt]")
+ remap(/\xCB/, "[cyrillick]")
+ remap(/\xCC/, "[cyrillicl]")
+ remap(/\xCD/, "[cyrillicm]")
+ remap(/\xCE/, "[cyrillicn]")
+ remap(/\xCF/, "[cyrillico]")
+ remap(/\xD0/, "[cyrillicp]")
+ remap(/\xD2/, "[cyrillicr]")
+ remap(/\xD3/, "[cyrillics]")
+ remap(/\xD4/, "[cyrillict]")
+ remap(/\xD5/, "[cyrillicu]")
+ remap(/\xC6/, "[cyrillicf]")
+ remap(/\xC8/, "[cyrillich]")
+ remap(/\xC3/, "[cyrillicc]")
+ remap(/\xDE/, "[cyrillicch]")
+ remap(/\xDB/, "[cyrillicsh]")
+ remap(/\xDD/, "[cyrillicshch]")
+ remap(/\xDF/, "[cyrillichrdsn]")
+ remap(/\xD9/, "[cyrillicery]")
+ remap(/\xD8/, "[cyrillicsftsn]")
+ remap(/\xDC/, "[cyrillicerev]")
+ remap(/\xC0/, "[cyrillicyu]")
+ remap(/\xD1/, "[cyrillicya]")
+ remap(/\xA3/, "[cyrillicyo]")
+ when 'tr' then
+ remap(/\^\^11/, "[dotlessi]")
+ else
+ end
+
+ if ! encoding.empty? then
+ begin
+ filename = Kpse.found(filename, 'context')
+ if data = IO.readlines(filename.chomp) then
+ report("preloading #{encoding} character mappings")
+ accept = false
+ data.each do |line|
+ case line.chomp
+ when /\\start(en|)coding\s*\[(.*?)\]/io then
+ enc = $2
+ if accept = (enc == encoding) then
+ report("accepting vector #{enc}")
+ else
+ report("skipping vector #{enc}")
+ end
+ when /\\stop(en|)coding/io then
+ accept = false
+ when accept && /\\definecharacter\s*([a-zA-Z]+)\s*(\d+)\s*/o then
+ name, number = $1, $2
+ remap(/\^\^#{sprintf("%02x",number)}/, "[#{name}]")
+ if number.to_i > 127 then
+ remap(/#{sprintf("\\%03o",number)}/, "[#{name}]")
+ end
+ end
+ end
+ end
+ rescue
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ public
+
+ @@languagedata = Hash.new
+
+ def patternfiles
+ language = @commandline.argument('first')
+ if (language == 'all') || language.empty? then
+ languages = @@languagedata.keys.sort
+ elsif @@languagedata.key?(language) then
+ languages = [language]
+ else
+ languages = []
+ end
+ languages.each do |language|
+ encoding = @@languagedata[language][0] || ''
+ files = @@languagedata[language][1] || []
+ Language::generate(self,language,files,encoding)
+ end
+ end
+
+ private
+
+ # todo: filter the fallback list from context
+
+ # The first entry in the array is the encoding which will be used
+ # when interpreting the raw patterns. The second entry is a list of
+ # filesets (string|aray), each first match of a set is taken.
+
+ @@languagedata['ba' ] = [ 'ec' , ['bahyph.tex'] ]
+ @@languagedata['ca' ] = [ 'ec' , ['cahyph.tex'] ]
+ @@languagedata['cy' ] = [ 'ec' , ['cyhyph.tex'] ]
+ @@languagedata['cs' ] = [ 'ec' , ['czhyphen.tex','czhyphen.ex'] ]
+ @@languagedata['de' ] = [ 'ec' , ['dehyphn.tex'] ]
+ @@languagedata['deo'] = [ 'ec' , ['dehypht.tex'] ]
+ @@languagedata['da' ] = [ 'ec' , ['dkspecial.tex','dkcommon.tex'] ]
+ # elhyph.tex
+ @@languagedata['es' ] = [ 'ec' , ['eshyph.tex'] ]
+ @@languagedata['et' ] = [ 'ec' , ['ethyph.tex'] ]
+ @@languagedata['fi' ] = [ 'ec' , ['fihyph.tex'] ]
+ @@languagedata['fr' ] = [ 'ec' , ['frhyph.tex'] ]
+ # ghyphen.readme ghyph31.readme grphyph
+ @@languagedata['hr' ] = [ 'ec' , ['hrhyph.tex'] ]
+ @@languagedata['hu' ] = [ 'ec' , ['huhyphn.tex'] ]
+ @@languagedata['us' ] = [ 'default' , [['ushyphmax.tex'],['ushyph.tex'],['hyphen.tex']] ]
+ @@languagedata['us' ] = [ 'default' , [['ushyphmax.tex'],['ushyph.tex'],['hyphen.tex']] ]
+ # inhyph.tex
+ @@languagedata['is' ] = [ 'ec' , ['ishyph.tex'] ]
+ @@languagedata['it' ] = [ 'ec' , ['ithyph.tex'] ]
+ @@languagedata['la' ] = [ 'ec' , ['lahyph.tex'] ]
+ # mnhyph
+ @@languagedata['nl' ] = [ 'ec' , ['nehyph96.tex'] ]
+ # @@languagedata['no' ] = [ 'ec' , ['nohyphbx.tex'],['nohyphb.tex'],['nohyph2.tex'],['nohyph1.tex'],['nohyph.tex'] ]
+ @@languagedata['no' ] = [ 'ec' , [['asxsx.tex','nohyphbx.tex'],['nohyphb.tex'],['nohyph2.tex'],['nohyph1.tex'],['nohyph.tex']] ]
+ @@languagedata['agr'] = [ 'agr' , [['grahyph4.tex'], ['oldgrhyph.tex']] ] # new, todo
+ @@languagedata['pl' ] = [ 'ec' , ['plhyph.tex'] ]
+ @@languagedata['pt' ] = [ 'ec' , ['pthyph.tex'] ]
+ @@languagedata['ro' ] = [ 'ec' , ['rohyph.tex'] ]
+ @@languagedata['sl' ] = [ 'ec' , [['slhyph.tex'], ['sihyph.tex']] ]
+ @@languagedata['sk' ] = [ 'ec' , ['skhyphen.tex','skhyphen.ex'] ]
+ # sorhyph.tex / upper sorbian
+ # srhyphc.tex / cyrillic
+ @@languagedata['sv' ] = [ 'ec' , ['svhyph.tex'] ]
+ @@languagedata['tr' ] = [ 'ec' , ['tkhyph.tex'] ]
+ @@languagedata['gb' ] = [ 'default' , [['ukhyphen.tex'],['ukhyph.tex']] ]
+ # @@languagedata['ru' ] = [ 't2a' , ['ruhyphal.tex'] ] # t2a does not work
+ @@languagedata['ru' ] = [ 'cyr' , ['ruhyphal.tex'] ]
+end
+
+class Commands
+
+ include CommandBase
+
+ def dpxmapfiles
+
+ force = @commandline.option("force")
+
+ texmfroot = @commandline.argument('first')
+ texmfroot = '.' if texmfroot.empty?
+ if @commandline.option('maproot') != "" then
+ maproot = @commandline.option('maproot')
+ else
+ maproot = "#{texmfroot.gsub(/\\/,'/')}/fonts/map/pdftex/context"
+ end
+ if File.directory?(maproot) then
+ files = Dir.glob("#{maproot}/*.map")
+ if files.size > 0 then
+ files.each do |pdffile|
+ next if File.basename(pdffile) == 'pdftex.map'
+ pdffile = File.expand_path(pdffile)
+ dpxfile = File.expand_path(pdffile.sub(/(dvips|pdftex)/i,'dvipdfm'))
+ unless pdffile == dpxfile then
+ begin
+ if data = File.read(pdffile) then
+ report("< #{File.basename(pdffile)} - pdf(e)tex")
+ n = 0
+ data = data.collect do |line|
+ if line =~ /^[\%\#]+/mo then
+ ''
+ else
+ encoding = if line =~ /([a-z0-9\-]+)\.enc/io then $1 else '' end
+ fontfile = if line =~ /([a-z0-9\-]+)\.(pfb|ttf)/io then $1 else nil end
+ metrics = if line =~ /^([a-z0-9\-]+)[\s\<]+/io then $1 else nil end
+ slant = if line =~ /\"([\d\.]+)\s+SlantFont\"/io then "-s #{$1}" else '' end
+ if metrics && encoding && fontfile then
+ n += 1
+ "#{metrics} #{encoding} #{fontfile} #{slant}"
+ else
+ ''
+ end
+ end
+ end
+ data.delete_if do |line|
+ line.gsub(/\s+/,'').empty?
+ end
+ data.collect! do |line|
+ # remove line with "name name" lines
+ line.gsub(/^(\S+)\s+\1\s*$/) do
+ $1
+ end
+ end
+ begin
+ if force then
+ if n > 0 then
+ File.makedirs(File.dirname(dpxfile))
+ if f = File.open(dpxfile,'w') then
+ report("> #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
+ f.puts(data)
+ f.close
+ else
+ report("? #{File.basename(dpxfile)} - dvipdfm(x)")
+ end
+ else
+ report("- #{File.basename(dpxfile)} - dvipdfm(x) - no entries")
+ # begin File.delete(dpxname) ; rescue ; end
+ if f = File.open(dpxfile,'w') then
+ f.puts("% no map entries")
+ f.close
+ end
+ end
+ else
+ report(". #{File.basename(dpxfile)} - dvipdfm(x) - #{n}")
+ end
+ rescue
+ report("error in saving dvipdfm file")
+ end
+ else
+ report("error in loading pdftex file")
+ end
+ rescue
+ report("error in processing pdftex file")
+ end
+ end
+ end
+ if force then
+ begin
+ report("regenerating database for #{texmfroot}")
+ system("mktexlsr #{texmfroot}")
+ rescue
+ end
+ end
+ else
+ report("no mapfiles found in #{maproot}")
+ end
+ else
+ report("provide proper texmfroot")
+ end
+
+ end
+
+end
+
+class Array
+
+ def add_shebang(filename,program)
+ unless self[0] =~ /^\#/ then
+ self.insert(0,"\#!/usr/bin/env #{program}")
+ end
+ unless self[2] =~ /^\#.*?copyright\=/ then
+ self.insert(1,"\#")
+ self.insert(2,"\# copyright=pragma-ade readme=readme.pdf licence=cc-gpl")
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ self[2].gsub!(/ +/, ' ')
+ return true
+ else
+ return false
+ end
+ end
+
+ def add_directive(filename,program)
+ unless self[0] =~ /^\%/ then
+ self.insert(0,"\% content=#{program}")
+ end
+ unless self[2] =~ /^\%.*?copyright\=/ then
+ self.insert(1,"\%")
+ if File.expand_path(filename) =~ /[\\\/](doc|manuals)[\\\/]/ then
+ self.insert(2,"\% copyright=pragma-ade readme=readme.pdf licence=cc-by-nc-sa")
+ else
+ self.insert(2,"\% copyright=pragma-ade readme=readme.pdf licence=cc-gpl")
+ end
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ self[0].gsub!(/ +/, ' ')
+ return true
+ else
+ return false
+ end
+ end
+
+ def add_comment(filename)
+ if self[0] =~ /<\?xml.*?\?>/ && self[2] !~ /^<\!\-\-.*?copyright\=.*?\-\->/ then
+ self.insert(1,"")
+ if File.expand_path(filename) =~ /[\\\/](doc|manuals)[\\\/]/ then
+ self.insert(2,"<!-- copyright='pragma-ade' readme='readme.pdf' licence='cc-by-nc-sa' -->")
+ else
+ self.insert(2,"<!-- copyright='pragma-ade' readme='readme.pdf' licence='cc-gpl' -->")
+ end
+ self.insert(3,"") unless self[3].chomp.strip.empty?
+ return true
+ else
+ return false
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def brandfiles
+
+ force = @commandline.option("force")
+ files = @commandline.arguments # Dir.glob("**/*.*")
+ done = false
+
+ files.each do |filename|
+ if FileTest.file?(filename) then
+ ok = false
+ begin
+ data = IO.readlines(filename)
+ case filename
+ when /\.rb$/ then
+ ok = data.add_shebang(filename,'ruby')
+ when /\.pl$/ then
+ ok = data.add_shebang(filename,'perl')
+ when /\.py$/ then
+ ok = data.add_shebang(filename,'python')
+ when /\.lua$/ then
+ ok = data.add_shebang(filename,'lua')
+ when /\.tex$/ then
+ ok = data.add_directive(filename,'tex')
+ when /\.mp$/ then
+ ok = data.add_directive(filename,'metapost')
+ when /\.mf$/ then
+ ok = data.add_directive(filename,'metafont')
+ when /\.(xml|xsl|fo|fx|rlx|rng|exa)$/ then
+ ok = data.add_comment(filename)
+ end
+ rescue
+ report("fatal error in processing #{filename}") # maybe this catches the mac problem taco reported
+ else
+ if ok then
+ report()
+ report(filename)
+ report()
+ for i in 0..4 do
+ report(' ' + data[i].chomp)
+ end
+ if force && f = File.open(filename,'w') then
+ f.puts data
+ f.close
+ end
+ done = true
+ end
+ end
+ else
+ report("no file named #{filename}")
+ end
+ end
+ report() if done
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ # usage : ctxtools --listentities entities.xml
+ # document: <!DOCTYPE something SYSTEM "entities.xml">
+
+ def flushentities(handle,entities,doctype=nil) # 'stylesheet'
+ if doctype then
+ tab = "\t"
+ handle << "<?xml version='1.0' encoding='utf-8'?>\n\n"
+ handle << "<!-- !DOCTYPE entities SYSTEM 'entities.xml' -->\n\n"
+ handle << "<!DOCTYPE #{doctype} [\n"
+ else
+ tab = ""
+ end
+ entities.keys.sort.each do |k|
+ handle << "#{tab}<!ENTITY #{k} \"\&\#x#{entities[k]};\">\n"
+ end
+ if doctype then
+ handle << "]>\n"
+ end
+ end
+
+ def listentities
+
+ filenames = ['enco-uc.tex','contextnames.txt']
+ outputname = @commandline.argument('first')
+ doctype = @commandline.option('doctype')
+ entities = Hash.new
+
+ filenames.each do |filename|
+ filename = Kpse.found(filename, 'context')
+ if filename and not filename.empty? and FileTest.file?(filename) then
+ report("loading #{filename.gsub(/\\/,'/')}") unless outputname.empty?
+ IO.readlines(filename).each do |line|
+ case line
+ when /^[\#\%]/io then
+ # skip comment line
+ when /\\definecharacter\s+([a-z]+)\s+\{\\uchar\{*(\d+)\}*\{(\d+)\}\}/io then
+ name, code = $1, sprintf("%04X",$2.to_i*256 + $3.to_i)
+ entities[name] = code.rjust(4,'0') unless entities.key?(name)
+ when /^([A-F0-9]+)\;([a-z][a-z]+)\;(.*?)\;(.*?)\s*$/io then
+ code, name, adobe, comment = $1, $2, $3, $4
+ entities[name] = code.rjust(4,'0') unless entities.key?(name)
+ end
+ end
+ end
+ end
+ if outputname and not outputname.empty? then
+ if f = File.open(outputname,'w') then
+ report("saving #{entities.size} entities in #{outputname}")
+ flushentities(f,entities,doctype)
+ f.close
+ else
+ flushentities($stdout,entities,doctype)
+ end
+ else
+ flushentities($stdout,entities,doctype)
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def platformize
+
+ pattern = if @commandline.arguments.empty? then "*.{rb,pl,py}" else @commandline.arguments end
+ recurse = @commandline.option("recurse")
+ force = @commandline.option("force")
+ pattern = "#{if recurse then '**/' else '' end}#{pattern}"
+ Dir.glob(pattern).each do |file|
+ if File.file?(file) then
+ size = File.size(file)
+ data = IO.readlines(file)
+ if force then
+ if f = File.open(file,'w')
+ data.each do |line|
+ f.puts(line.chomp)
+ end
+ f.close
+ end
+ if File.size(file) == size then # not robust
+ report("file '#{file}' is unchanged")
+ else
+ report("file '#{file}' is platformized")
+ end
+ else
+ report("file '#{file}' is a candidate")
+ end
+ end
+ end
+ end
+
+end
+
+class TexDeps
+
+ @@cs_tex = %q/
+ above abovedisplayshortskip abovedisplayskip
+ abovewithdelims accent adjdemerits advance afterassignment
+ aftergroup atop atopwithdelims
+ badness baselineskip batchmode begingroup
+ belowdisplayshortskip belowdisplayskip binoppenalty botmark
+ box boxmaxdepth brokenpenalty
+ catcode char chardef cleaders closein closeout clubpenalty
+ copy count countdef cr crcr csname
+ day deadcycles def defaulthyphenchar defaultskewchar
+ delcode delimiter delimiterfactor delimeters
+ delimitershortfall delimeters dimen dimendef discretionary
+ displayindent displaylimits displaystyle
+ displaywidowpenalty displaywidth divide
+ doublehyphendemerits dp dump
+ edef else emergencystretch end endcsname endgroup endinput
+ endlinechar eqno errhelp errmessage errorcontextlines
+ errorstopmode escapechar everycr everydisplay everyhbox
+ everyjob everymath everypar everyvbox exhyphenpenalty
+ expandafter
+ fam fi finalhyphendemerits firstmark floatingpenalty font
+ fontdimen fontname futurelet
+ gdef global group globaldefs
+ halign hangafter hangindent hbadness hbox hfil horizontal
+ hfill horizontal hfilneg hfuzz hoffset holdinginserts hrule
+ hsize hskip hss horizontal ht hyphenation hyphenchar
+ hyphenpenalty hyphen
+ if ifcase ifcat ifdim ifeof iffalse ifhbox ifhmode ifinner
+ ifmmode ifnum ifodd iftrue ifvbox ifvmode ifvoid ifx
+ ignorespaces immediate indent input inputlineno input
+ insert insertpenalties interlinepenalty
+ jobname
+ kern
+ language lastbox lastkern lastpenalty lastskip lccode
+ leaders left lefthyphenmin leftskip leqno let limits
+ linepenalty line lineskip lineskiplimit long looseness
+ lower lowercase
+ mag mark mathaccent mathbin mathchar mathchardef mathchoice
+ mathclose mathcode mathinner mathop mathopen mathord
+ mathpunct mathrel mathsurround maxdeadcycles maxdepth
+ meaning medmuskip message mkern month moveleft moveright
+ mskip multiply muskip muskipdef
+ newlinechar noalign noboundary noexpand noindent nolimits
+ nonscript scriptscript nonstopmode nulldelimiterspace
+ nullfont number
+ omit openin openout or outer output outputpenalty over
+ overfullrule overline overwithdelims
+ pagedepth pagefilllstretch pagefillstretch pagefilstretch
+ pagegoal pageshrink pagestretch pagetotal par parfillskip
+ parindent parshape parskip patterns pausing penalty
+ postdisplaypenalty predisplaypenalty predisplaysize
+ pretolerance prevdepth prevgraf
+ radical raise read relax relpenalty right righthyphenmin
+ rightskip romannumeral
+ scriptfont scriptscriptfont scriptscriptstyle scriptspace
+ scriptstyle scrollmode setbox setlanguage sfcode shipout
+ show showbox showboxbreadth showboxdepth showlists showthe
+ skewchar skip skipdef spacefactor spaceskip span special
+ splitbotmark splitfirstmark splitmaxdepth splittopskip
+ string
+ tabskip textfont textstyle the thickmuskip thinmuskip time
+ toks toksdef tolerance topmark topskip tracingcommands
+ tracinglostchars tracingmacros tracingonline tracingoutput
+ tracingpages tracingparagraphs tracingrestores tracingstats
+ uccode uchyph underline unhbox unhcopy unkern unpenalty
+ unskip unvbox unvcopy uppercase
+ vadjust valign vbadness vbox vcenter vfil vfill vfilneg
+ vfuzz voffset vrule vsize vskip vsplit vss vtop
+ wd widowpenalty write
+ xdef xleaders xspaceskip
+ year
+ /.split
+
+ @@cs_etex = %q/
+ beginL beginR botmarks
+ clubpenalties currentgrouplevel currentgrouptype
+ currentifbranch currentiflevel currentiftype
+ detokenize dimexpr displaywidowpenalties
+ endL endR eTeXrevision eTeXversion everyeof
+ firstmarks fontchardp fontcharht fontcharic fontcharwd
+ glueexpr glueshrink glueshrinkorder gluestretch
+ gluestretchorder gluetomu
+ ifcsname ifdefined iffontchar interactionmode
+ interactionmode interlinepenalties
+ lastlinefit lastnodetype
+ marks topmarks middle muexpr mutoglue
+ numexpr
+ pagediscards parshapedimen parshapeindent parshapelength
+ predisplaydirection
+ savinghyphcodes savingvdiscards scantokens showgroups
+ showifs showtokens splitdiscards splitfirstmarks
+ TeXXeTstate tracingassigns tracinggroups tracingifs
+ tracingnesting tracingscantokens
+ unexpanded unless
+ widowpenalties
+ /.split
+
+ @@cs_pdftex = %q/
+ pdfadjustspacing pdfannot pdfavoidoverfull
+ pdfcatalog pdfcompresslevel
+ pdfdecimaldigits pdfdest pdfdestmargin
+ pdfendlink pdfendthread
+ pdffontattr pdffontexpand pdffontname pdffontobjnum pdffontsize
+ pdfhorigin
+ pdfimageresolution pdfincludechars pdfinfo
+ pdflastannot pdflastdemerits pdflastobj
+ pdflastvbreakpenalty pdflastxform pdflastximage
+ pdflastximagepages pdflastxpos pdflastypos
+ pdflinesnapx pdflinesnapy pdflinkmargin pdfliteral
+ pdfmapfile pdfmaxpenalty pdfminpenalty pdfmovechars
+ pdfnames
+ pdfobj pdfoptionpdfminorversion pdfoutline pdfoutput
+ pdfpageattr pdfpageheight pdfpageresources pdfpagesattr
+ pdfpagewidth pdfpkresolution pdfprotrudechars
+ pdfrefobj pdfrefxform pdfrefximage
+ pdfsavepos pdfsnaprefpoint pdfsnapx pdfsnapy pdfstartlink
+ pdfstartthread
+ pdftexrevision pdftexversion pdfthread pdfthreadmargin
+ pdfuniqueresname
+ pdfvorigin
+ pdfxform pdfximage
+ /.split
+
+ @@cs_omega = %q/
+ odelimiter omathaccent omathchar oradical omathchardef omathcode odelcode
+ leftghost rightghost
+ charwd charht chardp charit
+ localleftbox localrightbox
+ localinterlinepenalty localbrokenpenalty
+ pagedir bodydir pardir textdir mathdir
+ boxdir nextfakemath
+ pagewidth pageheight pagerightoffset pagebottomoffset
+ nullocp nullocplist ocp externalocp ocplist pushocplist popocplist clearocplists ocptracelevel
+ addbeforeocplist addafterocplist removebeforeocplist removeafterocplist
+ OmegaVersion
+ InputTranslation OutputTranslation DefaultInputTranslation DefaultOutputTranslation
+ noInputTranslation noOutputTranslation
+ InputMode OutputMode DefaultInputMode DefaultOutputMode
+ noInputMode noOutputMode noDefaultInputMode noDefaultOutputMode
+ /.split
+
+ @@cs_metatex = %q/
+ /.split
+
+ @@cs_xetex = %q/
+ /.split
+
+ @@cs_skip = %q/
+ v\! c\! s\! e\! m\! f\!
+ \!tf \!tt \!tq \!ta \?\?
+ csname endcsname relax
+ \!\!string[a-f] \!\!dimen[a-k] \!\!count[a-f] \!\!toks[a-e] \!\!box[a-e]
+ \!\!width[a-c] \!\!height[a-c] \!\!depth[a-c]
+ \!\!done[a-f] if\!\!done[a-f] if\:\!\!done[a-f]
+ scratch globalscratch
+ ascii[a-d] globalascii
+ @@expanded @@globalexpanded @EA @EAEA @EAEAEA
+ bgroup egroup par next nextnext docommand dodocommand dododocommand
+ \!\!width \!\!height \!\!depth \!\!plus \!\!minus \!\!to
+ /.split
+
+ @@cs_skip = %q/
+ [vcsemf]\! \?\?
+ \!t[ftqa]
+ csname endcsname relax
+ \!\!string[a-f] \!\!dimen[a-k] \!\!count[a-f] \!\!toks[a-e] \!\!box[a-e]
+ \!\!width[a-c] \!\!height[a-c] \!\!depth[a-c]
+ \!\!done[a-f] if\!\!done[a-f] if\:\!\!done[a-f]
+ scratch globalscratch
+ ascii[a-d] globalascii
+ @@expanded @@globalexpanded @(EA)+
+ [be]group par next nextnext (do)+command
+ \!\!(width|height|depth|plus|minus|to)
+ /.split
+
+ # let's ignore \dimendef etc
+
+ @@primitives_def = %q/
+ def edef xdef gdef let
+ newcount newdimen newskip newbox newtoks newmarks newif newinsert newmuskip
+ chardef mathchardef dimendef countdef toksdef
+ newconditional definecomplexorsimple definecomplexorsimpleempty
+ newcounter newpersistentmark
+ installinsertion installspecial\s*\\[* installoutput\s*\\[*
+ /.split
+
+ @@types = [['invalid','*'],['okay','='],['forward','>'],['backward','<'],['unknown','?']]
+
+ @@skips = /^(#{@@cs_skip.join('|')})/o
+
+ def initialize(logger=nil,compact=false)
+ @defined = Hash.new
+ @definitive = Hash.new
+ @used_before = Hash.new
+ @used_after = Hash.new
+ @dependencies = Hash.new
+ @fineorder = Hash.new
+ @forward = Hash.new
+ @backward = Hash.new
+ @disorder = Hash.new
+ @disordercs = Hash.new
+ @type = Hash.new
+ @filename = 'context.tex'
+ @files = Array.new # keep load order !
+ @order = Hash.new
+ @logger = logger
+ @filefilter = nil
+ @namefilter = nil
+ @compact = compact
+ #
+ @@cs_tex.each do |cs| @defined[cs] = ['-tex--------'] end
+ @@cs_etex.each do |cs| @defined[cs] = ['-etex-------'] end
+ @@cs_pdftex.each do |cs| @defined[cs] = ['-pdftex-----'] end
+ @@cs_omega.each do |cs| @defined[cs] = ['-omega------'] end
+ @@cs_xetex.each do |cs| @defined[cs] = ['-xetex------'] end
+ @@cs_metatex.each do |cs| @defined[cs] = ['-metatex----'] end
+ end
+
+ def report(str)
+ @logger.report(str) rescue false
+ end
+
+ def setfilter(data)
+ data.split(/\s*\,\s*/).each do |d|
+ if d =~ /\.tex$/ then
+ @filefilter = Array.new unless @filefilter
+ @filefilter << d
+ else
+ @namefilter = Array.new unless @namefilter
+ @namefilter << d
+ end
+ end
+ end
+
+ def load(filename='context.tex')
+ begin
+ @filename = filename
+ n = 0
+ File.open(filename) do |f|
+ f.each do |line|
+ if line =~ /^(\\input\s+|\\load[a-z]+\{)([a-z\-\.]+)(\}*)/ then
+ ante, name, post = $1, $2, $3
+ @files.push(name)
+ @order[name] = n += 1
+ end
+ end
+ end
+ rescue
+ @files = Array.new
+ @order = Hash.new
+ end
+ end
+
+ def save(filename='context.tex')
+ unless @filefilter || @namefilter then
+ begin
+ data = IO.readlines(filename).each do |line|
+ line.gsub!(/^(\\input\s+|\\load[a-z]+\{)([a-z\-\.]+)(\}*)\s*$/) do
+ ante, name, post = $1, $2, $3
+ fin = (@fineorder[name] || [])-[name]
+ dep = (@dependencies[name] || [])-[name]
+ dis = (@disorder[name] || [])-[name]
+ fin = if fin.size > 0 then " B[#{fin.join(' ')}]" else "" end
+ dep = if dep.size > 0 then " A[#{dep.join(' ')}]" else "" end
+ dis = if dis.size > 0 then " D[#{dis.join(' ')}]" else "" end
+ "#{ante}#{name}#{post} %#{fin}#{dep}#{dis}\n"
+ end
+ end
+ rescue
+ report("error: #{$!}")
+ else
+ begin
+ newname = filename.sub(/\..*$/,'.log')
+ report("")
+ report("writing to #{newname}")
+ report("")
+ File.open(newname,'w') do |f|
+ f << data
+ end
+ rescue
+ report("error: #{$!}")
+ end
+ end
+ end
+ end
+
+ def analyze
+ report('')
+ report("loading files")
+ report('')
+ n = 0
+# try tex and mkiv
+ @files.each do |filename|
+ if File.file?(filename) and f = File.open(filename) then
+ defs, uses, l = 0, 0, 0
+ n += 1
+ report("#{n.to_s.rjust(5,' ')} #{filename}")
+ f.each do |line|
+ l += 1
+ line.chomp!
+
+
+ line.sub!(/\%.*$/, '')
+ line.gsub!(/\\(unexpanded|unprotected|global|protected|long)\s*(\\)/, "\\")
+ # the superseded, overloaded, forwarded, and predefined macros
+ # are at the outer level anyway, so there we may ignore leading
+ # spaces (could be inside an \if); other definitions are only
+ # accepted when they start at the beginning of a line
+ case line
+ when /^\\ifx\s*\\[a-zA-Z\@\!\?]+\s*\\undefined\s*(\\else)*(.*?)$/ then
+ if $2 =~ /^\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})/o then
+ pushdef(filename,l,$2,5) # kind of auto-predefined
+ end
+ when /^\s*\\superseded\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,1)
+ moreuse(filename,l,rest)
+ when /^\s*\\overloaded\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,2)
+ moreuse(filename,l,rest)
+ when /^\s*\\forwarded\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,3)
+ moreuse(filename,l,rest)
+ when /^\s*\\predefined\s*\\(#{@@primitives_def.join('|')})\s*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3
+ pushdef(filename,l,name,4)
+ moreuse(filename,l,rest)
+ when /^\\(#{@@primitives_def.join('|')})[\=\s]*\\([a-zA-Z\@\?\!]{3,})(.*)$/o
+ name, rest = $2, $3 # \=* catches the \let \a = \b
+ pushdef(filename,l,name,0)
+ moreuse(filename,l,rest)
+ when /\\newevery\s*\\([a-zA-Z\@\?\!]+)\s*\\([a-zA-Z\@\?\!]+)/ then
+ a, b = $1, $2
+ pushdef(filename,l,a,0)
+ pushdef(filename,l,b,0)
+ else
+ moreuse(filename,l,line)
+ end
+ end
+ f.close
+ end
+ end
+ @used_after.each do |cs,files|
+ (@defined[cs] || []).each do |name|
+ @dependencies[name] = Array.new unless @dependencies[name]
+ files.each do |file|
+ @dependencies[name] << file unless @dependencies[name].include?(file)
+ end
+ end
+ end
+ @used_before.each do |cs,files|
+ (@defined[cs] || []).each do |name|
+ @disorder[name] = Array.new unless @disorder[name]
+ @disordercs[name] = Array.new unless @disordercs[name]
+ @fineorder[name] = Array.new unless @fineorder[name]
+ files.each do |file|
+ unless @disorder[name].include?(file) || name == file then
+ unless @defined[cs].include?(file) then
+ if @order[name] > @order[file] then
+ @disorder[name] << file
+ @disordercs[name] << "#{file}:#{cs}"
+ end
+ end
+ end
+ @fineorder[name] << file unless @fineorder[name].include?(file) || name == file
+ end
+ end
+ end
+ end
+
+ def moreuse(filename,l,line)
+ line.scan(/\\if([a-zA-Z@\?\!]{3,})/) do |name, rest| # rest, else array
+ pushuse(filename,l,"if#{name}") unless name =~ /^(true|false)$/
+ end
+ line.scan(/\\([a-zA-Z@\?\!]{3,})/) do |name, rest| # rest, else array
+ if name =~ /(true|false)$/ then
+ pushuse(filename,l,"if#{name}") unless name =~ /^(if|set)$/
+ else
+ pushuse(filename,l,name)
+ end
+ end
+ end
+
+ def feedback
+ begin
+ # get max length
+ l = 0
+ list = @defined.keys.sort
+ list.each do |cs|
+ l = cs.length if cs.length > l
+ end
+ if ! @compact then
+ n = 0
+ report('')
+ report("defined: #{@defined.size}")
+ report('')
+ @defined.keys.sort.each do |cs|
+ next if @namefilter && ! @namefilter.include?(cs)
+ next if @filefilter && ! @defined[cs].include?(cs)
+ if @defined[cs].size > 1 then
+ dlist = @defined[cs].collect do |d|
+ if d == @definitive[cs] then d else "[#{d}]" end
+ end
+ else
+ dlist = @defined[cs]
+ end
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{dlist.join(' ')}")
+ end
+ end
+ if true then
+ n = 0
+ report('')
+ report("used before defined: #{@used_before.size}")
+ report('')
+ @used_before.keys.sort.each do |cs|
+ next if @namefilter && ! @namefilter.include?(cs)
+ next if @filefilter && (@used_before[cs] & @filefilter).size == 0
+ used = @used_before[cs] - (@defined[cs] || [])
+ defined = (@defined[cs] || []).join(' ')
+ defined = "[ ? ]" if defined.empty?
+ if used.size > 0 then
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined} -> #{used.join(' ')}")
+ else
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined}")
+ end
+ end
+ report(' none') if n == 0
+ end
+ if ! @compact then
+ n = 0
+ report('')
+ report("used after defined: #{@used_after.size}")
+ report('')
+ @used_after.keys.sort.each do |cs|
+ next if @namefilter && ! @namefilter.include?(cs)
+ next if @filefilter && (@used_after[cs] & @filefilter).size == 0
+ used = @used_after[cs] - (@defined[cs] || [])
+ defined = (@defined[cs] || []).join(' ')
+ if used.size > 0 then
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined} <- #{used.join(' ')}")
+ else
+ report("#{(n += 1).to_s.rjust(5,' ')} #{cs.ljust(l,' ')} == #{defined}")
+ end
+ end
+ report(' none') if n == 0
+ end
+ if ! @compact then
+ unless @filefilter || @namefilter then
+ [false,true].each do |mode|
+ n = 0
+ report("")
+ report("file dependecies #{if mode then '(critical)' end}")
+ [@dependencies].each do |dependencies|
+ report("")
+ dependencies.keys.sort.each do |f|
+ if dependencies[f].size > 0 then
+ dependencies[f].delete(f)
+ end
+ if mode then
+ dep = dependencies[f].delete_if do |d|
+ f[0..3] == d[0..3] # same xxxx- prefix
+ end
+ else
+ dep = dependencies[f]
+ end
+ if dep.size > 0 then
+ name = f.nosuffix('tex').ljust(8,' ')
+ list = dep.sort.collect do |k| k.nosuffix('tex') end
+ report("#{(n += 1).to_s.rjust(5,' ')} #{name} !! #{list.join(' ')}")
+ end
+ end
+ end
+ report(' none') if n == 0
+ end
+ end
+ end
+ if true then
+ unless @filefilter || @namefilter then
+ [false,true].each do |mode|
+ [@disorder,@disordercs].each do |disorder|
+ n = 0
+ report("")
+ report("file disorder #{if mode then '(critical)' end}")
+ report("")
+ disorder.keys.sort.each do |f|
+ if disorder[f].size > 0 then
+ disorder[f].delete(f)
+ end
+ if mode then
+ dis = disorder[f].delete_if do |d|
+ f[0..3] == d[0..3] # same xxxx- prefix
+ end
+ else
+ dis = disorder[f]
+ end
+ if dis.size > 0 then
+ name = f.nosuffix('tex').ljust(8,' ')
+ list = dis.sort.collect do |k| k.nosuffix('tex') end
+ report("#{(n += 1).to_s.rjust(3,' ')} #{name} ?? #{list.join(' ')}")
+ end
+ end
+ end
+ report(' none') if n == 0
+ end
+ end
+ end
+ rescue
+ puts("fatal error: #{$!} #{$@.join("\n")}")
+ end
+ end
+
+ private
+
+ def csdefined?(cs,filename)
+ @defined[cs] && @defined[cs].include?(filename)
+ end
+ def csbefore?(cs,filename)
+ @used_before[cs] && @used_before[cs].include?(filename)
+ end
+ def csafter?(cs,filename)
+ @used_after[cs] && @used_after[cs].include?(filename)
+ end
+
+ def csignored?(cs)
+ cs.to_s =~ @@skips
+ end
+
+ def pushdef(filename,n,cs,type)
+ if csignored?(cs) then
+ # nothing
+ elsif @defined[cs] then
+ case type
+ when 5 then
+ # if test, no definition done
+ else
+ @definitive[cs] = filename
+ unless @filefilter || @namefilter then
+ report("#{cs} is redefined") unless csdefined?(cs,filename) || @compact
+ end
+ end
+ @defined[cs] << filename unless @defined[cs].include?(filename)
+ else
+ @defined[cs] = Array.new
+ @defined[cs] << filename
+ @definitive[cs] = filename
+ @type[cs] = type
+ end
+ end
+
+ def pushuse(filename,n,cs)
+ if csignored?(cs) then
+ # nothing
+ elsif @defined[cs] then
+ @used_after[cs] = Array.new unless @used_after[cs]
+ @used_after[cs] << filename unless csafter?(cs,filename)
+ else
+ @used_before[cs] = Array.new unless @used_before[cs]
+ @used_before[cs] << filename unless csbefore?(cs,filename)
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def dependencies
+
+ filename = if @commandline.arguments.empty? then 'context.tex' else @commandline.arguments.first end
+ compact = @commandline.option('compact')
+
+ ['context',''].each do |progname|
+ unless FileTest.file?(filename) then
+ name = Kpse.found(filename, progname)
+ if FileTest.file?(name) then
+ filename = name
+ break
+ end
+ end
+ end
+
+ if FileTest.file?(filename) && deps = TexDeps.new(logger,compact) then
+ deps.setfilter(@commandline.option('filter'))
+ deps.load
+ deps.analyze
+ deps.feedback
+ deps.save if @commandline.option('save')
+ else
+ report("unknown file #{filename}")
+ end
+
+ end
+
+end
+
+class Commands
+
+ @@re_utf_bom = /^\357\273\277/o # just utf-8
+
+ def disarmutfbom
+
+ if @commandline.arguments.empty? then
+ report("provide filename")
+ else
+ @commandline.arguments.each do |filename|
+ report("checking '#{filename}'")
+ if FileTest.file?(filename) then
+ begin
+ data = IO.read(filename)
+ if data.sub!(@@re_utf_bom,'') then
+ if @commandline.option('force') then
+ if f = File.open(filename,'wb') then
+ f << data
+ f.close
+ report("bom found and removed")
+ else
+ report("bom found and removed, but saving file fails")
+ end
+ else
+ report("bom found, use '--force' to remove it")
+ end
+ else
+ report("no bom found")
+ end
+ rescue
+ report("bom found, but removing it fails")
+ end
+ else
+ report("provide valid filename")
+ end
+ end
+ end
+ end
+
+end
+
+class Commands
+
+ include CommandBase
+
+ def updatecontext
+
+ def fetchfile(site, name, target=nil)
+ begin
+ proxy = @commandline.option('proxy')
+ if proxy && ! proxy.empty? then
+ address, port = proxy.split(":")
+ if address && port then
+ http = Net::HTTP::Proxy(address, port).new(site)
+ else
+ http = Net::HTTP::Proxy(proxy, 80).new(site)
+ end
+ else
+ http = Net::HTTP.new(site)
+ end
+ resp, data = http.get(name.gsub(/^\/*/, '/'))
+ rescue
+ return false
+ else
+ begin
+ if data then
+ name = File.basename(name)
+ File.open(target || name, 'wb') do |f|
+ f << data
+ end
+ else
+ return false
+ end
+ rescue
+ return false
+ else
+ return true
+ end
+ end
+ end
+
+ def locatedlocaltree
+ tree = Kpse.used_path('TEXMFLOCAL')
+ unless tree && FileTest.directory?(tree) then
+ tree = Kpse.used_path('TEXMF')
+ end
+ return tree
+ end
+
+ def extractarchive(archive)
+ unless FileTest.file?(archive) then
+ report("fatal error, '#{archive}' has not been downloaded")
+ return false
+ end
+ # unless system("unzip -uo #{archive}") then
+ unless system("unzip -o #{archive}") then
+ report("fatal error, make sure that you have 'unzip' in your path")
+ return false
+ end
+ stubs = "scripts/context/stubs/unix/*"
+ if System.unix? and not system("chmod +x #{stubs}") then
+ report("change x-permissions of '#{stubs}' manually")
+ end
+ return true
+ end
+
+ def remakeformats
+ system("mktexlsr")
+ system("luatools --selfupdate")
+ system("mtxrun --selfupdate")
+ system("luatools --generate")
+ system("texmfstart texexec --make --all --fast --pdftex")
+ system("texmfstart texexec --make --all --fast --luatex")
+ system("texmfstart texexec --make --all --fast --xetex")
+ return true
+ end
+
+ if localtree = locatedlocaltree then
+ report("updating #{localtree}")
+ begin
+ Dir.chdir(localtree)
+ rescue
+ report("unable to change to #{localtree}")
+ else
+ archive = 'cont-tmf.zip'
+ report("fetching #{archive}")
+ unless fetchfile("www.pragma-ade.com","/context/latest/#{archive}") then
+ report("unable to fetch #{archive}")
+ return
+ end
+ report("extracting #{archive}")
+ unless extractarchive(archive) then
+ report("unable to extract #{archive}")
+ return
+ end
+ report("remaking formats")
+ unless remakeformats then
+ report("unable to remak formats")
+ end
+ end
+ else
+ report("unable to locate local tree")
+ end
+
+ end
+
+end
+
+logger = Logger.new(banner.shift)
+commandline = CommandLine.new
+
+commandline.registeraction('touchcontextfile' , 'update context version')
+commandline.registeraction('contextversion' , 'report context version')
+commandline.registeraction('jeditinterface' , 'generate jedit syntax files [--pipe]')
+commandline.registeraction('bbeditinterface' , 'generate bbedit syntax files [--pipe]')
+commandline.registeraction('sciteinterface' , 'generate scite syntax files [--pipe]')
+commandline.registeraction('rawinterface' , 'generate raw syntax files [--pipe]')
+# commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]')
+commandline.registeraction('purgefiles' , 'remove temporary files [--all --recurse] [basename]')
+commandline.registeraction('documentation' , 'generate documentation [--type=] [filename]')
+commandline.registeraction('filterpages' ) # no help, hidden temporary feature
+commandline.registeraction('patternfiles' , 'generate pattern files [--all --xml --utf8] [languagecode]')
+commandline.registeraction('dpxmapfiles' , 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]')
+commandline.registeraction('listentities' , 'create doctype entity definition from enco-uc.tex')
+commandline.registeraction('brandfiles' , 'add context copyright notice [--force]')
+commandline.registeraction('platformize' , 'replace line-endings [--recurse --force] [pattern]')
+commandline.registeraction('dependencies' , 'analyze depedencies within context [--save --compact --filter=[macros|filenames]] [filename]')
+commandline.registeraction('updatecontext' , 'download latest version and remake formats [--proxy]')
+commandline.registeraction('disarmutfbom' , 'remove utf bom [--force]')
+
+commandline.registervalue('type','')
+commandline.registervalue('filter','')
+commandline.registervalue('maproot','')
+commandline.registervalue('proxy','')
+
+commandline.registerflag('recurse')
+commandline.registerflag('force')
+commandline.registerflag('compact')
+commandline.registerflag('pipe')
+commandline.registerflag('save')
+commandline.registerflag('all')
+commandline.registerflag('xml')
+commandline.registerflag('log')
+commandline.registerflag('utf8')
+commandline.registerflag('doctype')
+
+# general
+
+commandline.registeraction('help')
+commandline.registeraction('version')
+
+commandline.expand
+
+Commands.new(commandline,logger,banner).send(commandline.action || 'help')