From 28ba5960fc4486f4e667ee0cbd802335483e1c99 Mon Sep 17 00:00:00 2001
From: Hans Hagen <pragma@wxs.nl>
Date: Thu, 27 Oct 2005 00:00:00 +0200
Subject: stable 2005.10.27

---
 scripts/context/perl/makempy.pl       |   2 +-
 scripts/context/perl/mptopdf.pl       |   6 +-
 scripts/context/perl/texexec.pl       |  29 ++--
 scripts/context/perl/texfont.pl       |   9 +-
 scripts/context/ruby/base/ctx.rb      | 285 ++++++++++++++++++++++++++++++++++
 scripts/context/ruby/base/file.rb     |   2 +-
 scripts/context/ruby/base/kpsefast.rb |  10 +-
 scripts/context/ruby/base/switch.rb   |   8 +-
 scripts/context/ruby/base/tex.rb      | 277 ++++++++++++++++++++-------------
 scripts/context/ruby/ctxtools.rb      |  57 ++++++-
 scripts/context/ruby/fcd_start.rb     | 136 +++++++++++++---
 scripts/context/ruby/newtexexec.rb    |  37 +++--
 scripts/context/ruby/rlxtools.rb      |   1 +
 scripts/context/ruby/texmfstart.rb    |  47 ++++++
 scripts/context/ruby/textools.rb      |   2 +-
 scripts/context/ruby/tmftools.rb      |   4 +-
 16 files changed, 745 insertions(+), 167 deletions(-)
 create mode 100644 scripts/context/ruby/base/ctx.rb

(limited to 'scripts')

diff --git a/scripts/context/perl/makempy.pl b/scripts/context/perl/makempy.pl
index 5b52ee0bb..49fd9bbd2 100644
--- a/scripts/context/perl/makempy.pl
+++ b/scripts/context/perl/makempy.pl
@@ -46,7 +46,7 @@ my $format  = "plain" ; # can be "context" for plain users too
     "force"       => \$force   ,
     "pdftops"     => \$pmethod ,
     "xpdf"        => \$pmethod ,
-    "acrobat"     => \$amethod ,
+#   "acrobat"     => \$amethod , # nowadays the reader is not that clear about this being permitted
     "reader"      => \$amethod ,
     "gs"          => \$gmethod ,
     "ghostscript" => \$gmethod ,
diff --git a/scripts/context/perl/mptopdf.pl b/scripts/context/perl/mptopdf.pl
index 5b00d4679..7a4ac2f30 100644
--- a/scripts/context/perl/mptopdf.pl
+++ b/scripts/context/perl/mptopdf.pl
@@ -72,9 +72,9 @@ elsif ($pattern =~ /\.mp$/io)
       { if ($Latex)
           { $rest .= " $mplatexswitch" }
         if ($MetaFun) {
-            $mpbin = 'mpost --mem=mpost' ;
+            $mpbin = 'mpost --progname=mpost --mem=metafun' ;
         } else {
-            $mpbin = 'mpost --mem=metafun' ;
+            $mpbin = 'mpost --mem=mpost' ;
         }
     }
     else
@@ -99,7 +99,7 @@ foreach my $file (@files)
   { $_ = $file ;
     if (s/\.(\d+|mps)$// && -e $file)
       { if ($miktex)
-          { $command = "pdfetex -fmt=mptopdf" }
+          { $command = "pdfetex -undump=mptopdf" }
         else
           { $command = "pdfetex -progname=context -fmt=mptopdf" }
         if ($dosish)
diff --git a/scripts/context/perl/texexec.pl b/scripts/context/perl/texexec.pl
index d8e26da35..2f3b89e04 100644
--- a/scripts/context/perl/texexec.pl
+++ b/scripts/context/perl/texexec.pl
@@ -255,7 +255,7 @@ my $MakeMpy = '';
     "once"           => \$RunOnce,
     "output=s"       => \$OutputFormat,
     "pages=s"        => \$Pages,
-    "paper=s"        => \$PaperFormat,
+    "paperformat=s"  => \$PaperFormat,
     "passon=s"       => \$PassOn,
     "path=s"         => \$InpPath,
     "autopath"       => \$AutoPath,
@@ -1207,9 +1207,11 @@ sub MakeOptionFile {
     $_ = $PaperFormat;
     #unless (($PdfArrange)||($PdfSelect)||($PdfCombine)||($PdfCopy))
     unless ( ($PdfSelect) || ($PdfCombine) || ($PdfCopy) || ($PdfTrim) ) {
-        if    (/.4.3/goi)     { print OPT "\\setuppapersize[A4][A3]\n" }
-        elsif (/.5.4/goi)     { print OPT "\\setuppapersize[A5][A4]\n" }
-        elsif ( !/standard/ ) {
+        if (/.4.3/goi) {
+            print OPT "\\setuppapersize[A4][A3]\n" ;
+        } elsif (/.5.4/goi) {
+            print OPT "\\setuppapersize[A5][A4]\n" ;
+        } elsif ( !/standard/ ) {
             s/x/\*/io;
             if (/\w+\d+/) { $_ = uc $_ }
             my ( $from, $to ) = split(/\*/);
@@ -1761,9 +1763,7 @@ sub RunConTeXtFile {
             # scan xml preamble
             open(XML,"<$JobName.$JobSuffix") ;
             while (<XML>) {
-                if (/\<[a-z]+/io) {
-                    last ;
-                } elsif (/\<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?\>/o) {
+                if (/\<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?\>/o) {
                     my ($class, $key, $value, $rest) = ($1, $2, $3, $4) ;
                     if ($class eq 'job') {
                         if (($key eq 'mode') || ($key eq 'modes')) {
@@ -1778,6 +1778,8 @@ sub RunConTeXtFile {
                             if ($rest == 'purge') { $Purge = 1 }
                         }
                     }
+                } elsif (/\<[a-z]+/io) {
+                    last ;
                 }
             }
             close(XML) ;
@@ -2199,18 +2201,23 @@ sub RunSelect {
     else { print "             textwidth : $TextWidth\n" }
     open( SEL, ">$SelectFile.tex" );
     print SEL "% format=english\n";
-
-    if ( $PaperFormat ne 'standard' ) {
+    print SEL "\\definepapersize\n";
+    print SEL "  [offset=$PaperOffset]\n";
+    if ($PaperFormat =~ /fit/) {
+        print SEL "\\getfiguredimensions[$FileName]\n" ;
+        print SEL "\\expanded{\\definepapersize[fit][width=\\figurewidth,height=\\figureheight]}\n" ;
+        print SEL "\\setuppapersize[fit][fit]\n";
+        $PaperFormat = '' ; # avoid overloading in option file
+    } elsif ( $PaperFormat ne 'standard' ) {
         $_ = $PaperFormat;    # NO UPPERCASE !
         s/x/\*/io;
         my ( $from, $to ) = split(/\*/);
         if ( $to eq "" ) { $to = $from }
         print "             papersize : $PaperFormat\n";
         print SEL "\\setuppapersize[$from][$to]\n";
+        $PaperFormat = '' ; # avoid overloading in option file
     }
     #
-    print SEL "\\definepapersize\n";
-    print SEL "  [offset=$PaperOffset]\n";
     print SEL "\\setuplayout\n";
     print SEL "  [backspace=$BackSpace,\n";
     print SEL "    topspace=$TopSpace,\n";
diff --git a/scripts/context/perl/texfont.pl b/scripts/context/perl/texfont.pl
index 6a569ddfd..425417374 100644
--- a/scripts/context/perl/texfont.pl
+++ b/scripts/context/perl/texfont.pl
@@ -587,7 +587,12 @@ foreach my $path ($afmpath, $pfbpath)
       { print "file = $file\n";
 	system ("gzip -d $file") } }
 
-system ("mktexlsr $fontroot");  # needed ?
+# For gerben, we only generate a new database when an lsr file is present but for
+# myself we force this when texmf-fonts is used (else I get compatibility problems).
+
+if (($fontroot =~ /texmf\-fonts/o) || (-e "$fontroot/ls-R") || (-e "$fontroot/ls-r") || (-e "$fontroot/LS-R")) {
+    system ("mktexlsr $fontroot") ;
+}
 
 sub do_make_path
   { my $str = shift ;
@@ -897,7 +902,7 @@ foreach my $file (@files)
                 rename $encout, "$encpath/$use$cleanfont.bak" }
     	    UnLink "texfont.map" ;
             $tfmout = "$use$cleanfont$fontsuffix" ;
-            my $otfcommand = "otftotfm -a $varstr $encstr $passon $shape --name=\'$tfmout\' --encoding-dir=\'$encpath/\' --tfm-dir=\'$tfmpath/\' --vf-dir=\'$vfpath/\' --no-type1 --map-file=./texfont.map \'$file\'" ;
+            my $otfcommand = "otftotfm -a $varstr $encstr $passon $shape --name=\"$tfmout\" --encoding-dir=\"$encpath/\" --tfm-dir=\"$tfmpath/\" --vf-dir=\"$vfpath/\" --no-type1 --map-file=./texfont.map \"$file\"" ;
             print "$otfcommand\n"  if $trace ;
             system("$otfcommand") ;
             $encfil = $encout }
diff --git a/scripts/context/ruby/base/ctx.rb b/scripts/context/ruby/base/ctx.rb
new file mode 100644
index 000000000..852c3f704
--- /dev/null
+++ b/scripts/context/ruby/base/ctx.rb
@@ -0,0 +1,285 @@
+# module    : base/ctx
+# copyright : PRAGMA Advanced Document Engineering
+# version   : 2005
+# author    : Hans Hagen
+#
+# project   : ConTeXt / eXaMpLe
+# concept   : Hans Hagen
+# info      : j.hagen@xs4all.nl
+# www       : www.pragma-ade.com
+
+# todo: write systemcall for mpost to file so that it can be run
+# faster
+
+# report ?
+
+require 'base/system'
+require 'base/file'
+require 'base/switch' # has needsupdate, bad place
+
+require 'rexml/document'
+
+class CtxRunner
+
+    attr_reader :environments, :modules, :filters
+
+    def initialize(jobname=nil,logger=nil)
+        if @logger = logger then
+            def report(str='')
+                @logger.report(str)
+            end
+        else
+            def report(str='')
+                puts(str)
+            end
+        end
+        @jobname = jobname
+        @ctxname = nil
+        @xmldata = nil
+        @prepfiles = Hash.new
+        @environments = Array.new
+        @modules = Array.new
+        @filters = Array.new
+    end
+
+    def manipulate(ctxname=nil,defaultname=nil)
+
+        if ctxname then
+            @ctxname = ctxname
+            @jobname = File.suffixed(@ctxname,'tex') unless @jobname
+        else
+            @ctxname = File.suffixed(@jobname,'ctx') if @jobname
+        end
+
+        if not @ctxname then
+            report('provide ctx file')
+            return
+        end
+
+        if not FileTest.file?(@ctxname) and defaultname and FileTest.file?(defaultname) then
+            @ctxname = defaultname
+        end
+
+        if not FileTest.file?(@ctxname) then
+            report('provide ctx file')
+            return
+        end
+
+        @xmldata = IO.read(@ctxname)
+
+        unless @xmldata =~ /^.*<\?xml.*?\?>/moi then
+            report("ctx file #{@ctxname} is no xml file, skipping")
+            return
+        else
+            report("loading ctx file #{@ctxname}")
+        end
+
+        begin
+            @xmldata = REXML::Document.new(@xmldata)
+        rescue
+            report('provide valid ctx file (xml error)')
+            return
+        else
+            include(@xmldata,'ctx:include','name')
+        end
+
+        begin
+            variables = Hash.new
+            if @jobname then
+                variables['job'] = @jobname
+            end
+            REXML::XPath.each(@xmldata.root,"//ctx:value[@name='job']") do |val|
+                substititute(val,variables['job'])
+            end
+            REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:message") do |mes|
+                report("preprocessing: #{justtext(mes)}")
+            end
+            REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:environment") do |sty|
+                @environments << justtext(sty)
+            end
+            REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:module") do |mod|
+                @modules << justtext(mod)
+            end
+            REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:filter") do |fil|
+                @filters << justtext(mod)
+            end
+            REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:files") do |files|
+                REXML::XPath.each(files,"ctx:file") do |pattern|
+                    preprocessor = pattern.attributes['processor']
+                    if preprocessor and not preprocessor.empty? then
+                        pattern = justtext(pattern)
+                        Dir.glob(pattern).each do |oldfile|
+                            newfile = "#{oldfile}.prep"
+                            if File.needsupdate(oldfile,newfile) then
+                                begin
+                                    File.delete(newfile)
+                                rescue
+                                    # hope for the best
+                                end
+                                # there can be a sequence of processors
+                                preprocessor.split(',').each do |pp|
+                                    if command = REXML::XPath.first(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor[@name='#{pp}']") then
+                                        # a lie: no <?xml ...?>
+                                        command = REXML::Document.new(command.to_s) # don't infect original
+                                        command = command.elements["ctx:processor"]
+                                        report("preprocessing #{oldfile} using #{pp}")
+                                        REXML::XPath.each(command,"ctx:old") do |value| replace(value,oldfile) end
+                                        REXML::XPath.each(command,"ctx:new") do |value| replace(value,newfile) end
+                                        variables['old'] = oldfile
+                                        variables['new'] = newfile
+                                        REXML::XPath.each(command,"ctx:value") do |value|
+                                            if name = value.attributes['name'] then
+                                                substititute(value,variables[name.to_s])
+                                            end
+                                        end
+                                        command = justtext(command)
+                                        report(command)
+                                        unless ok = System.run(command) then
+                                            report("error in preprocessing file #{oldfile}")
+                                        end
+                                    end
+                                end
+                                if FileTest.file?(newfile) then
+                                    File.syncmtimes(oldfile,newfile)
+                                else
+                                    report("preprocessing #{oldfile} gave no #{newfile}")
+                                end
+                            else
+                                report("#{oldfile} needs no preprocessing")
+                            end
+                            @prepfiles[oldfile] = FileTest.file?(newfile)
+                        end
+                    end
+                end
+            end
+        rescue
+            report("fatal error in preprocessing #{@ctxname}: #{$!}")
+        end
+    end
+
+    def savelog(ctlname=nil)
+        unless ctlname then
+            if @jobname then
+                ctlname = File.suffixed(@jobname,'ctl')
+            elsif @ctxname then
+                ctlname = File.suffixed(@ctxname,'ctl')
+            else
+                return
+            end
+        end
+        if @prepfiles.length > 0 then
+            if log = File.open(ctlname,'w') then
+                log << "<?xml version='1.0' standalone='yes'?>\n\n"
+                log << "<ctx:preplist>\n"
+                @prepfiles.keys.sort.each do |prep|
+                    log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{File.basename(prep)}</ctx:prepfile>\n"
+                end
+                log << "</ctx:preplist>\n"
+                log.close
+            end
+        else
+            begin
+                File.delete(ctlname)
+            rescue
+            end
+        end
+    end
+
+    private
+
+    def include(xmldata,element='ctx:include',attribute='name')
+        loop do
+            begin
+                more = false
+                REXML::XPath.each(xmldata.root,element) do |e|
+                    begin
+                        name = e.attributes.get_attribute(attribute).to_s
+                        name = e.text.to_s if name.empty?
+                        name.strip! if name
+                        if name and not name.empty? and FileTest.file?(name) then
+                            if f = File.open(name,'r') and i = REXML::Document.new(f) then
+                                report("including ctx file #{name}")
+                                REXML::XPath.each(i.root,"*") do |ii|
+                                    xmldata.root.insert_after(e,ii)
+                                    more = true
+                                end
+                            end
+                        else
+                            report("no valid ctx inclusion file #{name}")
+                        end
+                    rescue Exception
+                        # skip this file
+                    ensure
+                        xmldata.root.delete(e)
+                    end
+                end
+                break unless more
+            rescue Exception
+                break # forget about inclusion
+            end
+        end
+    end
+
+    private
+
+    def yes_or_no(b)
+        if b then 'yes' else 'no' end
+    end
+
+    private # copied from rlxtools.rb
+
+    def justtext(str)
+        str = str.to_s
+        str.gsub!(/<[^>]*?>/o, '')
+        str.gsub!(/\s+/o, ' ')
+        str.gsub!(/&lt;/o, '<')
+        str.gsub!(/&gt;/o, '>')
+        str.gsub!(/&amp;/o, '&')
+        str.gsub!(/&quot;/o, '"')
+        str.gsub!(/[\/\\]+/o, '/')
+        return str.strip
+    end
+
+    def substititute(value,str)
+        if str then
+            begin
+                if value.attributes.key?('method') then
+                    str = filtered(str.to_s,value.attributes['method'].to_s)
+                end
+                if str.empty? && value.attributes.key?('default') then
+                    str = value.attributes['default'].to_s
+                end
+                value.insert_after(value,REXML::Text.new(str.to_s))
+            rescue Exception
+            end
+        end
+    end
+
+    def replace(value,str)
+        if str then
+            begin
+                value.insert_after(value,REXML::Text.new(str.to_s))
+            rescue Exception
+            end
+        end
+    end
+
+    def filtered(str,method)
+        str = str.to_s # to be sure
+        case method
+            when 'name' then # no path, no suffix
+                case str
+                    when /^.*[\\\/](.+?)\..*?$/o then $1
+                    when /^.*[\\\/](.+?)$/o      then $1
+                    when /^(.*)\..*?$/o          then $1
+                    else                              str
+                end
+            when 'path'     then if str =~ /^(.+)([\\\/])(.*?)$/o then $1 else ''  end
+            when 'suffix'   then if str =~ /^.*\.(.*?)$/o         then $1 else ''  end
+            when 'nosuffix' then if str =~ /^(.*)\..*?$/o         then $1 else str end
+            when 'nopath'   then if str =~ /^.*[\\\/](.*?)$/o     then $1 else str end
+            else                                                               str
+        end
+    end
+
+end
diff --git a/scripts/context/ruby/base/file.rb b/scripts/context/ruby/base/file.rb
index 16a1be09b..42fb346c4 100644
--- a/scripts/context/ruby/base/file.rb
+++ b/scripts/context/ruby/base/file.rb
@@ -37,7 +37,7 @@ class File
     end
 
     def File.splitname(name,suffix='')
-        if name =~ /(.*)\.([^\.]*?)$/o then
+        if name =~ /^(.*)\.([^\.]*?)$/o then
             [$1, $2]
         else
             [name, suffix]
diff --git a/scripts/context/ruby/base/kpsefast.rb b/scripts/context/ruby/base/kpsefast.rb
index e0204cf13..52ab28d0f 100644
--- a/scripts/context/ruby/base/kpsefast.rb
+++ b/scripts/context/ruby/base/kpsefast.rb
@@ -716,7 +716,7 @@ class KPSEFAST
                                 data << FileData.new(2,filename,File.size(filename),File.mtime(filename))
                             end
                         else
-                            data << FileData.new(3,filename)
+                            # data << FileData.new(3,filename)
                         end
                     end
                 end
@@ -727,7 +727,13 @@ class KPSEFAST
                             # data.each do |d| puts d.report end
                             # puts ''
                         # end
-                        data.sort! do |a,b| b.size <=> a.size 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|
diff --git a/scripts/context/ruby/base/switch.rb b/scripts/context/ruby/base/switch.rb
index d011a78ae..64d518bd4 100644
--- a/scripts/context/ruby/base/switch.rb
+++ b/scripts/context/ruby/base/switch.rb
@@ -141,9 +141,15 @@ module CommandBase
         version # is nilled when already given
         @commandline.helpkeys.each do |k|
             if @commandline.help?(k) then
+                kstr = ('--'+k).ljust(@commandline.helplength+2)
                 message = @commandline.helptext(k)
                 message = '' if message == CommandLine::NOHELP
-                report("#{('--'+k).ljust(@commandline.helplength+2)} #{message}")
+                message = message.split(/\s*\n\s*/)
+                loop do
+                    report("#{kstr} #{message.shift}")
+                    kstr = ' '*kstr.length
+                    break if message.length == 0
+                end
             end
         end
     end
diff --git a/scripts/context/ruby/base/tex.rb b/scripts/context/ruby/base/tex.rb
index 75970bcd8..2cc9d2542 100644
--- a/scripts/context/ruby/base/tex.rb
+++ b/scripts/context/ruby/base/tex.rb
@@ -19,6 +19,7 @@ require 'base/system'
 require 'base/state'
 require 'base/pdf'
 require 'base/file'
+require 'base/ctx'
 
 class String
 
@@ -127,12 +128,13 @@ class TEX
         'forcetexutil', 'texutil',
         'globalfile', 'autopath',
         'purge', 'purgeall', 'autopdf', 'simplerun', 'verbose',
+        'nooptionfile'
     ]
     @@stringvars = [
         'modefile', 'result', 'suffix', 'response', 'path',
         'filters', 'usemodules', 'environments', 'separation', 'setuppath',
         'arguments', 'input', 'output', 'randomseed', 'modes', 'filename',
-        'modefile',
+        'modefile', 'ctxfile'
     ]
     @@standardvars = [
         'mainlanguage', 'bodyfont', 'language'
@@ -168,7 +170,7 @@ class TEX
     @@temprunfile = 'texexec'
     @@temptexfile = 'texexec.tex'
 
-    def initialize(logger)
+    def initialize(logger=nil)
         if @logger = logger then
             def report(str='')
                 @logger.report(str)
@@ -524,7 +526,7 @@ class TEX
         # finalize
         cleanup
         reportruntime
-   end
+    end
 
     def checkcontext
 
@@ -660,29 +662,39 @@ class TEX
     private  # will become baee/context
 
     @@preamblekeys = [
-        ['tex','texengine'],['program','texengine'],
-        ['ctx','ctxfilter'],['translate','ctxfilter'],
-        ['output','backend'],['modes','modes'],['version','contextversion'],
-        ['format','texformat'],['interface','texformat']
+        ['tex','texengine'],
+        ['program','texengine'],
+        ['translate','tcxfilter'],
+        ['tcx','tcxfilter'],
+        ['output','backend'],
+        ['mode','mode'],
+        ['ctx','ctxfile'],
+        ['version','contextversion'],
+        ['format','texformat'],
+        ['interface','texformat']
     ]
 
     def scantexpreamble(filename)
-        if FileTest.file?(filename) and tex = File.open(filename) then
-            while str = tex.gets.chomp do
-                if str =~ /^\%\s*(.*)/o then
-                    vars = Hash.new
-                    $1.split(/\s+/o).each do |s|
-                        k, v = s.split('=')
-                        vars[k] = v
-                    end
-                    @@preamblekeys.each do |v|
-                        setvariable(v[1],vars[v[0]]) if vars.key?(v[0])
+        begin
+            if FileTest.file?(filename) and tex = File.open(filename) then
+                while str = tex.gets and str.chomp! do
+                    if str =~ /^\%\s*(.*)/o then
+                        vars = Hash.new
+                        $1.split(/\s+/o).each do |s|
+                            k, v = s.split('=')
+                            vars[k] = v
+                        end
+                        @@preamblekeys.each do |v|
+                            setvariable(v[1],vars[v[0]]) if vars.key?(v[0])
+                        end
+                    else
+                        break
                     end
-                else
-                    break
                 end
+                tex.close
             end
-            tex.close
+        rescue
+            # well, let's not worry too much
         end
     end
 
@@ -758,45 +770,46 @@ class TEX
         end
     end
 
-    def makestubfile(jobname,jobsuffix,forcexml=false)
-        if tmp = File.open(File.suffixed(jobname,'run'),'w') then
-            fullname = File.suffixed(jobname,jobsuffix)
+    def makestubfile(rawname,forcexml=false)
+        if tmp = File.open(File.suffixed(rawname,'run'),'w') then
             tmp << "\\starttext\n"
             if forcexml then
-                if FileTest.file?(fullname) && (xml = File.open(fullname)) then
+                if FileTest.file?(rawname) && (xml = File.open(rawname)) then
                     xml.each do |line|
-                        if line =~ /<[a-z]+/io then
-                            break
-                        elsif line =~ /<\?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
+                        case line
+                            when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then
+                                category, key, value, rest = $1, $2, $3, $4
+                                case category
+                                    when 'job' then
+                                        case key
+                                            when 'control' then
+                                                setvariable(value,if rest.empty? then true else rest end)
+                                            when 'mode', 'modes' then
+                                                tmp << "\\enablemode[#{value}]\n"
+                                            when 'stylefile', 'environment' then
+                                                tmp << "\\environment #{value}\n"
+                                            when 'module' then
+                                                tmp << "\\usemodule[#{value}]\n"
+                                            when 'interface' then
+                                                contextinterface = value
+                                        end
+                                end
+                            when /<[a-z]+/io then # beware of order, first pi test
+                                break
                         end
                     end
                     xml.close
                 end
-                tmp << "\\processXMLfilegrouped{#{fullname}}\n"
+                tmp << "\\processXMLfilegrouped{#{rawname}}\n"
             else
-                tmp << "\\processfile{#{fullname}}\n"
+                tmp << "\\processfile{#{rawname}}\n"
             end
             tmp << "\\stoptext\n"
             tmp.close
-            jobsuffix = "run"
+            return "run"
+        else
+            return File.splitname(rawname)[1]
         end
-        return jobsuffix
     end
 
 end
@@ -831,13 +844,21 @@ class TEX
         reportruntime
     end
 
-    def makeoptionfile(jobname, jobsuffix, finalrun, fastdisabled, kindofrun)
-        if opt = File.open(File.suffixed(jobname,'top'),'w') then
+    def deleteoptionfile(rawname)
+        begin
+            File.delete(File.suffixed(rawname,'top'))
+        rescue
+        end
+    end
+
+    def makeoptionfile(rawname, jobname, jobsuffix, finalrun, fastdisabled, kindofrun)
+        # jobsuffix = orisuffix
+        if topname = File.suffixed(rawname,'top') and opt = File.open(topname,'w') then
             # local handies
-            opt << "\% $JobName.top\n"
+            opt << "\% #{topname}\n"
             opt << "\\unprotect\n"
             opt << "\\setupsystem[\\c!n=#{kindofrun}]\n"
-            opt << "\\def\\MPOSTformatswitch\{#{prognameflag('metafun')} #{formatflag('mpost')}=\}"
+            opt << "\\def\\MPOSTformatswitch\{#{prognameflag('metafun')} #{formatflag('mpost')}=\}\n"
             if getvariable('batchmode') then
                 opt << "\\batchmode\n"
             end
@@ -853,7 +874,7 @@ class TEX
             if (str = File.unixfied(getvariable('result'))) && ! str.empty? then
                 opt << "\\setupsystem[file=#{str}]\n"
             elsif (str = getvariable('suffix')) && ! str.empty? then
-                opt << "\\setupsystem[file=#{File.suffixed(jobname,str,nil)}]\n"
+                opt << "\\setupsystem[file=#{jobname}.#{str}]\n"
             end
             if (str = File.unixfied(getvariable('path'))) && ! str.empty? then
                 opt << "\\usepath[#{str}]\n" unless str.empty?
@@ -930,7 +951,7 @@ class TEX
             if (str = getvariable('input')) && ! str.empty? then
                 opt << "\\setupsystem[inputfile=#{str}]\n"
             else
-                opt << "\\setupsystem[inputfile=#{File.suffixed(jobname,jobsuffix)}]\n"
+                opt << "\\setupsystem[inputfile=#{rawname}]\n"
             end
             if (str = getvariable('pages')) && ! str.empty? then
                 if str.downcase == 'odd' then
@@ -953,18 +974,11 @@ class TEX
                 end
             end
             opt << "\\protect\n";
-            begin
-                getvariable('filters').split(',').each do |f| opt << "\\useXMLfilter[#{f}]\n" end
-            rescue
-            end
-            begin
-                getvariable('usemodules').split(',').each do |m| opt << "\\usemodule[#{m}]\n" end
-            rescue
-            end
-            begin
-                getvariable('environments').split(',').each do |e| opt << "\\usemodule[#{e}]\n" end
-            rescue
-            end
+            begin getvariable('filters'     ).split(',').uniq.each do |f| opt << "\\useXMLfilter[#{f}]\n"   end ; rescue ; end
+            begin getvariable('usemodules'  ).split(',').uniq.each do |m| opt << "\\usemodule[#{m}]\n"      end ; rescue ; end
+            begin getvariable('environments').split(',').uniq.each do |e| opt << "\\environment #{e}\n"     end ; rescue ; end
+          # this will become:
+          # begin getvariable('environments').split(',').uniq.each do |e| opt << "\\useenvironment[#{e}]\n" end ; rescue ; end
             opt << "\\endinput\n"
             opt.close
         end
@@ -976,12 +990,12 @@ class TEX
             ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 'f'
             ENV['OPENOUT_ANY']  = ENV['OPENOUT_ANY']  || 'p'
             ENV['OPENIN_ANY']   = ENV['OPENIN_ANY']   || 'p'
-        else
+        elsif getvariable('notparanoid') then
             ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 't'
-            ENV['OPENOUT_ANY']  = ENV['OPENOUT_ANY']  || 'p'
+            ENV['OPENOUT_ANY']  = ENV['OPENOUT_ANY']  || 'a'
             ENV['OPENIN_ANY']   = ENV['OPENIN_ANY']   || 'a'
         end
-        if (ENV['OPENIN_ANY'] == 'p') || (ENV['OPENOUT_ANY'] == 'p') then
+        if ENV['OPENIN_ANY'] && (ENV['OPENIN_ANY'] == 'p') then # first test redundant
             setvariable('paranoid', true)
         end
         if ENV.key?('SHELL_ESCAPE') && (ENV['SHELL_ESCAPE'] == 'f') then
@@ -1115,19 +1129,19 @@ class TEX
         ENV['TEXFONTMAPS'] = ".;\$TEXMF/fonts/map/{#{backend},pdftex,dvips,}//"
     end
 
-    def runbackend(jobname)
+    def runbackend(rawname)
         case validbackend(getvariable('backend'))
             when 'dvipdfmx' then
                 fixbackendvars('dvipdfm')
-                system("dvipdfmx -d 4 #{File.unsuffixed(jobname)}")
+                system("dvipdfmx -d 4 #{File.unsuffixed(rawname)}")
             when 'xetex'    then
                 fixbackendvars('xetex')
-                system("xdv2pdf #{File.suffixed(jobname,'xdv')}")
+                system("xdv2pdf #{File.suffixed(jrawname,'xdv')}")
             when 'dvips'    then
                 fixbackendvars('dvips')
                 mapfiles = ''
                 begin
-                    if tuifile = File.suffixed(jobname,'tui') and FileTest.file?(tuifile) then
+                    if tuifile = File.suffixed(rawname,'tui') and FileTest.file?(tuifile) then
                         IO.read(tuifile).scan(/^c \\usedmapfile\{.\}\{(.*?)\}\s*$/o) do
                             mapfiles += "-u +#{$1} " ;
                         end
@@ -1135,7 +1149,7 @@ class TEX
                 rescue
                     mapfiles = ''
                 end
-                system("dvips #{mapfiles} #{File.unsuffixed(jobname)}")
+                system("dvips #{mapfiles} #{File.unsuffixed(rawname)}")
             when 'pdftex'   then
                 # no need for postprocessing
             else
@@ -1147,6 +1161,8 @@ class TEX
 
         takeprecautions
 
+        rawname = getvariable('filename')
+
         jobname = getvariable('filename')
         suffix  = getvariable('suffix')
         result  = getvariable('result')
@@ -1181,23 +1197,63 @@ class TEX
         # fuzzy code snippet: (we kunnen kpse: prefix gebruiken)
 
         unless FileTest.file?(File.suffixed(jobname,jobsuffix)) then
+            if FileTest.file?(rawname + '.tex') then
+                jobname = rawname.dup
+                jobsuffix  = 'tex'
+            end
+        end
+
+        # we can have funny names, like 2005.10.10 (given without suffix)
+
+        rawname = jobname + '.' + jobsuffix
+
+        unless FileTest.file?(rawname) then
             inppath.split(',').each do |ip|
-                break if dummyfile = FileTest.file?(File.join(ip,File.suffixed(jobname,jobsuffix)))
+                break if dummyfile = FileTest.file?(File.join(ip,rawname))
             end
         end
 
-        jobsuffix = makestubfile(jobname,jobsuffix,forcexml) if dummyfile || forcexml
+        # preprocess files
 
-        if globalfile || FileTest.file?(File.suffixed(jobname,jobsuffix)) then
+        ctx = CtxRunner.new(rawname,@logger)
+        if getvariable('ctxfile').empty? then
+            ctx.manipulate(File.suffixed(rawname,'ctx'),'jobname.ctx')
+        else
+            ctx.manipulate(File.suffixed(getvariable('ctxfile'),'ctx'))
+        end
+        ctx.savelog(File.suffixed(rawname,'ctl'))
+
+        envs = ctx.environments
+        mods = ctx.modules
+
+        # merge environment and module specs
+
+        envs << getvariable('environments') unless getvariable('environments').empty?
+        mods << getvariable('modules')      unless getvariable('modules')     .empty?
+
+        envs = envs.uniq.join(',')
+        mods = mods.uniq.join(',')
+
+        report("using environments #{envs}") if envs.length > 0
+        report("using modules #{mods}")      if mods.length > 0
+
+        setvariable('environments', envs)
+        setvariable('modules',      mods)
+
+        # end of preprocessing and merging
+
+        jobsuffix = makestubfile(rawname,forcexml) if dummyfile || forcexml
+
+        if globalfile || FileTest.file?(rawname) then
 
             if not dummyfile and not globalfile then
-                scantexpreamble(File.suffixed(jobname,jobsuffix))
-                scantexcontent(File.suffixed(jobname,jobsuffix)) if getvariable('texformats').standard?
+                scantexpreamble(rawname)
+                scantexcontent(rawname) if getvariable('texformats').standard?
             end
 
-            result = File.suffixed(jobname,suffix) unless suffix.empty?
+            result = File.suffixed(rawname,suffix) unless suffix.empty?
 
-            pushresult(jobname,result)
+            pushresult(rawname,result)
 
             method = validtexmethod(validtexformat(getvariable('texformats')))
 
@@ -1206,87 +1262,86 @@ class TEX
             case method
 
                 when 'context' then
-
                     if getvariable('simplerun') || runonce then
-                        makeoptionfile(jobname,orisuffix,true,true,3)
-                        ok = runtex(File.suffixed(jobname,jobsuffix))
+                        makeoptionfile(rawname,jobname,orisuffix,true,true,3) unless getvariable('nooptionfile')
+                        ok = runtex(rawname)
                         if ok then
-                            ok = runtexutil(jobname) if getvariable('texutil') || getvariable('forcetexutil')
-                            runbackend(jobname)
-                            popresult(jobname,result)
+                            ok = runtexutil(rawname) if getvariable('texutil') || getvariable('forcetexutil')
+                            runbackend(rawname)
+                            popresult(rawname,result)
                         end
-                        File.silentrename(File.suffixed(jobname,'top'),File.suffixed(jobname,'tmp'))
+                        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(jobname,s))
+                            state.register(File.suffixed(rawname,s))
                         end
                         if getvariable('automprun') then # check this
                             ['mprun','mpgraph'].each do |s|
-                                state.register(File.suffixed(jobname,s,'mp'),'randomseed')
-                                # state.register(File.suffixed(jobname,s,'mpo'))
+                                state.register(File.suffixed(rawname,s,'mp'),'randomseed')
                             end
                         end
                         while ! stoprunning && (texruns < nofruns) && ok do
                             texruns += 1
                             report("TeX run #{texruns}")
                             if texruns == 1 then
-                                makeoptionfile(jobname,orisuffix,false,false,1)
+                                makeoptionfile(rawname,jobname,orisuffix,false,false,1) unless getvariable('nooptionfile')
                             else
-                                makeoptionfile(jobname,orisuffix,false,false,2)
+                                makeoptionfile(rawname,jobname,orisuffix,false,false,2) unless getvariable('nooptionfile')
                             end
-                            ok = runtex(File.suffixed(jobname,jobsuffix))
+                            ok = runtex(File.suffixed(rawname,jobsuffix))
                             if ok && (nofruns > 1) then
                                 unless getvariable('nompmode') then
-                                    mprundone = runtexmpjob(jobname, "mpgraph")
-                                    mprundone = runtexmpjob(jobname, "mprun")
+                                    mprundone = runtexmpjob(rawname, "mpgraph")
+                                    mprundone = runtexmpjob(rawname, "mprun")
                                 end
-                                ok = runtexutil(jobname)
+                                ok = runtexutil(rawname)
                                 state.update
                                 stoprunning = state.stable?
                             end
                         end
-                        ok = runtexutil(jobname) if (nofruns == 1) && getvariable('texutil')
+                        ok = runtexutil(rawname) if (nofruns == 1) && getvariable('texutil')
                         if ok && finalrun && (nofruns > 1) then
-                            makeoptionfile(jobname,orisuffix,true,finalrun,4)
+                            makeoptionfile(rawname,jobname,orisuffix,true,finalrun,4) unless getvariable('nooptionfile')
                             report("final TeX run #{texruns}")
-                            ok = runtex(File.suffixed(jobname,jobsuffix))
+                            ok = runtex(File.suffixed(rawname,jobsuffix))
                         end
-                        File.silentcopy(File.suffixed(jobname,'top'),File.suffixed(jobname,'tmp'))
-                        ['tup','top'].each do |s| # previous tuo file / runtime option file
-                             File.silentdelete(File.suffixed(jobname,s))
+                        ['tmp','top'].each do |s| # previous tuo file / runtime option file
+                             File.silentdelete(File.suffixed(rawname,s))
                         end
+                        File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp'))
                         if ok then
-                            runbackend(jobname)
-                            popresult(jobname,result)
+                            runbackend(rawname)
+                            popresult(rawname,result)
                         end
                     end
 
-                    Kpse.runscript('ctxtools',jobname,'--purge')    if getvariable('purge')
-                    Kpse.runscript('ctxtools',jobname,'--purgeall') if getvariable('purgeall')
+                    Kpse.runscript('ctxtools',rawname,'--purge')    if getvariable('purge')
+                    Kpse.runscript('ctxtools',rawname,'--purgeall') if getvariable('purgeall')
 
                 when 'latex' then
 
-                    ok = runtex(File.suffixed(jobname,jobsuffix))
+                    ok = runtex(rawname)
 
                 else
 
-                    ok = runtex(File.suffixed(jobname,jobsuffix))
+                    ok = runtex(rawname)
 
             end
 
-            if (dummyfile or forcexml) and FileTest.file?(File.suffixed(jobname,jobsuffix)) then
+            if (dummyfile or forcexml) and FileTest.file?(rawname) then
                 begin
-                    File.delete(File.suffixed(jobname,jobsuffix))
+                    File.delete(File.suffixed(rawname,'run'))
                 rescue
                     report("unable to delete stub file")
                 end
             end
 
             if ok and getvariable('autopdf') then
-                PDFview.open(File.suffixed(if result.empty? then jobname else result end,'pdf'))
+                PDFview.open(File.suffixed(if result.empty? then rawname else result end,'pdf'))
             end
 
         end
diff --git a/scripts/context/ruby/ctxtools.rb b/scripts/context/ruby/ctxtools.rb
index 75720d8b8..94e6e735a 100644
--- a/scripts/context/ruby/ctxtools.rb
+++ b/scripts/context/ruby/ctxtools.rb
@@ -379,6 +379,7 @@ class Commands
 
         pattern  = @commandline.arguments
         purgeall = @commandline.option("all") || all
+        recurse  = @commandline.option("recurse")
 
         $dontaskprefixes.push(Dir.glob("mpx-*"))
         $dontaskprefixes.flatten!
@@ -391,14 +392,14 @@ class Commands
         end
 
         if ! pattern || pattern.empty? then
-            globbed = "*.*"
+            globbed = if recurse then "**/*.*" else "*.*" end
             files = Dir.glob(globbed)
             report("purging files : #{globbed}")
         else
             pattern.each do |pat|
-                globbed = "#{pat}-*.*"
+                globbed = if recurse then "**/#{pat}-*.*" else "#{pat}-*.*" end
                 files = Dir.glob(globbed)
-                globbed = "#{pat}.*"
+                globbed = if recurse then "**/#{pat}.*" else "#{pat}.*" end
                 files.push(Dir.glob(globbed))
             end
             report("purging files : #{pattern.join(' ')}")
@@ -1313,6 +1314,53 @@ class Commands
 
 end
 
+class Commands
+
+    include CommandBase
+
+    # usage   : ctxtools --listentities entities.xml
+    # document: <!DOCTYPE something SYSTEM "entities.xml">
+
+    def flushentities(handle,entities,doctype=nil) # 'stylesheet'
+        tab = if doctype then "\t" else "" end
+        handle.puts("<!DOCTYPE #{doctype} [") if doctype
+        entities.keys.sort.each do |k|
+            handle.puts("#{tab}<!ENTITY #{k} \"\&\##{entities[k]};\">")
+        end
+        handle.puts("]>") if doctype
+    end
+
+    def listentities
+
+      # filename   = `texmfstart tmftools.rb --progname=context enco-uc.tex`.chomp
+        filename   = `kpsewhich --progname=context enco-uc.tex`.chomp
+        outputname = @commandline.argument('first')
+
+        if filename and not filename.empty? and FileTest.file?(filename) then
+            entities = Hash.new
+            IO.readlines(filename).each do |line|
+                if line =~ /\\definecharacter\s+([a-zA-Z]+)\s+\{\\uchar\{*(\d+)\}*\{(\d+)\}\}/o then
+                    name, low, high = $1, $2.to_i, $3.to_i
+                    entities[name] = low*256 + high
+                end
+            end
+            if outputname and not outputname.empty? then
+                if f = File.open(outputname,'w') then
+                    flushentities(f,entities)
+                    f.close
+                else
+                    flushentities($stdout,entities)
+                end
+            else
+                flushentities($stdout,entities)
+            end
+        end
+
+    end
+
+end
+
+
 logger      = Logger.new(banner.shift)
 commandline = CommandLine.new
 
@@ -1325,7 +1373,7 @@ commandline.registeraction('sciteinterface', 'generate scite syntax files [--pip
 commandline.registeraction('rawinterface', 'generate raw syntax files [--pipe]')
 
 commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]')
-commandline.registeraction('purgefiles', 'remove temporary files [--all] [basename]')
+commandline.registeraction('purgefiles', 'remove temporary files [--all --recurse] [basename]')
 
 commandline.registeraction('documentation', 'generate documentation [--type=] [filename]')
 
@@ -1335,6 +1383,7 @@ commandline.registeraction('purgeallfiles') # no help, compatibility feature
 commandline.registeraction('patternfiles', 'generate pattern files [--all --xml --utf8] [languagecode]')
 
 commandline.registeraction('dpxmapfiles', 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]')
+commandline.registeraction('listentities', 'create doctype entity definition from enco-uc.tex')
 
 commandline.registervalue('type','')
 
diff --git a/scripts/context/ruby/fcd_start.rb b/scripts/context/ruby/fcd_start.rb
index 4f8019416..8ac48f79e 100644
--- a/scripts/context/ruby/fcd_start.rb
+++ b/scripts/context/ruby/fcd_start.rb
@@ -28,6 +28,15 @@
 # You can create a stub with:
 #
 # ruby fcd_start.rb --stub --verbose
+#
+# usage:
+#
+# fcd --make t:\
+# fcd --add f:\project
+# fcd [--find] whatever
+# fcd [--find] whatever c (c being a list entry)
+# fcd [--find] whatever . (last choice with this pattern)
+# fcd --list
 
 require 'rbconfig'
 
@@ -87,13 +96,16 @@ class FastCD
 
     @@selfpath = File.dirname($0)
     @@datafile = File.join(@@rootpath,'fcd_state.dat')
+    @@histfile = File.join(@@rootpath,'fcd_state.his')
     @@cdirfile = File.join(@@rootpath,if @@mswindows then 'fcd_stage.cmd' else 'fcd_stage.sh' end)
     @@stubfile = File.join(@@selfpath,if @@mswindows then 'fcd.cmd'       else 'fcd'          end)
 
     def initialize(verbose=false)
         @list = Array.new
+        @hist = Hash.new
         @result = Array.new
         @pattern = ''
+        @result = ''
         @verbose = verbose
         if f = File.open(@@cdirfile,'w') then
             f << "#{if @@mswindows then 'rem' else '#' end} no dir to change to"
@@ -115,6 +127,20 @@ class FastCD
         print(str) if verbose
     end
 
+    def clear
+        if FileTest.file?(@@histfile)
+            begin
+                File.delete(@@histfile)
+            rescue
+                report("error in deleting history file '#{@histfile}'")
+            else
+                report("history file '#{@histfile}' is deleted")
+            end
+        else
+            report("no history file '#{@histfile}'")
+        end
+    end
+
     def scan(dir='.')
         begin
             [dir].flatten.sort.uniq.each do |dir|
@@ -147,13 +173,36 @@ class FastCD
                     f.puts(l)
                 end
                 f.close
+                report("#{@list.size} status bytes saved in #{@@datafile}")
+            else
+                report("unable to save status in #{@@datafile}")
             end
-            report("#{@list.size} status bytes saved in #{@@datafile}")
         rescue
             report("error in saving status in #{@@datafile}")
         end
     end
 
+    def remember
+        if @hist[@pattern] == @result then
+            # no need to save result
+        else
+            begin
+                if f = File.open(@@histfile,'w') then
+                    @hist[@pattern] = @result
+                    @hist.keys.each do |k|
+                        f.puts("#{k} #{@hist[k]}")
+                    end
+                    f.close
+                    report("#{@hist.size} history entries saved in #{@@histfile}")
+                else
+                    report("unable to save history in #{@@histfile}")
+                end
+            rescue
+                report("error in saving history in #{@@histfile}")
+            end
+        end
+    end
+
     def load
         begin
             @list = IO.read(@@datafile).split("\n")
@@ -161,12 +210,38 @@ class FastCD
         rescue
             report("error in loading status from #{@@datafile}")
         end
+        begin
+            IO.readlines(@@histfile).each do |line|
+                if line =~ /^(.*?)\s+(.*)$/i then
+                    @hist[$1] = $2
+                end
+            end
+            report("#{@hist.length} history entries loaded from #{@@histfile}")
+        rescue
+            report("error in loading history from #{@@histfile}")
+        end
     end
 
     def show
         begin
-            @list.each do |l|
-                puts(l)
+            puts("directories:")
+            puts("\n")
+            if @list.length > 0 then
+                @list.each do |l|
+                    puts(l)
+                end
+            else
+                puts("no entries")
+            end
+            puts("\n")
+            puts("history:")
+            puts("\n")
+            if @hist.length > 0 then
+                @hist.keys.sort.each do |h|
+                    puts("#{h} >> #{@hist[h]}")
+                end
+            else
+                puts("no entries")
             end
         rescue
         end
@@ -196,6 +271,7 @@ class FastCD
                         f.puts("cd #{dir.gsub("\\",'/')}")
                     end
                 end
+                @result = dir
                 report("changing to #{dir}",true)
             else
                 report("not changing dir")
@@ -215,11 +291,19 @@ class FastCD
                     else
                         list = @result.dup
                         begin
-                            if answer = args[1] then
-                                index = answer[0] - ?a
-                                if dir = list[index] then
-                                    chdir(dir)
-                                    return
+                            if answer = args[1] then # assignment & test
+                                if answer == '.' and @hist.key?(@pattern) then
+                                    if FileTest.directory?(@hist[@pattern]) then
+                                        print("last choice ")
+                                        chdir(@hist[@pattern])
+                                        return
+                                    end
+                                else
+                                    index = answer[0] - ?a
+                                    if dir = list[index] then
+                                        chdir(dir)
+                                        return
+                                    end
                                 end
                             end
                         rescue
@@ -241,6 +325,9 @@ class FastCD
                                     if dir = list[index] then
                                         print("#{answer.chr} ")
                                         chdir(dir)
+                                    elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then
+                                        print("last choice ")
+                                        chdir(@hist[@pattern])
                                     else
                                         print("quit\n")
                                     end
@@ -249,6 +336,10 @@ class FastCD
                                     @@maxlength.times do |i| list.shift end
                                     print("next set")
                                     print("\n")
+                                elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then
+                                    print("last choice ")
+                                    chdir(@hist[@pattern])
+                                    break
                                 else
                                     print("quit\n")
                                     break
@@ -312,19 +403,20 @@ class FastCD
 
 end
 
-verbose, action, args = false, 30, Array.new
+verbose, action, args = false, :find, Array.new
 
 usage = "fcd [--make|add|show|find] [--verbose] [pattern]"
 
 ARGV.each do |a|
     case a
         when '-v', '--verbose' then verbose = true
-        when '-m', '--make'    then action = 10
-        when '-a', '--add'     then action = 11
-        when '-s', '--show'    then action = 20
-        when '-l', '--list'    then action = 20
-        when '-f', '--find'    then action = 30
-        when       '--stub'    then action = 40
+        when '-m', '--make'    then action = :make
+        when '-c', '--clear'   then action = :clear
+        when '-a', '--add'     then action = :add
+        when '-s', '--show'    then action = :show
+        when '-l', '--list'    then action = :show
+        when '-f', '--find'    then action = :find
+        when       '--stub'    then action = :stub
         when '-h', '--help'    then puts "usage: #{usage}" ; exit
         when /^\-\-.*/         then puts "unknown switch: #{a}" + "\n" + "usage: #{usage}" ; exit
                                else args << a
@@ -338,20 +430,24 @@ fcd = FastCD.new(verbose)
 fcd.report("Fast Change Dir / version 1.0")
 
 case action
-    when 10 then
+    when :make then
+        fcd.clear
         fcd.scan(args)
         fcd.save
-    when 11 then
+    when :clear then
+        fcd.clear
+    when :add then
         fcd.load
         fcd.scan(args)
         fcd.save
-    when 20 then
+    when :show then
         fcd.load
         fcd.show
-    when 30 then
+    when :find then
         fcd.load
         fcd.find(args)
         fcd.choose(args)
-    when 40
+        fcd.remember
+    when :stub
         fcd.check
 end
diff --git a/scripts/context/ruby/newtexexec.rb b/scripts/context/ruby/newtexexec.rb
index 9f0084efe..27f8eeed9 100644
--- a/scripts/context/ruby/newtexexec.rb
+++ b/scripts/context/ruby/newtexexec.rb
@@ -117,6 +117,7 @@ class Commands
                     f.close
                     job.setvariable('interface','english')
                     job.setvariable('simplerun',true)
+                    # job.setvariable('nooptionfile',true)
                     job.setvariable('files',[job.tempfilename])
                     job.processtex
                 else
@@ -161,6 +162,7 @@ class Commands
                     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')
@@ -215,6 +217,7 @@ class Commands
                                 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|
@@ -239,10 +242,10 @@ class Commands
             if files.length > 0 then
                 if f = File.open(job.tempfilename('tex'),'w') then
                     emptypages  = @commandline.checkedoption('addempty', '')
-                    paperoffset = @commandline.checkedoption('paperoffset', '1cm')
+                    paperoffset = @commandline.checkedoption('paperoffset', '0cm')
                     textwidth   = @commandline.checkedoption('textwidth', '0cm')
-                    backspace   = @commandline.checkedoption('backspace', '1.5cm')
-                    topspace    = @commandline.checkedoption('topspace', '1.5cm')
+                    backspace   = @commandline.checkedoption('backspace', '0cm')
+                    topspace    = @commandline.checkedoption('topspace', '0cm')
                     f << "\\definepapersize\n"
                     f << "  [offset=#{paperoffset}]\n"
                     f << "\\setuplayout\n"
@@ -270,6 +273,7 @@ class Commands
                     f.close
                     job.setvariable('interface','english')
                     job.setvariable('simplerun',true)
+                    # job.setvariable('nooptionfile',true)
                     job.setvariable('files',[job.tempfilename])
                     job.processtex
                 else
@@ -290,12 +294,24 @@ class Commands
             if files.length > 0 then
                 if f = File.open(job.tempfilename('tex'),'w') then
                     selection   = @commandline.checkedoption('selection', '')
-                    paperoffset = @commandline.checkedoption('paperoffset', '1cm')
+                    paperoffset = @commandline.checkedoption('paperoffset', '0cm')
                     textwidth   = @commandline.checkedoption('textwidth', '0cm')
-                    backspace   = @commandline.checkedoption('backspace', '1.5cm')
-                    topspace    = @commandline.checkedoption('topspace', '1.5cm')
-                    paperformat = @commandline.checkedoption('paperoffset', 'A4*A4').split(/[\*x]/o)
+                    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";
@@ -319,6 +335,7 @@ class Commands
                     f.close
                     job.setvariable('interface','english')
                     job.setvariable('simplerun',true)
+                    # job.setvariable('nooptionfile',true)
                     job.setvariable('files',[job.tempfilename])
                     job.processtex
                 else
@@ -348,7 +365,7 @@ class Commands
                 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', '1cm')
+                    paperoffset = @commandline.checkedoption('paperoffset', '0cm')
                     f << "\\starttext\n"
                     files.each do |filename|
                         result = @commandline.checkedoption('result','texexec')
@@ -381,6 +398,7 @@ class Commands
                     f.close
                     job.setvariable('interface','english')
                     job.setvariable('simplerun',true)
+                    # job.setvariable('nooptionfile',true)
                     job.setvariable('files',[job.tempfilename])
                     job.processtex
                 else
@@ -400,7 +418,7 @@ class Commands
             files = @commandline.arguments.sort
             if files.length > 0 then
                 if f = File.open(job.tempfilename('tex'),'w') then
-                    paperoffset = @commandline.checkedoption('paperoffset', '1cm')
+                    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'
@@ -435,6 +453,7 @@ class Commands
                     f.close
                     job.setvariable('interface','english')
                     job.setvariable('simplerun',true)
+                    # job.setvariable('nooptionfile',true)
                     job.setvariable('files',[job.tempfilename])
                     job.processtex
                 else
diff --git a/scripts/context/ruby/rlxtools.rb b/scripts/context/ruby/rlxtools.rb
index a3f41b06b..7962474eb 100644
--- a/scripts/context/ruby/rlxtools.rb
+++ b/scripts/context/ruby/rlxtools.rb
@@ -197,6 +197,7 @@ class Commands
         str.gsub!(/&lt;/o, '<')
         str.gsub!(/&gt;/o, '>')
         str.gsub!(/&amp;/o, '&')
+        str.gsub!(/&quot;/o, '"')
         str.gsub!(/[\/\\]+/o, '/')
         return str.strip
     end
diff --git a/scripts/context/ruby/texmfstart.rb b/scripts/context/ruby/texmfstart.rb
index 887139061..dc166bf92 100644
--- a/scripts/context/ruby/texmfstart.rb
+++ b/scripts/context/ruby/texmfstart.rb
@@ -703,6 +703,49 @@ def process(&block)
 
 end
 
+def checktree(tree)
+    unless tree.empty? then
+        begin
+            setuptex = File.join(tree,'setuptex.tmf')
+            if FileTest.file?(setuptex) then
+                report('')
+                report("tex tree : #{setuptex}")
+                ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/'
+                ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME']
+                case RUBY_PLATFORM
+                    when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin'
+                    when /(linux)/i                     then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux'
+                    when /(darwin|rhapsody|nextstep)/i  then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx'
+                #   when /(netbsd|unix)/i               then # todo
+                    else                                     # todo
+                end
+                ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}"
+                report('')
+                report("preset   : TEXPATH => #{ENV['TEXPATH']}")
+                report("preset   : TEXOS   => #{ENV['TEXOS']}")
+                report("preset   : TEXMFOS => #{ENV['TEXMFOS']}")
+                report("preset   : TMP => #{ENV['TMP']}")
+                report('')
+                IO.readlines(File.join(tree,'setuptex.tmf')).each do |line|
+                    case line
+                        when /^[\#\%]/ then
+                            # comment
+                        when /^(.*?)\s+\=\s+(.*)\s*$/ then
+                            k, v = $1, $2
+                            ENV[k] = v.gsub(/\%(.*?)\%/) do
+                                ENV[$1] || ''
+                            end
+                            report("user set : #{k} => #{ENV[k]}")
+                    end
+                end
+            else
+                report("no setup file '#{setuptex}'")
+            end
+        rescue
+        end
+    end
+end
+
 def execute(arguments)
 
     arguments = arguments.split(/\s+/) if arguments.class == String
@@ -724,6 +767,8 @@ def execute(arguments)
     $locate      = $directives['locate']    || false
 
     $path        = $directives['path']      || ''
+    $tree        = $directives['tree']      || ''
+
 
     $make        = $directives['make']      || false
     $unix        = $directives['unix']      || false
@@ -762,10 +807,12 @@ def execute(arguments)
 
     if $help || ! $filename || $filename.empty? then
         usage
+        checktree($tree)
     elsif $batch && $filename && ! $filename.empty? then
         # todo, take commands from file and avoid multiple starts and checks
     else
         report("texmfstart version #{$version}")
+        checktree($tree)
         if $make then
             if $windows then
                 make($filename,true,false)
diff --git a/scripts/context/ruby/textools.rb b/scripts/context/ruby/textools.rb
index bf0639328..78982f175 100644
--- a/scripts/context/ruby/textools.rb
+++ b/scripts/context/ruby/textools.rb
@@ -736,7 +736,7 @@ class Commands
             report("scanning #{root}")
             rootfiles = Dir.glob("#{root}/**/*")
         else
-            report("provide sourse root")
+            report("provide source root")
             return
         end
         if rootfiles.size > 0 then
diff --git a/scripts/context/ruby/tmftools.rb b/scripts/context/ruby/tmftools.rb
index 305a52370..d125c5cae 100644
--- a/scripts/context/ruby/tmftools.rb
+++ b/scripts/context/ruby/tmftools.rb
@@ -13,6 +13,8 @@
 # The script based alternative is not slower than the kpse one.
 # Loading is a bit faster when the log file is used.
 
+# todo: create database
+
 # tmftools [some of the kpsewhich switches]
 
 # tmftools --analyze
@@ -118,7 +120,7 @@ commandline.registerflag('strict')
 commandline.registerflag('delete')
 commandline.registerflag('force')
 
-commandline.registeraction('analyze')
+commandline.registeraction('analyze', "[--strict --sort --rootpath --treepath]\n[--delete [--force]] [pattern]")
 
 # general purpose options
 
-- 
cgit v1.2.3