diff options
author | Karl Berry <karl@freefriends.org> | 2006-01-09 01:54:09 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2006-01-09 01:54:09 +0000 |
commit | 50b347972956e0bfbe7029305e0f459e5ce3ac0c (patch) | |
tree | d1b824bbc33a30bf7fcf54b866a1cff949d2e0bf /Master/texmf-dist/scripts/context | |
parent | 52f01b2f769ac290674a469d46f149985042ee2e (diff) |
trunk/Master/texmf-dist/scripts
git-svn-id: svn://tug.org/texlive/trunk@92 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/scripts/context')
44 files changed, 22819 insertions, 0 deletions
diff --git a/Master/texmf-dist/scripts/context/perl/cont_mis.pm b/Master/texmf-dist/scripts/context/perl/cont_mis.pm new file mode 100644 index 00000000000..6bd449bf045 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/cont_mis.pm @@ -0,0 +1,69 @@ +#D \module +#D [ file=cont\_mis.pm, +#D version=1999.05.05, +#D title=General modules, +#D subtitle=all kind of subs, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +#D Not yet documented, source will be cleaned up. + +package cont_mis ; + +use strict ; + +my ($message, $separator, $content) ; + +format = +@>>>>>>>>>>>>>>>>>>>>> @ @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$message,$separator,$content +. + +sub report + { ($message, $separator, $content) = @_ ; write } + +sub crlf { print "\n" } +sub banner { crlf ; report (shift , '/', shift) ; crlf } +sub message { report (shift , ':', shift) } +sub help { report (shift , ' ', shift) } +sub status { message ('status' , shift) } +sub warning { message ('warning' , shift) } +sub error { message ('error' , shift) } +sub continue { message ('' , shift) } + +sub hex_color + { my ($r,$g,$b) = @_ ; + if ($r>1) { $r=0xffff } else { $r = 0xffff*$r } + if ($g>1) { $g=0xffff } else { $g = 0xffff*$g } + if ($b>1) { $b=0xffff } else { $b = 0xffff*$b } + local $_ = sprintf "%4x%4x%4x", $r, $g, $b ; + s/ /0/go ; + return $_ } + +sub InterfaceFound + { local $_ = shift ; + if (/^\%.*interface=(.*?)\b/) + { return $1 } + elsif (/\\(starttekst|stoptekst|startonderdeel)/) + { return 'nl' } + elsif (/\\(stelle|verwende|umgebung|benutze)/) + { return 'de' } + elsif (/\\(stel|gebruik|omgeving)/) + { return 'nl' } + elsif (/\\(use|setup|environment)/) + { return 'en' } + elsif (/(hoogte|breedte|letter)=/) + { return 'nl' } + elsif (/(height|width|style)=/) + { return 'en' } + elsif (/(hoehe|breite|schrift)=/) + { return 'de' } + else + { return '' } } + +1; diff --git a/Master/texmf-dist/scripts/context/perl/cont_set.pm b/Master/texmf-dist/scripts/context/perl/cont_set.pm new file mode 100644 index 00000000000..41c62e75440 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/cont_set.pm @@ -0,0 +1,670 @@ +#D \module +#D [ file=cont\_set.pm, +#D version=1999.04.01, +#D title=General modules, +#D subtitle=showing \CONTEXT\ commands, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}, +#D suggestions={Tobias Burnus \& Taco Hoekater}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +# todo: tacos speed patch + +#D As always: thanks to Taco and Tobias for testing this +#D module and providing suggestions and code snippets as +#D well as haunting bugs. + +package cont_set ; + +#D This module (package) deals with providing help information +#D about the \CONTEXT\ commands. The data needed is derived +#D from the setup files by \CONTEXT\ itself. The data is +#D stored in files with suffix \type {tws} (tex work setup). +#D This module introduces some subroutines: +#D +#D \starttabulatie[|Tl|p|] +#D \NC \type {set\_setup\_interface} \NC sets the primary interface +#D to search in \NC \NR +#D \NC \type {set\_setup\_title} \NC sets the title of the main +#D window title \NC \NR +#D \NC \type {setups\_found} \NC locate the \type {tws} files +#D using the \type {kpsewhich} +#D program \NC \NR +#D \NC \type {show\_setups} \NC allocate the radio buttons +#D that can be used to select a +#D command set \NC \NR +#D \NC \type {load\_setup(filename)} \NC load the names \type {tws} +#D file \NC \NR +#D \NC \type {load\_setups} \NC all found command files can +#D be loaded at once \NC \NR +#D \NC \type {setup\_found(filename)} \NC this routine returns~1 when +#D the file is loaded \NC \NR +#D \NC \type {update\_setup} \NC when we browse the list with +#D commands, this routine takes care +#D of updating the text area \NC \NR +#D \NC \type {change\_setup} \NC we can manually set the +#D command set we want to browse, +#D and this routine takes care of +#D this \NC \NR +#D \NC \type {show\_setup(command)} \NC context sensitive help can be +#D provided by calling this sub \NC \NR +#D \stoptabulatie +#D +#D First we load some packages and set some constants. + +use Tk ; +use Tk::ROText ; +use Config ; + +use strict; + +use subs qw/ update_setup / ; + +my $dosish = ($Config{'osname'} =~ /dos|win/i) ; +my $default_size = $dosish ? 9 : 12 ; + +my $textfont = "Courier $default_size " ; +my $userfont = "Courier $default_size italic" ; +my $buttonfont = "Helvetica $default_size bold " ; + +unless ($dosish) + { $textfont = "-adobe-courier-bold-r-normal--$default_size-120-75-75-m-70-iso8859-1" ; + $userfont = "-adobe-courier-bold-o-normal--$default_size-120-75-75-m-70-iso8859-1" ; + $buttonfont = "-adobe-helvetica-bold-r-normal--$default_size-120-75-75-p-69-iso8859-1" } + +my $s_vertical = 30 ; +my $s_horizontal = 72 ; +my $c_horizontal = 24 ; + +#D The main window is not resizable, but the text area and +#D command list will have scrollbars. + +my %lw ; # stack of lists + +my $mw = MainWindow -> new ( -title => 'ConTeXt commands' ) ; + +$mw -> withdraw() ; $mw -> resizable ('y', 'y') ; + +sub SetupWindow { return $mw } ; + +my $bw = $mw -> Frame () ; # buttons +my $tw = $mw -> Frame () ; # sw + fw +my $fw = $tw -> Frame () ; # string + list + +my $request = $fw -> Entry ( -font => $textfont , + -background => 'ivory1' , + -width => $c_horizontal ) ; + +my $cw = $fw -> Scrolled ( 'Listbox' , + -scrollbars => 'e' , + -font => $textfont , + -width => $c_horizontal , + -selectbackground => 'gray' , + -background => 'ivory1' , + -selectmode => 'browse' ) ; + +$cw -> pack ( -side => 'bottom' , -fill => 'both' , -expand => 1 ) ; +$request -> pack ( -side => 'top' , -fill => 'x' ) ; + +my $sw = $tw -> Scrolled ( 'ROText' , + -scrollbars => 'se' , + -height => $s_vertical , + -width => $s_horizontal , + -wrap => 'none' , + -background => 'ivory1' , + -font => $textfont ) ; + + +#D And the whole bunch of widgets are packed in the main +#D window. + +sub pack_them_all + { $sw -> pack ( -side => 'left' , -fill => 'both' , -expand => 1 ) ; + $fw -> pack ( -side => 'right' , -fill => 'y' , -expand => 0 ) ; + $bw -> pack ( -side => 'top' , -fill => 'x' , -anchor => 'w' , -expand => 1 ) ; + $tw -> pack ( -side => 'bottom', -fill => 'both' , -expand => 1 ) } + +sub unpack_them_all + { } + +pack_them_all ; + +#D We scan for available setup files, with suffix \type {tws}. +#D These should be somewhere on the system, grouped in one +#D directory. At least the english file \type {cont-en.tws} +#D should be found. + +my $tws_path = '' ; +my @setup_files = ('cont-en.tws') ; +my $setup_file = $setup_files[0] ; +my $setup_interface = 'en' ; +my $old_setup_file = '' ; + +sub set_setup_interface + { $setup_interface = shift } + +sub set_setup_title + { $mw -> configure ( -title => shift ) } + +sub setups_found + { $tws_path = `kpsewhich --format="other text files" --progname=context cont-en.tws` ; + $tws_path =~ s/cont-en\.tws.*// ; + chop $tws_path ; + @setup_files = glob ("$tws_path*.tws") ; + if (@setup_files) + { foreach (@setup_files) { s/\.tws// ; s/.*\/// } + $setup_file = $setup_files[0] ; + return 1 } + else + { return 0 } } + +#D A hide button + +sub show_hide_button + { my $hb = $bw -> Button ( -text => "hide" , + -font => $buttonfont , + -command => \&hide_widget ) ; + $hb -> pack ( -padx => '2p', + -pady => '2p', + -side => 'right' ) } + +sub hide_widget + { $mw -> withdraw() } + +#D The setup files can be shown and chosen. + +sub show_setups + { unpack_them_all ; + foreach (@setup_files) + { $lw{$_} = $bw -> Radiobutton ( -text => lc $_ , + -value => $_ , + -font => $buttonfont , + -selectcolor => 'ivory1' , + -indicatoron => 0 , + -command => \&change_setup , + -variable => \$setup_file ) ; + $lw{$_} -> pack ( -padx => '2p', + -pady => '2p', + -side => 'left' ) } + pack_them_all } + +$cw -> bind ('<B1-Motion>', \&update_setup ) ; +$cw -> bind ('<1>' , \&update_setup ) ; +$cw -> bind ('<Key>' , \&update_setup ) ; + +$sw -> tag ('configure', 'user' , -font => $userfont ) ; +$sw -> tag ('configure', 'command' , -foreground => 'green3' ) ; +$sw -> tag ('configure', 'variable' , -font => $userfont ) ; +$sw -> tag ('configure', 'default' , -underline => 1 ) ; +$sw -> tag ('configure', 'symbol' , -foreground => 'blue3' ) ; +$sw -> tag ('configure', 'or' , -foreground => 'yellow3' ) ; +$sw -> tag ('configure', 'argument' , -foreground => 'red3' ) ; +$sw -> tag ('configure', 'par' , -lmargin1 => '4m' , + -lmargin2 => '6m' ) ; + +my %setups ; +my %commands ; +my %loadedsetups ; +my %positions ; +my %crosslinks ; + +my $current_setup = '' ; + +#D Setups are organized in files called \type {*.tws} and +#D alike. Several files can be loaded simultaneously. When +#D loading, we grab whole paragraphs. The variables and values +#D belonging to a command, are stored in the hash table \type +#D {setups}, one per language. The command templates are +#D stored in \type {commands}. +#D +#D A \type {tws} file is generated by \CONTEXT\ from the setup +#D definition files. Only \CONTEXT\ knows the current meaning +#D of commands and keywords. The files are generating by +#D simply saying something like: +#D +#D \starttypen +#D texexec --interface=en setupd +#D texexec --interface=de setupd +#D texexec --interface=nl setupd +#D texexec --interface=cz setupd +#D texexec --interface=it setupd +#D \stoptypen +#D +#D This results in files formatted as: +#D +#D \starttypen +#D startsetup +#D com:setupcolors +#D typ:vars/ +#D var:state:start,stop,global,local: +#D var:conversion:yes,no,always: +#D var:reduction:yes,no: +#D var:rgb:yes,no: +#D var:cmyk:yes,no: +#D stopsetup +#D \stoptypen +#D +#D This format can be stored rather efficient and parsed rather +#D fast. What more do we need. + +sub load_setup + { my $filename = shift ; + unless (keys %{$commands{$filename}}) + { local $/ = 'stopsetup' ; # in plaats van '' ivm unix ; (taco) + $current_setup = '' ; + if (open(SETUP, "$tws_path$filename.tws" )) + { my $position = 0 ; + while (<SETUP>) + { chomp ; + s/startsetup//mso ; + s/stopsetup//mso ; # redundant geworden + s/\r\n //gms ; # in plaats van s/ //gms ; (taco) + s/com\:(.*?)\:\s(.*)//mso ; + my $string = $1 ; + my $command = $1 ; + my $setup = $2 ; + ++$position ; + $string =~ s/(.*?)\<\<(.*?)\>\>(.*?)/$1$2$3/o ; + $setups {$filename}{$string} = $setup ; + $commands {$filename}{$string} = $command ; + $positions {$filename}{$string} = $position ; + $crosslinks{$filename}[$position] = $string } + close (SETUP) } } + my @list = sort {lc $a cmp lc $b} keys %{$commands{$filename}} ; + $cw -> delete ('0.0', 'end') ; + $cw -> insert ('end', @list) ; + $cw -> selectionSet ('0.0', '0.0') ; + $cw -> activate ('0.0') ; + $setup_file = $filename ; + update_setup } + +sub load_setups + { foreach my $setup (@setup_files) { load_setup ($setup) } ; + $mw -> deiconify() } + +#D The core of this module deals with transforming the +#D definitions like shown earlier. Details on the format +#D can be found in the file \type {setupd.tex}. We use the +#D \type {Tk::Text} automatic hanging identation features. +#D The next subs are examples of the kind you write once +#D and never look at again. + +my @arguments = () ; +my $nested_setup = 0 ; +my $continue_setup = 0 ; +my $argument = 0 ; +my $stopsuffix = '' ; +my $stopcommand = '' ; + +my %arg ; + +$arg {repeat} = '//n*/' ; +$arg {arg} = 'argument/{/.../}' ; +$arg {args} = 'argument/{/..,...,../}' ; +$arg {dis} = 'argument/$$/.../$$' ; +$arg {idx} = 'argument/{/.../}' ; +$arg {idxs} = 'argument/{/..+...+../}' ; +$arg {mat} = 'argument/$/...:$' ; +$arg {nop} = '//.../' ; +$arg {fil} = '//.../' ; +$arg {pos} = 'symbol/(/.../)' ; +$arg {poss} = 'symbol/(/...,.../)' ; +$arg {sep} = 'command//\\\\/' ; +$arg {ref} = 'symbol/[/ref/]' ; +$arg {refs} = 'symbol/[/ref,../]' ; +$arg {val} = 'symbol/[/.../]' ; +$arg {vals} = 'symbol/[/..,...,../]' ; +$arg {var} = 'symbol/[/..=../]' ; +$arg {vars} = 'symbol/[/..,..=..,../]' ; +$arg {cmd} = 'command//\cmd/' ; +$arg {dest} = 'symbol/[/..ref/]' ; +$arg {dests} = 'symbol/[/..,..refs,../]' ; +$arg {trip} = 'symbol/[/x:y:z=/]' ; +$arg {trips} = 'symbol/[/x:y:z=,../]' ; +$arg {wrd} = 'argument/{/.../}' ; +$arg {wrds} = 'argument/{/......./}' ; +$arg {par} = 'command//\par/' ; +$arg {stp} = '//stop/' ; +$arg {par} = 'command///' ; + +sub show_command + { my $command = shift ; + local $_ = $commands{$setup_file}{$command} ; + if ($command eq $_) + { $sw -> insert ('end', "\\$command", 'command' ) } + elsif (/(.*?)\<\<(.*?)\>\>(.*?)/o) + { $sw -> insert ('end', "\\", 'command' ) ; + if ($1) { $sw -> insert ('end', $1, 'command' ) } + if ($2) { $sw -> insert ('end', $2, ['command','user'] ) } + if ($3) { $sw -> insert ('end', $3, 'command' ) } + $stopsuffix = $2 } } + +sub show_left_argument + { local $_ = shift ; + my @thearg = split (/\//, $arg{$arguments[$_]}) ; + $sw -> insert ('end', $thearg[1], ['par',$thearg[0]] ) } + +sub show_middle_argument + { local $_ = shift ; + my @thearg = split (/\//, $arg{$arguments[$_]}) ; + if ($thearg[1]) + { $sw -> insert ('end', $thearg[2], 'par' ) } + else + { $sw -> insert ('end', $thearg[2], ['par',$thearg[0]] ) } } + +sub show_right_argument + { local $_ = shift ; + my @thearg = split (/\//, $arg{$arguments[$_]}) ; + $sw -> insert ('end', $thearg[3], ['par',$thearg[0]] ) ; + ++$argument } + +sub show_reference + { if (($nested_setup<=1)&&(defined($arguments[$argument]))) + { if ($arguments[$argument]=~/ref/) + { $sw -> insert ('end', "\n" ) ; + show_left_argument ($argument) ; + show_middle_argument ($argument) ; + show_right_argument ($argument) } } } + +sub show_stop_command + { my $before_stop = shift ; + if ($stopcommand) + { if ($stopsuffix) + { $sw -> insert ('end', '\\stop', 'command' ) ; + $sw -> insert ('end', $stopsuffix, ['command','user'] ) } + else + { $sw -> insert ('end', $stopcommand, 'command' ) } } } + +sub show_whatever_left + { while ($argument<@arguments) + { $sw -> insert ('end', "\n" ) ; + show_left_argument ($argument) ; + show_middle_argument ($argument) ; + show_right_argument ($argument) ; + ++$argument } + if ($stopcommand) + { $sw -> insert ('end', "\n...\n...\n...\n", 'par') ; + show_stop_command } } + +sub do_update_setup # type: 0=all 1=vars 2=vals + { my ($command, $type) = @_ ; + my $setup = $setups{$setup_file}{$command} ; + my $default = '' ; + my $key = '' ; + my $meaning = '' ; + my @values = () ; + local $_ ; + ++$nested_setup ; + while ($setup=~/(typ|var|val|ivr|ivl)\:(.*?)\:\s/mgo) + { $key = $1 ; + $meaning = $2 ; + if (($key=~/var/o)&&($type!=2)) + { $_ = $meaning ; s/(.*?)\:(.*?)\:(.*)//o ; + if (($nested_setup>1)&&(!$2)) { next } + $key = $1 ; + if ($3) { $default = $3 } else { $default = '' } + $_= $2 ; s/\s//go ; @values = split (/,/,$_) ; + if ($continue_setup) + { $sw -> insert ('end', ",\n ", 'par') } + else + { $continue_setup = 1 ; + $sw -> insert ('end', "\n", 'par') ; + show_left_argument($argument) } + $sw -> insert ('end', $key , 'par' ) ; + $sw -> insert ('end', '=', ['symbol','par'] ) ; + #while (1) + while (@values) + { my $value = shift @values ; + if ($value =~ /^\*/o) + { $value =~ s/^\*//o ; + $sw -> insert ('end', lc $value, ['variable','par'] ) } + elsif ($value eq $default) + { $sw -> insert ('end', $value, ['default','par'] ) } + else + { $sw -> insert ('end', $value, 'par' ) } + if (@values) + { $sw -> insert ('end', '|' , ['or','par'] ) } + else + { last } } } + elsif (($key=~/val/o)&&($type!=1)) + { $_ = $meaning ; s/(.*)\:(.*)//o ; + if (($nested_setup>1)&&(!$2)) { next } + $_ = $1 ; s/\s//go ; @values = split (/,/,$_) ; + if ($2) { $default = $2 } else { $default = '' } + if ($continue_setup) + { $continue_setup = 0 ; + show_right_argument($argument) } + $sw -> insert ('end', "\n" , 'par') ; + show_left_argument($argument) ; + #while (1) + while (@values) + { unless (@values) { last } + my $value = shift (@values) ; + if ($value =~ /^\*/o) + { $value =~ s/^\*//o ; + $sw -> insert ('end', lc $value, ['variable','par'] ) } + elsif ($value eq $default) + { $sw -> insert ('end', $value, ['default','par'] ) } + else + { $sw -> insert ('end', $value, 'par' ) } + if (@values) + { $sw -> insert ('end', ', ', 'par' ) } + else + { last } } + show_right_argument($argument) } + elsif ($key=~/typ/o) + { if ($nested_setup==1) + { show_command ($command) ; + my $arguments = $meaning ; + if ($arguments=~/stp/) + { $_ = $command ; + s/start(.*)/$1/o ; + $stopcommand = "\\stop$_" ; + $arguments =~ s/stp//go } + @arguments = split (/\//,$arguments) ; + if (@arguments) + { for (my $i=0;$i<@arguments;$i++) + { show_left_argument ($i) ; + show_middle_argument ($i) ; + show_right_argument ($i) } + if ($stopcommand) + { $sw -> insert ('end', ' ... ') ; + show_stop_command } + $sw -> insert ('end', "\n\n") ; + show_command ($command) } + $argument = 0 ; + $continue_setup = 0 } } + elsif ($key=~/ivr/o) + { $meaning =~ s/(.*)\:(.*)//o ; + do_update_setup ($1,1) } + elsif ($key=~/ivl/o) + { $meaning =~ s/(.*)\:(.*)//o ; + do_update_setup ($1,2) } + show_reference } + --$nested_setup ; + if (($continue_setup)&&(!$nested_setup)) + { show_right_argument ; + show_whatever_left } } + +#D Now the real work is done, we only have to define a few +#D housekeeping routines. The next sub adapts the text area +#D to the current selected command and normally is bound to +#D the list browsing commands. + +sub update_setup + { $old_setup_file = $setup_file ; + if (keys %{$commands{$setup_file}}) + { my $key ; + unless ($cw->curselection) + { $cw -> selectionSet('0.0','0.0') } + $key = $cw -> get($cw->curselection) ; + if ($current_setup ne $key) + { $current_setup = $key ; + $sw -> delete ('1.0', 'end' ) ; + $nested_setup = 0 ; + $argument = 0 ; + $stopcommand = '' ; + $stopsuffix = '' ; + do_update_setup ($key,0) ; + $mw -> raise ; + $mw -> focus } } } + +#D In editors we want to provide context sensitive help +#D information. The next sub first tries to locate the +#D commands asked for in the setup data currently selected, +#D and when not found takes a look at all the loaded files. + +sub show_setup + { my $asked_for = shift ; + unless ($asked_for) { return } + my $found = 0 ; + $asked_for =~ s/^\\// ; + if ($setup_interface) + { $found = 0 ; + foreach my $name (@setup_files) + { if (($name=~/\-$setup_interface/)&&(exists($commands{$name}{$asked_for}))) + { $found = 1 ; + $setup_file = $name ; + last } } } + if (!($found)&&(exists($commands{$setup_file}{$asked_for}))) + { $found = 1 } + else + { $found = 0 ; + foreach my $name (@setup_files) + { if (exists($commands{$name}{$asked_for})) + { $found = 1 ; + $setup_file = $name ; + last } } } + if ($found) + { my @list = sort {lc $a cmp lc $b} keys %{$commands{$setup_file}} ; + $cw -> delete ('0.0', 'end') ; + $cw -> insert ('end', @list) ; + $found = 0 ; + foreach (@list) { if ($_ eq $asked_for) { last } ++$found } + my $index = "$found.0" ; + $cw -> selectionSet ($index, $index) ; + $cw -> activate ($index) ; + $cw -> see ($index) ; + update_setup ; + $mw -> raise ; + $mw -> focus } } + +#D Whenever a new set of commands is selected (by means of the +#D buttons on top the screen) the list and text are to be +#D updated. + +sub change_setup + { my $command = '' ; + if ($old_setup_file) + { unless ($cw->curselection) + { $cw -> selectionSet('0.0','0.0') } + $command = $cw -> get($cw->curselection) ; + my $position = $positions{$old_setup_file}{$command} ; + $command = $crosslinks{$setup_file}[$position] } + load_setup($setup_file) ; + my @list = sort {lc $a cmp lc $b} keys %{$commands{$setup_file}} ; + $cw -> delete ('0.0', 'end') ; + $cw -> insert ('end', @list) ; + if ($command) + { show_setup($command) } + else + { $cw -> selectionClear ('0.0','end') ; + $cw -> selectionSet ('0.0', '0.0') ; + $cw -> see ('0.0') ; + $cw -> activate ('0.0') } + update_setup ; + $mw -> raise ; + $mw -> focus } + +#D Sometimes we want to make sure the dat is loaded indeed: + +sub setup_found + { my $filename = shift ; + if (-e "$tws_path$filename.tws") + { $setup_file = $filename ; + return 1 } + else + { return 0 } } + +#D The next feature is dedicated to Tobias, who suggested +#D it, and Taco, who saw it as yet another proof of the +#D speed of \PERL. It's also dedicated to Ton, who needs it +#D for translating the big manual. + +sub handle_request + { my $index = $cw -> index('end') ; + unless ($index) { return } + my $req = $request -> get ; + unless ($req) { return } + $req =~ s/\\//o ; + $req =~ s/\s//go ; + $request -> delete('0','end') ; + $request -> insert('0',$req) ; + unless ($req) { return } + my ($l,$c) = split (/\./,$index) ; + for (my $i=0;$i<=$l;$i++) + { $index = "$i.0" ; + my $str = $cw -> get ($index, $index) ; + if ($str =~ /^$req/) + { $cw -> selectionClear ('0.0','end') ; + $cw -> selectionSet ($index, $index) ; + $cw -> activate ($index) ; + $cw -> see ($index) ; + update_setup ; + $mw -> raise ; + $mw -> focus ; + return } } } + +$request -> bind ('<Return>', sub { handle_request } ) ; + +sub insert_request + { my ($self, $chr) = @_ ; + if ($self ne $request) + { $request -> insert ('end', $chr) } + handle_request } + +foreach my $chr ('a'..'z','A'..'Z') + { $mw -> bind ( "<KeyPress-$chr>", sub { insert_request(shift, $chr) } ) } + +$mw -> bind ( "<backslash>", sub { insert_request(shift, "\\") } ) ; + +sub delete_request + { my $self = shift ; + if ($self ne $request) + { my $to = $request -> index ('end') ; + my $from = $to - 1 ; + if ($from<0) { $from = 0 } + $request -> delete ($from,$to) } + handle_request } + +$mw -> bind ( "<BackSpace>", sub { delete_request } ) ; + +sub new_request + { $request -> delete (0,'end') ; + handle_request } + +$mw -> bind ( "<space>", sub { new_request } ) ; + +#D Just in case: + +sub raise_setup + { $mw -> raise } + +sub dont_exit + { $mw -> protocol( 'WM_DELETE_WINDOW' => sub { } ) } + +#D An example use is: +#D +#D \starttypen +#D load_setup ("cont-$nl") ; +#D show_setup ('omlijnd') ; +#D MainLoop () ; +#D \stoptypen +#D +#D Now everything is done, we return 1: + +1 ; diff --git a/Master/texmf-dist/scripts/context/perl/fdf2tan.pl b/Master/texmf-dist/scripts/context/perl/fdf2tan.pl new file mode 100644 index 00000000000..c612f9886e1 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/fdf2tan.pl @@ -0,0 +1,121 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +#D \module +#D [ file=fdf2tan.pl, +#D version=2000.02.06, +#D title=converting \FDF\ annotations, +#D subtitle=fdf2tan, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +#D This is a preliminary version, that will probably be changed +#D and merged into a more general module. + +use Text::Wrap ; + +my $filename = $ARGV[0] ; exit if ($filename eq '') ; + +$filename =~ s/\..*$//o ; + +my $D = "[0-9\-\.]" ; +my $nn = 0 ; + +my %stack ; + +sub saveobject + { $n = shift ; $str = shift ; + if ($n>$max) { $max = $n } + if ($str =~ s/\/Type\s+\/Annot\s+\/Subtype\s+\/Text//o) + { ++$nn ; + $str =~ s/\/Page\s+(\d+)//o ; + $page = $1 ; ++$page ; + $str =~ s/\/Rect\s+\[\s*(.*?)\s*\]//o ; + $rec = $1 ; + if ($rec =~ /($D+)\s*($D+)\s*($D+)\s*($D+)/o) + { $FDFllx = $1 ; $FDFlly = $2 ; $FDFurx = $3 ; $FDFury = $4 } + $X = $FDFllx - $PDFllx ; + $Y = $PDFury - $FDFury ; + $str =~ s/\/M\s.*$//o ; + $str =~ s/\/T\s.*$//o ; + $str =~ s/^.*\/Contents\s.*?\(//o ; + $str =~ s/\)\s+$//o ; + $str =~ s/\\\\r/@@@@@@/o ; + $str =~ s/\\r/\n/go; + $str =~ s/@@@@@@/\\r/o ; + $str =~ s/\\([\<\>\(\)\{\}\\])/$1/go ; + $stack{sprintf("test:%3d %3d %3d\n",$page,$Y,$X)} = + "\\startFDFcomment[$page]" . + sprintf("[%.3f,%.3f]",$X,$Y) . + "\n$str\n\\stopFDFcomment\n\n" } } + +exit unless (open (PDF,"<$filename.pdf")) ; binmode PDF ; +exit unless (open (FDF,"<$filename.fdf")) ; +exit unless (open (TAN,">$filename.tan")) ; + +print "processing $filename ... " ; + +$PDFllx = 0 ; $PDFlly = 0 ; $PDFurx = 597 ; $PDFury = 847 ; + +while (<PDF>) + { if (/\/MediaBox\s*\[\s*($D+)\s*($D+)\s*($D+)\s*($D+)/o) + { $PDFllx = $1 ; $PDFlly = $2 ; $PDFurx = $3 ; $PDFury = $4 ; + last } } + +$_ = "" ; while ($Line=<FDF>) { chomp $Line; $_ .= $Line } + +s/\\n/ /go ; +s/\\\s//go ; + +s/\\225/\\\/L/go ; s/\\226/\\OE/go ; s/\\227/\\vS/go ; +s/\\230/\\"Y/go ; s/\\231/\\vZ/go ; s/\\233/\\\/l/go ; +s/\\234/\\oe/go ; s/\\235/\\vs/go ; s/\\236/\\vz/go ; +s/\\253/\\<</go ; s/\\273/\\>>/go ; s/\\300/\\`A/go ; +s/\\301/\\'A/go ; s/\\302/\\^A/go ; s/\\303/\\~A/go ; +s/\\304/\\"A/go ; s/\\305/\\oA/go ; s/\\306/\\AE/go ; +s/\\307/\\,C/go ; s/\\310/\\`E/go ; s/\\311/\\'E/go ; +s/\\312/\\^E/go ; s/\\313/\\"E/go ; s/\\314/\\`I/go ; +s/\\315/\\'I/go ; s/\\316/\\^I/go ; s/\\317/\\"I/go ; +s/\\321/\\~N/go ; s/\\322/\\`O/go ; s/\\323/\\'O/go ; +s/\\324/\\^O/go ; s/\\325/\\~O/go ; s/\\326/\\"O/go ; +s/\\330/\\\/O/go ; s/\\331/\\`U/go ; s/\\332/\\'U/go ; +s/\\333/\\^U/go ; s/\\334/\\"U/go ; s/\\335/\\'Y/go ; +s/\\337/\\SS/go ; s/\\340/\\`a/go ; s/\\341/\\'a/go ; +s/\\342/\\^a/go ; s/\\343/\\~a/go ; s/\\344/\\"a/go ; +s/\\345/\\oa/go ; s/\\346/\\ae/go ; s/\\347/\\,c/go ; +s/\\350/\\`e/go ; s/\\351/\\'e/go ; s/\\352/\\^e/go ; +s/\\353/\\"e/go ; s/\\354/\\`i/go ; s/\\355/\\'i/go ; +s/\\356/\\^i/go ; s/\\357/\\"i/go ; s/\\361/\\~n/go ; +s/\\362/\\`o/go ; s/\\363/\\'o/go ; s/\\364/\\^o/go ; +s/\\365/\\~o/go ; s/\\366/\\"o/go ; s/\\370/\\\/o/go ; +s/\\371/\\`u/go ; s/\\372/\\'u/go ; s/\\373/\\^u/go ; +s/\\374/\\"u/go ; s/\\375/\\'y/go ; s/\\377/\\"y/go ; + +s/\\(\d\d\d)/[$1]/go ; + +while (s/(\d+)(\s+\d+\s+obj)(.*?)endobj/saveobject($1,$3)/goe) { } + +$wrap::columns = 80 ; + +foreach $key (sort keys %stack) + { print TAN wrap("","",$stack{$key}) } + +close (PDF) ; close (FDF) ; close (TAN) ; + +if (open (TAN,">fdf-tan.tex")) + { print TAN "% interface=en output=pdftex\n\n" . + "\\setupcolors[state=start]\n\n" . + "\\setupinteraction[state=start]\n\n" . + "\\setupbodyfont[pos,10pt]\n\n" . + "\\starttext\n\n" . + "\\usemodule[fdfann]\n\n" . + "\\annotatepages[$filename]\n\n" . + "\\stoptext\n" ; + close (TAN) } + +print "$nn annotations found, run 'texexec fdf-tan'\n" ; diff --git a/Master/texmf-dist/scripts/context/perl/fdf2tex.pl b/Master/texmf-dist/scripts/context/perl/fdf2tex.pl new file mode 100644 index 00000000000..f9684cd9f21 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/fdf2tex.pl @@ -0,0 +1,213 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +# not yet public + +# We're dealing with: + +$InpFile = $ARGV[0] ; $OutFile = $ARGV[1] ; $Field = $ARGV[2] ; + +# beware: fields are funny sorted + +$Program = "fdf2tex 1.02 - ConTeXt / PRAGMA 1997-2000" ; + +if ($Field eq "") + { print "\n$Program\n\n" } + +# filter \type{filename.fdf} into \type{filename.fdt} + +unless ($OutFile) + { $OutFile = $InpFile ; + $OutFile =~ s/\..*// } + +unless ($InpFile=~/\./) + { if (-e "$InpFile.fdf") + { $InpFile .= ".fdf" } + elsif (-e "$InpFile.xml") + { $InpFile .= ".xml" } } + +unless ($OutFile=~/\./) + { $OutFile .= ".fdt" } + +if (open (FDF, "<$InpFile")) + { binmode FDF ; + open (FDT, ">$OutFile") ; + if ($Field eq "") + { print " input file : $InpFile\n" ; + print " output file : $OutFile\n" } } +else + { if ($Field eq "") + { print " error : $InpFile not found\n" } + exit } + +# load the whole file in the buffer + +$_ = "" ; while ($Line=<FDF>) { chomp $Line; $_ .= $Line } + +# or faster: dan ///s gebruiken (ipv m) + +# $/ = "\0777" ; $_ = <FDF> ; + +# zoom in on the objects and remove the header and trialer + +if ($InpFile =~ /\.xml$/) + +{ # begin kind of xml alternative + +s/\>\s*\</\>\</goms ; +$N = s/\<field\s+(.*?)\/\>/\\FDFfield\[$1\]\n/goms ; +s/(name|value)\=\"(.*?)\"/$1=\{$2\}/goms ; +s/\} (name|value)/\}\,$1/goms ; +s/\<fdfobject\>(.*?)\<\/fdfobject\>/\\beginFDFobject\n$1\\endFDFobject\n/goms ; +s/\<fdfdata\>(.*?)\<\/fdfdata\>/\\beginFDFdata\n$1\\endFDFdata\n/goms ; +s/\<fdffields\>(.*?)\<\/fdffields\>/\\beginFDFfields\n$1\\endFDFfields\n/goms ; + +} # end kind of xml alternative + +else + +{ # begin fdf alternative + +s/.*?obj\s*?<<(.*?)>>\s*?endobj/\n\\beginFDFobject$1\n\\endFDFobject/go; +s/trailer.*//; + +# zoom in on the FDF data + +s/\/FDF.*?<<(.*)>>/\n\\beginFDFdata$1\n\\endFDFdata/go; + +# zoom in on the Field collection and remove whatever else + +s/\/Fields.*?\[.*?<<.*?(.*).*?>>.*?\]/\n\\beginFDFfields<<$1>>\n\\endFDFfields/go; +s/\\endFDFfields.*\n\\endFDFdata/\\endFDFfields\n\\endFDFdata/go; + +# tag each field + +$N = s/\<<(.*?)>>/\n\\FDFfield[$1]/go; + +# remove non relevant entries, but keep \type{/T} and \type{/V} + +s/\s*?\/[Kids|Opt]\s*?<<.*?>>//go; +s/\s*?\/[Ff|setFf|ClrFf|F|SetF|ClrF]\s*?\d*?//go; +s/\s*?\/[AP|A|AS]\s*?\[.*?\]//go; +s/\s*?\/AS\s*?\/.*?\s//go; + +# format the field identifier + +s/(.*?)\/T.*?\((.*?)\)/$1 name=$2,/go; + +# format the value, which can be a name or string + +s/\/V\s?\((.*?)\)/value=\{$1\},/go; +s/\/V\s?\/(.*?)[\s|\/]/value=\{$1\},/go; + +# sanitize some special \TeX\ tokens + +s/(\#|\$|\&|\^|\_|\|)/\\$1/go; + +# remove spaces and commas + +#s/\s?([name|value])/$1/go; +s/\[\s*/\[/go; +s/,\]/\]/go; + +# convert PDFDocEncoding + +s/\\225/\\\/L/ ; +s/\\226/\\OE/ ; +s/\\227/\\vS/ ; +s/\\230/\\"Y/ ; +s/\\231/\\vZ/ ; +s/\\233/\\\/l/ ; +s/\\234/\\oe/ ; +s/\\235/\\vs/ ; +s/\\236/\\vz/ ; +s/\\253/\\<</ ; +s/\\273/\\>>/ ; +s/\\300/\\`A/ ; +s/\\301/\\'A/ ; +s/\\302/\\^A/ ; +s/\\303/\\~A/ ; +s/\\304/\\"A/ ; +s/\\305/\\oA/ ; +s/\\306/\\AE/ ; +s/\\307/\\,C/ ; +s/\\310/\\`E/ ; +s/\\311/\\'E/ ; +s/\\312/\\^E/ ; +s/\\313/\\"E/ ; +s/\\314/\\`I/ ; +s/\\315/\\'I/ ; +s/\\316/\\^I/ ; +s/\\317/\\"I/ ; +s/\\321/\\~N/ ; +s/\\322/\\`O/ ; +s/\\323/\\'O/ ; +s/\\324/\\^O/ ; +s/\\325/\\~O/ ; +s/\\326/\\"O/ ; +s/\\330/\\\/O/ ; +s/\\331/\\`U/ ; +s/\\332/\\'U/ ; +s/\\333/\\^U/ ; +s/\\334/\\"U/ ; +s/\\335/\\'Y/ ; +s/\\337/\\ss/ ; +s/\\340/\\`a/ ; +s/\\341/\\'a/ ; +s/\\342/\\^a/ ; +s/\\343/\\~a/ ; +s/\\344/\\"a/ ; +s/\\345/\\oa/ ; +s/\\346/\\ae/ ; +s/\\347/\\,c/ ; +s/\\350/\\`e/ ; +s/\\351/\\'e/ ; +s/\\352/\\^e/ ; +s/\\353/\\"e/ ; +s/\\354/\\`i/ ; +s/\\355/\\'i/ ; +s/\\356/\\^i/ ; +s/\\357/\\"i/ ; +s/\\361/\\~n/ ; +s/\\362/\\`o/ ; +s/\\363/\\'o/ ; +s/\\364/\\^o/ ; +s/\\365/\\~o/ ; +s/\\366/\\"o/ ; +s/\\370/\\\/o/ ; +s/\\371/\\`u/ ; +s/\\372/\\'u/ ; +s/\\373/\\^u/ ; +s/\\374/\\"u/ ; +s/\\375/\\'y/ ; +s/\\377/\\"y/ ; + +s/\\\</\</ ; +s/\\\>/\>/ ; +s/\\\(/\(/ ; +s/\\\)/\)/ ; +s/\#/\\#/ ; + +# convert newline and return commands + +s/\\n/ /go; +s/\\r/\\par /go; + +} # end fdf alternative + +# flush buffer + +print FDT $_ ; + +close FDT ; +close FDF ; + +# report some characteristics + +if ($Field eq "") + { print " number of fields : $N\n" } +else + { if (/\\FDFfield\[value\=\{(.*)\}\,\s*name=$Field/mos) + { print "$1" } + elsif (/\\FDFfield\[name=$Field\,\s*value\=\{(.*)\}/mos) + { print "$1" } } diff --git a/Master/texmf-dist/scripts/context/perl/makempy.pl b/Master/texmf-dist/scripts/context/perl/makempy.pl new file mode 100644 index 00000000000..49fd9bbd21a --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/makempy.pl @@ -0,0 +1,365 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +#D \module +#D [ file=makempy.pl, +#D version=2000.12.14, +#D title=\METAFUN, +#D subtitle=\METAPOST\ Text Graphics, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +# Tobias Burnus provided the code needed to proper testing +# of binaries on UNIX as well as did some usefull suggestions +# to improve the functionality. + +# This script uses GhostScript and PStoEdit as well as +# pdfTeX, and if requested TeXEdit and ConTeXt. + +# todo: we can nowadays do without the intermediate step, because GS +# can now handle PDF quite good + +use Getopt::Long ; +use Config ; +use strict ; + +$Getopt::Long::passthrough = 1 ; # no error message +$Getopt::Long::autoabbrev = 1 ; # partial switch accepted + +my $help = 0 ; +my $silent = 0 ; +my $force = 0 ; +my $noclean = 0 ; + +my $amethod = my $pmethod = my $gmethod = 0 ; + +my $format = "plain" ; # can be "context" for plain users too + +&GetOptions + ( "help" => \$help , + "silent" => \$silent , + "force" => \$force , + "pdftops" => \$pmethod , + "xpdf" => \$pmethod , +# "acrobat" => \$amethod , # nowadays the reader is not that clear about this being permitted + "reader" => \$amethod , + "gs" => \$gmethod , + "ghostscript" => \$gmethod , + "noclean" => \$noclean ) ; + +my $mpochecksum = 0 ; + +my %tex ; my %start ; my %stop ; + +$tex{plain} = "pdftex" ; +$tex{latex} = "pdflatex" ; +$tex{context} = "texexec --batch --once --interface=en --pdf" ; + +$start{plain} = '' ; +$stop{plain} = '\end' ; + +$start{latex} = '\begin{document}' ; +$stop{latex} = '\end{document}' ; + +$start{context} = '\starttext' ; +$stop{context} = '\stoptext' ; + +my $ghostscript = "" ; +my $pstoedit = "" ; +my $pdftops = "" ; +my $acroread = "" ; + +my $wereondos = ($Config{'osname'} =~ /dos|mswin/io) ; + +# Unix only: assume that "gs" in the path. We could also +# use $ghostscipt = system "which gs" but this would require +# that which is installedd on the system. + +sub checkenv + { my ($var, $env) = @_ ; + if ($var) + { return $var } + elsif ($ENV{$env}) + { return $ENV{$env} } + else + { return $var } } + +$ghostscript = checkenv ($ghostscript, "GS_PROG" ) ; +$ghostscript = checkenv ($ghostscript, "GS" ) ; +$pstoedit = checkenv ($pstoedit , "PSTOEDIT") ; +$pdftops = checkenv ($pdftops , "PDFTOPS" ) ; +$acroread = checkenv ($acroread , "ACROREAD") ; + +sub setenv + { my ($var, $unix, $win) = @_ ; + if ($var) + { return $var } + elsif ($wereondos) + { return $win } + else + { return $unix } } + +$ghostscript = setenv($ghostscript, "gs" , "gswin32c") ; +$pstoedit = setenv($pstoedit , "pstoedit", "pstoedit") ; +$pdftops = setenv($pdftops , "pdftops" , "pdftops" ) ; +$acroread = setenv($acroread , "acroread", "" ) ; + +# Force a method if unknown. + +unless ($pmethod||$amethod||$gmethod) + { if ($wereondos) { $pmethod = 1 } else { $amethod = 1 } } + +# Set the error redirection used under Unix: +# stderr -> stdout + +my $logredirection = '>>' ; + +# This unfortunally doesn't work with the ksh and simple sh +# +# if (!$wereondos) +# { $logredirection = '2>&1 >>' ; # Bash +# $logredirection = '>>&' ; # tcsh, Bash +# default $logredirection. } + +# Some TeX Code Snippets. + +my $macros = ' + +% auxiliary macros + +\input supp-mis.tex + +\def\startTEXpage[scale=#1]% + {\output{} + \batchmode + \pdfoutput=1 + \pdfcompresslevel=9 + \hoffset=-1in + \voffset=\hoffset + \scratchcounter=#1 + \divide\scratchcounter1000 + \edef\TEXscale{\the\scratchcounter\space} + \forgetall + \setbox0=\vbox\bgroup} + +\def\stopTEXpage + {\egroup + \dimen0=\ht0 \advance\dimen0 \dp0 + \setbox2=\vbox to 10\dimen0 + {\pdfliteral{\TEXscale 0 0 \TEXscale 0 0 cm} + \copy0 + \pdfliteral{1 0 0 1 0 0 cm} + \vfill} + \wd2=10\wd0 + \pdfpageheight=\ht2 + \pdfpagewidth=\wd2 + \ScaledPointsToBigPoints{\number\pdfpageheight}\pdfcropheight + \ScaledPointsToBigPoints{\number\pdfpagewidth }\pdfcropwidth + \expanded{\pdfpageattr{/CropBox [0 0 \pdfcropwidth \space \pdfcropheight]}} + \shipout\hbox{\box2}} + +% end of auxiliary macros' ; + +sub report + { return if $silent ; + my $str = shift ; + if ($str =~ /(.*?)\s+([\:\/])\s+(.*)/o) + { if ($1 eq "") { $str = " " } else { $str = $2 } + print sprintf("%22s $str %s\n",$1,$3) } } + +sub error + { report("processing aborted : " . shift) ; + exit } + +sub process + { report("generating : " . shift) } + +sub banner + { return if $silent ; + print "\n" ; + report ("MakeMPY 1.1 - MetaFun / PRAGMA ADE 2000-2004") ; + print "\n" } + +my $metfile = "" ; # main metapost file +my $mpofile = "" ; # metapost text specifiation file (provided) +my $mpyfile = "" ; # metapost text picture file (generated) +my $texfile = "" ; # temporary tex file +my $pdffile = "" ; # temporary pdf file +my $tmpfile = "" ; # temporary metapost file +my $posfile = "" ; # temporary postscript file +my $logfile = "" ; # temporary log file +my $errfile = "" ; # final log file (with suffix log) + +sub show_help_info + { banner ; + report ("--help : this message" ) ; + report ("--noclean : don't remove temporary files" ) ; + report ("--force : force processing (ignore checksum)" ) ; + report ("--silent : don't show messages" ) ; + print "\n" ; + report ("--acrobat : use acrobat (reader) for pdf->ps (on unix)") ; + report ("--pdftops : use pdftops (xpdf) pdf->ps") ; + report ("--ghostscript : use ghostscript (gs) for pdf->ps") ; + print "\n" ; + report ("input file : metapost file with graphics") ; + report ("programs needed : texexec and english context") ; + report (" : pdftops from the xpdf suite, or") ; # page size buggy + report (" : pdf2ps and ghostscript, or") ; + report (" : acrobat reader for unix, and") ; + report (" : pstoedit and ghostscript") ; + report ("output file : metapost file with pictures") ; + exit } + +sub check_input_file + { my $file = $ARGV[0] ; + if ((!defined($file))||($file eq "")) + { banner ; error("no filename given") } + else + { $file =~ s/\.mp.*$//o ; + $metfile = "$file.mp" ; + $mpofile = "$file.mpo" ; + $mpyfile = "$file.mpy" ; + $logfile = "$file.log" ; + $texfile = "mpy-$file.tex" ; + $pdffile = "mpy-$file.pdf" ; + $posfile = "mpy-$file.pos" ; + $tmpfile = "mpy-$file.tmp" ; + $errfile = "mpy-$file.log" ; + if (! -f $metfile) + { banner ; error("$metfile is empty") } + elsif (-s $mpofile < 32) + { unlink $mpofile ; # may exist with zero length + unlink $mpyfile ; # get rid of left overs + exit } + else + { banner ; report("processing file : $mpofile") } } } + +sub verify_check_sum # checksum calculation from perl documentation + { return unless (open (MPO,"$mpofile")) ; + $mpochecksum = do { local $/ ; unpack("%32C*",<MPO>) % 65535 } ; + close (MPO) ; + return unless open (MPY,"$mpyfile") ; + my $str = <MPY> ; chomp $str ; + close (MPY) ; + if ($str =~ /^\%\s*mpochecksum\s*\:\s*(\d+)/o) + { if ($mpochecksum eq $1) + { report("mpo checksum : $mpochecksum / unchanged") ; + exit unless $force } + else + { report("mpo checksum : $mpochecksum / changed") } } } + +sub cleanup_files + { my @files = <mpy-*.*> ; + foreach (@files) { unless (/\.log/o) { unlink $_ } } } + +sub construct_tex_file + { my $n = 0 ; + unless (open (MPO, "<$mpofile")) + { error("unable to open $mpofile") } + unless (open (TEX, ">$texfile")) + { error("unable to open $texfile") } + my $textext = "" ; + while (<MPO>) + { s/\s*$//mois ; + if (/\%\s*format=(\w+)/) + { $format = $1 } + else # if (!/^\%/) + { if (/startTEXpage/o) + { ++$n ; + $textext .= "$start{$format}\n" ; + $start{$format} = "" } + $textext .= "$_\n" } } + unless (defined($tex{$format})) { $format = "plain" } + if ($format eq "context") { $macros = "" } + # print TEX "$start{$format}\n$macros\n$textext\n$stop{$format}\n" ; + print TEX "$start{$format}\n\n" if $start{$format} ; + print TEX "$macros\n" if $macros ; + print TEX "$textext\n" if $textext ; + print TEX "$stop{$format}\n" if $stop{$format} ; + close (MPO) ; + close (TEX) ; + report("tex format : $format") ; + report("requested texts : $n") } + +sub construct_mpy_file + { unless (open (TMP, "<$tmpfile")) + { error("unable to open $tmpfile file") } + unless (open (MPY, ">$mpyfile")) + { error("unable to open $mpyfile file") } + print MPY "% mpochecksum : $mpochecksum\n" ; + my $copying = my $n = 0 ; + while (<TMP>) + { if (s/beginfig/begingraphictextfig/o) + { print MPY $_ ; $copying = 1 ; ++$n } + elsif (s/endfig/endgraphictextfig/o) + { print MPY $_ ; $copying = 0 } + elsif ($copying) + { print MPY $_ } } + close (TMP) ; + close (MPY) ; + report("processed texts : $n") ; + report("produced file : $mpyfile") } + +sub run + { my ($resultfile, $program,$arguments) = @_ ; + my $result = system("$program $arguments $logredirection $logfile") ; + unless (-f $resultfile) { error("invalid `$program' run") } } + +sub make_pdf_pages + { process ("pdf file") ; + run ($pdffile, "$tex{$format}", "$texfile") } + +sub make_mp_figures + { process ("postscript file") ; + if ($pmethod) { run($posfile, "$pdftops", + "-paper match $pdffile $posfile") } + if ($gmethod) { run($posfile, "$ghostscript", + "-q -sOutputFile=$posfile -dNOPAUSE -dBATCH -dSAFER -sDEVICE=pswrite $pdffile") } + if ($amethod) { run($posfile, "$acroread", + "-toPostScript -pairs $pdffile $posfile") } } + +sub make_mp_pictures_ps + { process ("metapost file") ; + run ($tmpfile, "$pstoedit", "-ssp -dt -f mpost $posfile $tmpfile") } + +sub make_mp_pictures_pdf + { process ("metapost file") ; + run ($tmpfile, "$pstoedit", "-ssp -dt -f mpost $pdffile $tmpfile") } + +if ($help) { show_help_info } + +check_input_file ; +verify_check_sum ; +cleanup_files ; +construct_tex_file ; +make_pdf_pages ; +if (1) + { make_mp_pictures_pdf ; } +else + { make_mp_figures ; + make_mp_pictures_ps ; } +construct_mpy_file ; # less save : rename $tmpfile, $mpyfile ; +unless ($noclean) { cleanup_files } + +# a simple test file (needs context) +# +# % output=pdftex +# +# \starttext +# +# \startMPpage +# graphictext +# "\bf MAKE" +# scaled 8 +# zscaled (1,2) +# withdrawcolor \MPcolor{blue} +# withfillcolor \MPcolor{gray} +# withpen pencircle scaled 5pt ; +# \stopMPpage +# +# \stoptext diff --git a/Master/texmf-dist/scripts/context/perl/mptopdf.pl b/Master/texmf-dist/scripts/context/perl/mptopdf.pl new file mode 100644 index 00000000000..efbc5ba0122 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/mptopdf.pl @@ -0,0 +1,123 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +# MikTeX users can set environment variable TEXSYSTEM to "miktex". + +#D \module +#D [ file=mptopdf.pl, +#D version=2005.10.31, +#D title=converting MP to PDF, +#D subtitle=\MPTOPDF, +#D author=Hans Hagen, +#D date=\currentdate, +#D url=www.pragma-ade.nl, +#D copyright={PRAGMA ADE / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +# use File::Copy ; # not in every perl + +use Config ; +use Getopt::Long ; +use strict ; + +$Getopt::Long::passthrough = 1 ; # no error message +$Getopt::Long::autoabbrev = 1 ; # partial switch accepted + +my $Help = my $Latex = my $RawMP = my $MetaFun = 0 ; +my $PassOn = '' ; + +&GetOptions + ( "help" => \$Help , + "rawmp" => \$RawMP, + "metafun" => \$MetaFun, + "passon" => \$PassOn, + "latex" => \$Latex ) ; + +my $program = "MPtoPDF 1.3" ; +my $pattern = $ARGV[0] ; +my $done = 0 ; +my $report = '' ; +my $texlatexswitch = " --tex=latex --format=latex " ; +my $mplatexswitch = " --tex=latex " ; + +my $dosish = ($Config{'osname'} =~ /^(ms)?dos|^os\/2|^(ms|cyg)win/i) ; +my $miktex = ($ENV{"TEXSYSTEM"} =~ /miktex/io); +my $escapeshell = ( ($ENV{'SHELL'}) && ($ENV{'SHELL'} =~ m/sh/i )); + +my @files ; +my $command = my $mpbin = '' ; + +sub CopyFile # agressive copy, works for open files like in gs + { my ($From,$To) = @_ ; + return unless open(INP,"<$From") ; binmode INP ; + return unless open(OUT,">$To") ; binmode OUT ; + while (<INP>) { print OUT $_ } + close (INP) ; + close (OUT) } + +if (($pattern eq '')||($Help)) + { print "\n$program : provide MP output file (or pattern)\n" ; + exit } +elsif ($pattern =~ /\.mp$/io) + { shift @ARGV ; my $rest = join(" ", @ARGV) ; + if (open(INP,$pattern)) + { while (<INP>) + { if (/(documentstyle|documentclass|begin\{document\})/io) + { $Latex = 1 ; last } } + close (INP) } + if ($RawMP) + { if ($Latex) + { $rest .= " $mplatexswitch" } + if ($MetaFun) { + $mpbin = 'mpost --mem=metafun --progname=mpost' ; + } else { + $mpbin = 'mpost --mem=mpost' ; + } + } + else + { if ($Latex) + { $rest .= " $texlatexswitch" } + $mpbin = 'texexec --mptex $PassOn' } + my $error = system ("$mpbin $rest $pattern") ; + if ($error) + { print "\n$program : error while processing mp file\n" ; exit } + else + { $pattern =~ s/\.mp$//io ; + @files = glob "$pattern.*" } } +elsif (-e $pattern) + { @files = ($pattern) } +elsif ($pattern =~ /.\../o) + { @files = glob "$pattern" } +else + { $pattern .= '.*' ; + @files = glob "$pattern" } + +foreach my $file (@files) + { $_ = $file ; + if (s/\.(\d+|mps)$// && -e $file) + { if ($miktex) + { $command = "pdfetex -undump=mptopdf" } + else + { $command = "pdfetex --fmt=mptopdf --progname=context" } + if ($dosish) + { $command = "$command \\relax $file" } + else + { $command = "$command \\\\relax $file" } + #~ print $command ; + system($command) ; + rename ("$_.pdf", "$_-$1.pdf") ; + if (-e "$_.pdf") { CopyFile ("$_.pdf", "$_-$1.pdf") } + if ($done) { $report .= " +" } + $report .= " $_-$1.pdf" ; + ++$done } } + +if ($report eq '') + { $report = '*' } + +if ($done) + { print "\n$program : $pattern is converted to$report\n" } +else + { print "\n$program : no filename matches $pattern\n" } diff --git a/Master/texmf-dist/scripts/context/perl/path_tre.pm b/Master/texmf-dist/scripts/context/perl/path_tre.pm new file mode 100644 index 00000000000..546afcd27b6 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/path_tre.pm @@ -0,0 +1,36 @@ +#D \module +#D [ file=path\_tre.pm, +#D version=1999.05.05, +#D title=Path modules, +#D subtitle=selecting a path, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +#D Not yet documented, source will be cleaned up. + +package Tk::path_tre ; + +use Tk; +require Tk::DirTree ; + +use base qw(Tk::DirTree); +use strict; + +Construct Tk::Widget 'PathTree'; + +sub ClassInit + { my ($class,$mw) = @_ ; + return $class -> SUPER::ClassInit ($mw) } + +sub dirnames + { my ( $w, $dir ) = @_ ; + unless ($dir=~/\//) { $dir .= '/' } + my @names = $w->Callback("-dircmd", $dir, $w->cget("-showhidden")); + return( @names ) } + +__END__ diff --git a/Master/texmf-dist/scripts/context/perl/texexec.pl b/Master/texmf-dist/scripts/context/perl/texexec.pl new file mode 100644 index 00000000000..2f3b89e0461 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/texexec.pl @@ -0,0 +1,3252 @@ +eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' && eval 'exec perl -w -S $0 $argv:q' + if 0 ; + +#D \module +#D [ file=texexec.pl, +#D version=2004.08.29, +#D title=running \ConTeXt, +#D subtitle=\TEXEXEC, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +# Thanks to Tobias Burnus for the german translations. +# Thanks to Thomas Esser for hooking it into web2c +# Thanks to Taco Hoekwater for suggesting improvements +# Thanks to Wybo Dekker for the advanced help interface and making it strict +# Thanks to Fabrice Popineau for windows path trickery and fixes + +# (I still have to completely understand the help code -) + +#D We started with a hack provided by Thomas Esser. This +#D expression replaces the unix specific line \type +#D {#!/usr/bin/perl}. + +#D History has learned that writing wrappers like this is quite painful +#D because of differences between platforms, changes in the tex command +#D line flags (fmt), default behaviour (e.g. 8 bit), and the assumption +#D that everyone runs the same tex and that distributers take care of +#D everything. Well, the result is a messy script like this ... Sorry. + +use strict ; + +my $OriginalArgs = join(' ',@ARGV) ; + +#~ use warnings ; # strange warnings, todo + +# todo: second run of checksum of mp file with --nomprun changes +# todo: warning if no args +# todo: <<<< in messages +# todo: cleanup + +use Cwd; +use Time::Local; +use Config; +use Getopt::Long; +use Class::Struct; # needed for help subsystem +use FindBin; +use File::Compare; +use File::Temp; +use Digest::MD5; + +#~ use IO::Handle; autoflush STDOUT 1; + +my %ConTeXtInterfaces; # otherwise problems with strict +my %ResponseInterface; # since i dunno how to allocate else + +my %Help; + +#D In this script we will launch some programs and other +#D scripts. \TEXEXEC\ uses an ini||file to sort out where +#D those programs are stored. Two boolean variables keep +#D track of the way to call the programs. In \TEXEXEC, +#D \type {$dosish} keeps track of the operating system. +#D It will be no surprise that Thomas Esser provided me +#D the neccessary code to accomplish this. + +$ENV{"MPXCOMMAND"} = "0"; # otherwise loop + +my $TotalTime = time; + +# start random seed hack +# +# This hack is needed since tex has 1 minute resolution, so +# we need to be smaller about 1440 (== 24*60 == tex's max time) +# in which case (david a's) random calculator will overflow. + +# my ( $sec, $min, $rest ) = gmtime; +# my $RandomSeed = $min * 60 + $sec; +# +# # i have to look up the mod function -) +# +# if ( $RandomSeed > 2880 ) { $RandomSeed -= 2880 } +# if ( $RandomSeed > 1440 ) { $RandomSeed -= 1440 } + +my ($sec, $min) = gmtime; +my $RandomSeed = ($min * 60 + $sec) % 2880; # else still overflow + +# See usage of $Random and $RandomSeed later on. +# +# end random seed hack + +my $dosish = ( $Config{'osname'} =~ /^(ms)?dos|^os\/2|^mswin/i ); +my $escapeshell = ( ($ENV{'SHELL'}) && ($ENV{'SHELL'} =~ m/sh/i )); + +my $TeXUtil = 'texutil'; +my $TeXExec = 'texexec'; +my $MetaFun = 'metafun'; +my $MpToPdf = 'mptopdf'; + +$Getopt::Long::passthrough = 1; # no error message +$Getopt::Long::autoabbrev = 1; # partial switch accepted + +my $AddEmpty = ''; +my $Alone = 0; +my $Optimize = 0; +my $ForceTeXutil = 0; +my $Arrange = 0; +my $BackSpace = '0pt'; +my $Background = ''; +my $CenterPage = 0; +my $ConTeXtInterface = 'unknown'; +my $Convert = ''; +my $DoMPTeX = 0; +my $DoMPXTeX = 0; +my $EnterBatchMode = 0; +my $EnterNonStopMode = 0; +my $Environments = ''; +my $Modules = ''; +my $FastMode = 0; +my $FinalMode = 0; +my $Format = ''; +my $MpDoFormat = ''; +my $HelpAsked = 0; +my $Version = 0; +my $MainBodyFont = 'standard'; +my $MainLanguage = 'standard'; +my $MainResponse = 'standard'; +my $MakeFormats = 0; +my $Markings = 0; +my $Mode = ''; +my $NoArrange = 0; +my $NoDuplex = 0; +my $NOfRuns = 8; +my $NoMPMode = 0; +my $NoMPRun = 0; +my $NoBanner = 0; +my $AutoMPRun = 0; +my $OutputFormat = 'standard'; +my $Pages = ''; +my $PageScale = '1000'; # == 1.0 +my $PaperFormat = 'standard'; +my $PaperOffset = '0pt'; +my $PassOn = ''; +my $PdfArrange = 0; +my $PdfSelect = 0; +my $PdfCombine = 0; +my $PdfOpen = 0; +my $PdfClose = 0; +my $AutoPdf = 0; +my $PrintFormat = 'standard'; +my $ProducePdfT = 0; +my $ProducePdfM = 0; +my $ProducePdfX = 0; +my $ProducePdfXTX = 0; +my $ProducePs = 0; +my $Input = ""; +my $Result = ''; +my $Suffix = ''; +my $RunOnce = 0; +my $Selection = ''; +my $Combination = '2*4'; +my $SilentMode = 0; +my $TeXProgram = ''; +my $TeXTranslation = ''; +my $TextWidth = '0pt'; +my $TopSpace = '0pt'; +my $TypesetFigures = 0; +my $ForceFullScreen = 0; +my $ScreenSaver = 0; +my $TypesetListing = 0; +my $TypesetModule = 0; +my $UseColor = 0; +my $Verbose = 0; +my $PdfCopy = 0; +my $PdfTrim = 0; +my $LogFile = ""; +my $MpyForce = 0; +my $InpPath = ""; +my $AutoPath = 0; +my $RunPath = ""; +my $Arguments = ""; +my $Pretty = 0; +my $SetFile = ""; +my $TeXTree = ""; +my $TeXRoot = ""; +my $Purge = 0; +my $Separation = ""; +my $ModeFile = ""; +my $GlobalFile = 0; +my $AllPatterns = 0; +my $ForceXML = 0; +my $Random = 0; +my $Filters = ''; +my $NoMapFiles = 0 ; +my $Foxet = 0 ; +my $TheEnginePath = 0 ; +my $Paranoid = 0 ; +my $NotParanoid = 0 ; +my $BoxType = '' ; +my $Local = '' ; + +my $TempDir = '' ; + +my $StartLine = 0 ; +my $StartColumn = 0 ; +my $EndLine = 0 ; +my $EndColumn = 0 ; + +my $MpEngineSupport = 0 ; # not now, we also need to patch executemp in context itself + +# makempy : + +my $MakeMpy = ''; + +&GetOptions( + "arrange" => \$Arrange, + "batch" => \$EnterBatchMode, + "nonstop" => \$EnterNonStopMode, + "color" => \$UseColor, + "centerpage" => \$CenterPage, + "convert=s" => \$Convert, + "environments=s" => \$Environments, + "usemodules=s" => \$Modules, + "xml" => \$ForceXML, + "xmlfilters=s" => \$Filters, + "fast" => \$FastMode, + "final" => \$FinalMode, + "format=s" => \$Format, + "mpformat=s" => \$MpDoFormat, + "help" => \$HelpAsked, + "version" => \$Version, + "interface=s" => \$ConTeXtInterface, + "language=s" => \$MainLanguage, + "bodyfont=s" => \$MainBodyFont, + "results=s" => \$Result, + "response=s" => \$MainResponse, + "make" => \$MakeFormats, + "mode=s" => \$Mode, + "module" => \$TypesetModule, + "figures=s" => \$TypesetFigures, + "fullscreen" => \$ForceFullScreen, + "screensaver" => \$ScreenSaver, + "listing" => \$TypesetListing, + "mptex" => \$DoMPTeX, + "mpxtex" => \$DoMPXTeX, + "noarrange" => \$NoArrange, + "nomp" => \$NoMPMode, + "nomprun" => \$NoMPRun, + "nobanner" => \$NoBanner, + "automprun" => \$AutoMPRun, + "once" => \$RunOnce, + "output=s" => \$OutputFormat, + "pages=s" => \$Pages, + "paperformat=s" => \$PaperFormat, + "passon=s" => \$PassOn, + "path=s" => \$InpPath, + "autopath" => \$AutoPath, + "pdf" => \$ProducePdfT, + "pdm" => \$ProducePdfM, + "dpm" => \$ProducePdfM, + "pdx" => \$ProducePdfX, + "dpx" => \$ProducePdfX, + "xtx" => \$ProducePdfXTX, + "ps" => \$ProducePs, + "pdfarrange" => \$PdfArrange, + "pdfselect" => \$PdfSelect, + "pdfcombine" => \$PdfCombine, + "pdfcopy" => \$PdfCopy, + "pdftrim" => \$PdfTrim, + "scale=s" => \$PageScale, + "selection=s" => \$Selection, + "combination=s" => \$Combination, + "noduplex" => \$NoDuplex, + "paperoffset=s" => \$PaperOffset, + "backspace=s" => \$BackSpace, + "topspace=s" => \$TopSpace, + "markings" => \$Markings, + "textwidth=s" => \$TextWidth, + "addempty=s" => \$AddEmpty, + "background=s" => \$Background, + "logfile=s" => \$LogFile, + "print=s" => \$PrintFormat, + "suffix=s" => \$Suffix, + "runs=s" => \$NOfRuns, + "silent" => \$SilentMode, + "tex=s" => \$TeXProgram, + "verbose" => \$Verbose, + "alone" => \$Alone, + "optimize" => \$Optimize, + "texutil" => \$ForceTeXutil, + "mpyforce" => \$MpyForce, + "input=s" => \$Input, + "arguments=s" => \$Arguments, + "pretty" => \$Pretty, + "setfile=s" => \$SetFile, # obsolete + "purge" => \$Purge, + #### yet undocumented ################# + "runpath=s" => \$RunPath, + "random" => \$Random, + "makempy=s" => \$MakeMpy, + "allpatterns" => \$AllPatterns, + "separation=s" => \$Separation, + "textree=s" => \$TeXTree, + "texroot=s" => \$TeXRoot, + "translate=s" => \$TeXTranslation, + "pdfclose" => \$PdfClose, + "pdfopen" => \$PdfOpen, + "autopdf" => \$AutoPdf, + "modefile=s" => \$ModeFile, # additional modes file + "globalfile" => \$GlobalFile, + "nomapfiles" => \$NoMapFiles, + "foxet" => \$Foxet, + "engine" => \$TheEnginePath, + "paranoid" => \$Paranoid, + "notparanoid" => \$NotParanoid, + "boxtype=s" => \$BoxType, # media art crop bleed trim + "local" => \$Local, + #### unix is unsafe (symlink viruses) + "tempdir=s" => \$TempDir, + #### experiment + "startline=s" => \$StartLine, + "startcolumn=s" => \$StartColumn, + "endline=s" => \$EndLine, + "endcolumn=s" => \$EndColumn +); # don't check name + +if ($Foxet) { + $ProducePdfT = 1 ; + $ForceXML = 1 ; + $Modules = "foxet" ; + $Purge = 1 ; +} + +# a set file (like blabla.bat) can set paths now + +if ( $SetFile ne "" ) { load_set_file( $SetFile, $Verbose ); $SetFile = "" } + +# later we will do a second attempt. + +$SIG{INT} = "IGNORE"; + +if ( $ARGV[0] && $ARGV[0] =~ /\.mpx$/io ) { # catch -tex=.... bug in mpost + $TeXProgram = ''; + $DoMPXTeX = 1; + $NoMPMode = 1; +} + +#### + +if ($Paranoid) { + $ENV{shell_escape} = 'f' ; + $ENV{openout_any} = 'p' ; + $ENV{openin_any} = 'p' ; +} elsif ($NotParanoid) { + $ENV{shell_escape} = 't' ; + $ENV{openout_any} = 'p' ; + $ENV{openin_any} = 'a' ; +} + +if (defined $ENV{openin_any} && $ENV{openin_any} eq 'p') { + $Paranoid = 1 ; # extra test in order to set readlevel +} + +if ((defined $ENV{shell_escape} && $ENV{shell_escape} eq 'f') || + (defined $ENV{SHELL_ESCAPE} && $ENV{SHELL_ESCAPE} eq 'f')) { + $AutoMPRun = 1 ; +} + +if ($ScreenSaver) { + $ForceFullScreen = 1; + $TypesetFigures = 'c'; + $ProducePdfT = 1; + $Purge = 1; +} + +if ( $DoMPTeX || $DoMPXTeX ) { + $RunOnce = 1; + $ProducePdfT = 0; + $ProducePdfX = 0; + $ProducePdfM = 0; + $ProducePdfXTX = 0; + $ProducePs = 0; +} + +if ( $PdfArrange || $PdfSelect || $PdfCopy || $PdfTrim || $PdfCombine ) { + $ProducePdfT = 1; + $RunOnce = 1; +} + +if ($ProducePdfT) { $OutputFormat = "pdftex" } +elsif ($ProducePdfM) { $OutputFormat = "dvipdfm" } +elsif ($ProducePdfX) { $OutputFormat = "dvipdfmx" } +elsif ($ProducePdfXTX) { $OutputFormat = "xetex" } +elsif ($ProducePs) { $OutputFormat = "dvips" } + +if ( $ProducePdfXTX ) { + $TeXProgram = 'xetex' ; # ignore the default pdfetex engine + $PassOn .= ' -no-pdf ' ; # Adam Lindsay's preference +} + +if ($AutoPdf) { + $PdfOpen = $PdfClose = 1 ; +} + +# this is our hook into paranoid path extensions, assumes that +# these three vars are part of path specs in texmf.cnf + +foreach my $i ('TXRESOURCES','MPRESOURCES','MFRESOURCES') { + foreach my $j ($RunPath,$InpPath) { + if ($j ne '') { + if ($ENV{$i} ne '') { + $ENV{$i} = $ENV{$i} . ',' . $j ; + } else { + $ENV{$i} = $j ; + } + } + } +} + +if ( $RunOnce || $Pages || $TypesetFigures || $TypesetListing ) { $NOfRuns = 1 } + +if ( ( $LogFile ne '' ) && ( $LogFile =~ /\w+\.log$/io ) ) { + open( LOGFILE, ">$LogFile" ); + *STDOUT = *LOGFILE; + *STDERR = *LOGFILE; +} + +my $Program = " TeXExec 5.4.3 - ConTeXt / PRAGMA ADE 1997-2005"; + +print "\n$Program\n\n"; + +if ($Verbose) { print " current path : " . cwd . "\n" } + +my $pathslash = '/'; +if ( $FindBin::Bin =~ /\\/ ) { $pathslash = "\\" } +my $cur_path = ".$pathslash"; + +# we need to handle window's "Program Files" path (patch by Fabrice P) + +my $own_path = "$FindBin::Bin/"; +my $own_type = $FindBin::Script; +my $own_quote = ( $own_path =~ m/^[^\"].* / ? "\"" : "" ); +my $own_stub = ""; + +if ( $own_type =~ /(\.pl|perl)/oi ) { $own_stub = "perl " } + +if ( $own_type =~ /(\.(pl|bin|exe))$/io ) { $own_type = $1 } +else { $own_type = '' } + +sub checked_path { + my $path = shift; + if ( ( defined($path) ) && ( $path ne '' ) ) { + $path =~ s/[\/\\]/$pathslash/go; + $path =~ s/[\/\\]*$//go; + $path .= $pathslash; + } else { + $path = ''; + } + return $path; +} + +sub checked_file { + my $path = shift; + if ( ( defined($path) ) && ( $path ne '' ) ) { + $path =~ s/[\/\\]/$pathslash/go; + } else { + $path = ''; + } + return $path; +} + +sub CheckPath { + my ( $Key, $Value ) = @_; + if ( ( $Value =~ /\// ) && ( $Value !~ /\;/ ) ) # no multipath test yet + { + $Value = checked_path($Value); + unless ( -d $Value ) { + print " error : $Key set to unknown path $Value\n"; + } + } +} + +# set <variable> to <value> +# for <script> set <variable> to <value> +# except for <script> set <variable> to <value> + +my $IniPath = ''; + +#D The kpsewhich program is not available in all tex distributions, so +#D we have to locate it before running it (as suggested by Thomas). + +my @paths; + +if ( $ENV{PATH} =~ /\;/ ) { @paths = split( /\;/, $ENV{PATH} ) } +else { @paths = split( /\:/, $ENV{PATH} ) } + +my $kpsewhich = ''; + +sub found_ini_file { + my $suffix = shift ; + #~ $IniPath = $0 ; + #~ $IniPath ~= s/\.pl$//io ; + #~ $IniPath = $InPath . ".'" + $suffix ; + #~ if (-e $IniPath) { + #~ } + # not really needed to check on texmfscripts, better on own path + print " locating ini file : kpsewhiching texexec.$suffix on scripts\n" if $Verbose ; + my $IniPath = `$kpsewhich --format="texmfscripts" -progname=context texexec.$suffix` ; + chomp($IniPath) ; + if ($IniPath eq '') { + print " locating ini file : kpsewhiching texexec.$suffix elsewhere\n" if $Verbose ; + $IniPath = `$kpsewhich --format="other text files" -progname=context texexec.$suffix` ; + chomp($IniPath) ; + } + return $IniPath ; +} + +if ( $IniPath eq '' ) { + foreach (@paths) { + my $p = checked_path($_) . 'kpsewhich'; + if ( ( -e $p ) || ( -e $p . '.exe' ) ) { + $kpsewhich = $p; + # FP: catch spurious error messages here if there $p has + # spaces and $own_quote is not set + $kpsewhich = ($kpsewhich =~ m/^[^\"].* / ? "\"$kpsewhich\"" : "$kpsewhich") ; + $IniPath = found_ini_file("ini"); + unless ( -e $IniPath ) { $IniPath = found_ini_file("rme") } + last; + } + } + if ($Verbose) { + if ( $kpsewhich eq '' ) { + print " locating ini file : kpsewhich not found in path\n"; + } elsif ( $IniPath eq '' ) { + print " locating ini file : not found by kpsewhich\n"; + } else { + if ( $IniPath =~ /rme/oi ) { + print " locating ini file : not found by kpsewhich, using '.rme' file\n"; + } else { + print " locating ini file : found by kpsewhich\n"; + } + } + } +} + +#D Now, when we didn't find the \type {kpsewhich}, we have +#D to revert to some other method. We could have said: +#D +#D \starttypen +#D unless ($IniPath) +#D { $IniPath = `perl texpath.pl texexec.ini` } +#D \stoptypen +#D +#D But loading perl (for the second time) take some time. Instead of +#D providing a module, which can introduce problems with loading, I +#D decided to copy the code of \type {texpath} into this file. + +use File::Find; +# use File::Copy ; no standard in perl + +my ( $ReportPath, $ReportName, $ReportFile ) = ( 0, 0, 1 ); +my ( $FileToLocate, $PathToStartOn ) = ( '', '' ); +my ( $LocatedPath, $LocatedName, $LocatedFile ) = ( '', '', '' ); + +sub DoLocateFile { # we have to keep on pruning + if ( lc $_ eq $FileToLocate ) { + $LocatedPath = $File::Find::dir; + $LocatedName = $_; + $LocatedFile = $File::Find::name; + } + if ($LocatedName) { $File::Find::prune = 1 } +} + +sub LocatedFile { + $PathToStartOn = shift; + $FileToLocate = lc shift; + if ( $FileToLocate eq '' ) { + $FileToLocate = $PathToStartOn; + $PathToStartOn = $own_path; + } + ( $LocatedPath, $LocatedName, $LocatedFile ) = ( '', '', '' ); + if ( $FileToLocate ne '' ) { + if ( -e $cur_path . $FileToLocate ) { + $LocatedPath = $cur_path; + $LocatedName = $FileToLocate; + $LocatedFile = $cur_path . $FileToLocate; + } else { + $_ = checked_path($PathToStartOn); + if ( -e $_ . $FileToLocate ) { + $LocatedPath = $_; + $LocatedName = $FileToLocate; + $LocatedFile = $_ . $FileToLocate; + } else { + $_ = checked_path($PathToStartOn); + if (/(.*?[\/\\]texmf[\/\\]).*/i) { + my $SavedRoot = $1; + File::Find::find( \&DoLocateFile, + checked_path( $1 . 'context/' ) ); + unless ($LocatedFile) { + File::Find::find( \&DoLocateFile, $SavedRoot ); + } + } else { + $_ = checked_path($_); + File::Find::find( \&DoLocateFile, $_ ); + } + } + } + } + return ( $LocatedPath, $LocatedName, $LocatedFile ); +} + +#D So now we can say: + +unless ($IniPath) { + ( $LocatedPath, $LocatedName, $IniPath ) = + LocatedFile( $own_path, 'texexec.ini' ); + if ($Verbose) { + if ( $IniPath eq '' ) { + print " locating ini file : not found by searching\n"; + } else { + print " locating ini file : found by searching\n"; + } + } +} + +#D The last resorts: + +unless ($IniPath) { + if ( $ENV{TEXEXEC_INI_FILE} ) { + $IniPath = checked_path( $ENV{TEXEXEC_INI_FILE} ) . 'texexec.ini'; + unless ( -e $IniPath ) { $IniPath = '' } + } + if ($Verbose) { + if ( $IniPath eq '' ) { + print " locating ini file : no environment variable set\n"; + } else { + print " locating ini file : found by environment variable\n"; + } + } +} + +unless ($IniPath) { + $IniPath = $own_path . 'texexec.ini'; + unless ( -e $IniPath ) { $IniPath = '' } + if ($Verbose) { + if ( $IniPath eq '' ) { + print " locating ini file : not found in own path\n"; + } else { + print " locating ini file : found in own path\n"; + } + } +} + +#D Now we're ready for loading the initialization file! We +#D also define some non strict variables. Using \type {$Done} +#D permits assignments. + +my %Done; + +unless ($IniPath) { $IniPath = 'texexec.ini' } + +if ( open( INI, $IniPath ) ) { + if ($Verbose) { print " reading : $IniPath\n" } + while (<INI>) { + if ( !/^[a-zA-Z\s]/oi ) { } + elsif (/except for\s+(\S+)\s+set\s+(\S+)\s*to\s*(.*)\s*/goi) { + my $one = $1; + my $two = $2; + my $three = $3; + if ( $one ne $Done{"TeXShell"} ) { + $three =~ s/^[\'\"]//o; + $three =~ s/[\'\"]$//o; + $three =~ s/\s*$//o; + if ($Verbose) { + print " setting : '$two' to '$three' except for '$one'\n"; + } + $Done{"$two"} = $three; + CheckPath( $two, $three ); + } + } elsif (/for\s+(\S+)\s+set\s+(\S+)\s*to\s*(.*)\s*/goi) { + my $one = $1; + my $two = $2; + my $three = $3; + $three =~ s/\s*$//o; + if ( $one eq $Done{"TeXShell"} ) { + $three =~ s/^[\'\"]//o; + $three =~ s/[\'\"]$//o; + if ($Verbose) { + print +" setting : '$two' to '$three' for '$one'\n"; + } + $Done{"$two"} = $three; + CheckPath( $two, $three ); + } + } elsif (/set\s+(\S+)\s*to\s*(.*)\s*/goi) { + my $one = $1; + my $two = $2; + unless ( defined( $Done{"$one"} ) ) { + $two =~ s/^[\'\"]//o; + $two =~ s/[\'\"]$//o; + $two =~ s/\s*$//o; + if ($Verbose) { + print + " setting : '$one' to '$two' for 'all'\n"; + } + $Done{"$one"} = $two; + CheckPath( $one, $two ); + } + } + } + close(INI); + if ($Verbose) { print "\n" } +} elsif ($Verbose) { + print + " warning : $IniPath not found, did you read 'texexec.rme'?\n"; + exit 1; +} else { + print + " warning : $IniPath not found, try 'texexec --verbose'\n"; + exit 1; +} + +sub IniValue { + my ( $Key, $Default ) = @_; + if ( defined( $Done{$Key} ) ) { $Default = $Done{$Key} } + if ($Default =~ /^(true|yes|on)$/io) { + $Default = 1 ; + } elsif ($Default =~ /^(false|no|off)$/io) { + $Default = 0 ; + } + if ($Verbose) { print " used setting : $Key = $Default\n" } + return $Default; +} + +my $TeXShell = IniValue( 'TeXShell', '' ); +my $SetupPath = IniValue( 'SetupPath', '' ); +my $UserInterface = IniValue( 'UserInterface', 'en' ); +my $UsedInterfaces = IniValue( 'UsedInterfaces', 'en' ); +my $TeXFontsPath = IniValue( 'TeXFontsPath', '.' ); +my $MpExecutable = IniValue( 'MpExecutable', 'mpost' ); +my $MpToTeXExecutable = IniValue( 'MpToTeXExecutable', 'mpto' ); +my $DviToMpExecutable = IniValue( 'DviToMpExecutable', 'dvitomp' ); +my $TeXProgramPath = IniValue( 'TeXProgramPath', '' ); +my $TeXFormatPath = IniValue( 'TeXFormatPath', '' ); +my $ConTeXtPath = IniValue( 'ConTeXtPath', '' ); +my $TeXScriptsPath = IniValue( 'TeXScriptsPath', '' ); +my $TeXHashExecutable = IniValue( 'TeXHashExecutable', '' ); +my $TeXExecutable = IniValue( 'TeXExecutable', 'tex' ); +my $TeXVirginFlag = IniValue( 'TeXVirginFlag', '-ini' ); +my $TeXBatchFlag = IniValue( 'TeXBatchFlag', '-interaction=batchmode' ); +my $TeXNonStopFlag = IniValue( 'TeXNonStopFlag', '-interaction=nonstopmode' ); +my $MpBatchFlag = IniValue( 'MpBatchFlag', '-interaction=batchmode' ); +my $MpNonStopFlag = IniValue( 'MpNonStopFlag', '-interaction=nonstopmode' ); +my $TeXPassString = IniValue( 'TeXPassString', '' ); +my $TeXFormatFlag = IniValue( 'TeXFormatFlag', '' ); +my $MpFormatFlag = IniValue( 'MpFormatFlag', '' ); +my $MpVirginFlag = IniValue( 'MpVirginFlag', '-ini' ); +my $MpPassString = IniValue( 'MpPassString', '' ); +my $MpFormat = IniValue( 'MpFormat', $MetaFun ); +my $MpFormatPath = IniValue( 'MpFormatPath', $TeXFormatPath ); +my $UseEnginePath = IniValue( 'UseEnginePath', ''); + +if ($TheEnginePath) { $UseEnginePath = 1 } + +# ok, let's force the engine; let's also forget about +# fmtutil, since it does not support $engine subpaths +# we will replace texexec anyway + +$UseEnginePath = 1 ; +$Alone = 1 ; + +my $FmtLanguage = IniValue( 'FmtLanguage', '' ); +my $FmtBodyFont = IniValue( 'FmtBodyFont', '' ); +my $FmtResponse = IniValue( 'FmtResponse', '' ); +my $TcXPath = IniValue( 'TcXPath', '' ); + + +$SetFile = IniValue( 'SetFile', $SetFile ); + +if ( ($Verbose) && ( $kpsewhich ne '' ) ) { + print "\n"; + my $CnfFile = `$kpsewhich -progname=context texmf.cnf`; + chomp($CnfFile); + print " applications will use : $CnfFile\n"; +} + +if ( ($FmtLanguage) && ( $MainLanguage eq 'standard' ) ) { + $MainLanguage = $FmtLanguage; +} +if ( ($FmtBodyFont) && ( $MainBodyFont eq 'standard' ) ) { + $MainBodyFont = $FmtBodyFont; +} +if ( ($FmtResponse) && ( $MainResponse eq 'standard' ) ) { + $MainResponse = $FmtResponse; +} + +# new versions, > 2004 will have -fmt as switch + +if ( $TeXFormatFlag eq "" ) { + if ($TeXProgram =~ /(etex|pdfetex)/) { + $TeXFormatFlag = "-efmt=" ; # >=2004 -fmt= + } elsif ($TeXProgram =~ /(eomega)/) { + $TeXFormatFlag = "-eoft=" ; # >=2004 obsolete + } elsif ($TeXProgram =~ /(aleph)/) { + $TeXFormatFlag = "-fmt=" ; + } else { + $TeXFormatFlag = "-fmt=" ; + } +} + +if ( $MpFormatFlag eq "" ) { + $MpFormatFlag = "-mem=" ; +} + +if ($TeXProgram) { $TeXExecutable = $TeXProgram } + +my $fmtutil = ''; + +# obsolete +# +# if ( $MakeFormats || $Verbose ) { +# if ($Alone || $UseEnginePath) { +# if ($Verbose) { print " generating format : not using fmtutil\n" } +# } elsif ( $TeXShell =~ /tetex|fptex/i ) { +# foreach (@paths) { +# my $p = checked_path($_) . 'fmtutil'; +# if ( -e $p ) { $fmtutil = $p; last } +# elsif ( -e $p . '.exe' ) { $fmtutil = $p . '.exe'; last } +# } +# $fmtutil = ($fmtutil =~ m/^[^\"].* / ? "\"$fmtutil\"" : "$fmtutil") ; +# if ($Verbose) { +# if ( $fmtutil eq '' ) { +# print " locating fmtutil : not found in path\n"; +# } else { +# print " locating fmtutil : $fmtutil\n"; +# } +# } +# } +# } + +if ($Verbose) { print "\n" } + +unless ($TeXScriptsPath) { $TeXScriptsPath = $own_path } + +unless ($ConTeXtPath) { $ConTeXtPath = $TeXScriptsPath } + +if ( $ENV{"HOME"} ) { + if ($SetupPath) { $SetupPath .= "," } +# my $home = $ENV{"HOME"}; +# $home = ($home =~ m/^[^\"].* / ? "\"$home\"" : "$home") ; +# $SetupPath .= $home; + $SetupPath .= $ENV{"HOME"}; +} + +if ($TeXFormatPath) { $TeXFormatPath =~ s/[\/\\]$//; $TeXFormatPath .= '/' } +if ($MpFormatPath) { $MpFormatPath =~ s/[\/\\]$//; $MpFormatPath .= '/' } +if ($ConTeXtPath) { $ConTeXtPath =~ s/[\/\\]$//; $ConTeXtPath .= '/' } +if ($SetupPath) { $SetupPath =~ s/[\/\\]$//; $SetupPath .= '/' } +if ($TeXScriptsPath) { $TeXScriptsPath =~ s/[\/\\]$//; $TeXScriptsPath .= '/' } + +sub QuotePath { + my ($path) = @_; + my @l = split(",", $path); + map { my $e = $_; $e = ($e =~ m/^[^\"].* / ? "\"$e\"" : "$e"); $_ = $e ;} @l; + return join(",", @l); +} + +$SetupPath = &QuotePath($SetupPath); + +$SetupPath =~ s/\\/\//go; + +my %OutputFormats; + +# the mother of all drivers + +$OutputFormats{dvips} = "dvips"; + +# needs an update + +$OutputFormats{acrobat} = "acrobat"; + +# the core drivers + +$OutputFormats{pdftex} = "pdftex"; $OutputFormats{pdf} = "pdftex"; +$OutputFormats{dvipdfm} = "dvipdfm"; $OutputFormats{dpm} = "dvipdfm"; +$OutputFormats{dvipdfmx} = "dvipdfmx"; $OutputFormats{dpx} = "dvipdfmx"; +$OutputFormats{xetex} = "xetex"; $OutputFormats{xtx} = "xetex"; +$OutputFormats{dvips} = "dvips"; $OutputFormats{ps} = "dvips"; + +# kind of obsolete now that yandy is gone + +$OutputFormats{dvipsone} = "dvipsone"; +$OutputFormats{dviwindo} = "dviwindo"; + +# it was never finished + +$OutputFormats{dviview} = "dviview"; + +my @ConTeXtFormats = ( "nl", "en", "de", "cz", "uk", "it", "ro", "xx"); + +sub SetInterfaces { + my ( $short, $long, $full ) = @_; + $ConTeXtInterfaces{$short} = $short; + $ConTeXtInterfaces{$long} = $short; + $ResponseInterface{$short} = $full; + $ResponseInterface{$long} = $full; +} + +#SetInterfaces ( "en" , "unknown" , "english" ) ; + +SetInterfaces( "nl", "dutch", "dutch" ); +SetInterfaces( "en", "english", "english" ); +SetInterfaces( "de", "german", "german" ); +SetInterfaces( "cz", "czech", "czech" ); +SetInterfaces( "uk", "brittish", "english" ); +SetInterfaces( "it", "italian", "italian" ); +SetInterfaces( "no", "norwegian", "norwegian" ); +SetInterfaces( "ro", "romanian", "romanian" ); + +# Sub-option + +struct Subopt => { + desc => '$', # description + vals => '%' # assignable values +}; + +# Main option + +struct Opt => { + desc => '$', # desciption + vals => '%', # assignable values + subs => '%' # suboptions +}; + +my $helpdone = 0; + +sub print_subopt { + my ( $k, $opt ) = @_; + $~ = 'H3'; + write; + for $k ( sort keys %{ $opt->vals } ) { + print_val( $k, ${ $opt->vals }{$k} ); + } + format H3 = +@>>>>>>>>>>>>>>>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +"--$k",$opt->desc +. +} + +sub print_val { + my ( $k, $opt ) = @_; + $~ = 'H2'; + write; + format H2 = + @<<<<<<<< : @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +$k,$opt +. +} + +# read all options + +my $recurse = -1 ; +my $shorthelp; +my @help; +my @opts = <DATA>; +while (@opts) { + $_ = shift @opts; + last if /^--+/; + my ( $k, $v ) = split( /\s+/, $_, 2 ); # was \t + $Help{$k} = read_options($v); +} + +# read a main option plus its +# description, +# assignable values and +# sub-options and their +# description and +# assignable values + +sub read_options { + $recurse++; + my $v = shift; + chomp; + my $opt = $recurse ? Subopt->new() : Opt->new(); + $opt->desc($v); + + while (@opts) { + $_ = shift @opts; + if (/^--+/) { unshift @opts, $_ if $recurse; last } + if ( $recurse && !/^=/ ) { unshift @opts, $_; last } + chomp; + my ( $kk, $vv ) = split( /\s+/, $_, 2 ); # was \t + $vv ||= ''; + if (/^=/) { $opt->vals( $kk, $vv ) } + elsif ( !$recurse ) { $opt->subs( $kk, read_options($vv) ) } + } + $recurse--; + $opt; +} + +sub print_opt { + my ( $k, $opt ) = @_; + if ($helpdone) { $shorthelp or print "\n" } + $helpdone = 1; # hh + $~ = 'H1'; + write; + return if $shorthelp < 0; + for $k ( sort keys %{ $opt->vals } ) { + print_val( $k, ${ $opt->vals }{$k} ); + } + return if $shorthelp > 0; + + for $k ( sort keys %{ $opt->subs } ) { + print_subopt( $k, ${ $opt->subs }{$k} ); + } + format H1 = +@>>>>>>>>>>>>>>>>>>>>> @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< +"--$k",$opt->desc +. +} + +# help to help + +sub show_help_options { + print # "\n" . + " --help overview of all options and their values\n" + . " --help all all about all options\n" + . " --help short just the main options\n" + . " --help mode ... pdf all about a few options\n" + . " --help '*.pdf' all about options containing 'pdf'\n" + . "\n" + . " more info http://www.pragma-ade.com/general/manuals/mtexexec.pdf\n" + . " http://www.ntg.nl/mailman/listinfo/ntg-context\n"; +} + +# determine what user wants to see + +if ($HelpAsked) { + $shorthelp = 0; + @help = ( sort keys %Help ); + if ( "@ARGV" eq "all" ) { # everything + } elsif ( "@ARGV" eq "short" ) { # nearly everything + $shorthelp--; + } elsif ( "@ARGV" eq "help" ) { # help on help + show_help_options; + exit; + } elsif (@ARGV) { # one or a few options, completely + my @h = @ARGV; + @help = (); + for (@h) { # print "testing $_\n"; + # next if (/^[\*\?]/) ; # HH, else error + if (/^[\*\?]/) { $_ = ".$_" } # HH, else error + $Help{$_} and push( @help, $_ ) or do { + my $unknown = $_; + for ( keys %Help ) { /$unknown/ and push( @help, $_ ) } + } + } + } else { # all main option and their assignable values + $shorthelp++; + } +} + +sub show_help_info { + map { print_opt( $_, $Help{$_} ) } @help; +} + +# uncomment this to see the structure of a Help element: +# print Dumper($Help{pdfselect}); + +#### end of help system + +my $FinalRunNeeded = 0; + +sub MPJobName { + my $JobName = shift; + my $MPfile = shift; + my $MPJobName = ''; + if ( -e "$JobName-$MPfile.mp" && -s "$JobName-$MPfile.mp" > 100 ) { + $MPJobName = "$JobName-$MPfile.mp" + } elsif ( -e "$MPfile.mp" && -s "$MPfile.mp" > 100 ) { + $MPJobName = "$MPfile.mp" + } else { $MPJobName = "" } + return $MPJobName; +} + +sub System { + my $cmd = shift ; + unless ( $dosish && ! $escapeshell ) { + $cmd =~ s/([^\\])\&/$1\\\&/io ; + } + if ($Verbose) { + print "\n$cmd\n\n" ; + } + system($cmd) +} + +sub Pipe { + my $cmd = shift ; + unless ( $dosish && ! $escapeshell ) { + $cmd =~ s/([^\\])\&/$1\\\&/io ; + } + if ($Verbose) { + print "\n$cmd\n\n" ; + } + return `$cmd` +} + + +sub RunPerlScript { + my ( $ScriptName, $Options ) = @_; + my $cmd = ''; + $own_quote = ($own_path =~ m/^[^\"].* / ? "\"" : "") ; + if ($Verbose) { + $Options .= ' --verbose' ; + } + if ($dosish) { + if ( -e "own_path$ScriptName$own_type" ) { + $cmd = +"$own_stub$own_quote$own_path$ScriptName$own_type$own_quote $Options"; + } elsif ( -e "$TeXScriptsPath$ScriptName$own_type" ) { + $cmd = +"$own_stub$own_quote$TeXScriptsPath$ScriptName$own_type$own_quote $Options"; + } else { + $cmd = ""; + } + } else { + $cmd = "$ScriptName $Options"; + } + unless ( $cmd eq "" ) { + System($cmd) ; + } +} + +my $FullFormat = ''; + +sub CheckOutputFormat { + my $Ok = 1; + if ( $OutputFormat ne 'standard' ) { + my @OutputFormat = split( /,/, $OutputFormat ); + foreach my $F (@OutputFormat) { + if ( defined( $OutputFormats{ lc $F } ) ) { + my $OF = $OutputFormats{ lc $F }; + next if ( ",$FullFormat," =~ /\,$OF\,/ ); + if ($FullFormat) { $FullFormat .= "," } + $FullFormat .= "$OutputFormats{lc $F}"; + } else { + $Ok = 0; + } + } + if ( !$Ok ) { + print(" unknown output format : $OutputFormat\n"); + } + } + unless ($FullFormat) { $FullFormat = $OutputFormat } +} # 'standard' to terminal + +sub MakeOptionFile { + my ( $FinalRun, $FastDisabled, $JobName, $JobSuffix, $KindOfRun ) = @_; + open( OPT, ">$JobName.top" ); + print OPT "\% $JobName.top\n"; + print OPT "\\unprotect\n"; + if ($EnterBatchMode) { print OPT "\\batchmode\n" } + if ($EnterNonStopMode) { print OPT "\\nonstopmode\n" } + if ($Paranoid) { + print " paranoid file mode : very true\n"; + print OPT "\\def\\maxreadlevel{1}\n" ; + } + $ModeFile =~ s/\\/\//gio ; # do this at top of file + $Result =~ s/\\/\//gio ; # do this at top of file + if ( $ModeFile ne '' ) { print OPT "\\readlocfile{$ModeFile}{}{}" } + if ( $Result ne '' ) { print OPT "\\setupsystem[file=$Result]\n" } + elsif ($Suffix) { print OPT "\\setupsystem[file=$JobName$Suffix]\n" } + if ( $InpPath ne "" ) { + $InpPath =~ s/\\/\//go; + $InpPath =~ s/\/$//go; + print OPT "\\usepath[$InpPath]\n"; + } + $MainLanguage = lc $MainLanguage; + unless ( $MainLanguage eq "standard" ) { + print OPT "\\setuplanguage[$MainLanguage]\n"; + } + # can best become : \use...[mik] / [web] + if ( $TeXShell =~ /MikTeX/io ) { + print OPT "\\def\\MPOSTbatchswitch \{$MpBatchFlag\}"; + print OPT "\\def\\MPOSTnonstopswitch \{$MpNonStopFlag\}"; + print OPT "\\def\\MPOSTformatswitch \{$MpPassString $MpFormatFlag\}"; + } + # + if ( $FullFormat ne 'standard' ) { + print OPT "\\setupoutput[$FullFormat]\n"; + } + if ($UseColor) { print OPT "\\setupcolors[\\c!state=\\v!start]\n" } + if ( $NoMPMode || $NoMPRun || $AutoMPRun ) { + print OPT "\\runMPgraphicsfalse\n"; + } + if ( ($FastMode) && ( !$FastDisabled ) ) { print OPT "\\fastmode\n" } + if ($SilentMode) { print OPT "\\silentmode\n" } + if ( $Separation ne "" ) { + print OPT "\\setupcolors[\\c!split=$Separation]\n"; + } + if ($SetupPath) { print OPT "\\setupsystem[\\c!directory=\{$SetupPath\}]\n" } + print OPT "\\setupsystem[\\c!n=$KindOfRun]\n"; + $_ = $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/ ) { + s/x/\*/io; + if (/\w+\d+/) { $_ = uc $_ } + my ( $from, $to ) = split(/\*/); + if ( $to eq "" ) { $to = $from } + print OPT "\\setuppapersize[$from][$to]\n"; + } + } + if ( ( $PdfSelect || $PdfCombine || $PdfCopy || $PdfTrim || $PdfArrange ) + && ( $Background ne '' ) ) + { + print " background graphic : $Background\n"; + print OPT "\\defineoverlay[whatever][{\\externalfigure[$Background][\\c!factor=\\v!max]}]\n"; + print OPT "\\setupbackgrounds[\\v!page][\\c!background=whatever]\n"; + } + if ($CenterPage) { + print OPT + "\\setuplayout[\\c!location=\\v!middle,\\c!marking=\\v!on]\n"; + } + if ($NoMapFiles) { + print OPT "\\disablemapfiles\n"; + } + if ($NoArrange) { print OPT "\\setuparranging[\\v!disable]\n" } + elsif ( $Arrange || $PdfArrange ) { + $FinalRunNeeded = 1; + if ($FinalRun) { + my $DupStr; + if ($NoDuplex) { $DupStr = "" } + else { $DupStr = ",\\v!doublesided" } + if ( $PrintFormat eq '' ) { + print OPT "\\setuparranging[\\v!normal]\n"; + } elsif ( $PrintFormat =~ /.*up/goi ) { + print OPT "\\setuparranging[2UP,\\v!rotated$DupStr]\n"; + } elsif ( $PrintFormat =~ /.*down/goi ) { + print OPT "\\setuparranging[2DOWN,\\v!rotated$DupStr]\n"; + } elsif ( $PrintFormat =~ /.*side/goi ) { + print OPT "\\setuparranging[2SIDE,\\v!rotated$DupStr]\n"; + } else { + print OPT "\\setuparranging[$PrintFormat]\n"; + } + } else { + print OPT "\\setuparranging[\\v!disable]\n"; + } + } + if ($Arguments) { print OPT "\\setupenv[$Arguments]\n" } + if ($Input) { print OPT "\\setupsystem[inputfile=$Input]\n" } + else { print OPT "\\setupsystem[inputfile=$JobName.$JobSuffix]\n" } + if ($Random) { print OPT "\\setupsystem[\\c!random=$RandomSeed]\n" } + if ($Mode) { print OPT "\\enablemode[$Mode]\n" } + if ($Pages) { + if ( lc $Pages eq "odd" ) { + print OPT "\\chardef\\whichpagetoshipout=1\n"; + } elsif ( lc $Pages eq "even" ) { + print OPT "\\chardef\\whichpagetoshipout=2\n"; + } else { + my @Pages = split( /\,/, $Pages ); + $Pages = ''; + foreach my $page (@Pages) { + if ( $page =~ /\:/ ) { + my ( $from, $to ) = split( /\:/, $page ); + foreach ( my $i = $from ; $i <= $to ; $i++ ) { + $Pages .= $i . ','; + } + } else { + $Pages .= $page . ','; + } + } + chop $Pages; + print OPT "\\def\\pagestoshipout\{$Pages\}\n"; + } + } + print OPT "\\protect\n"; + if ( $Filters ne "" ) { + foreach my $F ( split( /,/, $Filters ) ) { + print OPT "\\useXMLfilter[$F]\n"; + } + } + if ( $Modules ne "" ) { + foreach my $M ( split( /,/, $Modules ) ) { + print OPT "\\usemodule[$M]\n"; + } + } + if ( $Environments ne "" ) { + foreach my $E ( split( /,/, $Environments ) ) { + print OPT "\\environment $E\n"; + } + } + close(OPT); +} + +my $UserFileOk = 0; +my @MainLanguages; +my $AllLanguages = ''; + +sub MakeUserFile { + $UserFileOk = 0; + if ($AllPatterns) { + open( USR, ">cont-fmt.tex" ); + print USR "\\preloadallpatterns\n"; + } else { + return + if ( ( $MainLanguage eq 'standard' ) + && ( $MainBodyFont eq 'standard' ) ); + print " preparing user file : cont-fmt.tex\n"; + open( USR, ">cont-fmt.tex" ); + print USR "\\unprotect\n"; + $AllLanguages = $MainLanguage; + if ( $MainLanguage ne 'standard' ) { + @MainLanguages = split( /\,/, $MainLanguage ); + foreach (@MainLanguages) { + print USR "\\installlanguage[\\s!$_][\\c!state=\\v!start]\n"; + } + $MainLanguage = $MainLanguages[0]; + print USR "\\setupcurrentlanguage[\\s!$MainLanguage]\n"; + } + if ( $MainBodyFont ne 'standard' ) { + print USR "\\definetypescriptsynonym[cmr][$MainBodyFont]"; + print USR "\\definefilesynonym[font-cmr][font-$MainBodyFont]\n"; + } + print USR "\\protect\n"; + } + print USR "\\endinput\n"; + close(USR); + ReportUserFile(); + print "\n"; + $UserFileOk = 1; +} + +sub RemoveResponseFile { unlink "mult-def.tex" } + +sub MakeResponseFile { + if ( $MainResponse eq 'standard' ) { RemoveResponseFile() } + elsif ( !defined( $ResponseInterface{$MainResponse} ) ) { + RemoveResponseFile(); + } else { + my $MR = $ResponseInterface{$MainResponse}; + print " preparing interface file : mult-def.tex\n"; + print " response language : $MR\n"; + open( DEF, ">mult-def.tex" ); + print DEF "\\def\\currentresponses\{$MR\}\n\\endinput\n"; + close(DEF); + } +} + +sub RestoreUserFile { + unlink "cont-fmt.log"; + rename "cont-fmt.tex", "cont-fmt.log"; + ReportUserFile(); +} + +sub ReportUserFile { + return unless ($UserFileOk); + print "\n"; + if ( $MainLanguage ne 'standard' ) { + print " additional patterns : $AllLanguages\n"; + print " default language : $MainLanguage\n"; + } + if ( $MainBodyFont ne 'standard' ) { + print " default bodyfont : $MainBodyFont\n"; + } +} + +sub CheckPositions { } + +my $ConTeXtVersion = "unknown"; +my $ConTeXtModes = ''; + +sub ScanTeXPreamble { + my ($FileName) = @_; + open( TEX, $FileName ); + while (<TEX>) { + chomp; + if (/^\%.*/) { + if (/tex=([a-z]*)/goi) { $TeXExecutable = $1 } + if (/translat.*?=([\:\/0-9\-a-z]*)/goi) { $TeXTranslation = $1 } + if (/program=([a-z]*)/goi) { $TeXExecutable = $1 } + if (/output=([a-z\,\-]*)/goi) { $OutputFormat = $1 } + if (/modes=([a-z\,\-]*)/goi) { $ConTeXtModes = $1 } + if (/textree=([a-z\-]*)/goi) { $TeXTree = $1 } + if (/texroot=([a-z\-]*)/goi) { $TeXRoot = $1 } + if ( $ConTeXtInterface eq "unknown" ) { + + if (/format=([a-z]*)/goi) { + $ConTeXtInterface = $ConTeXtInterfaces{$1}; + } + if (/interface=([a-z]*)/goi) { + $ConTeXtInterface = $ConTeXtInterfaces{"$1"}; + } + } + if (/version=([a-z]*)/goi) { $ConTeXtVersion = $1 } + } else { + last; + } + } + close(TEX); + + # handy later on + + $ProducePdfT = ($OutputFormat eq "pdftex") ; + $ProducePdfM = ($OutputFormat eq "dvipdfm") ; + $ProducePdfX = ($OutputFormat eq "dvipdfmx") ; + $ProducePdfXTX = ($OutputFormat eq "xetex") ; + $ProducePs = ($OutputFormat eq "dvips") ; +} + +sub ScanContent { + my ($ConTeXtInput) = @_; + open( TEX, $ConTeXtInput ); + while (<TEX>) { + next if (/^\%/) ; + if ( +/\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/ + ) + { + $ConTeXtInterface = "nl"; + last; + } elsif (/\\(stelle|verwende|umgebung|benutze)/) { + $ConTeXtInterface = "de"; + last; + } elsif (/\\(stel|gebruik|omgeving)/) { + $ConTeXtInterface = "nl"; + last; + } elsif (/\\(use|setup|environment)/) { + $ConTeXtInterface = "en"; + last; + } elsif (/\\(usa|imposta|ambiente)/) { + $ConTeXtInterface = "it"; + last; + } elsif (/(height|width|style)=/) { + $ConTeXtInterface = "en"; + last; + } elsif (/(hoehe|breite|schrift)=/) { + $ConTeXtInterface = "de"; + last; + } + # brr, can be \c! + elsif (/(hoogte|breedte|letter)=/) { $ConTeXtInterface = "nl"; last } + elsif (/(altezza|ampiezza|stile)=/) { $ConTeXtInterface = "it"; last } + elsif (/externfiguur/) { $ConTeXtInterface = "nl"; last } + elsif (/externalfigure/) { $ConTeXtInterface = "en"; last } + elsif (/externeabbildung/) { $ConTeXtInterface = "de"; last } + elsif (/figuraesterna/) { $ConTeXtInterface = "it"; last } + } + close(TEX); +} + +if ( $ConTeXtInterfaces{$ConTeXtInterface} ) { + $ConTeXtInterface = $ConTeXtInterfaces{$ConTeXtInterface}; +} + +my $Problems = my $Ok = 0; + +sub PrepRunTeX { + my ( $JobName, $JobSuffix, $PipeString ) = @_; + my $cmd; + my $TeXProgNameFlag = ''; + if ( !$dosish ) # we assume tetex on linux + { + $TeXProgramPath = ''; + $TeXFormatPath = ''; + if ( !$TeXProgNameFlag + && ( $Format =~ /^cont/ ) + && ( $TeXPassString !~ /progname/io ) ) + { + $TeXProgNameFlag = "-progname=context"; + } + } + $own_quote = ($TeXProgramPath =~ m/^[^\"].* / ? "\"" : "") ; + $cmd = join( ' ', + "$own_quote$TeXProgramPath$TeXExecutable$own_quote", + $TeXProgNameFlag, $TeXPassString, $PassOn, "" ); + if ($EnterBatchMode) { $cmd .= "$TeXBatchFlag " } + if ($EnterNonStopMode) { $cmd .= "$TeXNonStopFlag " } + if ( $TeXTranslation ne '' ) { $cmd .= "-translate-file=$TeXTranslation " } + $cmd .= "$TeXFormatFlag$TeXFormatPath$Format $JobName.$JobSuffix $PipeString"; + return $cmd; +} + +my $emergencyend = "" ; +#~ my $emergencyend = "\\emergencyend" ; + +sub RunTeX { + my ( $JobName, $JobSuffix ) = @_; + my $StartTime = time; + my $cmd = PrepRunTeX($JobName, $JobSuffix, ''); + if ($EnterBatchMode) { + $Problems = System("$cmd $emergencyend"); + } else { + $Problems = System("$cmd $emergencyend"); + } + my $StopTime = time - $StartTime; + print "\n return code : $Problems"; + print "\n run time : $StopTime seconds\n"; + return $Problems; +} + +sub PushResult { + my $File = shift; + $File =~ s/\..*$//o; + $Result =~ s/\..*$//o; + if ( ( $Result ne '' ) && ( $Result ne $File ) ) { + print " outputfile : $Result\n"; + unlink "texexec.tuo"; + rename "$File.tuo", "texexec.tuo"; + unlink "texexec.log"; + rename "$File.log", "texexec.log"; + unlink "texexec.dvi"; + rename "$File.dvi", "texexec.dvi"; + unlink "texexec.pdf"; + rename "$File.pdf", "texexec.pdf"; + + if ( -e "$Result.tuo" ) { + unlink "$File.tuo"; + rename "$Result.tuo", "$File.tuo"; + } + } + if ($Optimize) { unlink "$File.tuo" } +} + +sub PopResult { + my $File = shift; + $File =~ s/\..*$//o; + $Result =~ s/\..*$//o; + if ( ( $Result ne '' ) && ( $Result ne $File ) ) { + print " renaming : $File to $Result\n"; + unlink "$Result.tuo"; + rename "$File.tuo", "$Result.tuo"; + unlink "$Result.log"; + rename "$File.log", "$Result.log"; + unlink "$Result.dvi"; + rename "$File.dvi", "$Result.dvi"; + if ( -e "$File.dvi" ) { CopyFile( "$File.dvi", "$Result.dvi" ) } + unlink "$Result.pdf"; + rename "$File.pdf", "$Result.pdf"; + if ( -e "$File.pdf" ) { CopyFile( "$File.pdf", "$Result.pdf" ) } + return if ( $File ne "texexec" ); + rename "texexec.tuo", "$File.tuo"; + rename "texexec.log", "$File.log"; + rename "texexec.dvi", "$File.dvi"; + rename "texexec.pdf", "$File.pdf"; + } +} + +sub RunTeXutil { + my $StopRunning; + my $JobName = shift; + unlink "$JobName.tup"; + rename "$JobName.tuo", "$JobName.tup"; + print " sorting and checking : running texutil\n"; + my $TcXSwitch = ''; + if ( $TcXPath ne '' ) { $TcXSwitch = "--tcxpath=$TcXPath" } + RunPerlScript( $TeXUtil, "--ref --ij --high $TcXPath $JobName" ); + + if ( -e "$JobName.tuo" ) { + CheckPositions($JobName); + #~ print " utility file check : $JobName.tup <-> $JobName.tuo\n"; + $StopRunning = !compare( "$JobName.tup", "$JobName.tuo" ); + } else { + $StopRunning = 1; + } # otherwise potential loop + if ( !$StopRunning ) { + print "\n utility file analysis : another run needed\n"; + } + return $StopRunning; +} + +sub PurgeFiles { + my $JobName = shift; + print "\n purging files : $JobName\n"; + RunPerlScript( $TeXUtil, "--purge $JobName" ); + unlink( $Result . '.log' ) if ( -f $Result . '.log' ); +} + +sub RunTeXMP { + my $JobName = shift; + my $MPfile = shift; + my $MPrundone = 0; + my $MPJobName = MPJobName( $JobName, $MPfile ); + my $MPFoundJobName = ""; + if ( $MPJobName ne "" ) { + if ( open( MP, "$MPJobName" ) ) { + $_ = <MP>; + chomp; # we should handle the prefix as well + if (/^\%\s+translate.*?\=([\w\d\-]+)/io) { $TeXTranslation = $1 } + if (/collected graphics of job \"(.+)\"/i) { $MPFoundJobName = $1 } + close(MP); + if ( $MPFoundJobName ne "" ) { + if ( $JobName =~ /$MPFoundJobName$/i ) { + if ( $MpExecutable ne '' ) { + print + " generating graphics : metaposting $MPJobName\n"; + my $ForceMpy = ""; + if ($MpyForce) { $ForceMpy = "--mpyforce" } + my $ForceTCX = ''; + if ( $TeXTranslation ne '' ) { + $ForceTCX = "--translate=$TeXTranslation "; + } + if ($EnterBatchMode) { + RunPerlScript( $TeXExec, +"$ForceTCX $ForceMpy --mptex --nomp --batch $MPJobName" + ); + } elsif ($EnterNonStopMode) { + RunPerlScript( $TeXExec, +"$ForceTCX $ForceMpy --mptex --nomp --nonstop $MPJobName" + ); + } else { + RunPerlScript( $TeXExec, + "$ForceTCX $ForceMpy --mptex --nomp $MPJobName" + ); + } + } else { + print + " generating graphics : metapost cannot be run\n"; + } + $MPrundone = 1; + } + } + } + } + return $MPrundone; +} + +sub CopyFile { # agressive copy, works for open files like in gs + my ( $From, $To ) = @_; + return unless open( INP, "<$From" ); + binmode INP; + return unless open( OUT, ">$To" ); + binmode OUT; + while (<INP>) { print OUT $_ } + close(INP); + close(OUT); +} + +#~ sub CheckMPChanges { + #~ my $JobName = shift; + #~ my $checksum = 0; + #~ my $MPJobName = MPJobName( $JobName, "mpgraph" ); + #~ if ( open( MP, $MPJobName ) ) { + #~ while (<MP>) { + #~ unless (/random/oi) { + #~ $checksum += do { unpack( "%32C*", <MP> ) % 65535 } + #~ } + #~ } + #~ close(MP); + #~ } + #~ $MPJobName = MPJobName( $JobName, "mprun" ); + #~ if ( open( MP, $MPJobName ) ) { + #~ while (<MP>) { + #~ unless (/random/oi) { + #~ $checksum += do { unpack( "%32C*", <MP> ) % 65535 } + #~ } + #~ } + #~ close(MP); + #~ } + #~ print " mpgraph/mprun : $checksum\n"; + #~ return $checksum; +#~ } + +sub CheckMPChanges { + my $JobName = shift; my $str = '' ; + my $MPJobName = MPJobName( $JobName, "mpgraph" ); + if ( open( MP, $MPJobName ) ) { + $str .= do { local $/ ; <MP> ; } ; + close(MP) ; + } + $MPJobName = MPJobName( $JobName, "mprun" ); + if ( open( MP, $MPJobName ) ) { + $str .= do { local $/ ; <MP> ; } ; + close(MP) ; + } + $str =~ s/^.*?random.*?$//oim ; + return Digest::MD5::md5_hex($str) ; +} + +#~ sub CheckTubChanges { + #~ my $JobName = shift; + #~ my $checksum = 0; + #~ if ( open( TUB, "$JobName.tub" ) ) { + #~ while (<TUB>) { + #~ $checksum += do { unpack( "%32C*", <TUB> ) % 65535 } + #~ } + #~ close(TUB); + #~ } + #~ return $checksum; +#~ } + +sub CheckTubChanges { + my $JobName = shift; my $str = '' ; + if ( open( TUB, "$JobName.tub" ) ) { + $str = do { local $/ ; <TUB> ; } ; + close(TUB); + } + return Digest::MD5::md5_hex($str); +} + + +my $DummyFile = 0; + +sub isXMLfile { + my $Name = shift; + if ( ($ForceXML) || ( $Name =~ /\.(xml|fo|fox)$/io ) ) { return 1 } + else { + open( XML, $Name ); + my $str = <XML>; + close(XML); + return ( $str =~ /\<\?xml /io ); + } +} + +sub RunConTeXtFile { + my ( $JobName, $JobSuffix ) = @_; + if ($AutoPath) { + if ($JobName =~ /^(.*)[\/\\](.*?)$/o) { + $InpPath = $1 ; + $JobName = $2 ; + } + } + $JobName =~ s/\\/\//goi; + $InpPath =~ s/\\/\//goi; + my $OriSuffix = $JobSuffix; + if ($JobSuffix =~ /\_fo$/i) { + if (! -f $JobName) { + print "stripping funny suffix : _fo\n"; + $JobName =~ s/\_fo$//io ; + $JobSuffix =~ s/\_fo$//io ; + $OriSuffix =~ s/\_fo$//io ; + } + } + if (($dosish) && ($PdfClose)) { + my $ok = System("pdfclose --file $JobName.pdf") if -e "$JobName.pdf" ; + if (($Result ne '') && (-e "$Result.pdf")) { + $ok = System("pdfclose --file $Result.pdf") ; + } + System("pdfclose --all") unless $ok ; + } + if ( -e "$JobName.$JobSuffix" ) { + $DummyFile = ( ($ForceXML) || ( $JobSuffix =~ /(xml|fo|fox)/io ) ); + } + # to be considered : + # { $DummyFile = isXMLfile("$JobName.$JobSuffix") } + elsif ( $InpPath ne "" ) { + my @InpPaths = split( /,/, $InpPath ); + foreach my $rp (@InpPaths) { + if ( -e "$rp/$JobName.$JobSuffix" ) { $DummyFile = 1; last } + } + } + if ($DummyFile) { + open( TMP, ">$JobName.run" ); + if ( ( $JobSuffix =~ /(xml|fo|fox)/io ) || $ForceXML ) { + # scan xml preamble + open(XML,"<$JobName.$JobSuffix") ; + while (<XML>) { + 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')) { + print TMP "\\enablemode[$value]\n" ; + } elsif (($key eq 'stylefile') || ($key eq 'environment')) { + print TMP "\\environment $value\n" ; + } elsif ($key eq 'module') { + print TMP "\\usemodule[$value]\n" ; + } elsif ($key eq 'interface') { + $ConTeXtInterface = $value ; + } elsif ($key eq 'control') { + if ($rest == 'purge') { $Purge = 1 } + } + } + } elsif (/\<[a-z]+/io) { + last ; + } + } + close(XML) ; + if ( $Filters ne "" ) { + print " using xml filters : $Filters\n"; + } + print TMP "\\starttext\n"; + print TMP "\\processXMLfilegrouped{$JobName.$JobSuffix}\n"; + print TMP "\\stoptext\n"; + } else { + print TMP "\\starttext\n"; + print TMP "\\processfile{$JobName.$JobSuffix}\n"; + print TMP "\\stoptext\n"; + } + close(TMP); + $JobSuffix = "run"; + } + if ( ( -e "$JobName.$JobSuffix" ) || ($GlobalFile) ) { + unless ($DummyFile) { # we don't need this for xml + ScanTeXPreamble("$JobName.$JobSuffix"); + if ( $ConTeXtInterface eq "unknown" ) { + ScanContent("$JobName.$JobSuffix"); + } + } + if ( $ConTeXtInterface eq "unknown" ) { + $ConTeXtInterface = $UserInterface; + } + if ( $ConTeXtInterface eq "unknown" ) { $ConTeXtInterface = "en" } + if ( $ConTeXtInterface eq "" ) { $ConTeXtInterface = "en" } + CheckOutputFormat; + my $StopRunning = 0; + my $MPrundone = 0; + if ( $Format eq '' ) { $Format = "cont-$ConTeXtInterface" } + print " executable : $TeXProgramPath$TeXExecutable\n"; + print " format : $TeXFormatPath$Format\n"; + if ($InpPath) { print " source path : $InpPath\n" } + + if ($DummyFile) { + print " dummy file : $JobName.$JobSuffix\n"; + } + print " inputfile : $JobName\n"; + print " output : $FullFormat\n"; + print " interface : $ConTeXtInterface\n"; + if ( $TeXTranslation ne '' ) { + print " translation : $TeXTranslation\n"; + } + my $Options = ''; + if ($Random) { $Options .= " random" } + if ($FastMode) { $Options .= " fast" } + if ($FinalMode) { $Options .= " final" } + if ($Verbose) { $Options .= " verbose" } + if ($TypesetListing) { $Options .= " listing" } + if ($TypesetModule) { $Options .= " module" } + if ($TypesetFigures) { $Options .= " figures" } + if ($MakeFormats) { $Options .= " make" } + if ($RunOnce) { $Options .= " once" } + if ($UseColor) { $Options .= " color" } + if ($EnterBatchMode) { $Options .= " batch" } + if ($EnterNonStopMode) { $Options .= " nonstop" } + if ($NoMPMode) { $Options .= " nomp" } + if ($CenterPage) { $Options .= " center" } + if ($Arrange) { $Options .= " arrange" } + if ($NoArrange) { $Options .= " no-arrange" } + if ($Options) { print " options :$Options\n" } + if ($ConTeXtModes) { print " possible modes : $ConTeXtModes\n" } + if ($Mode) { print " current mode : $Mode\n" } + else { print " current mode : none\n" } + if ($Arguments) { print " arguments : $Arguments\n" } + if ($Modules) { print " modules : $Modules\n" } + if ($Environments) { print " environments : $Environments\n" } + if ($Suffix) { $Result = "$JobName$Suffix" } + PushResult($JobName); + $Problems = 0; + my $TeXRuns = 0; + + if ( ($PdfArrange) || ($PdfSelect) || ($RunOnce) ) { + MakeOptionFile( 1, 1, $JobName, $OriSuffix, 3 ); + print "\n"; + $Problems = RunTeX( $JobName, $JobSuffix ); + if ($ForceTeXutil) { $Ok = RunTeXutil($JobName) } + CopyFile( "$JobName.top", "$JobName.tmp" ); + unlink "$JobName.top"; # runtime option file + PopResult($JobName); + } else { + while ( !$StopRunning && ( $TeXRuns < $NOfRuns ) && ( !$Problems ) ) + { + ++$TeXRuns; + if ( $TeXRuns == 1 ) { + MakeOptionFile( 0, 0, $JobName, $OriSuffix, 1 ); + } else { + MakeOptionFile( 0, 0, $JobName, $OriSuffix, 2 ); + } + print " TeX run : $TeXRuns\n\n"; + my ( $mpchecksumbefore, $mpchecksumafter ) = ( '', '' ); + my ( $tubchecksumbefore, $tubchecksumafter ) = ( '', '' ); + if ($AutoMPRun) { $mpchecksumbefore = CheckMPChanges($JobName) } + $tubchecksumbefore = CheckTubChanges($JobName) ; + $Problems = RunTeX( $JobName, $JobSuffix ); + $tubchecksumafter = CheckTubChanges($JobName) ; + if ($AutoMPRun) { $mpchecksumafter = CheckMPChanges($JobName) } + if ( ( !$Problems ) && ( $NOfRuns > 1 ) ) { + unless ( $NoMPMode ) { + $MPrundone = RunTeXMP( $JobName, "mpgraph" ); + $MPrundone = RunTeXMP( $JobName, "mprun" ); + } + $StopRunning = RunTeXutil($JobName); + if ($AutoMPRun) { + $StopRunning = + ( $StopRunning + && ( $mpchecksumafter eq $mpchecksumbefore ) ); + } + $StopRunning = + ( $StopRunning + && ( $tubchecksumafter eq $tubchecksumbefore ) ); + } + } + if ( ( $NOfRuns == 1 ) && $ForceTeXutil ) { + $Ok = RunTeXutil($JobName); + } + if ( ( !$Problems ) + && ( ( $FinalMode || $FinalRunNeeded ) ) + && ( $NOfRuns > 1 ) ) + { + MakeOptionFile( 1, $FinalMode, $JobName, $OriSuffix, 4 ); + print " final TeX run : $TeXRuns\n\n"; + $Problems = RunTeX( $JobName, $JobSuffix ); + } + CopyFile( "$JobName.top", "$JobName.tmp" ); + unlink "$JobName.tup"; # previous tuo file + unlink "$JobName.top"; # runtime option file + if ($ProducePdfX) { + $ENV{'backend'} = $ENV{'progname'} = 'dvipdfm' ; + $ENV{'TEXFONTMAPS'} = '.;$TEXMF/fonts/map/{dvipdfm,dvips,}//' ; + System("dvipdfmx -d 4 $JobName") ; + } elsif ($ProducePdfM) { + $ENV{'backend'} = $ENV{'progname'} = 'dvipdfm' ; + $ENV{'TEXFONTMAPS'} = '.;$TEXMF/fonts/map/{dvipdfm,dvips,}//' ; + System("dvipdfm $JobName") ; + } elsif ($ProducePdfXTX) { + $ENV{'backend'} = $ENV{'progname'} = 'xetex' ; + $ENV{'TEXFONTMAPS'} = '.;$TEXMF/fonts/map/{xetex,pdftex,dvips,}//' ; + System("xdv2pdf $JobName.xdv") ; + } elsif ($ProducePs) { + $ENV{'backend'} = $ENV{'progname'} = 'dvips' ; + $ENV{'TEXFONTMAPS'} = '.;$TEXMF/fonts/map/{dvips,pdftex,}//' ; + # temp hack, some day there will be map file loading in specials + my $mapfiles = '' ; + if (-f "$JobName.tui") { + open(TUI,"$JobName.tui") ; + while (<TUI>) { + if (/c \\usedmapfile\{.\}\{(.*?)\}/o) { + $mapfiles .= "-u +$1 " ; + } + } + close(TUI) ; + } + System("dvips $mapfiles $JobName.dvi") ; + } + PopResult($JobName); + } + if ($Purge) { PurgeFiles($JobName) } + if ($DummyFile) # $JobSuffix == run + { + unlink "$JobName.$JobSuffix"; + } + if ((!$Problems) && ($PdfOpen)) { + if ($Result ne '') { + System("pdfopen --file $Result.pdf") if -f "$Result.pdf" + } else { + System("pdfopen --file $JobName.pdf") if -f "$JobName.pdf" + } + } + } +} + +sub RunSomeTeXFile { + my ( $JobName, $JobSuffix ) = @_; + if ( -e "$JobName.$JobSuffix" ) { + PushResult($JobName); + print " executable : $TeXProgramPath$TeXExecutable\n"; + print " format : $TeXFormatPath$Format\n"; + print " inputfile : $JobName.$JobSuffix\n"; + $Problems = RunTeX( $JobName, $JobSuffix ); + PopResult($JobName); + } +} + +my $ModuleFile = "texexec"; +my $ListingFile = "texexec"; +my $FiguresFile = "texexec"; +my $ArrangeFile = "texexec"; +my $SelectFile = "texexec"; +my $CopyFile = "texexec"; +my $CombineFile = "texexec"; + +sub RunModule { + my @FileNames = sort @_; + if ($FileNames[0]) { + unless ( -e $FileNames[0] ) { + my $Name = $FileNames[0]; + @FileNames = ( "$Name.tex", "$Name.mp", "$Name.pl", "$Name.pm" ); + } + foreach my $FileName (@FileNames) { + next unless -e $FileName; + my ( $Name, $Suffix ) = split( /\./, $FileName ); + next unless $Suffix =~ /(tex|mp|pl|pm)/io; + DoRunModule( $Name, $Suffix ); + } + } else { + print " module : no modules found\n\n"; + } +} + +# the next one can be more efficient: directly process ted +# file a la --use=abr-01,mod-01 + +sub checktexformatpath { + # engine support is either broken of not implemented in some + # distributions, so we need to take care of it ourselves + my $texformats ; + if (defined($ENV{'TEXFORMATS'})) { + $texformats = $ENV{'TEXFORMATS'} ; + } else { + $texformats = '' ; + } + if ($texformats eq '') { + if ($UseEnginePath) { + if ($dosish) { + $texformats = `kpsewhich --engine=$TeXExecutable --expand-var=\$TEXFORMATS` ; + } else { + $texformats = `kpsewhich --engine=$TeXExecutable --expand-var=\\\$TEXFORMATS` ; + } + } else { + if ($dosish) { + $texformats = `kpsewhich --expand-var=\$TEXFORMATS` ; + } else { + $texformats = `kpsewhich --expand-var=\\\$TEXFORMATS` ; + } + } + chomp($texformats) ; + } + if (($texformats !~ /web2c\/.*$TeXExecutable/) && ($texformats !~ /web2c[\/\\].*\$engine/i)) { + $texformats =~ s/(web2c\/\{)(\,\})/$1\$engine$2/ ; # needed for empty engine flags + if ($texformats !~ /web2c[\/\\].*\$ENGINE/) { + $texformats =~ s/web2c/web2c\/{\$engine,}/ ; # needed for me + } + $ENV{'TEXFORMATS'} = $texformats ; + print " fixing texformat path : $ENV{'TEXFORMATS'}\n"; + } else { + print " using texformat path : $ENV{'TEXFORMATS'}\n" if ($Verbose) ; + } + if (! defined($ENV{'ENGINE'})) { + if ($MpEngineSupport) { + $ENV{'ENGINE'} .= $MpExecutable ; + } ; + $ENV{'ENGINE'} = $TeXExecutable ; + print "fixing engine variable : $ENV{'ENGINE'}\n"; + } +} + +sub DoRunModule { + my ( $FileName, $FileSuffix ) = @_; + RunPerlScript( $TeXUtil, "--documents $FileName.$FileSuffix" ); + print " module : $FileName\n\n"; + open( MOD, ">$ModuleFile.tex" ); + # we need to signal to texexec what interface to use + open( TED, "$FileName.ted" ); + my $firstline = <TED>; + close(TED); + if ( $firstline =~ /interface=/ ) { + print MOD $firstline ; + } else { + print MOD "% interface=en\n" ; + } + # so far + print MOD "\\usemodule[abr-01,mod-01]\n"; + print MOD "\\def\\ModuleNumber{1}\n"; + print MOD "\\starttext\n"; + print MOD "\\readlocfile{$FileName.ted}{}{}\n"; + print MOD "\\stoptext\n"; + close(MOD); + checktexformatpath ; + RunConTeXtFile( $ModuleFile, "tex" ); + if ( $FileName ne $ModuleFile ) { + foreach my $FileSuffix ( "dvi", "pdf", "tui", "tuo", "log" ) { + unlink("$FileName.$FileSuffix"); + rename( "$ModuleFile.$FileSuffix", "$FileName.$FileSuffix" ); + } + } + unlink("$ModuleFile.tex"); +} + +sub RunFigures { + my @Files = @_ ; + $TypesetFigures = lc $TypesetFigures; + return unless ( $TypesetFigures =~ /[abcd]/o ); + unlink "$FiguresFile.pdf"; + if (@Files) { RunPerlScript( $TeXUtil, "--figures @Files" ) } + open( FIG, ">$FiguresFile.tex" ); + print FIG "% format=english\n"; + print FIG "\\setuplayout\n"; + print FIG " [topspace=1.5cm,backspace=1.5cm,\n"; + print FIG " header=1.5cm,footer=0pt,\n"; + print FIG " width=middle,height=middle]\n"; + if ($ForceFullScreen) { + print FIG "\\setupinteraction\n"; + print FIG " [state=start]\n"; + print FIG "\\setupinteractionscreen\n"; + print FIG " [option=max]\n"; + } + if ($BoxType ne '') { + if ($BoxType !~ /box$/io) { + $BoxType .= "box" ; + } + } + print FIG "\\starttext\n"; + print FIG "\\showexternalfigures[alternative=$TypesetFigures,offset=$PaperOffset,size=$BoxType]\n"; + print FIG "\\stoptext\n"; + close(FIG); + $ConTeXtInterface = "en"; + checktexformatpath ; + RunConTeXtFile( $FiguresFile, "tex" ); + unlink('texutil.tuf') if ( -f 'texutil.tuf' ); +} + +sub CleanTeXFileName { + my $str = shift; + $str =~ s/([\$\_\#])/\\$1/go; + $str =~ s/([\~])/\\string$1/go; + return $str; +} + +sub RunListing { + my $FileName = my $CleanFileName = shift; + my @FileNames = glob $FileName; + return unless -f $FileNames[0]; + print " input file : $FileName\n"; + if ( $BackSpace eq "0pt" ) { $BackSpace = "1.5cm" } + else { print " backspace : $BackSpace\n" } + if ( $TopSpace eq "0pt" ) { $TopSpace = "1.5cm" } + else { print " topspace : $TopSpace\n" } + open( LIS, ">$ListingFile.tex" ); + print LIS "% format=english\n"; + print LIS "\\setupbodyfont[11pt,tt]\n"; + print LIS "\\setuplayout\n"; + print LIS " [topspace=$TopSpace,backspace=$BackSpace,\n"; + print LIS " header=0cm,footer=1.5cm,\n"; + print LIS " width=middle,height=middle]\n"; + print LIS "\\setuptyping[lines=yes]\n"; + if ($Pretty) { print LIS "\\setuptyping[option=color]\n" } + print LIS "\\starttext\n"; + + foreach $FileName (@FileNames) { + $CleanFileName = lc CleanTeXFileName($FileName); + print LIS "\\page\n"; + print LIS "\\setupfootertexts[\\tttf $CleanFileName][\\tttf \\pagenumber]\n"; + print LIS "\\typefile\{$FileName\}\n"; + } + print LIS "\\stoptext\n"; + close(LIS); + $ConTeXtInterface = "en"; + checktexformatpath ; + RunConTeXtFile( $ListingFile, "tex" ); +} + +sub RunArrange { + my @files = @_; + print " backspace : $BackSpace\n"; + print " topspace : $TopSpace\n"; + print " paperoffset : $PaperOffset\n"; + if ( $AddEmpty eq '' ) { print " empty pages added : none\n" } + else { print " empty pages added : $AddEmpty\n" } + if ( $TextWidth eq '0pt' ) { print " textwidth : unknown\n" } + else { print " textwidth : $TextWidth\n" } + open( ARR, ">$ArrangeFile.tex" ); + print ARR "% format=english\n"; + print ARR "\\definepapersize\n"; + print ARR " [offset=$PaperOffset]\n"; + print ARR "\\setuplayout\n"; + print ARR " [backspace=$BackSpace,\n"; + print ARR " topspace=$TopSpace,\n"; + + if ($Markings) { + print ARR " marking=on,\n"; + print " cutmarkings : on\n"; + } + print ARR " width=middle,\n"; + print ARR " height=middle,\n"; + print ARR " location=middle,\n"; + print ARR " header=0pt,\n"; + print ARR " footer=0pt]\n"; + if ($NoDuplex) { print " duplex : off\n" } + else { + print " duplex : on\n"; + print ARR "\\setuppagenumbering\n"; + print ARR " [alternative=doublesided]\n"; + } + print ARR "\\starttext\n"; + foreach my $FileName (@files) { + print " pdffile : $FileName\n"; + print ARR "\\insertpages\n [$FileName]"; + if ( $AddEmpty ne '' ) { print ARR "[$AddEmpty]" } + print ARR "[width=$TextWidth]\n"; + } + print ARR "\\stoptext\n"; + close(ARR); + $ConTeXtInterface = "en"; + checktexformatpath ; + RunConTeXtFile( $ModuleFile, "tex" ); +} + +sub RunSelect { + my $FileName = shift; + print " pdffile : $FileName\n"; + print " backspace : $BackSpace\n"; + print " topspace : $TopSpace\n"; + print " paperoffset : $PaperOffset\n"; + if ( $TextWidth eq '0pt' ) { print " textwidth : unknown\n" } + else { print " textwidth : $TextWidth\n" } + open( SEL, ">$SelectFile.tex" ); + print SEL "% format=english\n"; + 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 "\\setuplayout\n"; + print SEL " [backspace=$BackSpace,\n"; + print SEL " topspace=$TopSpace,\n"; + if ($Markings) { + print SEL " marking=on,\n"; + print " cutmarkings : on\n"; + } + print SEL " width=middle,\n"; + print SEL " height=middle,\n"; + print SEL " location=middle,\n"; + print SEL " header=0pt,\n"; + print SEL " footer=0pt]\n"; + print SEL "\\setupexternalfigures\n"; + print SEL " [directory=]\n"; + print SEL "\\starttext\n"; + + if ( $Selection ne '' ) { + print SEL "\\filterpages\n"; + print SEL " [$FileName][$Selection][width=$TextWidth]\n"; + } + print SEL "\\stoptext\n"; + close(SEL); + $ConTeXtInterface = "en"; + checktexformatpath ; + RunConTeXtFile( $SelectFile, "tex" ); +} + +sub RunCopy { + my $DoTrim = shift ; + my @Files = @_ ; + if ( $PageScale == 1000 ) { + print " offset : $PaperOffset\n"; + } else { + print " scale : $PageScale\n"; + if ( $PageScale < 10 ) { $PageScale = int( $PageScale * 1000 ) } + } + open( COP, ">$CopyFile.tex" ); + print COP "% format=english\n"; + print COP "\\starttext\n"; + for my $FileName (@Files) { + print " pdffile : $FileName\n"; + print COP "\\getfiguredimensions\n"; + print COP " [$FileName]\n"; + print COP " [page=1"; + if ($DoTrim) { + print COP ",\n size=trimbox"; + } + print COP "]\n"; + print COP "\\definepapersize\n"; + print COP " [copy]\n"; + print COP " [width=\\naturalfigurewidth,\n"; + print COP " height=\\naturalfigureheight]\n"; + print COP "\\setuppapersize\n"; + print COP " [copy][copy]\n"; + print COP "\\setuplayout\n"; + print COP " [page]\n"; + print COP "\\setupexternalfigures\n"; + print COP " [directory=]\n"; + print COP "\\copypages\n"; + print COP " [$FileName]\n"; + print COP " [scale=$PageScale,\n"; + if ($Markings) { + print COP " marking=on,\n"; + print " cutmarkings : on\n"; + } + if ($DoTrim) { + print COP " size=trimbox,\n"; + print " cropping to : trimbox\n"; + } + print COP " offset=$PaperOffset]\n"; + } + print COP "\\stoptext\n"; + close(COP); + $ConTeXtInterface = "en"; + checktexformatpath ; + RunConTeXtFile( $CopyFile, "tex" ); +} + +sub RunCombine { + my @Files = @_; + $Combination =~ s/x/\*/io; + my ( $nx, $ny ) = split( /\*/, $Combination, 2 ); + return unless ( $nx && $ny ); + print " combination : $Combination\n"; + open( COM, ">$CombineFile.tex" ); + print COM "% format=english\n"; + if ( $PaperFormat ne 'standard' ) { + $_ = $PaperFormat; # NO UPPERCASE ! + s/x/\*/io; + my ( $from, $to ) = split(/\*/); + if ( $to eq "" ) { $to = $from } + print " papersize : $PaperFormat\n"; + print COM "\\setuppapersize[$from][$to]\n"; + } + # + if ( $PaperOffset eq '0pt' ) { $PaperOffset = '1cm' } + print " paper offset : $PaperOffset\n"; + print COM "\\setuplayout\n"; + print COM " [topspace=$PaperOffset,\n"; + print COM " backspace=$PaperOffset,\n"; + print COM " header=0pt,\n"; + print COM " footer=1cm,\n"; + print COM " width=middle,\n"; + print COM " height=middle]\n"; + + if ($NoBanner) { + print COM "\\setuplayout\n"; + print COM " [footer=0cm]\n"; + } + print COM "\\setupexternalfigures\n"; + print COM " [directory=]\n"; + print COM "\\starttext\n"; + for my $FileName (@Files) { + next if ( $FileName =~ /^texexec/io ); + next if (($Result ne '') && ( $FileName =~ /^$Result/i )); + print " pdffile : $FileName\n"; + my $CleanFileName = CleanTeXFileName($FileName); + print COM "\\setupfootertexts\n"; + print COM " [\\tttf $CleanFileName\\quad\\quad\\currentdate\\quad\\quad\\pagenumber]\n"; + print COM "\\combinepages[$FileName][nx=$nx,ny=$ny]\n"; + print COM "\\page\n"; + } + print COM "\\stoptext\n"; + close(COM); + $ConTeXtInterface = "en"; + checktexformatpath ; + RunConTeXtFile( $CombineFile, "tex" ); +} + +sub LocatedFormatPath { # watch out $engine is lowercase in kpse + my $FormatPath = shift; + my $EnginePath = shift; + my $EngineDone = shift; + if ($Local) { + $FormatPath = '.' ; # for patrick + } else { + if ( ( $FormatPath eq '' ) && ( $kpsewhich ne '' ) ) { + unless ($EngineDone) { + my $str = $ENV{"TEXFORMATS"} ; + $str =~ s/\$engine//io ; + $ENV{"TEXFORMATS"} = $str ; + } + # expanded paths + print " assuming engine : $EnginePath\n"; + if (($UseEnginePath)&&($EngineDone)) { + $FormatPath = `$kpsewhich --engine=$EnginePath --show-path=fmt` ; + } else { + $FormatPath = `$kpsewhich --show-path=fmt` ; + } + chomp($FormatPath) ; + if ( ( $FormatPath ne '' ) && $Verbose ) { + print "located formatpath (1) : $FormatPath\n"; + } + # fall back + if ($FormatPath eq '') { + if (($UseEnginePath)&&($EngineDone)) { + if ($dosish) { + $FormatPath = `$kpsewhich --engine=$EnginePath --expand-path=\$TEXFORMATS` ; + } else { + $FormatPath = `$kpsewhich --engine=$EnginePath --expand-path=\\\$TEXFORMATS` ; + } + } + chomp($FormatPath) ; + # either no enginepath or failed run + if ($FormatPath eq '') { + if ($dosish) { + $FormatPath = `$kpsewhich --expand-path=\$TEXFORMATS` ; + } else { + $FormatPath = `$kpsewhich --expand-path=\\\$TEXFORMATS` ; + } + } + chomp $FormatPath ; + } + chomp($FormatPath) ; + if ( ( $FormatPath ne '' ) && $Verbose ) { + print "located formatpath (2) : $FormatPath\n"; + } + $FormatPath =~ s/\\/\//g ; + if ($FormatPath ne '') { + my @fpaths ; + if ($dosish) { + @fpaths = split(';', $FormatPath) ; + } else { + @fpaths = split(':', $FormatPath) ; + } + # take first writable unless current + foreach my $fp (@fpaths) { + # remove funny patterns + $fp =~ s/\/+$// ; + $fp =~ s/^!!// ; + $fp =~ s/unsetengine/$EnginePath/ ; + if (($fp ne '') && ($fp ne '.')) { + # correct if needed + # append engine unless engine is already there + $fp =~ "$fp/$EnginePath" if ($fp =~ /[\\\/]$EnginePath[\\\/]*$/) ; + # path may not yet be present + # check if usable format path + my $fpp = $fp ; + $fpp =~ s/\/*$EnginePath\/*// ; + if ((-d $fpp) && (-w $fpp)) { + $FormatPath = $fpp ; + last ; + } + } + } + } + $FormatPath = '.' if (($FormatPath eq '') || (! -w $FormatPath)) ; + if ( ( $FormatPath ne '' ) && $Verbose ) { + print "located formatpath (3) : $FormatPath\n"; + } + $FormatPath .= '/'; + } + if ($UseEnginePath && $EngineDone && ($FormatPath ne '') && ($FormatPath !~ /$EnginePath\/$/)) { + $FormatPath .= $EnginePath ; + unless (-d $FormatPath) { + mkdir $FormatPath ; + } + $FormatPath .= '/' ; + } + } + print " using formatpath : $FormatPath\n" if $Verbose ; + return $FormatPath; +} + +sub RunOneFormat { + my ($FormatName) = @_; + my @TeXFormatPath; + my $TeXPrefix = ""; + if ( ( $fmtutil ne "" ) && ( $FormatName !~ /metafun|mptopdf/io ) ) { + # could not happen, not supported any more + my $cmd = "$fmtutil --byfmt $FormatName"; + MakeUserFile; # this works only when the path is kept + MakeResponseFile; + $Problems = System("$cmd"); + RemoveResponseFile; + RestoreUserFile; + } else { + $Problems = 1; + } + if ($Problems) { + $Problems = 0; + if ( $TeXExecutable =~ /etex|eetex|pdfetex|pdfeetex|pdfxtex|xpdfetex|eomega|aleph|xetex/io ) { + $TeXPrefix = "*"; + } + my $CurrentPath = cwd(); + my $TheTeXFormatPath = LocatedFormatPath($TeXFormatPath, $TeXExecutable,1); + if ( $TheTeXFormatPath ne '' ) { chdir $TheTeXFormatPath } + MakeUserFile; + MakeResponseFile; + $own_quote = ($TeXProgramPath =~ m/^[^\"].* / ? "\"" : "") ; + my $cmd = + "$own_quote$TeXProgramPath$TeXExecutable$own_quote $TeXVirginFlag " + . "$TeXPassString $PassOn ${TeXPrefix}$FormatName"; + $Problems = System($cmd) ; + RemoveResponseFile; + RestoreUserFile; + + if ( ( $TheTeXFormatPath ne '' ) && ( $CurrentPath ne '' ) ) { + print "\n"; + if ($UseEnginePath) { + print " used engineformatpath : $TheTeXFormatPath\n"; + } else { + print " used formatpath : $TheTeXFormatPath\n"; + } + print "\n"; + chdir $CurrentPath; + } + } +} + +sub RunFormats { + my $ConTeXtFormatsPrefix; + my $MetaFunDone = 0; + if (@ARGV) { @ConTeXtFormats = @ARGV } + elsif ( $UsedInterfaces ne '' ) { + @ConTeXtFormats = split /[\,\s]/, $UsedInterfaces; + } + if ($Format) { @ConTeXtFormats = $Format; $ConTeXtFormatsPrefix = ''; } + else { $ConTeXtFormatsPrefix = "cont-"; } + if ( $TeXHashExecutable ne '' ) { + unless ($FastMode) { + $own_quote = ($TeXProgramPath =~ m/^[^\"].* / ? "\"" : "") ; + my $cmd = "$own_quote$TeXProgramPath$TeXHashExecutable$own_quote"; + print "\n"; + print " TeX hash binary : $TeXProgramPath$TeXHashExecutable\n"; + print " comment : hashing may take a while ...\n"; + System($cmd); + } + } + foreach my $Interface (@ConTeXtFormats) { + if ( $Interface eq $MetaFun ) { + RunMpFormat($MetaFun); + $MetaFunDone = 1; + } elsif ( $Interface eq $MpToPdf ) { + if ( $TeXExecutable =~ /pdf/io ) { RunOneFormat("$MpToPdf") } + } else { + RunOneFormat("$ConTeXtFormatsPrefix$Interface"); + } + } + print "\n"; + print " TeX binary : $TeXProgramPath$TeXExecutable\n"; + print " format(s) : @ConTeXtFormats\n\n"; +} + +sub RunMpFormat { + # engine is not supported by MP + my $MpFormat = shift; + return if ( $MpFormat eq '' ); + my $CurrentPath = cwd(); + my $TheMpFormatPath = LocatedFormatPath($MpFormatPath,$MpExecutable,$MpEngineSupport); + if ( $TheMpFormatPath ne '' ) { chdir $TheMpFormatPath } + $own_quote = ($MpExecutable =~ m/^[^\"].* / ? "\"" : "") ; + my $cmd = + "$own_quote$MpExecutable$own_quote $MpVirginFlag $MpPassString $MpFormat"; + System($cmd ) ; + if ( ( $TheMpFormatPath ne '' ) && ( $CurrentPath ne '' ) ) { + print "\n"; + print " used formatpath : $TheMpFormatPath\n"; + print "\n"; + chdir $CurrentPath; + } +} + + +my $dir = File::Temp::tempdir(CLEANUP=>1) ; +my ($fh, $filename) = File::Temp::tempfile(DIR=>$dir, UNLINK=>1); + +sub RunFiles { + my $currentpath = cwd() ; + my $oldrunpath = $RunPath ; + # new + checktexformatpath ; + # test if current path is writable + if (! -w "$currentpath") { + print " current path readonly : $currentpath\n"; + # + # we cannot use the following because then the result will + # also be removed and users will not know where to look + # + # $RunPath = File::Temp::tempdir(CLEANUP=>1) ; + # if ($RunPath) { + # print " using temp path : $RunPath\n"; + # } else { + # print " problematic temp path : $currentpath\n"; + # exit ; + # } + # + foreach my $d ($ENV{"TMPDIR"},$ENV{"TEMP"},$ENV{"TMP"},"/tmp") { + if ($d && -e $d) { $RunPath = $d ; last ; } + } + if ($TempDir eq '') { + print " provide temp path for : $RunPath\n"; + exit ; + } elsif ($RunPath ne $oldrunpath) { + chdir ($RunPath) ; + unless (-e $TempDir) { + print " creating texexec path : $TempDir\n"; + mkdir ("$TempDir", 077) + } + if (-e $TempDir) { + $RunPath += $TempDir ; + } else { + # we abort this run because on unix an invalid tmp + # path can be an indication of a infected system + print " problematic temp path : $RunPath\n"; + exit ; + } + } else { + print " no writable temp path : $RunPath\n"; + exit ; + } + } + # test if we need to change paths + if (($RunPath ne "") && (-w "$RunPath")) { + print " changing to path : $RunPath\n"; + $InpPath = $currentpath ; + chdir ($RunPath) ; + } + # start working + if ($PdfArrange) { + my @arrangedfiles = (); + foreach my $JobName (@ARGV) { + unless ( $JobName =~ /.*\.pdf$/oi ) { + if ( -f "$JobName.pdf" ) { $JobName .= ".pdf" } + else { $JobName .= ".PDF" } + } + push @arrangedfiles, $JobName; + } + if (@arrangedfiles) { RunArrange(@arrangedfiles) } + } elsif ( ($PdfSelect) || ($PdfCopy) || ($PdfTrim) || ($PdfCombine) ) { + my $JobName = $ARGV[0]; + if ( $JobName ne '' ) { + unless ( $JobName =~ /.*\.pdf$/oi ) { + if ( -f "$JobName.pdf" ) { $JobName .= ".pdf" } + else { $JobName .= ".PDF" } + } + if ($PdfSelect) { + RunSelect($JobName) ; + } elsif ($PdfCopy) { + # RunCopy($JobName) ; + RunCopy(0,@ARGV) ; + } elsif ($PdfTrim) { + # RunCopy($JobName) ; + RunCopy(1,@ARGV) ; + } else { + # RunCombine ($JobName) ; + RunCombine(@ARGV); + } + } + } elsif ($TypesetModule) { + RunModule(@ARGV); + } else { + my $JobSuffix = "tex"; + foreach my $JobName (@ARGV) { + next if ($JobName =~ /^\-/io) ; + # start experiment - full name spec including suffix is prerequisite + if (($StartLine>0) && ($EndLine>=$StartLine) && (-e $JobName)) { + if (open(INP,$JobName) && open(OUT,'>texexec.tex')) { + print " writing partial file : $JobName\n"; + my $Line = 1 ; + my $Preamble = 1 ; + while (my $str = <INP>) { + if ($Preamble) { + if ($str =~ /\\start(text|tekst|product|project|component)/io) { + $Preamble = 0 ; + } else { + print OUT $str; + } + } elsif ($Line==$StartLine) { + print OUT "\\starttext\n" ; # todo: multilingual + print OUT $str ; + } elsif ($Line==$EndLine) { + print OUT $str ; + print OUT "\\stoptext\n" ; # todo: multilingual + last ; + } elsif (($Line>$StartLine) && ($Line<$EndLine)) { + print OUT $str ; + } + $Line += 1 ; + } + close(INP) ; + close(OUT) ; + $JobName = 'texexec.tex' ; + print " using job name : $JobName\n"; + } + } + # end experiment + if ( $JobName =~ s/\.(\w+)$//io ) { $JobSuffix = $1 } + if ( ( $Format eq '' ) || ( $Format =~ /^cont.*/io ) ) { + RunConTeXtFile( $JobName, $JobSuffix ); + } else { + RunSomeTeXFile( $JobName, $JobSuffix ); + } + unless ( -s "$JobName.log" ) { unlink("$JobName.log") } + unless ( -s "$JobName.tui" ) { unlink("$JobName.tui") } + } + } +} + +my $MpTmp = "tmpgraph"; # todo: prefix met jobname +my $MpKep = "$MpTmp.kep"; # sub => MpTmp("kep") +my $MpLog = "$MpTmp.log"; +my $MpBck = "$MpTmp.bck"; +my $MpTex = "$MpTmp.tex"; +my $MpDvi = "$MpTmp.dvi"; + +my %mpbetex; + +sub RunMP { ########### + if ( ($MpExecutable) && ($MpToTeXExecutable) && ($DviToMpExecutable) ) { + foreach my $RawMpName (@ARGV) { + my ( $MpName, $Rest ) = split( /\./, $RawMpName, 2 ); + my $MpFile = "$MpName.mp"; + if ( -e $MpFile + and ( -s $MpFile > 25 ) ) # texunlink makes empty file + { + unlink "$MpName.mpt"; + doRunMP( $MpName, 0 ); + # test for graphics, new per 14/12/2000 + my $mpgraphics = checkMPgraphics($MpName); + # test for labels + my $mplabels = checkMPlabels($MpName); + if ( $mpgraphics || $mplabels ) { + doRunMP( $MpName, $mplabels ); + } + } + } + } +} + +my $mpochecksum = ''; + +#~ sub checkMPgraphics { # also see makempy + #~ my $MpName = shift; + #~ if ( $MakeMpy ne '' ) { $MpName .= " --$MakeMpy " } # extra switches + #~ if ($MpyForce) { $MpName .= " --force " } # dirty + #~ else { + #~ return 0 unless -s "$MpName.mpo" > 32; + #~ return 0 unless ( open( MPO, "$MpName.mpo" ) ); + #~ $mpochecksum = do { local $/; unpack( "%32C*", <MPO> ) % 65535 }; + #~ close(MPO); + #~ if ( open( MPY, "$MpName.mpy" ) ) { + #~ my $str = <MPY>; + #~ chomp $str; + #~ close(MPY); + #~ if ( $str =~ /^\%\s*mpochecksum\s*\:\s*(\d+)/o ) { + #~ return 0 if ( ( $mpochecksum eq $1 ) && ( $mpochecksum ne 0 ) ); + #~ } + #~ } + #~ } + #~ RunPerlScript( "makempy", "$MpName" ); + #~ print " second MP run needed : text graphics found\n"; + #~ return 1; +#~ } + +sub checkMPgraphics { # also see makempy + my $MpName = shift; + if ( $MakeMpy ne '' ) { $MpName .= " --$MakeMpy " } # extra switches + if ($MpyForce) { $MpName .= " --force " } # dirty + else { + return 0 unless -s "$MpName.mpo" > 32; + return 0 unless ( open( MPO, "$MpName.mpo" ) ); + $mpochecksum = do { local $/; Digest::MD5::md5_hex(<MPO>) ; }; + close(MPO); + if ( open( MPY, "$MpName.mpy" ) ) { + my $str = <MPY>; + chomp $str; + close(MPY); + if ( $str =~ /^\%\s*mpochecksum\s*\:\s*([a-fA-F0-9]+)/o ) { + return 0 if ( ( $mpochecksum eq $1 ) && ( $mpochecksum ne '' ) ); + } + } + } + RunPerlScript( "makempy", "$MpName" ); + print " second MP run needed : text graphics found\n"; + return 1; +} + +sub checkMPlabels { + my $MpName = shift; + return 0 unless ((-f "$MpName.mpt") && ((-s "$MpName.mpt")>10) ); + return 0 unless open( MP, "$MpName.mpt" ); + my $n = 0; + my $t = "" ; + while (<MP>) { + if (/% setup : (.*)/o) { + $t = $1 ; + } else { + $t = "" ; + } + if (/% figure (\d+) : (.*)/o) { + if ($t ne "") { + $mpbetex{$1} .= "$t\n" ; + $t = "" ; + } + $mpbetex{$1} .= "$2\n"; + ++$n ; + } + } + close(MP); + print " second MP run needed : $n tex labels found\n" if $n; + return $n; +} + +sub doMergeMP { + # make sure that the verbatimtex ends up before btex etc + my ($n,$str) = @_ ; + if ($str =~ /(.*?)(verbatimtex.*?etex)\s*\;(.*)/mois) { + return "beginfig($n)\;\n$1$2\;\n$mpbetex{$n}\n$3\;endfig\;\n" ; + } else { + return "beginfig($n)\;\n$mpbetex{$n}\n$str\;endfig\;\n" ; + } +} + +sub doRunMP { ########### + my ( $MpName, $MergeBE ) = @_; + my $TexFound = 0; + my $MpFile = "$MpName.mp"; + if ( open( MP, $MpFile ) ) { # fails with % + my $MPdata = ""; + while (<MP>) { + unless (/^\%/) { $MPdata .= $_ } + } + $_ = $MPdata; + close(MP); + + # save old file + unlink($MpKep); + return if ( -e $MpKep ); + rename( $MpFile, $MpKep ); + # check for tex stuff + + $TexFound = $MergeBE || /btex .*? etex/o; + + # shorten lines into new file if okay + unless ( -e $MpFile ) { + open( MP, ">$MpFile" ); + s/(btex.*?)\;(.*?etex)/$1\@\@\@$2/gmois; + s/(\".*?)\;(.*?\")/$1\@\@\@$2/gmois; # added + s/\;/\;\n/gmois; + s/\n\n/\n/gmois; + s/(btex.*?)\@\@\@(.*?etex)/$1\;$2/gmois; + s/(\".*?)\@\@\@(.*?\")/$1\;$2/gmois; # added + # merge labels + if ($MergeBE) { + # i hate this indirect (sub regexp) mess + s/beginfig\s*\((\d+)\)\s*\;(.*?)endfig\s*\;/doMergeMP($1,$2)/gems ; + } + unless (/beginfig\s*\(\s*0\s*\)/gmois) { + if (defined($mpbetex{0})) { # test added, warning + print MP $mpbetex{0} ; + } + } + print MP $_; + print MP "\n" . "end" . "\n"; + close(MP); + } + if ($TexFound) { + print " metapost to tex : $MpName\n"; + $own_quote = ($MpToTeXExecutable =~ m/^[^\"].* / ? "\"" : "") ; + $Problems = + System("$own_quote$MpToTeXExecutable$own_quote $MpFile > $MpTex"); + if ( -e $MpTex && !$Problems ) { + open( TMP, ">>$MpTex" ); + print TMP "\\end\{document\}\n"; # to be sure + close(TMP); + if ( ( $Format eq '' ) || ( $Format =~ /^cont.*/io ) ) { + $OutputFormat = "dvips"; + RunConTeXtFile( $MpTmp, "tex" ); + } else { + RunSomeTeXFile( $MpTmp, "tex" ); + } + if ( -e $MpDvi && !$Problems ) { + print " dvi to metapost : $MpName\n"; + $own_quote = ($DviToMpExecutable =~ m/^[^\"].* / ? "\"" : "") ; + $Problems = System("$own_quote$DviToMpExecutable$own_quote $MpDvi $MpName.mpx"); + } + unlink $MpBck; + rename $MpTex, $MpBck; + unlink $MpDvi; + } + } + print " metapost : $MpName\n"; + $own_quote = ($MpExecutable =~ m/^[^\"].* / ? "\"" : "") ; + my $cmd = "$own_quote$MpExecutable$own_quote"; + if ($EnterBatchMode) { $cmd .= " $MpBatchFlag " } + if ($EnterNonStopMode) { $cmd .= " $MpNonStopFlag " } + if ( ( $MpFormat ne '' ) && ( $MpFormat !~ /(plain|mpost)/oi ) ) { + print " format : $MpFormat\n"; + $cmd .= " $MpPassString $MpFormatFlag$MpFormat "; + } + # prevent nameclash, experimental + my $MpMpName = "$MpName"; + $Problems = System("$cmd $MpMpName"); + open( MPL, "$MpName.log" ); + while (<MPL>) # can be one big line unix under win + { + while (/^l\.(\d+)\s/gmois) { + print " error in metapost run : $MpName.mp:$1\n"; + } + } + close(MPL) ; + unlink "mptrace.tmp"; + rename( $MpFile, "mptrace.tmp" ); + if ( -e $MpKep ) { + unlink($MpFile); + rename( $MpKep, $MpFile ); + } + } +} + +sub RunMPX { + my $MpName = shift; + $MpName =~ s/\..*$//o; + my $MpFile = $MpName . ".mp"; + if ( ($MpToTeXExecutable) + && ($DviToMpExecutable) + && ( -e $MpFile ) + && ( -s $MpFile > 5 ) + && open( MP, $MpFile ) ) + { + local $/ = "\0777"; + $_ = <MP>; + close(MP); + if (/(btex|etex|verbatimtex)/mos) { + print " generating mpx file : $MpName\n"; + $own_quote = ($MpToTeXExecutable =~ m/^[^\"].* / ? "\"" : "") ; + $Problems = + System("$own_quote$MpToTeXExecutable$own_quote $MpFile > $MpTex"); + if ( -e $MpTex && !$Problems ) { + open( TMP, ">>$MpTex" ); + print TMP "\\end\n"; # to be sure + close(TMP); + checktexformatpath ; + if ( ( $Format eq '' ) || ( $Format =~ /^cont.*/io ) ) { + RunConTeXtFile( $MpTmp, "tex" ); + } else { + RunSomeTeXFile( $MpTmp, "tex" ); + } + if ( -e $MpDvi && !$Problems ) { + $own_quote = ($DviToMpExecutable =~ m/^[^\"].* / ? "\"" : "") ; + $Problems = + System("$own_quote$DviToMpExecutable$own_quote $MpDvi $MpName.mpx"); + } + unlink $MpTex; + unlink $MpDvi; + } + } + } +} + +sub load_set_file { + my %new; + my %old; + my ( $file, $trace ) = @_; + if ( open( BAT, $file ) ) { + while (<BAT>) { + chomp; + if (/\s*SET\s+(.+?)\=(.+)\s*/io) { + my ( $var, $val ) = ( $1, $2 ); + $val =~ s/\%(.+?)\%/$ENV{$1}/goi; + unless ( defined( $old{$var} ) ) { + if ( defined( $ENV{$var} ) ) { $old{$var} = $ENV{$var} } + else { $old{$var} = "" } + } + $ENV{$var} = $new{$var} = $val; + } + } + close(BAT); + } + if ($trace) { + foreach my $key ( sort keys %new ) { + if ( $old{$key} ne $new{$key} ) { + print " changing env variable : '$key' from '$old{$key}' to '$new{$key}'\n"; + } elsif ( $old{$key} eq "" ) { + print " setting env variable : '$key' to '$new{$key}'\n"; + } else { + print " keeping env variable : '$key' at '$new{$key}'\n"; + } + } + print "\n"; + } +} + +if ( $SetFile ne "" ) { load_set_file( $SetFile, $Verbose ) } + +sub check_texmf_root { } +sub check_texmf_tree { } + +sub AnalyzeVersion + { my $str = join("\n", @_) ; + my ($texengine,$type) = ('unknown', 'unknown'); + open (LOG, "<texvers.log") ; + while (<LOG>) + { /^\s*This is (.*(pdf)?(|e|x)TeX.*?)$/o and $texengine = $1 ; + /^\s*ConTeXt (.*int: ([a-z]+).*?)\s*$/o and $type = $1; } + $type =~ s/ int: ([a-z]+)//; + $texengine =~ s/ Version//; + $texengine =~ s/ \(format.*$//; + close (LOG); + return ($texengine,$type) } + +sub show_version_info { + my ($texengine,$type); + open (TEX,">texvers.tex") ; + print TEX "\\bye " ; + close (TEX) ; + my $texutil = `$TeXUtil --help`; + $texutil =~ s/.*(TeXUtil[^\n]+)\n.*?$/$1/s; + print " texexec :$Program\n" ; + print " texutil : $texutil" ; + my $contexttext = `$kpsewhich context.tex`; + my $contextversion = "<not found>"; + if ($contexttext) { + chop $contexttext; + { local $/; + open (IN,"<$contexttext"); + $contextversion = <IN>; + close IN; + } + $contextversion =~ s/.*contextversion\{([0-9.]+)\}.*/$1/s; + } + $EnterBatchMode = 1; + $Format = 'cont-en'; + my $cmd = PrepRunTeX("texvers","tex",'') ; + ($texengine,$type) = AnalyzeVersion(Pipe($cmd)) ; + print " tex : $texengine\n" ; + print " context : ver: $contextversion\n" ; + print " cont-en : $type\n" ; + foreach my $a (qw(cz de it nl ro uk xx)) { + my $test = Pipe("$kpsewhich -format='fmt' cont-$a") ; + if (defined $test && $test) { + $Format = 'cont-' . $a; + $cmd = PrepRunTeX("texvers","tex",''); + ($texengine,$type) = AnalyzeVersion(Pipe($cmd)) ; + print " cont-$a : $type\n" ; + } + } + unlink <texvers.*>; +} + +# the main thing + +if ($HelpAsked) { + show_help_info +} elsif ($Version) { + show_version_info +} elsif ($TypesetListing) { + check_texmf_root; + check_texmf_tree; + RunListing(@ARGV); +} elsif ($TypesetFigures) { + check_texmf_root; + check_texmf_tree; + RunFigures(@ARGV); +} elsif ($DoMPTeX) { + check_texmf_root; + check_texmf_tree; + RunMP; +} elsif ($DoMPXTeX) { + check_texmf_root; + check_texmf_tree; + RunMPX( $ARGV[0] ); +} elsif ($MakeFormats) { + check_texmf_root; + check_texmf_tree; + if ( $MpDoFormat ne '' ) { + RunMpFormat($MpDoFormat) ; + } + else { + RunFormats ; + } +} elsif (@ARGV) { + check_texmf_root; + check_texmf_tree; + @ARGV = <@ARGV>; + RunFiles; +} elsif ( !$HelpAsked ) { + show_help_options; +} + +$TotalTime = time - $TotalTime; + +unless ($HelpAsked) { print "\n total run time : $TotalTime seconds\n" } + +if ($Problems) { exit 1 } + +__DATA__ +arrange process and arrange +----------- +batch run in batch mode (don't pause) +----------- +nonstop run in non stop mode (don't pause) +----------- +centerpage center the page on the paper +----------- +color enable color (when not yet enabled) +----------- +usemodule load some modules first +=name list of modules +----------- +xmlfilter apply XML filter +=name list of filters +----------- +environment load some environments first +=name list of environments +----------- +fast skip as much as possible +----------- +figures typeset figure directory +=a room for corrections +=b just graphics +=c one (cropped) per page +paperoffset room left at paper border +fullscreen force full screen mode (pdf) +----------- +screensaver turn graphic file into a (pdf) full screen file +----------- +final add a final run without skipping +----------- +format fmt file +=name format file (memory dump) +----------- +mpformat mem file +=name format file (memory dump) +----------- +interface user interface +=en English +=nl Dutch +=de German +=cz Czech +=uk Brittish +=it Italian +----------- +language main hyphenation language +=xx standard abbreviation +----------- +listing produce a verbatim listing +backspace inner margin of the page +topspace top/bottom margin of the page +pretty enable pretty printing +color use color for pretty printing +----------- +make build format files +language patterns to include +bodyfont bodyfont to preload +response response interface language +format TeX format +mpformat MetaPost format +program TeX program +----------- +mode running mode +=list modes to set +----------- +module typeset tex/pl/mp module +----------- +mptex run an MetaPost plus btex-etex cycle +----------- +mpxtex generatet an MetaPostmpx file +----------- +noarrange process but ignore arrange +----------- +nomp don't run MetaPost at all +----------- +nomprun don't run MetaPost at runtime +----------- +automprun MetaPost at runtime when needed +----------- +once run TeX only once (no TeXUtil either) +----------- +output specials to use +=pdftex Han The Than's pdf backend +=dvips Thomas Rokicky's dvi to ps converter +=dvipsone YandY's dvi to ps converter +=dviwindo YandY's windows previewer +=dvipdfm Mark Wicks' dvi to pdf converter +=dvipdfmx Jin-Hwan Cho's extended dvipdfm +----------- +passon switches to pass to TeX (--src for MikTeX) +----------- +pages pages to output +=odd odd pages +=even even pages +=x,y:z pages x and y to z +----------- +paper paper input and output format +=a4a3 A4 printed on A3 +=a5a4 A5 printed on A4 +----------- +path document source path +=string path +----------- +pdf produce PDF directly using pdf(e)tex +----------- +pdfarrange arrange pdf pages +paperoffset room left at paper border +paper paper format +noduplex single sided +backspace inner margin of the page +topspace top/bottom margin of the page +markings add cutmarks +background +=string background graphic +addempty add empty page after +textwidth width of the original (one sided) text +----------- +pdfcombine combine pages to one page +paperformat paper format +combination n*m pages per page +paperoffset room left at paper border +nobanner no footerline +----------- +pdfcopy scale pages down/up +scale new page scale +paperoffset room left at paper border +markings add cutmarks +background +=string background graphic +----------- +pdfselect select pdf pages +selection pages to select +=odd odd pages +=even even pages +=x,y:z pages x and y to z +paperoffset room left at paper border +paperformat paper format +backspace inner margin of the page +topspace top/bottom margin of the page +markings add cutmarks +background +=string background graphic +addempty add empty page after +textwidth width of the original (one sided) text +----------- +print page imposition scheme +=up 2 pages per sheet doublesided +=down 2 rotated pages per sheet doublesided +----------- +result resulting file +=name filename +----------- +input input file (if used) +=name filename +----------- +suffix resulting file suffix +=string suffix +----------- +runs maximum number of TeX runs +=n number of runs +----------- +silent minimize (status) messages +----------- +tex TeX binary +=name binary of executable +----------- +textree additional texmf tree to be used +=path subpath of tex root +----------- +texroot root of tex trees +=path tex root +----------- +verbose shows some additional info +----------- +help show this or more, e.g. '--help interface' +----------- +alone bypass utilities (e.g. fmtutil for non-standard fmt's) +----------- +texutil force TeXUtil run +----------- +version display various version information +----------- +setfile load environment (batch) file diff --git a/Master/texmf-dist/scripts/context/perl/texexec.rme b/Master/texmf-dist/scripts/context/perl/texexec.rme new file mode 100644 index 00000000000..f38b4be7fe0 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/texexec.rme @@ -0,0 +1,169 @@ +% == introduction == +% +% This is 'texexec.ini', the file used by texexec to determine where +% to find files, what TeX to use, what flags to pass, etc. Although +% TeXexec tries to locate things itself, a little help is sometimes +% needed. One can influence texexec by setting some variables. These +% are only needed when the automatic determined settings fail. +% +% == interfacing == +% +% UsedInterfaces nl,en the formats generated with --make +% UserInterface nl the default format used +% +% == binaries == +% +% TeXExecutable pdfetex the TeX binary to use +% MpExecutable mpost the MetaPost binary to use +% MpToTeXExecutable mpto the MetaPost to TeX converter +% DviToMpExecutable dvitomp the DVI to MetaPost converter +% +% == Scripts == +% +% DviSpecialScript dvispec the DVI special filter script +% +% == flags == +% +% TeXFormatFlag -fmt= the format introducer +% TeXVirginFlag -ini the format generation switch +% +% == paths == +% +% TeXFormatPath texmf/... fmt files +% ConTeXtPath texmf/tex/context/base sources +% SetupPath texmf/tex/base/user cont-sys/usr file +% TeXScriptsPath texmf/context/perltk scripts +% TeXFontsPath texmf font files +% +% MpFormatPath TeXFormatPath mem files +% +% == the main shell setting == +% +% As shown below, one can define his/her own sections. We default to +% the teTeX/fpTeX web2c based settings. + +set TeXShell to tetex +%set TeXShell to fptex +%set TeXShell to miktex +%set TeXShell to private + +% == setting up the variables == +% +% Here are some general defaults. They can be overruled later. + +set UseEnginePath to true + +set UsedInterfaces to en nl metafun mptopdf +set UserInterface to en + +set TeXExecutable to tex + +set MpExecutable to mpost +set MpToTeXExecutable to mpto +set DviToMpExecutable to dvitomp +set DviSpecialScript to dvispec + +set MpFormat to metafun + +set TeXFormatFlag to & +set MpFormatFlag to & + +% For teTeX the next settings will do. + +% -default-translate-file=cp8bit -file-line-error-style + +for tetex set TeXHashExecutable to mktexlsr +for tetex set TeXExecutable to pdfetex +for tetex set TeXVirginFlag to -ini +for tetex set TeXPassString to -progname=context +for tetex set TeXBatchFlag to -int=batchmode +for tetex set TeXNonStopFlag to -int=nonstopmode +for tetex set MpToTeXExecutable to mpto +for tetex set MpVirginFlag to -ini +for tetex set MpPassString to -progname=mpost +for tetex set MpBatchFlag to -int=batchmode +for tetex set MpNonStopFlag to -int=nonstopmode + +% These also apply to fpTeX. + +% -default-translate-file=cp8bit -file-line-error-style + +for fptex set TeXHashExecutable to mktexlsr +for fptex set TeXExecutable to pdfetex +for fptex set TeXVirginFlag to -ini +for fptex set TeXPassString to -progname=context +for fptex set TeXBatchFlag to -int=batchmode +for fptex set TeXNonStopFlag to -int=nonstopmode +for fptex set MpToTeXExecutable to mpto +for fptex set MpVirginFlag to -ini +for fptex set MpPassString to -progname=mpost +for fptex set MpBatchFlag to -int=batchmode +for fptex set MpNonStopFlag to -int=nonstopmode + +% a downward compatibility test (<7.5.3), next year we will use -8bit + +for tetex set TeXVirginFlag to -ini -translate-file=natural.tcx +for fptex set TeXVirginFlag to -ini -translate-file=natural.tcx +for tetex set MpVirginFlag to -ini -translate-file=natural.tcx +for fptex set MpVirginFlag to -ini -translate-file=natural.tcx + +for tetex set TeXPassString to -progname=context -translate-file=natural.tcx +for fptex set TeXPassString to -progname=context -translate-file=natural.tcx +for tetex set MpPassString to -progname=mpost -translate-file=natural.tcx +for fptex set MpPassString to -progname=mpost -translate-file=natural.tcx + +% MikTeX users probably have to set up some paths too. + +for miktex set TeXHashExecutable to initexmf --update-fndb +for miktex set TeXExecutable to pdfetex +for miktex set TeXVirginFlag to --initialize +for miktex set TeXPassString to --alias=context --translate-file=natural.tcx +for miktex set TeXBatchFlag to --interaction=batchmode +for miktex set TeXNonStopFlag to --interaction=nonstopmode +for miktex set MpToTeXExecutable to mptotex +for miktex set MpVirginFlag to --initialize +for miktex set MpPassString to --alias=mpost --translate-file=natural.tcx +for miktex set MpBatchFlag to --interaction=batchmode +for miktex set MpNonStopFlag to --interaction=nonstopmode + +for miktex set TeXFormatFlag to --undump= +for miktex set MpFormatFlag to --undump= + +% These are the settings used on some machines at PRAGMA ADE that +% don't use the texmf tree. They can serve as an example for local +% settings. Local settings should either be added to the previous +% one, or go without the 'for' directives. Consider these +% settings as an example. + +for private set UsedInterfaces to en nl +for private set UserInterface to nl +for private set ConTeXtPath to t:/sources/ +for private set SetupPath to t:/perl/ +for private set TeXScriptsPath to t:/perl/ + +% == read this too == +% +% If this file is called 'texexec.rme', copy it to 'texexec.ini', +% check the settings above, change them according to your TeX +% distribution, and say: +% +% texexec --verbose +% +% When set up properly, you should see your local settings fly by. +% When these settings are ok, the next call should work: +% +% texexec --make +% +% and you should be able to process a file by saying +% +% texexec filename +% +% See 'mtexexec.pdf' for more information on the flags you can use with +% 'texexec'. Also make sure you have the 'texutil' script installed in +% the same path as 'texexec'. + +% Experimental +% +% set TcXPath to d:/tex/texmf/web2c +% set FmtLanguage to pl +% set FmtBodyFont to plr diff --git a/Master/texmf-dist/scripts/context/perl/texfind.pl b/Master/texmf-dist/scripts/context/perl/texfind.pl new file mode 100644 index 00000000000..53a560c7954 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/texfind.pl @@ -0,0 +1,270 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +#D \module +#D [ file=texfind.pl, +#D version=1998.05.10, +#D title=\TEXFIND, +#D subtitle=searching files, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +# test with "doif(un|)defined" + +use strict ; +use Getopt::Long ; +use File::Find ; +use Cwd ; +use Tk ; +use Tk::widgets ; +use Tk::ROText ; + +use FindBin ; +use lib $FindBin::Bin ; +use path_tre ; + +my $FileSuffix = 'tex' ; +my $SearchString = '' ; +my $Recurse = 0 ; +my $NumberOfHits = 0 ; +my $QuitSearch = 0 ; +my $Location = '' ; +my $currentpath = '.' ; + +my @FileList ; + +my ($dw, $mw, $log, $sea, $fil, $num, $but, $dir, $loc) ; + +$mw = MainWindow -> new () ; +$dw = MainWindow -> new () ; + +$mw -> protocol( 'WM_DELETE_WINDOW' => sub { exit } ) ; +$dw -> protocol( 'WM_DELETE_WINDOW' => sub { exit } ) ; + +$log = $mw -> Scrolled ( 'ROText' , + -scrollbars => 'se' , + -font => 'courier' , + -wrap => 'none' , + -width => 65 , + -height => 22 ) + -> pack ( -side => 'bottom' , + -padx => 2 , + -pady => 2 , + -expand => 1 , + -fill => 'both' ) ; + +$sea = $mw -> Entry ( -textvariable => \$SearchString , + -font => 'courier' , + -width => 20 ) + -> pack ( -side => 'left' , + -padx => 2 , + -pady => 2 ) ; + +$fil = $mw -> Entry ( -textvariable => \$FileSuffix , + -font => 'courier' , + -width => 5 ) + -> pack ( -side => 'left' , + -padx => 2 , + -pady => 2 ) ; + +$but = $mw -> Checkbutton ( -variable => \$Recurse , + -text => 'recurse' ) + -> pack ( -side => 'left' ) ; + +$num = $mw -> Entry ( -textvariable => \$NumberOfHits , + -font => 'courier' , + -justify => 'right' , + -width => 5 ) + -> pack ( -side => 'right' , + -padx => 2 , + -pady => 2 ) ; + +$loc = $mw -> Entry ( -textvariable => \$Location , + -font => 'courier' , + -width => 8 ) + -> pack ( -side => 'right' , + -padx => 2 , + -pady => 2 ) ; + +sub BuildDir + { if (Exists($dir)) { $dir -> destroy } ; + $dir = $dw -> Scrolled ( 'PathTree' , + -scrollbars => 'se' ) + -> pack ( -expand => 1 , + -fill => 'both' , + -padx => 2 , + -pady => 2 ) ; + $dir -> configure ( -font => 'courier' , + -height => 24 , + -width => 65 , + -selectbackground => 'blue3' , + -browsecmd => \&ChangePath ) ; + $dir -> bind ('<Return>' , \&ShowFile ) ; + $dir -> bind ('<Double-1>' , \&ShowFile ) } + +BuildDir ; + +sub ShowFile { $mw -> raise ; $sea -> focusForce } +sub ShowPath { $dw -> raise ; $dir -> focusForce } + +$log -> tagConfigure ( 'found', -foreground => 'green3' ) ; +$log -> tagConfigure ( 'title', -foreground => 'blue3' ) ; + +$sea -> bind ('<Return>' , \&LocateStrings ) ; +$fil -> bind ('<Return>' , \&LocateStrings ) ; +$loc -> bind ('<Return>' , \&ChangeLocation ) ; +$log -> bind ('<Return>' , \&ShowPath ) ; + +$sea -> bind ('<KeyPress>' , \&QuitSearch ) ; +$fil -> bind ('<KeyPress>' , \&QuitSearch ) ; +$loc -> bind ('<KeyPress>' , \&QuitSearch ) ; + +$sea -> bind ('<Escape>' , \&QuitSearch ) ; +$fil -> bind ('<Escape>' , \&QuitSearch ) ; +$loc -> bind ('<Escape>' , \&QuitSearch ) ; +$log -> bind ('<Escape>' , \&QuitSearch ) ; + +$sea -> bind ('<Double-1>' , \&LocateStrings ) ; +$fil -> bind ('<Double-1>' , \&LocateStrings ) ; +$loc -> bind ('<Double-1>' , \&ChangeLocation ) ; +$log -> bind ('<Double-1>' , \&ShowPath ) ; + +sub ChangePath + { my $currentpath = shift ; +chdir($currentpath) ; + $QuitSearch = 1 ; + $log -> delete ('1.0', 'end') ; + $log -> insert ('end', "$currentpath\n\n", 'title') } + +sub ChangeLocation + { $QuitSearch = 1 ; + $log -> delete ('1.0', 'end') ; + $Location =~ s/^\s*//o ; + $Location =~ s/\s*$//o ; + $Location =~ s/(\\|\/\/)/\//go ; + unless (-d $Location) + { unless ($Location =~ /\//) { $Location .= '/' } } + if (-d $Location) + { $log -> insert ('end', "changed to location '$Location'\n\n", 'title') ; + $currentpath = $Location ; + chdir ($currentpath) ; + $dir -> destroy ; + BuildDir ; + $dw -> raise ; + $dw -> focusForce } + else + { $log -> insert ('end', "unknown location '$Location'\n\n", 'title') ; + $Location = '' } } + +sub QuitSearch + { $QuitSearch = 1 } + +sub SearchFile + { my ($FileName, $SearchString) = @_ ; + my $Ok = 0 ; my $len ; + open (TEX, $FileName) ; + my $LineNumber = 0 ; + while (<TEX>) + { ++$LineNumber ; + if ($QuitSearch) + { if ($Ok) { $log -> see ('end') } + last } + if (/$SearchString/i) + { ++$NumberOfHits ; $num -> update ; + unless ($Ok) + { $Ok = 1 ; + $log -> insert ('end', "$FileName\n\n",'title') } + $log -> insert ('end', sprintf("%5i : ",$LineNumber), 'title') ; + s/^\s*//o ; +# + $len = 0 ; + while (/(.*?)($SearchString)/gi) + { $len += length($1) + length($2) ; + $log -> insert ('end', "$1") ; + $log -> insert ('end', "$2", 'found' ) } + $_ = substr($_,$len) ; + $log -> insert ('end', "$_") ; +# + $log -> update ; + $log -> see ('end') } } + if ($Ok) { $log -> insert ('end', "\n") } + close (TEX) } + +sub DoLocateFiles + { @FileList = () ; + $NumberOfHits = 0 ; + if ($FileSuffix ne "") + { $log -> delete ('1.0', 'end') ; + if ($Recurse) + { $log -> insert ('end', "recursively identifying files\n", 'title') ; + $log -> see ('end') ; + find (\&wanted, $currentpath) ; + sub wanted + { if ($QuitSearch) { last ; return } + if (/.*\.$FileSuffix/i) + { ++$NumberOfHits ; $num -> update ; + push @FileList, $File::Find::name } } } + else + { $log -> insert ('end', "identifying files\n", 'title') ; + $log -> see ('end') ; + opendir(DIR, $currentpath) ; my @TEMPLIST = readdir(DIR) ; closedir(DIR) ; + foreach my $FileName (@TEMPLIST) + { if ($FileName =~ /.*\.$FileSuffix/i) + { ++$NumberOfHits ; $num -> update ; + if ($QuitSearch) + { last } + push @FileList, $FileName } } } + @FileList = sort @FileList } } + +sub DoLocateStrings + { $log -> delete ('1.0', 'end') ; + $log -> update ; + $log -> see ('end') ; + $NumberOfHits = 0 ; + if ($SearchString ne "") + { foreach my $FileName (@FileList) + { if ($QuitSearch) + { $log -> insert ('end', "search aborted\n", 'title') ; + $log -> see ('end') ; + last } + SearchFile($FileName,$SearchString) } } + unless ($QuitSearch) + { $log -> insert ('end', "done\n", 'title') ; + $log -> see ('end') } } + +sub LocateStrings + { $QuitSearch = 0 ; + DoLocateFiles() ; + DoLocateStrings() } + +$log -> insert ('end', + + "data fields\n\n" , '' , + + + "string :", 'title', " regular expression to search for\n" , '' , + "suffix :", 'title', " type of file to search in\n" , '' , + "recurse :", 'title', " enable searching subpaths\n" , '' , + "location :", 'title', " drive of root path\n" , '' , + "counter :", 'title', " file/hit counter\n\n" , '' , + + "key bindings\n\n" , '' , + + "double 1 :", 'title', " directory window <-> search window\n" , '' , + "enter :", 'title', " start searching\n" , '' , + "escape :", 'title', " quit searching\n\n" , '' , + + "current path\n\n" , '' , + + cwd(), 'title', "\n\n" , 'title' ) ; + +$log -> update ; + +ShowPath ; + +MainLoop() ; diff --git a/Master/texmf-dist/scripts/context/perl/texfont.pl b/Master/texmf-dist/scripts/context/perl/texfont.pl new file mode 100644 index 00000000000..425417374de --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/texfont.pl @@ -0,0 +1,1154 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +# This is an example of a crappy unstructured file but once +# I know what should happen exactly, I will clean it up. + +# once it works all right, afmpl will be default + +# todo : ttf (partially doen already) + +# added: $pattern in order to avoid fuzzy shelle expansion of +# filenames (not consistent over perl and shells); i hate that +# kind of out of control features. + +#D \module +#D [ file=texfont.pl, +#D version=2004.02.06, % 2000.12.14 +#D title=Font Handling, +#D subtitle=installing and generating, +#D author=Hans Hagen ++, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +#D For usage information, see \type {mfonts.pdf}. + +#D Todo : copy afm/pfb from main to local files to ensure metrics +#D Todo : Wybo's help system +#D Todo : list of encodings [texnansi, ec, textext] + +#D Thanks to George N. White III for solving a couple of bugs. +#D Thanks to Adam T. Lindsay for adding Open Type support (and more). + +use strict ; + +my $savedoptions = join (" ",@ARGV) ; + +use Config ; +use FindBin ; +use File::Copy ; +use Getopt::Long ; + +$Getopt::Long::passthrough = 1 ; # no error message +$Getopt::Long::autoabbrev = 1 ; # partial switch accepted + +# Unless a user has specified an installation path, we take +# the dedicated font path or the local path. + +## $dosish = ($Config{'osname'} =~ /dos|mswin/i) ; +my $dosish = ($Config{'osname'} =~ /^(ms)?dos|^os\/2|^(ms|cyg)win/i) ; + +my $IsWin32 = ($^O =~ /MSWin32/i); +my $SpacyPath = 0 ; + +# great, the win32api is not present in all perls + +BEGIN { + $IsWin32 = ($^O =~ /MSWin32/i) ; + $SpacyPath = 0 ; + if ($IsWin32) { + my $str = `kpsewhich -expand-path=\$TEXMF` ; + $SpacyPath = ($str =~ / /) ; + if ($SpacyPath) { + require Win32::API; import Win32::API; + } + } +} + +# great, glob changed to bsd glob in an incompatible way ... sigh, we now +# have to catch a failed glob returning the pattern +# +# to stupid either: +# +# sub validglob { +# my @globbed = glob(shift) ; +# if ((@globbed) && (! -e $globbed[0])) { +# return () ; +# } else { +# return @globbed ; +# } +# } +# +# so now we have: + +sub validglob { + my @globbed = glob(shift) ; + my @globout = () ; + foreach my $file (@globbed) { + push (@globout,$file) if (-e $file) ; + } + return @globout ; +} + +sub GetShortPathName { + my ($filename) = @_ ; + return $filename unless (($IsWin32)&&($SpacyPath)) ; + my $GetShortPathName = new Win32::API('kernel32', 'GetShortPathName', 'PPN', 'N') ; + if(not defined $GetShortPathName) { + die "Can't import API GetShortPathName: $!\n" ; + } + my $buffer = " " x 260; + my $len = $GetShortPathName->Call($filename, $buffer, 260) ; + return substr($buffer, 0, $len) ; +} + +my $installpath = "" ; + +if (defined($ENV{TEXMFLOCAL})) { + $installpath = "TEXMFLOCAL" ; +} + +if (defined($ENV{TEXMFFONTS})) { + $installpath = "TEXMFFONTS" ; +} + +if ($installpath eq "") { + $installpath = "TEXMFLOCAL" ; # redundant +} + +my $encoding = "texnansi" ; +my $vendor = "" ; +my $collection = "" ; +my $fontroot = "" ; #/usr/people/gwhite/texmf-fonts" ; +my $help = 0 ; +my $makepath = 0 ; +my $show = 0 ; +my $install = 0 ; +my $sourcepath = "." ; +my $passon = "" ; +my $extend = "" ; +my $narrow = "" ; +my $slant = "" ; +my $spaced = "" ; +my $caps = "" ; +my $noligs = 0 ; +my $test = 0 ; +my $virtual = 0 ; +my $novirtual = 0 ; +my $listing = 0 ; +my $remove = 0 ; +my $expert = 0 ; +my $trace = 0 ; +my $afmpl = 0 ; +my $trees = 'TEXMFFONTS,TEXMFLOCAL,TEXMFEXTRA,TEXMFMAIN,TEXMFDIST' ; +my $pattern = '' ; + +my $fontsuffix = "" ; +my $namesuffix = "" ; + +my $batch = "" ; + +my $weight = "" ; +my $width = "" ; + +my $preproc = 0 ; # atl: formerly OpenType switch +my $variant = "" ; # atl: encoding variant +my $extension = "pfb" ; # atl: default font extension +my $lcdf = "" ; # atl: trigger for lcdf otftotfm + +my @cleanup = () ; # atl: build list of generated files to delete + +# todo: parse name for style, take face from command line +# +# @Faces = ("Serif","Sans","Mono") ; +# @Styles = ("Slanted","Spaced", "Italic","Bold","BoldSlanted","BoldItalic") ; +# +# for $fac (@Faces) { for $sty (@Styles) { $FacSty{"$fac$sty"} = "" } } + +&GetOptions + ( "help" => \$help, + "makepath" => \$makepath, + "noligs" => \$noligs, + "show" => \$show, + "install" => \$install, + "encoding=s" => \$encoding, + "variant=s" => \$variant, # atl: used as a suffix to $encfile only + "vendor=s" => \$vendor, + "collection=s" => \$collection, + "fontroot=s" => \$fontroot, + "sourcepath=s" => \$sourcepath, + "passon=s" => \$passon, + "slant=s" => \$slant, + "spaced=s" => \$spaced, + "extend=s" => \$extend, + "narrow=s" => \$narrow, + "listing" => \$listing, + "remove" => \$remove, + "test" => \$test, + "virtual" => \$virtual, + "novirtual" => \$novirtual, + "caps=s" => \$caps, + "batch" => \$batch, + "weight=s" => \$weight, + "width=s" => \$width, + "expert" => \$expert, + "afmpl" => \$afmpl, + "afm2pl" => \$afmpl, + "rootlist=s" => \$trees, + "pattern=s" => \$pattern, + "trace" => \$trace, # --verbose conflicts with --ve + "preproc" => \$preproc, # atl: trigger conversion to pfb + "lcdf" => \$lcdf ) ; # atl: trigger use of lcdf fonttoools + +# for/from Fabrice: + +my $own_path = "$FindBin::Bin/" ; + +$FindBin::RealScript =~ m/([^\.]*)(\.pl|\.bat|\.exe|)/io ; + +my $own_name = $1 ; +my $own_type = $2 ; +my $own_stub = "" ; + +if ($own_type =~ /pl/oi) { + $own_stub = "perl " +} + +if ($caps) { $afmpl = 0 } # for the moment + +# so we can use both combined + +if ($lcdf) { + $novirtual = 1 ; +} + +if (!$novirtual) { + $virtual = 1 ; +} + +# A couple of routines. + +sub report { + my $str = shift ; + $str =~ s/ / /goi ; + if ($str =~ /(.*?)\s+([\:\/])\s+(.*)/o) { + if ($1 eq "") { + $str = " " ; + } else { + $str = $2 ; + } + print sprintf("%22s $str %s\n",$1,$3) ; + } +} + +sub error { + report("processing aborted : " . shift) ; + print "\n" ; + report "--help : show some more info" ; + exit ; +} + +# The banner. + +print "\n" ; +report ("TeXFont 2.2.1 - ConTeXt / PRAGMA ADE 2000-2004") ; +print "\n" ; + +# Handy for scripts: one can provide a preferred path, if it +# does not exist, the current path is taken. + +if (!(-d $sourcepath)&&($sourcepath ne 'auto')) { $sourcepath = "." } + +# Let's make multiple masters if requested. + +sub create_mm_font + { my ($name,$weight,$width) = @_ ; my $flag = my $args = my $tags = "" ; + my $ok ; + if ($name ne "") + { report ("mm source file : $name") } + else + { error ("missing mm source file") } + if ($weight ne "") + { report ("weight : $weight") ; + $flag .= " --weight=$weight " ; + $tags .= "-weight-$weight" } + if ($width ne "") + { report ("width : $width") ; + $flag .= " --width=$width " ; + $tags .= "-width-$width" } + error ("no specification given") if ($tags eq "") ; + error ("no amfm file found") unless (-f "$sourcepath/$name.amfm") ; + error ("no pfb file found") unless (-f "$sourcepath/$name.pfb") ; + $args = "$flag --precision=5 --kern-precision=0 --output=$sourcepath/$name$tags.afm" ; + my $command = "mmafm $args $sourcepath/$name.amfm" ; + print "$command\n" if $trace ; + $ok = `$command` ; chomp $ok ; + if ($ok ne "") { report ("warning $ok") } + $args = "$flag --precision=5 --output=$sourcepath/$name$tags.pfb" ; + $command = "mmpfb $args $sourcepath/$name.pfb" ; + print "$command\n" if $trace ; + $ok = `$command` ; chomp $ok ; + if ($ok ne "") { report ("warning $ok") } + report ("mm result file : $name$tags") } + +if (($weight ne "")||($width ne "")) + { create_mm_font($ARGV[0],$weight,$width) ; + exit } + +# go on + +if (($listing||$remove)&&($sourcepath eq ".")) + { $sourcepath = "auto" } + +if ($fontroot eq "") + { if ($dosish) + { $fontroot = `kpsewhich -expand-path=\$$installpath` } + else + { $fontroot = `kpsewhich -expand-path=\\\$$installpath` } + chomp $fontroot } + + +if ($fontroot =~ /\s+/) # needed for windows, spaces in name + { $fontroot = &GetShortPathName($fontroot) } # but ugly when not needed + +if ($test) + { $vendor = $collection = "test" ; + $install = 1 } + +if (($spaced ne "") && ($spaced !~ /\d/)) { $spaced = "50" } +if (($slant ne "") && ($slant !~ /\d/)) { $slant = "0.167" } +if (($extend ne "") && ($extend !~ /\d/)) { $extend = "1.200" } +if (($narrow ne "") && ($narrow !~ /\d/)) { $narrow = "0.800" } +if (($caps ne "") && ($caps !~ /\d/)) { $caps = "0.800" } + +$encoding = lc $encoding ; +$vendor = lc $vendor ; +$collection = lc $collection ; + +if ($encoding =~ /default/oi) { $encoding = "texnansi" } + +my $lcfontroot = lc $fontroot ; + +# Auto search paths + +my @trees = split(/\,/,$trees) ; + +# Test for help asked. + +if ($help) + { report "--fontroot=path : texmf destination font root (default: $lcfontroot)" ; + report "--rootlist=paths : texmf source roots (default: $trees)" ; + report "--sourcepath=path : when installing, copy from this path (default: $sourcepath)" ; + report "--sourcepath=auto : locate and use vendor/collection" ; + print "\n" ; + report "--vendor=name : vendor name/directory" ; + report "--collection=name : font collection" ; + report "--encoding=name : encoding vector (default: $encoding)" ; + report "--variant=name : encoding variant (.enc file or otftotfm features)" ; + print "\n" ; + report "--spaced=s : space glyphs in font by promille of em (0 - 1000)" ; + report "--slant=s : slant glyphs in font by factor (0.0 - 1.5)" ; + report "--extend=s : extend glyphs in font by factor (0.0 - 1.5)" ; + report "--caps=s : capitalize lowercase chars by factor (0.5 - 1.0)" ; + report "--noligs : remove ligatures" ; + print "\n" ; + report "--install : copy files from source to font tree" ; + report "--listing : list files on auto sourcepath" ; + report "--remove : remove files on auto sourcepath" ; + report "--makepath : when needed, create the paths" ; + print "\n" ; + report "--test : use test paths for vendor/collection" ; + report "--show : run tex on texfont.tex" ; + print "\n" ; + report "--batch : process given batch file" ; + print "\n" ; + report "--weight : multiple master weight" ; + report "--width : multiple master width" ; + print "\n" ; + report "--expert : also handle expert fonts" ; + print "\n" ; + report "--afmpl : use afm2pl instead of afm2tfm" ; + report "--preproc : pre-process ttf/otf, converting them to pfb" ; + report "--lcdf : use lcdf fonttools to create virtual encoding" ; + exit } + +if (($batch)||(($ARGV[0]) && ($ARGV[0] =~ /.+\.dat$/io))) + { my $batchfile = $ARGV[0] ; + unless (-f $batchfile) + { if ($batchfile !~ /\.dat$/io) { $batchfile .= ".dat" } } + unless (-f $batchfile) + { report ("trying to locate : $batchfile") ; + $batchfile = `kpsewhich -format="other text files" -progname=context $batchfile` ; + chomp $batchfile } + error ("unknown batch file $batchfile") unless -e $batchfile ; + report ("processing batch file : $batchfile") ; + my $select = (($vendor ne "")||($collection ne "")) ; + my $selecting = 0 ; + if (open(BAT, $batchfile)) + { while (<BAT>) + { chomp ; + s/(.+)\#.*/$1/o ; + next if (/^\s*$/io) ; + if ($select) + { if ($selecting) + { if (/^\s*[\#\%]/io) { if (!/\-\-/o) { last } else { next } } } + elsif ((/^\s*[\#\%]/io)&&(/$vendor/i)&&(/$collection/i)) + { $selecting = 1 ; next } + else + { next } } + else + { next if (/^\s*[\#\%]/io) ; + next unless (/\-\-/oi) } + s/\s+/ /gio ; + s/(--en.*\=)\?/$1$encoding/io ; + report ("batch line : $_") ; + # system ("perl $0 --fontroot=$fontroot $_") } + my $own_quote = ( $own_path =~ m/^[^\"].* / ? "\"" : "" ); + my $switches = '' ; + $switches .= "--afmpl " if $afmpl ; + system ("$own_stub$own_quote$own_path$own_name$own_type$own_quote $switches --fontroot=$fontroot $_") } + close (BAT) } + exit } + +error ("unknown vendor $vendor") unless $vendor ; +error ("unknown collection $collection") unless $collection ; +error ("unknown tex root $lcfontroot") unless -d $fontroot ; + +my $varlabel = $variant ; + +if ($lcdf) + { $varlabel =~ s/,/-/goi ; + $varlabel =~ tr/a-z/A-Z/ } + +if ($varlabel ne "") + { $varlabel = "-$varlabel" } + +my $identifier = "$encoding$varlabel-$vendor-$collection" ; + +my $outlinepath = $sourcepath ; my $path = "" ; + +my $shape = "" ; + +if ($noligs) + { report ("ligatures : removed") ; + $fontsuffix .= "-unligatured" ; + $namesuffix .= "-NoLigs" } + +if ($caps ne "") + { if ($caps <0.5) { $caps = 0.5 } + elsif ($caps >1.0) { $caps = 1.0 } + $shape .= " -c $caps " ; + report ("caps factor : $caps") ; + $fontsuffix .= "-capitalized-" . int(1000*$caps) ; + $namesuffix .= "-Caps" } + +if ($extend ne "") + { if ($extend<0.0) { $extend = 0.0 } + elsif ($extend>1.5) { $extend = 1.5 } + report ("extend factor : $extend") ; + if ($lcdf) + { $shape .= " -E $extend " } + else + { $shape .= " -e $extend " } + $fontsuffix .= "-extended-" . int(1000*$extend) ; + $namesuffix .= "-Extended" } + +if ($narrow ne "") # goodie + { $extend = $narrow ; + if ($extend<0.0) { $extend = 0.0 } + elsif ($extend>1.5) { $extend = 1.5 } + report ("narrow factor : $extend") ; + if ($lcdf) + { $shape .= " -E $extend " } + else + { $shape .= " -e $extend " } + $fontsuffix .= "-narrowed-" . int(1000*$extend) ; + $namesuffix .= "-Narrowed" } + +if ($slant ne "") + { if ($slant <0.0) { $slant = 0.0 } + elsif ($slant >1.5) { $slant = 1.5 } + report ("slant factor : $slant") ; + if ($lcdf) + { $shape .= " -S $slant " } + else + { $shape .= " -s $slant " } + $fontsuffix .= "-slanted-" . int(1000*$slant) ; + $namesuffix .= "-Slanted" } + +if ($spaced ne "") + { if ($spaced < 0) { $spaced = 0 } + elsif ($spaced >1000) { $spaced = 1000 } + report ("space factor : $spaced") ; + if ($lcdf) + { $shape .= " -L $spaced " } + else + { $shape .= " -m $spaced " } + $fontsuffix .= "-spaced-" . $spaced ; + $namesuffix .= "-Spaced" } + +if ($sourcepath eq "auto") # todo uppercase root + { foreach my $root (@trees) + { if ($dosish) + { $path = `kpsewhich -expand-path=\$$root` } + else + { $path = `kpsewhich -expand-path=\\\$$root` } + chomp $path ; + $path = $ENV{$root} if (($path eq '') && defined($ENV{$root})) ; + report ("checking root : $root") ; + if ($preproc) + { $sourcepath = "$path/fonts/truetype/$vendor/$collection" } + else + { $sourcepath = "$path/fonts/afm/$vendor/$collection" } + unless (-d $sourcepath) + { my $ven = $vendor ; $ven =~ s/(........).*/$1/ ; + my $col = $collection ; $col =~ s/(........).*/$1/ ; + $sourcepath = "$path/fonts/afm/$ven/$col" ; + if (-d $sourcepath) + { $vendor = $ven ; $collection = $col } } + $outlinepath = "$path/fonts/type1/$vendor/$collection" ; + if (-d $sourcepath) + { # $install = 0 ; # no copy needed + $makepath = 1 ; # make on local if needed + my @files = validglob("$sourcepath/*.afm") ; + if ($preproc) + { @files = validglob("$sourcepath/*.otf") ; + report("locating : otf files") } + unless (@files) + { @files = validglob("$sourcepath/*.ttf") ; + report("locating : ttf files") } + if (@files) + { if ($listing) + { report ("fontpath : $sourcepath" ) ; + print "\n" ; + foreach my $file (@files) + { if (open(AFM,$file)) + { my $name = "unknown name" ; + while (<AFM>) + { chomp ; + if (/^fontname\s+(.*?)$/oi) + { $name = $1 ; last } } + close (AFM) ; + if ($preproc) + { $file =~ s/.*\/(.*)\..tf/$1/io } + else + { $file =~ s/.*\/(.*)\.afm/$1/io } + report ("$file : $name") } } + exit } + elsif ($remove) + { error ("no removal from : $root") if ($root eq 'TEXMFMAIN') ; + foreach my $file (@files) + { if ($preproc) + { $file =~ s/.*\/(.*)\..tf/$1/io } + else + { $file =~ s/.*\/(.*)\.afm/$1/io } + foreach my $sub ("tfm","vf") + { foreach my $typ ("","-raw") + { my $nam = "$path/fonts/$sub/$vendor/$collection/$encoding$varlabel$typ-$file.$sub" ; + # { my $nam = "$path/fonts/$sub/$vendor/$collection/$encoding$varlabel$typ-$file$fontsuffix.$sub" ; + if (-s $nam) + { report ("removing : $encoding$varlabel$typ-$file.$sub") ; + unlink $nam } } } } + my $nam = "$encoding$varlabel-$vendor-$collection.tex" ; + if (-e $nam) + { report ("removing : $nam") ; + unlink "$nam" } + my $mapfile = "$encoding$varlabel-$vendor-$collection" ; + my $maproot = "$fontroot/fonts/map/pdftex/context"; + if (-e "$maproot$mapfile.map") + { report ("renaming : $mapfile.map -> $mapfile.bak") ; + unlink "$maproot$mapfile.bak" ; + rename "$maproot$mapfile.map", "$maproot$mapfile.bak" } + exit } + else + { last } } } } + error ("unknown subpath ../fonts/afm/$vendor/$collection") unless -d $sourcepath } + +error ("unknown source path $sourcepath") unless -d $sourcepath ; +error ("unknown option $ARGV[0]") if (($ARGV[0]||'') =~ /\-\-/) ; + +my $afmpath = "$fontroot/fonts/afm/$vendor/$collection" ; +my $tfmpath = "$fontroot/fonts/tfm/$vendor/$collection" ; +my $vfpath = "$fontroot/fonts/vf/$vendor/$collection" ; +my $pfbpath = "$fontroot/fonts/type1/$vendor/$collection" ; +my $ttfpath = "$fontroot/fonts/truetype/$vendor/$collection" ; +my $mappath = "$fontroot/fonts/map/pdftex/context" ; +my $encpath = "$fontroot/fonts/enc/dvips/context" ; + +# are not on local path ! ! ! ! + +foreach my $path ($afmpath, $pfbpath) + { my @gzipped = <$path/*.gz> ; + foreach my $file (@gzipped) + { print "file = $file\n"; + system ("gzip -d $file") } } + +# 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 ; + if ($str =~ /^(.*)\/.*?$/) + { do_make_path($1) } + mkdir $str, 0755 unless -d $str } + +sub make_path + { my $str = shift ; + do_make_path("$fontroot/fonts") ; + do_make_path("$fontroot/fonts/$str") ; + do_make_path("$fontroot/fonts/$str/$vendor") ; + do_make_path("$fontroot/fonts/$str/$vendor/$collection") } + +if ($makepath&&$install) + { make_path ("afm") ; make_path ("type1") } + +do_make_path($mappath) ; +do_make_path($encpath) ; + +# now fonts/map and fonts/enc + +make_path ("vf") ; +make_path ("tfm") ; + +if ($install) + { error ("unknown afm path $afmpath") unless -d $afmpath ; + error ("unknown pfb path $pfbpath") unless -d $pfbpath } + +error ("unknown tfm path $tfmpath") unless -d $tfmpath ; +error ("unknown vf path $vfpath" ) unless -d $vfpath ; +error ("unknown map path $mappath") unless -d $mappath ; + +my $mapfile = "$identifier.map" ; +my $bakfile = "$identifier.bak" ; +my $texfile = "$identifier.tex" ; + + report "encoding vector : $encoding" ; +if ($variant) { report "encoding variant : $variant" } + report "vendor name : $vendor" ; + report " source path : $sourcepath" ; + report "font collection : $collection" ; + report "texmf font root : $lcfontroot" ; + report "pdftex map file : $mapfile" ; + +if ($install) { report "source path : $sourcepath" } + +my $fntlist = "" ; + +my $runpath = $sourcepath ; + +my @files ; + +sub UnLink + { foreach my $f (@_) + { if (unlink $f) + { report "deleted : $f" if $trace } } } + +sub globafmfiles + { my ($runpath, $pattern) = @_ ; + my @files = validglob("$runpath/$pattern.afm") ; + report("locating afm files : using pattern $runpath/$pattern.afm"); + if ($preproc && !$lcdf) + { @files = validglob("$runpath/$pattern.*tf") ; + report("locating otf files : using pattern $runpath/$pattern.*tf"); + unless (@files) + { @files = validglob("$sourcepath/$pattern.ttf") ; + report("locating ttf files : using pattern $sourcepath/$pattern.ttf") } + } + if (@files) # also elsewhere + { report("locating afm files : using pattern $pattern") } + else + { @files = validglob("$runpath/$pattern.ttf") ; + if (@files) + { report("locating afm files : using ttf files") ; + $extension = "ttf" ; + foreach my $file (@files) + { $file =~ s/\.ttf$//io ; + report ("generating afm file : $file.afm") ; + my $command = "ttf2afm \"$file.ttf\" -o \"$file.afm\"" ; + system($command) ; + print "$command\n" if $trace ; + push(@cleanup, "$file.afm") } + @files = validglob("$runpath/$pattern.afm") } + else # try doing the pre-processing earlier + { report("locating afm files : using otf files") ; + $extension = "otf" ; + @files = validglob("$runpath/$pattern.otf") ; + foreach my $file (@files) + { $file =~ s/\.otf$//io ; + if (!$lcdf) + { report ("generating afm file : $file.afm") ; + preprocess_font("$file.otf", "$file.bdf") ; + push(@cleanup,"$file.afm") } + if ($preproc) + { my $command = "cfftot1 --output=$file.pfb $file.otf" ; + print "$command\n" if $trace ; + report("converting : $file.otf to $file.pfb") ; + system($command) ; + push(@cleanup, "$file.pfb") ; + } + } + if ($lcdf) + { @files = validglob("$runpath/$pattern.otf") } + else + { @files = validglob("$runpath/$pattern.afm") } + } + } + return @files } + +if ($pattern eq '') { if ($ARGV[0]) { $pattern = $ARGV[0] } } + +if ($pattern ne '') + { report ("processing files : all in pattern $pattern") ; + @files = globafmfiles($runpath,$pattern) } +elsif ("$extend$narrow$slant$spaced$caps" ne "") + { error ("transformation needs file spec") } +else + { $pattern = "*" ; + report ("processing files : all on afm path") ; + @files = globafmfiles($runpath,$pattern) } + +sub copy_files + { my ($suffix,$sourcepath,$topath) = @_ ; + my @files = validglob("$sourcepath/$pattern.$suffix") ; + return if ($topath eq $sourcepath) ; + report ("copying files : $suffix") ; + foreach my $file (@files) + { my $ok = $file =~ /(.*)\/(.+?)\.(.*)/ ; + my ($path,$name,$suffix) = ($1,$2,$3) ; + UnLink "$topath/$name.$suffix" ; + report ("copying : $name.$suffix") ; + copy ($file,"$topath/$name.$suffix") } } + +if ($install) + { copy_files("afm",$sourcepath,$afmpath) ; +# copy_files("tfm",$sourcepath,$tfmpath) ; # raw supplied names + copy_files("pfb",$outlinepath,$pfbpath) ; + if ($extension eq "ttf") + { make_path("truetype") ; + copy_files("ttf",$sourcepath,$ttfpath) } + if ($extension eq "otf") + { make_path("truetype") ; + copy_files("otf",$sourcepath,$ttfpath) } } + +error ("no afm files found") unless @files ; + +my $map = my $tex = 0 ; my $mapdata = my $texdata = "" ; + +copy ("$mappath/$mapfile","$mappath/$bakfile") ; + +if (open (MAP,"<$mappath/$mapfile")) + { report ("extending map file : $mappath/$mapfile") ; + while (<MAP>) { unless (/^\%/o) { $mapdata .= $_ } } + close (MAP) } +else + { report ("no map file at : $mappath/$mapfile") } + +if (open (TEX,"<$texfile")) + { while (<TEX>) { unless (/stoptext/o) { $texdata .= $_ } } + close (TEX) } + +$map = open (MAP,">$mapfile") ; +$tex = open (TEX,">$texfile") ; + +unless ($map) { report "warning : can't open $mapfile" } +unless ($tex) { report "warning : can't open $texfile" } + +if ($map) + { print MAP "% This file is generated by the TeXFont Perl script.\n" ; + print MAP "%\n" ; + print MAP "% You need to add the following line to your file:\n" ; + print MAP "%\n" ; + print MAP "% \\pdfmapfile{+$mapfile}\n" ; + print MAP "%\n" ; + print MAP "% In ConTeXt you can best use:\n" ; + print MAP "%\n" ; + print MAP "% \\loadmapfile\[$mapfile\]\n\n" } + +if ($tex) + { if ($texdata eq "") + { print TEX "% output=pdftex interface=en\n" ; + print TEX "\n" ; + print TEX "\\usemodule[fnt-01]\n" ; + print TEX "\n" ; + print TEX "\\loadmapfile[$mapfile]\n" ; + print TEX "\n" ; + print TEX "\\starttext\n\n" } + else + { print TEX "$texdata" ; + print TEX "\n\%appended section\n\n\\page\n\n" } } + +sub removeligatures + { my $filename = shift ; my $skip = 0 ; + copy ("$filename.vpl","$filename.tmp") ; + if ((open(TMP,"<$filename.tmp"))&&(open(VPL,">$filename.vpl"))) + { report "removing ligatures : $filename" ; + while (<TMP>) + { chomp ; + if ($skip) + { if (/^\s*\)\s*$/o) { $skip = 0 ; print VPL "$_\n" } } + elsif (/\(LIGTABLE/o) + { $skip = 1 ; print VPL "$_\n" } + else + { print VPL "$_\n" } } + close(TMP) ; close(VPL) } + UnLink ("$filename.tmp") } + +my $raw = my $use = my $maplist = my $texlist = my $report = "" ; + +$use = "$encoding$varlabel-" ; $raw = $use . "raw-" ; + +my $encfil = "" ; + +if ($encoding ne "") # evt -progname=context + { $encfil = `kpsewhich -progname=pdftex $encoding$varlabel.enc` ; + chomp $encfil ; if ($encfil eq "") { $encfil = "$encoding$varlabel.enc" } } + +sub preprocess_font + { my ($infont,$pfbfont) = @_ ; + if ($infont ne "") + { report ("otf/ttf source file : $infont") ; + report ("destination file : $pfbfont") ; } + else + { error ("missing otf/ttf source file") } + open (CONVERT, "| pfaedit -script -") || error ("couldn't open pipe to pfaedit") ; + report ("pre-processing with : pfaedit") ; + print CONVERT "Open('$infont');\n Generate('$pfbfont', '', 1) ;\n" ; + close (CONVERT) } + +foreach my $file (@files) + { my $option = my $slant = my $spaced = my $extend = my $vfstr = my $encstr = "" ; + my $strange = "" ; my ($rawfont,$cleanfont,$restfont) ; + $file = $file ; + my $ok = $file =~ /(.*)\/(.+?)\.(.*)/ ; + my ($path,$name,$suffix) = ($1,$2,$3) ; + # remove trailing _'s + my $fontname = $name ; + my $cleanname = $fontname ; + $cleanname =~ s/\_//gio ; + # atl: pre-process an opentype or truetype file by converting to pfb + if ($preproc && !$lcdf) + { unless (-f "$afmpath/$cleanname.afm" && -f "$pfbpath/$cleanname.pfb") + { preprocess_font("$path/$name.$suffix", "$pfbpath/$cleanname.pfb") ; + rename("$pfbpath/$cleanname.afm", "$afmpath/$cleanname.afm") + || error("couldn't move afm product of pre-process.") } + $path = $afmpath ; + $file = "$afmpath/$cleanname.afm" } + # cleanup + foreach my $suf ("tfm", "vf", "vpl") + { UnLink "$raw$cleanname$fontsuffix.$suf" ; + UnLink "$use$cleanname$fontsuffix.$suf" } + UnLink "texfont.log" ; + # set switches + if ($encoding ne "") + { $encstr = " -T $encfil" } + if ($caps ne "") + { $vfstr = " -V $raw$cleanname$fontsuffix" } + else # if ($virtual) + { $vfstr = " -v $raw$cleanname$fontsuffix" } + my $font = ""; + # let's see what we have here (we force texnansi.enc to avoid error messages) + if ($lcdf) + { my $command = "otfinfo -p $file" ; + print "$command\n" if $trace ; + $font = `$command` ; + chomp $font ; + $cleanname = $cleanfont = $font } + else + { my $command = "afm2tfm \"$file\" -p texnansi.enc texfont.tfm" ; + print "$command\n" if $trace ; + $font = `$command` ; + UnLink "texfont.tfm" ; + ($rawfont,$cleanfont,$restfont) = split(/\s/,$font) } + if ($font =~ /(math|expert)/io) { $strange = lc $1 } + $cleanfont =~ s/\_/\-/goi ; + $cleanfont =~ s/\-+$//goi ; + print "\n" ; + if (($strange eq "expert")&&($expert)) + { report ("font identifier : $cleanfont$namesuffix -> $strange -> tfm") } + elsif ($strange ne "") + { report ("font identifier : $cleanfont$namesuffix -> $strange -> skipping") } + elsif ($afmpl) + { report ("font identifier : $cleanfont$namesuffix -> text -> tfm") } + elsif ($virtual) + { report ("font identifier : $cleanfont$namesuffix -> text -> tfm + vf") } + else + { report ("font identifier : $cleanfont$namesuffix -> text -> tfm") } + # don't handle strange fonts + if ($strange eq "") + { # atl: support for lcdf otftotfm + if ($lcdf && $extension eq "otf") + { # no vf, bypass afm, use otftotfm to get encoding and tfm + my $varstr = my $encout = my $tfmout = "" ; + report "processing files : otf -> tfm + enc" ; + if ($encoding ne "") + { $encfil = `kpsewhich -progname=pdftex $encoding.enc` ; + chomp $encfil ; if ($encfil eq "") { $encfil = "$encoding.enc" } + $encstr = " -e $encfil " } + if ($variant ne "") + { ( $varstr = $variant ) =~ s/,/ -f /goi ; + $varstr = " -f $varstr" } + $encout = "$encpath/$use$cleanfont.enc" ; + if (-e $encout) + { report ("renaming : $encout -> $use$cleanfont.bak") ; + UnLink "$encpath/$use$cleanfont.bak" ; + 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\"" ; + print "$otfcommand\n" if $trace ; + system("$otfcommand") ; + $encfil = $encout } + else + { # generate tfm and vpl, $file is on afm path + my $font = '' ; + if ($afmpl) + { report " generating pl : $cleanname$fontsuffix (from $cleanname)" ; + $encstr = " -p $encfil" ; + my $command = "afm2pl -f afm2tfm $shape $passon $encstr $file $cleanname$fontsuffix.vpl" ; + print "$command\n" if $trace ; + my $ok = `$command` ; + if (open (TMP,"$cleanname$fontsuffix.map")) + { $font = <TMP> ; + close(TMP) ; + UnLink "$cleanname$fontsuffix.map" } } + else + { report "generating raw tfm/vpl : $raw$cleanname$fontsuffix (from $cleanname)" ; + my $command = "afm2tfm $file $shape $passon $encstr $vfstr $raw$cleanname$fontsuffix" ; + print "$command\n" if $trace ; + $font = `$command` } + # generate vf file if needed + chomp $font ; + if ($font =~ /.*?([\d\.]+)\s*ExtendFont/io) { $extend = $1 } + if ($font =~ /.*?([\d\.]+)\s*SlantFont/io) { $slant = $1 } + if ($extend ne "") { $option .= " $1 ExtendFont " } + if ($slant ne "") { $option .= " $1 SlantFont " } + if ($noligs) { removeligatures("$raw$cleanname$fontsuffix") } + if ($afmpl) + { report "generating new tfm : $use$cleanname$fontsuffix" ; + my $command = "pltotf $cleanname$fontsuffix.vpl $use$cleanname$fontsuffix.tfm" ; + print "$command\n" if $trace ; + my $ok = `$command` } + elsif ($virtual) + { report "generating new vf : $use$cleanname$fontsuffix (from $raw$cleanname)" ; + my $command = "vptovf $raw$cleanname$fontsuffix.vpl $use$cleanname$fontsuffix.vf $use$cleanname$fontsuffix.tfm" ; + print "$command\n" if $trace ; + my $ok = `$command` } + else + { report "generating new tfm : $use$cleanname$fontsuffix (from $raw$cleanname)" ; + my $command = "pltotf $raw$cleanname$fontsuffix.vpl $use$cleanname$fontsuffix.tfm" ; + print "$command\n" if $trace ; + my $ok = `$command` } } } + elsif (-e "$sourcepath/$cleanname.tfm" ) + { report "using existing tfm : $cleanname.tfm" } + elsif (($strange eq "expert")&&($expert)) + { report "creating tfm file : $cleanname.tfm" ; + my $command = "afm2tfm $file $cleanname.tfm" ; + print "$command\n" if $trace ; + my $font = `$command` } + else + { report "use supplied tfm : $cleanname" } + # report results + if (!$lcdf) + { ($rawfont,$cleanfont,$restfont) = split(/\s/,$font) } + $cleanfont =~ s/\_/\-/goi ; + $cleanfont =~ s/\-+$//goi ; + # copy files + my $usename = "$use$cleanname$fontsuffix" ; + my $rawname = "$raw$cleanname$fontsuffix" ; + + if ($lcdf eq "") + { if ($strange ne "") + { UnLink ("$vfpath/$cleanname.vf", "$tfmpath/$cleanname.tfm") ; + copy ("$cleanname.tfm","$tfmpath/$cleanname.tfm") ; + copy ("$usename.tfm","$tfmpath/$usename.tfm") ; + # or when available, use vendor one : + copy ("$sourcepath/$cleanname.tfm","$tfmpath/$cleanname.tfm") } + elsif ($virtual) + { UnLink ("$vfpath/$rawname.vf", "$vfpath/$usename.vf") ; + UnLink ("$tfmpath/$rawname.tfm", "$tfmpath/$usename.tfm") ; + copy ("$usename.vf" ,"$vfpath/$usename.vf") ; + copy ("$rawname.tfm","$tfmpath/$rawname.tfm") ; + copy ("$usename.tfm","$tfmpath/$usename.tfm") } + elsif ($afmpl) + { UnLink ("$vfpath/$rawname.vf", "$vfpath/$usename.vf", "$vfpath/$cleanname.vf") ; + UnLink ("$tfmpath/$rawname.tfm", "$tfmpath/$usename.tfm", "$tfmpath/$cleanname.tfm") ; + copy ("$usename.tfm","$tfmpath/$usename.tfm") } + else + { UnLink ("$vfpath/$usename.vf", "$tfmpath/$usename.tfm") ; + # slow but prevents conflicting vf's + my $rubish = `kpsewhich $usename.vf` ; chomp $rubish ; + if ($rubish ne "") { UnLink $rubish } + # + copy ("$usename.tfm","$tfmpath/$usename.tfm") } } + # cleanup + foreach my $suf ("tfm", "vf", "vpl") + { UnLink ("$rawname.$suf", "$usename.$suf") ; + UnLink ("$cleanname.$suf", "$fontname.$suf") ; + UnLink ("$cleanname$fontsuffix.$suf", "$fontname$fontsuffix.$suf") } + # add line to maps file + $option =~ s/^\s+(.*)/$1/o ; + $option =~ s/(.*)\s+$/$1/o ; + $option =~ s/ / /o ; + if ($option ne "") + { $option = "\"$option\" 4" } + else + { $option = "4" } + # adding cleanfont is kind of dangerous + my $thename = my $str = my $theencoding = "" ; + if ($strange ne "") + { $thename = $cleanname ; $theencoding = "" ; } + elsif ($lcdf) + { $thename = $usename ; $theencoding = " $encoding$varlabel-$cleanname.enc" } + elsif ($afmpl) + { $thename = $usename ; $theencoding = " $encoding$varlabel.enc" } + elsif ($virtual) + { $thename = $rawname ; $theencoding = " $encoding$varlabel.enc" } + else + { $thename = $usename ; $theencoding = " $encoding$varlabel.enc" } + # quit rest if no type 1 file + my $pfb_sourcepath = $sourcepath ; + $pfb_sourcepath =~ s@/afm/@/type1/@ ; + unless ((-e "$pfbpath/$fontname.$extension")|| + (-e "$pfb_sourcepath/$fontname.$extension")|| + (-e "$sourcepath/$fontname.$extension")|| + (-e "$ttfpath/$fontname.$extension")) + { if ($tex) { $report .= "missing file: \\type \{$fontname.pfb\}\n" } + report ("missing pfb file : $fontname.pfb") } + # now add entry to map + if ($strange eq "") { + if ($extension eq "otf") { + if ($lcdf) { + my $mapline = "" ; + if (open(ALTMAP,"texfont.map")) { + while (<ALTMAP>) { + chomp ; + # atl: we assume this b/c we always force otftotfm --no-type1 + if (/<<(.*)\.otf$/oi) { + $mapline = $_ ; last ; + } + } + close(ALTMAP) ; + } else { + report("no mapfile from otftotfm : texfont.map") ; + } + if ($preproc) { + $mapline =~ s/<\[/</; + $mapline =~ s/<<(\S+)\.otf$/<$1\.pfb/ ; + } else { + $mapline =~ s/<<(\S+)\.otf$/<< $ttfpath\/$fontname.$extension/ ; + } + $str = "$mapline\n" ; + } else { + if ($preproc) { + $str = "$thename $cleanfont $option < $fontname.pfb$theencoding\n" ; + } else { + # PdfTeX can't subset OTF files, so we have to include the whole thing + # It looks like we also need to be explicit on where to find the file + $str = "$thename $cleanfont $option << $ttfpath/$fontname.$extension <[$theencoding\n" ; + } + } + } else { + $str = "$thename $cleanfont $option < $fontname.$extension$theencoding\n" ; + } + } else { + $str = "$thename $cleanfont < $fontname.$extension\n" ; + } + # check for redundant entries + if ($map) { + $mapdata =~ s/^$thename\s.*?$//gmis ; + if ($afmpl) { + if ($mapdata =~ s/^$rawname\s.*?$//gmis) { + report ("removing raw file : $rawname") ; + } + } + $maplist .= $str ; + $mapdata .= $str ; + } + # write lines to tex file + if (($strange eq "expert")&&($expert)) { + $fntlist .= "\\definefontsynonym[$cleanfont$namesuffix][$cleanname] \% expert\n" ; + } elsif ($strange ne "") { + $fntlist .= "\%definefontsynonym[$cleanfont$namesuffix][$cleanname]\n" ; + } else { + $fntlist .= "\\definefontsynonym[$cleanfont$namesuffix][$usename][encoding=$encoding]\n" ; + } + next unless $tex ; + if (($strange eq "expert")&&($expert)) { + $texlist .= "\\ShowFont[$cleanfont$namesuffix][$cleanname]\n" ; + } elsif ($strange ne "") { + $texlist .= "\%ShowFont[$cleanfont$namesuffix][$cleanname]\n" ; + } else { + $texlist .= "\\ShowFont[$cleanfont$namesuffix][$usename][$encoding]\n" + } +} + +if ($map) + { report ("updating map file : $mapfile") ; + while ($mapdata =~ s/\n\n+/\n/mois) {} ; + $mapdata =~ s/^\s*//gmois ; + print MAP $mapdata } + +if ($tex) + { $mappath =~ s/\\/\//go ; + $savedoptions =~ s/^\s+//gmois ; $savedoptions =~ s/\s+$//gmois ; + $fntlist =~ s/^\s+//gmois ; $fntlist =~ s/\s+$//gmois ; + $maplist =~ s/^\s+//gmois ; $maplist =~ s/\s+$//gmois ; + print TEX "$texlist" ; + print TEX "\n" ; + print TEX "\\setupheadertexts[\\tttf example definitions]\n" ; + print TEX "\n" ; + print TEX "\\starttyping\n" ; + print TEX "texfont $savedoptions\n" ; + print TEX "\\stoptyping\n" ; + print TEX "\n" ; + print TEX "\\starttyping\n" ; + print TEX "$mappath/$mapfile\n" ; + print TEX "\\stoptyping\n" ; + print TEX "\n" ; + print TEX "\\starttyping\n" ; + print TEX "$fntlist\n" ; + print TEX "\\stoptyping\n" ; + print TEX "\n" ; + print TEX "\\page\n" ; + print TEX "\n" ; + print TEX "\\setupheadertexts[\\tttf $mapfile]\n" ; + print TEX "\n" ; + print TEX "\\starttyping\n" ; + print TEX "$maplist\n" ; + print TEX "\\stoptyping\n" ; + print TEX "\n" ; + print TEX "\\stoptext\n" } + +if ($map) { close (MAP) } +if ($tex) { close (TEX) } + +copy ($mapfile,"$mappath/$mapfile") ; + +# atl: global cleanup with generated files (afm & ttf don't mix) + +UnLink(@cleanup) ; + +print "\n" ; report ("generating : ls-r databases") ; + +# Refresh database. + +print "\n" ; system ("mktexlsr $fontroot") ; print "\n" ; + +# Process the test file. + +if ($show) { system ("texexec --once --silent $texfile") } + +@files = validglob("$identifier.*") ; + +foreach my $file (@files) + { unless ($file =~ /(tex|pdf|log|mp|tmp)$/io) { unlink $file } } + +exit ; diff --git a/Master/texmf-dist/scripts/context/perl/texshow.pl b/Master/texmf-dist/scripts/context/perl/texshow.pl new file mode 100644 index 00000000000..79222c9f03c --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/texshow.pl @@ -0,0 +1,936 @@ +eval '(exit $?0)' && eval 'exec perl -w -S $0 ${1+"$@"}' && eval 'exec perl -w -S $0 $argv:q' + if 0; + +#D \module +#D [ file=texshow.pl, +#D version=2005.01.06, +#D title=TeXShow, +#D subtitle=showing \CONTEXT\ commands, +#D author=Taco Hoekwater, +#D date=\currentdate, +#D copyright={Taco Hoekwater}] + +#D Early 1999 \TEXSHOW\ showed up in the \CONTEXT\ distribution. At that time +#D the user interface was described in files named \type {setup*.tex}. The +#D program used a stripped down version of these definition files, generated +#D by \CONTEXT\ itself. \TEXSHOW\ shows you the commands, their (optional) +#D arguments, as well as the parameters and their values. For some five years +#D there was no need to change \TEXSHOW. However, when a few years ago we +#D started providing an \XML\ variant of the user interface definitions, Taco +#D came up with \TEXSHOW||\XML. Because Patricks \CONTEXT\ garden and tools +#D like \CTXTOOLS\ also use the \XML\ definitions, it's time to drop the old +#D \TEX\ based definitions and move forward. From now on Taco's version is the +#D one to be used. +#D +#D Hans Hagen - Januari 2005 + +use strict; +use Getopt::Long ; +use XML::Parser; +use Data::Dumper; +use Tk; +use Tk::ROText ; +use Config; +use Time::HiRes; + +$Getopt::Long::passthrough = 1 ; # no error message +$Getopt::Long::autoabbrev = 1 ; # partial switch accepted + +my $ShowHelp = 0; +my $Debug = 0; +my $Editmode = 0; +my $Interface = 'cont-en'; +my $current_command; +my $current_interface; +my $current_part; +my @setup_files; + +my %setups; +my %commes; +my %descrs; +my %examps; +my %trees; +my %positions; +my %locations; +my %crosslinks; + + +&GetOptions + ( "help" => \$ShowHelp , + "interface=s" => \$Interface , + "debug" => \$Debug, + "edit" => \$Editmode) ; + +print "\n"; + +show('TeXShow-XML 0.2 beta','Taco Hoekwater 2004',"/"); + +print "\n"; + +if ($ShowHelp) { + show('--help','print this help'); + show('--interface=lg','primary interface'); + show('--debug','print debugging info'); + show('string','show info about command \'string\''); + show('string lg','show info about \'string\' in language \'lg\''); + print "\n"; + exit 0; +} + +my $command = $ARGV[0] || ''; +my $interface = $ARGV[1] || ''; +if ($interface =~ /^[a-z][a-z]$/i) { + $Interface = 'cont-' . lc($interface); +} elsif ($interface && $command =~ /^[a-z][a-z]$/i) { + show('debug',"switching '$interface' and '$command'"); + $Interface = 'cont-' . lc($command); + $command = $interface; +} + +if ($command =~ s/^\\//) { + show('debug','removed initial command backslash'); +} + +show('interface', $Interface); +if ($command){ + show ('command', "\\$command") ; +} + +print "\n"; + +show('status','searching for setup files'); + +my $setup_path; +my ($mainwindow,$interfaceframe,$partframe,$leftframe,$rightframe,$buttonframe); +my ($request,$listbox,$textwindow,%interfacebuttons,%partbuttons); + +my ($textfont,$userfont,$buttonfont); + +my $Part; + +if (setups_found($Interface)) { + $current_interface = ''; + $current_command = ''; + $current_part = 'Command'; + show('status','loading setups') ; + load_setups($Interface) ; + show ('status','initializing display') ; + initialize_display(); + change_setup(); + show_command ($command); + $mainwindow->deiconify(); + show ('status','entering main loop') ; + MainLoop () ; + show ('status','closing down') ; +} else { + show ('error','no setup files found') ; +} +print "\n"; + +sub initialize_display { + my $dosish = ($Config{'osname'} =~ /dos|win/i) ; + my $default_size = $dosish ? 9 : 12 ; + my $s_vertical = 30 ; + my $s_horizontal = 72 ; + my $c_horizontal = 24 ; + if (!$dosish) { + $textfont = "-adobe-courier-bold-r-normal--$default_size-120-75-75-m-70-iso8859-1" ; + $userfont = "-adobe-courier-bold-o-normal--$default_size-120-75-75-m-70-iso8859-1" ; + $buttonfont = "-adobe-helvetica-bold-r-normal--$default_size-120-75-75-p-69-iso8859-1"; + } else { + $textfont = "Courier $default_size " ; + $userfont = "Courier $default_size italic" ; + $buttonfont = "Helvetica $default_size bold " ; + } + $mainwindow = MainWindow -> new ( -title => 'ConTeXt commands' ) ; + $buttonframe = $mainwindow -> Frame () ; # buttons + $leftframe = $mainwindow -> Frame () ; # leftside + $rightframe = $mainwindow -> Frame(); + $request = $rightframe -> Entry (-font => $textfont, + -background => 'ivory1', + -width => $c_horizontal); + $listbox = $rightframe -> Scrolled ('Listbox', + -scrollbars => 'e', + -font => $textfont, + -width => $c_horizontal, + -selectbackground => 'gray', + -background => 'ivory1', + -selectmode => 'browse') ; + $textwindow = $leftframe -> Scrolled ('ROText', + -scrollbars => 'se', + -height => $s_vertical, + -width => $s_horizontal, + -wrap => 'none', + -background => 'ivory1', + -font => $textfont); + $interfaceframe = $leftframe -> Frame(); + $mainwindow -> withdraw() ; + $mainwindow -> resizable ('y', 'y') ; + foreach (@setup_files) { + $interfacebuttons{$_} = $buttonframe -> Radiobutton (-text => $_, + -value => $_, + -font => $buttonfont, + -selectcolor => 'ivory1', + -indicatoron => 0, + -command => \&change_setup, + -variable => \$Interface ); + + $interfacebuttons{$_} -> pack (-padx => '2p',-pady => '2p','-side' => 'left' ); + } + foreach (qw(Command Description Comments Examples)) { + $partbuttons{$_} = $interfaceframe -> Radiobutton (-text => $_, + -value => $_, + -font => $buttonfont, + -selectcolor => 'ivory1', + -indicatoron => 0, + -command => \&change_part, + -variable => \$Part ); + $partbuttons{$_} -> pack (-padx => '2p',-pady => '2p','-side' => 'left' ); + } + # global top + $buttonframe -> pack ( -side => 'top' , -fill => 'x' , -expand => 0 ) ; + # top in left + $interfaceframe -> pack ( -side => 'top' , -fill => 'x' , -expand => 0 ) ; + $textwindow -> pack ( -side => 'top' , -fill => 'both' , -expand => 1 ) ; + $leftframe -> pack ( -side => 'left' , -fill => 'both' , -expand => 1 ) ; + # right + $request -> pack ( -side => 'top' , -fill => 'x' ) ; + $listbox -> pack ( -side => 'bottom' , -fill => 'both' , -expand => 1 ) ; + $rightframe -> pack ( -side => 'right' , -fill => 'both' , -expand => 1 ) ; + $listbox -> bind ('<B1-Motion>', \&show_command ) ; + $listbox -> bind ('<1>' , \&show_command ) ; + $listbox -> bind ('<Key>' , \&show_command ) ; + $textwindow -> tag ('configure', 'user' , -font => $userfont ) ; + $textwindow -> tag ('configure', 'optional' , -font => $userfont ) ; + $textwindow -> tag ('configure', 'command' , -foreground => 'green3' ) ; + $textwindow -> tag ('configure', 'variable' , -font => $userfont ) ; + $textwindow -> tag ('configure', 'default' , -underline => 1 ) ; + $textwindow -> tag ('configure', 'symbol' , -foreground => 'blue3' ) ; + $textwindow -> tag ('configure', 'or' , -foreground => 'yellow3' ) ; + $textwindow -> tag ('configure', 'argument' , -foreground => 'red3' ) ; + $textwindow -> tag ('configure', 'par' , -lmargin1 => '4m' , + -wrap => 'word' , + -lmargin2 => '6m' ) ; + foreach my $chr ('a'..'z','A'..'Z') { + $mainwindow -> bind ( "<KeyPress-$chr>", sub { insert_request(shift, $chr) } ); + } + $request -> bind ('<Return>', sub { handle_request() } ) ; + $mainwindow -> bind ( "<backslash>", sub { insert_request(shift, "\\") } ) ; + $mainwindow -> bind ( "<space>", sub { new_request() } ) ; + $mainwindow -> bind ( "<BackSpace>", sub { delete_request() } ) ; + $mainwindow -> bind ( "<Prior>", sub { prev_command() } ) ; + $mainwindow -> bind ( "<Next>", sub { next_command() } ) ; +} + +sub show { + my ($pre,$post,$sep) = @_; + unless ($pre eq 'debug' && !$Debug) { + $sep = ':' unless defined $sep; + print sprintf("%22s $sep %+s\n",$pre,$post); + } +} + +sub change_setup { + # switches to another setup file + if ($current_interface ne $Interface ) { + my $loc = 0; + if ($current_command) { + $loc = $positions{$Interface}{$current_command} || 0; + } + my @list = sort {lc $a cmp lc $b} keys %{$setups{$Interface}} ; + my $num = 0; + map { $locations{$Interface}{$_} = $num++; } @list; + $listbox -> delete ('0.0', 'end') ; + $listbox -> insert ('end', @list) ; + # try to switch to other command as well, here. + if ($current_command ne '') { + show_command($crosslinks{$Interface}[$loc] || ''); + } else { + $listbox -> selectionSet ('0.0', '0.0') ; + $listbox -> activate ('0.0') ; + } + } + $current_interface = $Interface; + $mainwindow -> focus ; +} + +sub change_part { + if ($Part ne $current_part) { + if($Part eq 'Command') { + show_command(); + } elsif ($Part eq 'Description') { + show_description(); + } elsif ($Part eq 'Comments') { + show_comments(); + } elsif ($Part eq 'Examples') { + show_examples(); + } + } + $current_part = $Part; +} + + +sub setups_found { + # find the setup files + my ($primary) = @_; + $setup_path = `kpsewhich --progname=context cont-en.xml` ; + chomp $setup_path; + show ('debug', "path = '$setup_path'"); + if ($setup_path) { + $setup_path =~ s/cont-en\.xml.*// ; + @setup_files = glob ("${setup_path}cont\-??.xml") ; # HH: pattern patched, too greedy + show ('debug', "globbed path into '@setup_files'"); + if (@setup_files) { + my $found = 0; + foreach (@setup_files) { + s/\.xml.*$//; + s/^.*?cont-/cont-/; + if ($_ eq $primary) { + $found = 1; + show ('debug', "found primary setup '$primary'"); + } else { + show ('debug', "found non-primary setup '$_'"); + } + } + if ($found) { + return 1; + } else { + show('error',"setup file for '$primary' not found, using 'cont-en'"); + $Interface = 'cont-en'; + return 1; + } + } else { + show('error',"setup file glob failed"); + } + } elsif ($!) { + show('error','kpsewhich not found'); + } else { + show('error','setup files not found'); + } + return 0; +} + +sub load_setup { + my ($path,$filename) = @_; + unless (keys %{$setups{$filename}}) { + if (open(IN,"<${path}$filename.xml")) { + my $position = 0 ; + local $/ = '</cd:command>'; + while (my $data= <IN>) { + if ($data =~ /\<\/cd:interface/) { + next; + } + if ($data =~ /\<cd:interface/) { + $data =~ s/^(.*?)\<cd:command/\<cd:command/sm; + my $meta = $1; + } + # + $data =~ s/\s*\n\s*//g; + $data =~ /\<cd:command(.*?)\>/; + my $info = $1; + my ($name,$environment) = ('',''); + while ($info =~ s/^\s*(.*?)\s*=\s*(["'])(.*?)\2\s*//) { + my $a = $1; my $b = $3; + if ($a eq 'name') { + $name = $b; + } elsif ($a eq 'type') { + $environment = $b; + } + } + my $cmd = $name; + if ($environment) { + $cmd = "start" . $name; + } + $setups {$filename}{$cmd} = $data ; + $trees {$filename}{$cmd} = undef; + $positions {$filename}{$cmd} = ++$position ; + $crosslinks{$filename}[$position] = $cmd ; + } + close IN; + # now get explanations as well ... + my $explname = $filename; + $explname =~ s/cont-/expl-/; + my $extras = 0 ; + if (open(IN,"<${path}$explname.xml")) { + local $/ = '</cd:explanation>'; + while (my $data= <IN>) { + if ($data =~ /\<\/cd:explanations/) { + next; + } + if ($data =~ /\<cd:explanations/) { + $data =~ s/^(.*?)\<cd:explanation /\<cd:explanation /sm; + my $meta = $1; + } + # + $extras++; + $data =~ /\<cd:explanation(.*?)\>/; + my $info = $1; + my ($name,$environment) = ('',''); + while ($info =~ s/^\s*(.*?)\s*=\s*(["'])(.*?)\2\s*//) { + my $a = $1; my $b = $3; + if ($a eq 'name') { + $name = $b; + } elsif ($a eq 'type') { + $environment = $b; + } + } + my $cmd = $name; + if ($environment) { + $cmd = "start" . $name; + } + my $comment = ''; + my $description = ''; + my @examples = (); + $data =~ /\<cd:description\>(.*)\<\/cd:description\>/s and $description = $1; + $data =~ /\<cd:comment\>(.*)\<\/cd:comment\>/s and $comment = $1; + while ($data =~ s/\<cd:example\>(.*?)\<\/cd:example\>//s) { + push @examples, $1; + } + if (length($comment) && $comment =~ /\S/) { + $commes {$filename}{$cmd} = $comment; + } + if (length($description) && $description =~ /\S/) { + $descrs {$filename}{$cmd} = $description; + } + my $testex = "@examples"; + if (length($testex) && $testex =~ /\S/) { + $examps {$filename}{$cmd} = [@examples]; + } + } + } + if ($extras) { + show('debug',"interface '$filename', $position\&$extras commands"); + } else { + show('debug',"interface '$filename', $position commands"); + } + } else { + show ('debug',"open() of ${path}$filename.xml failed"); + } + } + $Interface = $filename ; +} + +sub load_setups { + my ($primary) = @_; + # load all setup files, but default to $primary + my $t0 = [Time::HiRes::gettimeofday()]; + foreach my $setup (@setup_files) { + if ($setup ne $primary) { + load_setup ($setup_path,$setup); + show('status',"loading '$setup' took " .Time::HiRes::tv_interval($t0) . " seconds"); + $t0 = [Time::HiRes::gettimeofday()]; + }; + }; + load_setup ($setup_path,$primary); + show('status',"loading '$primary' took " .Time::HiRes::tv_interval($t0) . " seconds"); +} + +my @history = (); +my $current_history = 0; + +sub show_command { + my ($command,$nofix) = @_; + if (keys %{$setups{$Interface}}) { + my $key = ''; + if (defined $command && $command && + (defined $setups{$Interface}{$command} || + defined $setups{$Interface}{"start" . $command})) { + $key = $command; + my $whence =$locations{$Interface}{$command}; + $listbox -> selectionClear ('0.0','end') ; + $listbox -> selectionSet($whence,$whence); + $listbox -> activate($whence); + $listbox -> see($whence); + } else { + $listbox -> selectionSet('0.0','0.0') unless $listbox->curselection(); + $key = $listbox -> get($listbox->curselection()) ; + } + show('debug',"current command: $current_command"); + show('debug'," new command: $key"); + $current_command = $key ; + $textwindow -> delete ('1.0', 'end' ) ; + $partbuttons{"Command"}->select(); + $partbuttons{"Command"}->configure('-state' => 'normal'); + $partbuttons{"Description"}->configure('-state' => 'disabled'); + $partbuttons{"Comments"}->configure('-state' => 'disabled'); + $partbuttons{"Examples"}->configure('-state' => 'disabled'); + if (defined $commes{$Interface}{$key}) { + $partbuttons{"Comments"}->configure('-state' => 'normal'); + } + if (defined $descrs{$Interface}{$key}) { + $partbuttons{"Description"}->configure('-state' => 'normal'); + } + if (defined $examps{$Interface}{$key}) { + $partbuttons{"Examples"}->configure('-state' => 'normal'); + } + unless (defined $nofix && $nofix) { + push @history, $key; + $current_history = $#history; + } + do_update_command ($key) ; + $mainwindow -> update(); + $mainwindow -> focus() ; + } +} + +sub prev_command { + if ($current_history > 0) { + $current_history--; + show_command($history[$current_history],1); + } +} + +sub next_command { + unless ($current_history == $#history) { + $current_history++; + show_command($history[$current_history],1); + } +} + +sub show_description { + $textwindow -> delete ('1.0', 'end' ) ; + if (defined $descrs{$current_interface}{$current_command}) { + $textwindow-> insert ('end',$descrs{$current_interface}{$current_command}); + } + $mainwindow -> update(); + $mainwindow -> focus() ; +} + +sub show_comments { + $textwindow -> delete ('1.0', 'end' ) ; + if (defined $commes{$current_interface}{$current_command}) { + $textwindow-> insert ('end',$commes{$current_interface}{$current_command}); + } + $mainwindow -> update(); + $mainwindow -> focus() ; +} + + +sub show_examples { + $textwindow -> delete ('1.0', 'end' ) ; + if (defined $examps{$current_interface}{$current_command}) { + $textwindow-> insert ('end',join("\n\n",@{$examps{$current_interface}{$current_command}})); + } + $mainwindow -> update(); + $mainwindow -> focus() ; +} + + + + +sub has_attr { + my ($elem,$att,$val) = @_; + return 1 if (attribute($elem,$att) eq $val); + return 0; +} + + +sub view_post { + my ($stuff,$extra) = @_; + $extra = '' unless defined $extra; + $stuff =~ /^(.)(.*?)(.)$/; + my ($l,$c,$r) = ($1,$2,$3); + if ($l eq '[' || $l eq '(') { + return ($l,['symbol','par',$extra],$c,['par',$extra],$r,['symbol','par',$extra],"\n",'par'); + } else { + return ($l,['argument','par',$extra],$c,['par',$extra],$r,['argument','par',$extra],"\n",'par'); + } +} + +sub view_pre { + my ($stuff) = @_; + $stuff =~ /^(.)(.*?)(.)$/; + my ($l,$c,$r) = ($1,$2,$3); + if ($l eq '[' || $l eq '(') { + return ($l,['symbol'],$c,'',$r,['symbol']); + } else { + return ($l,['argument'],$c,'',$r,['argument']); + } +} + +sub create_setup_arguments { + my $argx = shift; + my @predisp = (); + my @postdisp = (); + foreach my $arg (children($argx)) { + if (name($arg) eq 'cd:keywords') { + # children are Constant* & Inherit? & Variable* + my @children = children($arg); + my $optional = (attribute($arg,'optional') eq 'yes' ? 'optional' : ''); + if (@children){ + push @predisp,'[', ['symbol',$optional]; + if (has_attr($arg,'list', 'yes')) { + if (has_attr($arg,'interactive', 'exclusive')) { + push @predisp, '...', ''; + } else { + push @predisp, '..,...,..', ''; + } + } else { + push @predisp,'...', ''; + } + push @predisp,']', ['symbol',$optional]; + } + push @postdisp,'[', ['symbol','par',$optional]; + my $firsttrue = 1; + foreach my $kwd (@children) { + if ($firsttrue) { + $firsttrue = 0; + } else { + push @postdisp,', ', ['symbol','par']; + } + if (name($kwd) eq 'cd:constant' || + name($kwd) eq 'cd:variable') { + my $v = attribute($kwd,'type'); + my $def = ''; + my $var = ''; + $var = 'variable' if (name($kwd) eq 'cd:variable') ; + $def = 'default' if (has_attr($kwd,'default', 'yes')); + if ($v =~ /^cd:/) { + $v =~ s/^cd://; + $v .= "s" if (has_attr($arg,'list', 'yes')); + push @postdisp, $v, ['user',$def,'par',$var]; + } else { + push @postdisp, $v, [$def,'par',$var]; + } + } elsif (name($kwd) eq 'cd:inherit') { + my $v = attribute($kwd,'name'); + $textwindow -> tag ('configure', $v , -foreground => 'blue3',-underline => 1 ) ; + $textwindow -> tagBind($v,'<ButtonPress>',sub {show_command($v)} ); + push @postdisp,"see ","par", "$v", [$v,'par']; + } + } + push @postdisp,']', ['symbol','par',$optional]; + push @postdisp,"\n", 'par'; + } elsif (name($arg) eq 'cd:assignments') { + # children are Parameter* & Inherit? + my @children = children($arg); + my $optional = (attribute($arg,'optional') eq 'yes' ? 'optional' : ''); + if (@children) { + push @predisp,'[', ['symbol',$optional]; + if (has_attr($arg,'list', 'yes')) { + push @predisp, '..,..=..,..', ''; + } else { + push @predisp,'..=..', ''; + } + push @predisp,']', ['symbol',$optional]; + push @postdisp,'[', ['symbol','par',$optional]; + my $isfirst = 1; + foreach my $assn (@children) { + if ($isfirst) { + $isfirst = 0; + } else { + push @postdisp,",\n ", ['symbol','par']; + } + if (name($assn) eq 'cd:parameter') { + push @postdisp,attribute($assn,'name'), 'par'; + push @postdisp,'=', ['symbol','par']; + my $firstxtrue = 1; + foreach my $par (children($assn)) { + if ($firstxtrue) { + $firstxtrue = 0; + } else { + push @postdisp,'|', ['or','par']; + } + if (name($par) eq 'cd:constant' || name($par) eq 'cd:variable') { + my $var = ''; + $var = 'variable' if name($par) eq 'cd:variable'; + my $v = attribute($par,'type'); + if ($v =~ /^cd:/) { + $v =~ s/^cd://; + push @postdisp,$v, ['user','par',$var]; + } else { + push @postdisp,$v, ['par',$var]; + } + } + } + } elsif (name($assn) eq 'cd:inherit') { + my $v = attribute($assn,'name'); + $textwindow -> tag ('configure', $v , -foreground => 'blue3',-underline => 1 ) ; + $textwindow -> tagBind($v,'<ButtonPress>',sub {show_command($v)} ); + push @postdisp,"see ","par", "$v", [$v,'par']; + } + } + push @postdisp,"]", ['symbol','par',$optional], "\n", ''; + } + } elsif (name($arg) eq 'cd:content') { + push @predisp, view_pre('{...}'); + push @postdisp, view_post('{...}'); + } elsif (name($arg) eq 'cd:triplet') { + if (has_attr($arg,'list','yes')) { + push @predisp, view_pre('[x:y:z=,..]'); + push @postdisp,view_post('[x:y:z=,..]'); + } else { + push @predisp, view_pre('[x:y:z=]'); + push @postdisp,view_post('[x:y:z=]'); + } + } elsif (name($arg) eq 'cd:reference') { + my $optional = (attribute($arg,'optional') eq 'yes' ? 'optional' : ''); + if (has_attr($arg,'list','yes')) { + push @postdisp, view_post('[ref,..]',$optional); + push @predisp, view_pre('[ref,..]'); + } else { + push @postdisp, view_post('[ref]',$optional); + push @predisp, view_pre('[ref]');; + } + } elsif (name($arg) eq 'cd:word') { + if (has_attr($arg,'list','yes')) { + push @predisp, view_pre ('{...,...}'); + push @postdisp,view_post('{...,...}'); + } else { + push @predisp, view_pre('{...}'); + push @postdisp, view_post('{...}'); + } + } elsif (name($arg) eq 'cd:nothing') { + my $sep = attribute($arg,'separator'); + if ($sep) { + if($sep eq 'backslash') { +# push @postdisp,'\\\\','par'; + push @predisp,'\\\\',''; + } else { +# push @postdisp,$sep,'par'; + push @predisp,$sep,''; + } + } + push @predisp,'...',''; + push @postdisp,'text',['variable','par'],"\n",'par'; + } elsif (name($arg) eq 'cd:file') { + push @predisp,'...',['default']; + push @postdisp,'...',['default','par'],"\n",'par'; + } elsif (name($arg) eq 'cd:csname') { + push @predisp,'\command',['command']; + push @postdisp,'\command',['command','par'],"\n",'par'; + } elsif (name($arg) eq 'cd:index') { + if (has_attr($arg,'list','yes')) { + push @predisp,view_pre('{..+...+..}'); + push @postdisp,view_post('{..+...+..}'); + } else { + push @predisp, view_pre('{...}'); + push @postdisp,view_post('{...}'); + } + } elsif (name($arg) eq 'cd:position') { + if (has_attr($arg,'list','yes')) { + push @predisp,view_pre('(...,...)'); + push @postdisp,view_post('(...,...)'); + } else { + push @predisp,view_pre('(...)'); + push @postdisp,view_post('(...)'); + } + } elsif (name($arg) eq 'cd:displaymath') { + push @predisp, ('$$',['argument'],'...','','$$',['argument']); + push @postdisp, ('$$',['argument','par'],'...',['par'],'$$',['argument','par']); + } elsif (name($arg) eq 'cd:tex') { + my $sep = attribute($arg,'separator'); + if ($sep) { + if($sep eq 'backslash') { +# push @postdisp,'\\\\','par'; + push @predisp,'\\\\',''; + } else { +# push @postdisp,$sep,'par'; + push @predisp,$sep,''; + } + } + my $cmd = "\\" . attribute($arg,'command'); + push @predisp,$cmd,''; +# push @postdisp,$cmd,['command','par'],"\n",'par'; + } + } + return (\@predisp,\@postdisp); +} + + +# <foo><head id="a">Hello <em>there</em></head><bar>Howdy<ref/></bar>do</foo> +# +# would be: +# +# Tag Content +# ================================================================== +# [foo, [{}, head, [{id => "a"}, 0, "Hello ", em, [{}, 0, "there"]], +# bar, [ {}, 0, "Howdy", ref, [{}]], +# 0, "do" +# ] +# ] + +sub attribute { + my ($elem,$att) = @_; + if (defined $elem->[1] && defined $elem->[1]->[0] && defined $elem->[1]->[0]->{$att}) { + my $ret = $elem->[1]->[0]->{$att}; + show ('debug',"returning attribute $att=$ret"); + return $elem->[1]->[0]->{$att}; + } else { + return ''; + } +} + +sub name { + my ($elem) = @_; + if (defined $elem->[0] ) { + return $elem->[0]; + } else { + return ''; + } +} + +# return all children at a certain level +sub children { + my ($elem) = @_; + if (defined $elem->[1] && defined $elem->[1]->[1]) { + my @items = @{$elem->[1]}; + shift @items ; # deletes the attribute. + my @ret = (); + while (@items) { + push @ret, [shift @items, shift @items]; + } + return @ret; + } else { + return (); + } +} + +# return the first child with the right name +sub find { + my ($elem,$name) = @_; + if ($elem->[0] eq $name) { + return $elem; + } + if (ref($elem->[1]) eq 'ARRAY') { + my @contents = @{$elem->[1]}; + shift @contents; + while (my $ename = shift @contents) { + my $con = shift @contents; + if ($ename eq $name) { + return [$ename,$con]; + } + } + } + return []; +} + +sub do_update_command # type: 0=display, 1=compute only + { my ($command, $type) = @_ ; + $type = 0 unless defined $type; + my $setup; + if (!defined $trees{$Interface}{$command}) { + my $parser = XML::Parser->new('Style' => 'Tree'); + $trees{$Interface}{$command} = $parser->parse($setups{$Interface}{$command}); + } + $setup = $trees{$Interface}{$command} ; + my $predisp = undef; + my $postdisp = undef; + my @cmddisp = (); + my @cmddispafter = (); + my $pradisp = undef; + my $altdisp = undef; + if (attribute($setup,'file')) { + my $filename = attribute($setup,'file'); + my $fileline = attribute($setup,'line') || 0; + $textwindow->insert ('end',"$filename:${fileline}::\n\n", '' ); + } + # start with backslash + push @cmddisp, "\\", 'command' ; + my $env = 0; + if (has_attr($setup,'type','environment')) { + $env = 1; + } + if ($env) { push @cmddisp, "start", 'command' ; } + if ($env) { push @cmddispafter, " ... ", '', "\\stop", 'command' ; } + my $seq = find($setup,'cd:sequence'); + # display rest of command name + foreach my $seqpart (children($seq)) { + my $text = attribute($seqpart,'value'); + if (name($seqpart) eq 'cd:variable') { + push @cmddisp, $text, ['command','user']; + if ($env) { push @cmddispafter, $text, ['command','user']; } + } elsif (name($seqpart) eq 'cd:string') { + push @cmddisp, $text, 'command'; + if ($env) { push @cmddispafter, $text, 'command'; } + } + } + # + my $args = find($setup,'cd:arguments'); + # display commands + if ($args) { + my $curarg = 0; + foreach my $arg (children($args)) { + if (name($arg) eq 'cd:choice') { + my ($a,$b) = children($arg); + ($predisp,$postdisp) = create_setup_arguments(['cd:arguments',[{}, @$a]]); + ($pradisp,$altdisp) = create_setup_arguments(['cd:arguments',[{}, @$b]]); + } else { + ($predisp,$postdisp) = create_setup_arguments($args); + } + $curarg++; + } + } + return if $type; + if(defined $postdisp) { + if(defined $altdisp) { + $textwindow->insert('end',@cmddisp,@$predisp,@cmddispafter, "\n",'', + @cmddisp,@$pradisp,@cmddispafter, "\n\n",'', + @cmddisp, "\n",'', + @$postdisp, "\n",'', + @cmddisp, "\n",'', + @$altdisp); + } else { + $textwindow->insert('end',@cmddisp,@$predisp, @cmddispafter ,"\n\n",'', + @cmddisp,"\n",'', + @$postdisp); + } + } else { + $textwindow->insert('end',@cmddisp); + } +} + + +#D The next feature is dedicated to Tobias, who suggested +#D it, and Taco, who saw it as yet another proof of the +#D speed of \PERL. It's also dedicated to Ton, who needs it +#D for translating the big manual. + +sub handle_request { + my $index = $listbox -> index('end') ; + return unless $index; + my $req = $request -> get ; + return unless $req; + $req =~ s/\\//o ; + $req =~ s/\s//go ; + $request -> delete('0','end') ; + $request -> insert('0',$req) ; + return unless $req; + my ($l,$c) = split (/\./,$index) ; + for (my $i=0;$i<=$l;$i++) { + $index = "$i.0" ; + my $str = $listbox -> get ($index, $index) ; + if (defined $str && ref($str) eq 'ARRAY') { + $str = "@{$str}"; + } + if (defined $str && $str =~ /^$req/) { + show_command($str) ; + return ; + } + } +} + +sub insert_request { + my ($self, $chr) = @_ ; + # don't echo duplicate if $chr was keyed in in the (focussed) entrybox + $request -> insert ('end', $chr) unless $self eq $request; + handle_request(); +} + +sub delete_request { + my $self = shift ; + # delete last character, carefully + if ($self ne $request) { + my $to = $request -> index ('end') ; + my $from = $to - 1 ; + if ($from<0) { $from = 0 } + $request -> delete ($from,$to); + } + handle_request(); +} + +sub new_request { + $request -> delete (0,'end') ; + handle_request(); +} + diff --git a/Master/texmf-dist/scripts/context/perl/texutil.pl b/Master/texmf-dist/scripts/context/perl/texutil.pl new file mode 100644 index 00000000000..0a0d2975354 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/texutil.pl @@ -0,0 +1,2902 @@ +eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}' && eval 'exec perl -S $0 $argv:q' + if 0; + +#D \module +#D [ file=texutil.pl, +#D version=2003.09.16, +#D title=pre- and postprocessing utilities, +#D subtitle=\TEXUTIL, +#D author=Hans Hagen, +#D date=\currentdate, +#D copyright={PRAGMA / Hans Hagen \& Ton Otten}] +#C +#C This module is part of the \CONTEXT\ macro||package and is +#C therefore copyrighted by \PRAGMA. See licen-en.pdf for +#C details. + +# much functionality will move to ctxtools, xmltools and pdftools; that way texutil +# becomes limited to tui processing only, which is cleaner (also for taco's binary +# version) + +# Thanks to Tobias Burnus for the german translations. +# Thanks to Thomas Esser for hooking it into web2c +# Thanks to Taco Hoekwater for making the file -w proof and some fixes +# Thanks to Alex Knowles and friends for the right JPG specs +# Thanks to Sebastian Rahtz for the eps to PDF method +# Thanks to Fabrice Popineau for windows bin code + +#D We started with a hack provided by Thomas Esser. This +#D expression replaces the unix specific line \type +#D {#!/usr/bin/perl}. + +# undocumented: +# +# --analyze file.pdf : reports some statistics +# --purge [jobname] : removes temporary files +# --purgeall [jobname] : removes all temporary files + +#D This is \TEXUTIL, a utility program (script) to be used +#D alongside the \CONTEXT\ macro package. This \PERL\ script is +#D derived from the \MODULA\ version and uses slightly better +#D algoritms for sanitizing \TEX\ specific (sub|)|strings. +#D +#D This implementation has some features not found in the +#D binary version, like scanning illustrations other than \EPS. +#D I would suggest to keep an eye on the version number: + +$Program = "TeXUtil 9.0.0 - ConTeXt / PRAGMA ADE 1992-2004" ; + +#D By the way, this is my first \PERL\ script, which means +#D that it will be improved as soon as I find new and/or more +#D suitable solutions in the \PERL\ manuals. As can be seen in +#D the definition of \type{$Program}, this program is part of +#D the \CONTEXT\ suite, and therefore can communicate with the +#D users in english as well as some other languages. One can +#D set his favourite language by saying something like: + +#D \starttypen +#D perl texutil.pl --int=de --fig *.eps *.tif *.pdf *.png *.jpg +#D \stoptypen +#D +#D or simpler: +#D +#D \starttypen +#D perl texutil.pl --fig *.* +#D \stoptypen + +#D Of course one can also say \type{--interface=nl}, which +#D happens to be my native language. + +#D I won't go into too much detail on the algoritms used. +#D The next few pages show the functionality as reported by the +#D helpinformation and controled by command line arguments +#D and can serve as additional documentation. + +#D \TEXUTIL\ can handle different tasks; which one is active +#D depends on the command line arguments. These are handled by +#D a \PERL\ system module. This means that, at least for the +#D moment, there is no external control as provided by the +#D \PRAGMA\ environment system. + +use Getopt::Long ; +use FindBin ; + +#D We don't want error messages and accept partial switches, +#D which saves users some typing. + +$Getopt::Long::passthrough = 1 ; # no error message +$Getopt::Long::autoabbrev = 1 ; # partial switch accepted + +#D We also predefine the interface language and set a boolean +#D that keeps track of unknown options. \voetnoot {This feature +#D is still to be implemented.} + +$UserInterface = "en" ; +$UnknownOptions = 0 ; +$TcXPath = '' ; + +#D We need this for calling GS. + +use Config ; + +my $dosish = ($Config{'osname'} =~ /^(ms)?dos|^os\/2|^(ms|cyg)win/i) ; + +#D Here come the options: + +&GetOptions + ("references" => \$ProcessReferences, + "ij" => \$ProcessIJ, + "high" => \$ProcessHigh, + "quotes" => \$ProcessQuotes, + "tcxpath=s" => \$TcXPath, + "documents" => \$ProcessDocuments, + "type=s" => \$ProcessType, + "outputfile=s" => \$ProcessOutputFile, + "sources" => \$ProcessSources, + "setups" => \$ProcessSetups, + "templates" => \$ProcessTemplates, + "infos" => \$ProcessInfos, + "figures" => \$ProcessFigures, + "epspage" =>\$ProcessEpsPage, + "epstopdf" =>\$ProcessEpsToPdf, + "logfile" => \$ProcessLogFile, + "box" =>\$ProcessBox, + "hbox" =>\$ProcessHBox, + "vbox" =>\$ProcessVBox, + "criterium=f" =>\$ProcessCriterium, + "unknown" =>\$ProcessUnknown, + "purge" => \$PurgeFiles, + "purgeall" => \$PurgeAllFiles, + "analyze" => \$AnalyzeFile, + "filter" => \$FilterPages, + "help" => \$ProcessHelp, + "silent" => \$ProcessSilent, + "verbose" => \$ProcessVerbose, + "interface=s" => \$UserInterface) ; + +# A bit old, this code, could be an array. Anyhow, we will +# replace texutil soon. + +$InputFile = "@ARGV" ; # niet waterdicht + +#D We need some hacks to suppress terminal output. This +#D piece of code is based on page~193 of "Programming Perl". + +$ProgramLog = "texutil.log" ; + +# Well, it seems that unix' symlinks are sensitive for being +# hijacked. The assumption is that a known file can be a problem. +# Of course when one knows that certains files are processed, +# the names are always known and hijacking can always take +# place. But let's use a slightly less predictable name here: +# +# if ((@ARGV[0]) && (@ARGV[0] ne "")) { +# $ProgramLog = "@ARGV[0]-$ProgramLog" ; +# } else { +# # no need to be silent +# $ProcessSilent = 0 ; +# } +# +# or better, let's drop this feature, since i'm not in the mood +# now to test hacks like this (i'll just wait till the age of +# computer anarchy has ended). + +$ProgramLog = "/dev/null" ; + +# Maybe we should just write to the nul device. (In the rewritten +# version I can treat unix more strick.) + +sub RedirectTerminal + { open SAVEDSTDOUT, ">&STDOUT" ; + open STDOUT, ">$ProgramLog" ; + select STDOUT; $| = 1 } + +#D And, indeed: + +if ($ProcessSilent) + { RedirectTerminal } +else + { $ProcessVerbose = 0 } + +#D We can temporary open the terminal channel. + +sub OpenTerminal + { close STDOUT ; + open STDOUT, ">&SAVEDSTDOUT" } + +sub CloseTerminal + { open SAVEDSTDOUT, ">&STDOUT" ; + open STDOUT, ">>$ProgramLog" ; + select STDOUT; $| = 1 } + +#D By default wildcards are expanded into a list. The +#D subroutine below is therefore only needed when no file or +#D pattern is given. + +sub CompFileName + { my ($a,$b) = @_ ; + my ($fa,$sa) = split(/\./,$a) ; + my ($fb,$sb) = split(/\./,$b) ; + if (($sa =~ /^\d+$/o)&&($sb =~ /^\d+$/o)) + { $a = $fa . "." . sprintf("%10d",$sa) ; $a =~ s/\s/0/o ; + $b = $fb . "." . sprintf("%10d",$sb) ; $b =~ s/\s/0/o } + return (lc ($a) cmp lc ($b)) } + +sub CheckInputFiles + { @UserSuppliedFiles = glob $_[0] ; + @UserSuppliedFiles = sort { CompFileName($a,$b) } @UserSuppliedFiles } + +#D The next subroutine takes care of the optional output +#D filename (e.g. for figure dimensions). + +$ProcessOutputFile = "" ; + +my $Rubish ; + +sub SetOutputFile + { ($OutFilNam, $OutFilSuf) = split (/\./, $_[0], 2) ; + unless ($ProcessOutputFile eq "") + { $ProcessOutputFile .= "." . $OutFilSuf ; + ($OutFilNam, $OutFilSuf, $Rubish) = split (/\./, $ProcessOutputFile , 3)} + $OutputFile = $OutFilNam . "." . $OutFilSuf } + +#D Sometimes we need to split filenames. + +my ($FileName, $FileSuffix) = ("","") ; + +sub SplitFileName + { my $Rubish = "" ; + if ($_[0] =~ /^\.\//) + { ($Rubish, $FileName) = split ( /^\.\//, $_[0], 2) } + else + { $FileName = $_[0] } + return split (/\./, $FileName, 2) } + +#D In order to support multiple interfaces, we save the +#D messages in a hash table. As a bonus we can get a quick +#D overview of the messages we deal with. + +my %MS ; + +sub Report + { foreach $_ (@_) + { if (! defined $MS{$_}) + { print $_ } + else + { print $MS{$_} } + print " " } + print "\n" } + +#D The messages are saved in a hash table and are called +#D by name. This contents of this table depends on the +#D interface language in use. + +#D \startcompressdefinitions + +if ($UserInterface eq "nl") + + { # begin of dutch section + + $MS{"ProcessingReferences"} = "commando's, lijsten en indexen verwerken" ; + $MS{"MergingReferences"} = "indexen samenvoegen" ; + $MS{"GeneratingDocumentation"} = "ConTeXt documentatie file voorbereiden" ; + $MS{"GeneratingSources"} = "ConTeXt broncode file genereren" ; + $MS{"FilteringDefinitions"} = "ConTeXt definities filteren" ; + $MS{"CopyingTemplates"} = "TeXEdit toets templates copieren" ; + $MS{"CopyingInformation"} = "TeXEdit help informatie copieren" ; + $MS{"GeneratingFigures"} = "figuur file genereren" ; + $MS{"FilteringLogFile"} = "log file filteren (poor mans version)" ; + + $MS{"SortingIJ"} = "IJ sorteren onder Y" ; + $MS{"ConvertingHigh"} = "hoge ASCII waarden converteren" ; + $MS{"ProcessingQuotes"} = "characters met accenten afhandelen" ; + $MS{"ForcingFileType"} = "filetype instellen" ; + $MS{"UsingEps"} = "EPS files afhandelen" ; + $MS{"UsingTif"} = "TIF files afhandelen" ; + $MS{"UsingPdf"} = "PDF files afhandelen" ; + $MS{"UsingPng"} = "PNG files afhandelen" ; + $MS{"UsingJpg"} = "JPG files afhandelen" ; + $MS{"EpsToPdf"} = "EPS converteren naar PDF"; + $MS{"EpsPage"} = "EPS pagina instellen"; + $MS{"FilteringBoxes"} = "overfull boxes filteren" ; + $MS{"ApplyingCriterium"} = "criterium toepassen" ; + $MS{"FilteringUnknown"} = "onbekende ... filteren" ; + + $MS{"NoInputFile"} = "geen invoer file opgegeven" ; + $MS{"NoOutputFile"} = "geen uitvoer file gegenereerd" ; + $MS{"EmptyInputFile"} = "lege invoer file" ; + $MS{"NotYetImplemented"} = "nog niet beschikbaar" ; + + $MS{"Action"} = " actie :" ; + $MS{"Option"} = " optie :" ; + $MS{"Error"} = " fout :" ; + $MS{"Remark"} = " opmerking :" ; + $MS{"SystemCall"} = " systeemaanroep :" ; + $MS{"BadSystemCall"} = " foute systeemaanroep :" ; + $MS{"MissingSubroutine"} = " onbekende subroutine :" ; + + $MS{"EmbeddedFiles"} = " gebruikte files :" ; + $MS{"BeginEndError"} = " b/e fout in :" ; + $MS{"SynonymEntries"} = " aantal synoniemen :" ; + $MS{"SynonymErrors"} = " fouten :" ; + $MS{"RegisterEntries"} = " aantal ingangen :" ; + $MS{"RegisterErrors"} = " fouten :" ; + $MS{"PassedCommands"} = " aantal commando's :" ; + + $MS{"MultiPagePdfFile"} = " te veel pagina's :" ; + $MS{"MissingMediaBox"} = " geen mediabox :" ; + $MS{"MissingBoundingBox"} = " geen boundingbox :" ; + + $MS{"NOfDocuments"} = " documentatie blokken :" ; + $MS{"NOfDefinitions"} = " definitie blokken :" ; + $MS{"NOfSkips"} = " overgeslagen blokken :" ; + $MS{"NOfSetups"} = " gecopieerde setups :" ; + $MS{"NOfTemplates"} = " gecopieerde templates :" ; + $MS{"NOfInfos"} = " gecopieerde helpinfos :" ; + $MS{"NOfFigures"} = " verwerkte figuren :" ; + $MS{"NOfBoxes"} = " te volle boxen :" ; + $MS{"NOfUnknown"} = " onbekende ... :" ; + + $MS{"InputFile"} = " invoer file :" ; + $MS{"OutputFile"} = " outvoer file :" ; + $MS{"FileType"} = " type file :" ; + $MS{"EpsFile"} = " eps file :" ; + $MS{"PdfFile"} = " pdf file :" ; + $MS{"TifFile"} = " tif file :" ; + $MS{"PngFile"} = " png file :" ; + $MS{"JpgFile"} = " jpg file :" ; + $MS{"MPFile"} = " metapost file :" ; + + $MS{"LoadedFilter"} = " geladen filter :" ; + $MS{"RemappedKeys"} = " onderschepte keys :" ; + $MS{"WrongFilterPath"} = " fout filter pad :" ; + + $MS{"Overfull"} = "te vol" ; + $MS{"Entries"} = "ingangen" ; + $MS{"References"} = "verwijzingen" ; + + $MS{"PlugInInit"} = " plugin initialized :" ; + $MS{"PlugInReport"} = " plugin report :" ; + + } # end of dutch section + +elsif ($UserInterface eq "de") + + { # begin of german section + + $MS{"ProcessingReferences"} = "Verarbeiten der Befehle, Listen und Register" ; + $MS{"MergingReferences"} = "Register verschmelzen" ; + $MS{"GeneratingDocumentation"} = "Vorbereiten der ConTeXt-Dokumentationsdatei" ; + $MS{"GeneratingSources"} = "Erstellen einer nur Quelltext ConTeXt-Datei" ; + $MS{"FilteringDefinitions"} = "Filtern der ConTeXt-Definitionen" ; + $MS{"CopyingTemplates"} = "Kopieren der TeXEdit-Test-key-templates" ; + $MS{"CopyingInformation"} = "Kopieren der TeXEdit-Hilfsinformation" ; + $MS{"GeneratingFigures"} = "Erstellen einer Abb-Uebersichtsdatei" ; + $MS{"FilteringLogFile"} = "Filtern der log-Datei" ; + + $MS{"SortingIJ"} = "Sortiere IJ nach Y" ; + $MS{"ConvertingHigh"} = "Konvertiere hohe ASCII-Werte" ; + $MS{"ProcessingQuotes"} = "Verarbeiten der Akzentzeichen" ; + $MS{"ForcingFileType"} = "Dateityp einstellen" ; + $MS{"UsingEps"} = "EPS-Dateien verarbeite" ; + $MS{"UsingTif"} = "TIF-Dateien verarbeite" ; + $MS{"UsingPdf"} = "PDF-Dateien verarbeite" ; + $MS{"UsingPng"} = "PNG-Dateien verarbeite" ; + $MS{"UsingJpg"} = "JPG-Dateien verarbeite" ; + $MS{"EpsToPdf"} = "convert EPS to PDF"; + $MS{"EpsPage"} = "setup EPS page"; + + $MS{"FilteringBoxes"} = "Filtern der ueberfuellten Boxen" ; + $MS{"ApplyingCriterium"} = "Anwenden des uebervoll-Kriteriums" ; + $MS{"FilteringUnknown"} = "Filter unbekannt ..." ; + + $MS{"NoInputFile"} = "Keine Eingabedatei angegeben" ; + $MS{"NoOutputFile"} = "Keine Ausgabedatei generiert" ; + $MS{"EmptyInputFile"} = "Leere Eingabedatei" ; + $MS{"NotYetImplemented"} = "Noch nicht verfuegbar" ; + + $MS{"Action"} = " Aktion :" ; + $MS{"Option"} = " Option :" ; + $MS{"Error"} = " Fehler :" ; + $MS{"Remark"} = " Anmerkung :" ; + $MS{"SystemCall"} = " system call :" ; + $MS{"BadSystemCall"} = " bad system call :" ; + $MS{"MissingSubroutine"} = " missing subroutine :" ; + $MS{"SystemCall"} = " Systemaufruf :" ; + $MS{"BadSystemCall"} = " Fehlerhafter Aufruf :" ; + $MS{"MissingSubroutine"} = " Fehlende Unterroutine :" ; + + $MS{"EmbeddedFiles"} = " Eingebettete Dateien :" ; + $MS{"BeginEndError"} = " Beg./Ende-Fehler in :" ; + $MS{"SynonymEntries"} = " Synonymeintraege :" ; + $MS{"SynonymErrors"} = " Fehlerhafte Eintraege :" ; + $MS{"RegisterEntries"} = " Registereintraege :" ; + $MS{"RegisterErrors"} = " Fehlerhafte Eintraege :" ; + $MS{"PassedCommands"} = " Verarbeite Befehle :" ; + + $MS{"MultiPagePdfFile"} = " zu viele Seiten :" ; + $MS{"MissingMediaBox"} = " fehlende mediabox :" ; + $MS{"MissingBoundingBox"} = " fehlende boundingbox :" ; + + $MS{"NOfDocuments"} = " Dokumentbloecke :" ; + $MS{"NOfDefinitions"} = " Definitionsbloecke :" ; + $MS{"NOfSkips"} = "Uebersprungene Bloecke :" ; + $MS{"NOfSetups"} = " Kopierte setups :" ; + $MS{"NOfTemplates"} = " Kopierte templates :" ; + $MS{"NOfInfos"} = " Kopierte helpinfos :" ; + $MS{"NOfFigures"} = " Verarbeitete Abb. :" ; + $MS{"NOfBoxes"} = " Zu volle Boxen :" ; + $MS{"NOfUnknown"} = " Unbekannt ... :" ; + + $MS{"InputFile"} = " Eingabedatei :" ; + $MS{"OutputFile"} = " Ausgabedatei :" ; + $MS{"FileType"} = " Dateityp :" ; + $MS{"EpsFile"} = " eps-Datei :" ; + $MS{"PdfFile"} = " pdf-Datei :" ; + $MS{"TifFile"} = " tif-Datei :" ; + $MS{"PngFile"} = " png-Datei :" ; + $MS{"JpgFile"} = " jpg-Datei :" ; + $MS{"MPFile"} = " metapost-Datei :" ; + + $MS{"LoadedFilter"} = " loaded filter :" ; # tobias + $MS{"RemappedKeys"} = " remapped keys :" ; # tobias + $MS{"WrongFilterPath"} = " wrong filter path :" ; # tobias + + $MS{"Overfull"} = "zu voll" ; + $MS{"Entries"} = "Eintraege" ; + $MS{"References"} = "Referenzen" ; + + $MS{"ExtraProgram"} = " extra program :" ; + $MS{"PlugInInit"} = " plugin initialized :" ; + $MS{"PlugInReport"} = " plugin report :" ; + + } # end of german section + +elsif ($UserInterface eq "it") + + { # begin of italian section + + $MS{"ProcessingReferences"} = "elaborazione di comandi, liste e registri" ; + $MS{"MergingReferences"} = "fusione dei registri" ; + $MS{"GeneratingDocumentation"} = "preparazione del file di documentazione ConTeXt" ; + $MS{"GeneratingSources"} = "generazione del solo sorgente ConTeXt" ; + $MS{"FilteringDefinitions"} = "filtraggio delle definizioni formali ConTeXt" ; + $MS{"CopyingTemplates"} = "copia dei modelli rapidi di voci di TeXEdit" ; + $MS{"CopyingInformation"} = "copia delle informazioni di aiuto di TeXEdit" ; + $MS{"GeneratingFigures"} = "generazione del file di elengo delle figure" ; + $MS{"FilteringLogFile"} = "filtraggio del file di log" ; + + $MS{"SortingIJ"} = "IJ elencato sotto Y" ; + $MS{"ConvertingHigh"} = "conversione dei valori ASCII alti" ; + $MS{"ProcessingQuotes"} = "elaborazione dei caratteri accentati" ; + $MS{"ForcingFileType"} = "impostazine del tipo di file" ; + $MS{"UsingEps"} = "elaborazione del file EPS" ; + $MS{"UsingTif"} = "elaborazione del file TIF" ; + $MS{"UsingPdf"} = "elaborazione del file PDF" ; + $MS{"UsingPng"} = "elaborazione del file PNG" ; + $MS{"UsingJpg"} = "elaborazione del file JPG" ; + $MS{"EpsToPdf"} = "conversione da EPS a PDF"; + $MS{"EpsPage"} = "impostazione pagina EPS"; + + $MS{"FilteringBoxes"} = "filtraggio delle overfull boxes" ; + $MS{"ApplyingCriterium"} = "applicazione del criterio overfull" ; + $MS{"FilteringUnknown"} = "filtraggio dei messaggi non conosciuti ..." ; + + $MS{"NoInputFile"} = "nessun file di input specificato" ; + $MS{"NoOutputFile"} = "nessun file di output generato" ; + $MS{"EmptyInputFile"} = "file di input vuoto" ; + $MS{"NotYetImplemented"} = "non ancora disponibile" ; + + $MS{"Action"} = " azione :" ; + $MS{"Option"} = " opzione :" ; + $MS{"Error"} = " errore :" ; + $MS{"Remark"} = " commento :" ; + $MS{"SystemCall"} = " chiamata di sistema :" ; + $MS{"BadSystemCall"} = "chiamata di sistema er :" ; # GB: Hans, I need more space! + $MS{"MissingSubroutine"} = " subroutine mancante :" ; + + $MS{"EmbeddedFiles"} = " file inclusi :" ; + $MS{"BeginEndError"} = " errore di i/f in :" ; + $MS{"SynonymEntries"} = " voci di sinonimi :" ; + $MS{"SynonymErrors"} = " voci errate :" ; + $MS{"RegisterEntries"} = " voci di registro :" ; + $MS{"RegisterErrors"} = " voci errate :" ; + $MS{"PassedCommands"} = " comandi passati :" ; + + $MS{"MultiPagePdfFile"} = " troppe pagine :" ; + $MS{"MissingMediaBox"} = " mediabox mancante :" ; + $MS{"MissingBoundingBox"} = " boundingbox mancante :" ; + + $MS{"NOfDocuments"} = " blocchi di documento :" ; + $MS{"NOfDefinitions"} = "blocchi di definizioni :" ; + $MS{"NOfSkips"} = " blocchi saltati :" ; + $MS{"NOfSetups"} = " impostazioni copiate :" ; + $MS{"NOfTemplates"} = " modelli copiati :" ; + $MS{"NOfInfos"} = " helpinfo copiati :" ; + $MS{"NOfFigures"} = " figure elaborate :" ; + $MS{"NOfBoxes"} = " overfull boxes :" ; + $MS{"NOfUnknown"} = " sconosciuti ... :" ; + + $MS{"InputFile"} = " file di input :" ; + $MS{"OutputFile"} = " file di output :" ; + $MS{"FileType"} = " tipo di file :" ; + $MS{"EpsFile"} = " file eps :" ; + $MS{"PdfFile"} = " file pdf :" ; + $MS{"TifFile"} = " file tif :" ; + $MS{"PngFile"} = " file png :" ; + $MS{"JpgFile"} = " file jpg :" ; + $MS{"MPFile"} = " file metapost :" ; + + $MS{"LoadedFilter"} = " filtro caricato :" ; + $MS{"RemappedKeys"} = " voci rimappate :" ; + $MS{"WrongFilterPath"} = "percorso filtro errato :" ; + + $MS{"Overfull"} = "overfull" ; + $MS{"Entries"} = "voci" ; + $MS{"References"} = "riferimenti" ; + + $MS{"ExtraProgram"} = " extra program :" ; + $MS{"PlugInInit"} = " plugin initialized :" ; + $MS{"PlugInReport"} = " plugin report :" ; + + } # end of italian section + +else + + { # begin of english section + + $MS{"ProcessingReferences"} = "processing commands, lists and registers" ; + $MS{"MergingReferences"} = "merging registers" ; + $MS{"GeneratingDocumentation"} = "preparing ConTeXt documentation file" ; + $MS{"GeneratingSources"} = "generating ConTeXt source only file" ; + $MS{"FilteringDefinitions"} = "filtering formal ConTeXt definitions" ; + $MS{"CopyingTemplates"} = "copying TeXEdit quick key templates" ; + $MS{"CopyingInformation"} = "copying TeXEdit help information" ; + $MS{"GeneratingFigures"} = "generating figure directory file" ; + $MS{"FilteringLogFile"} = "filtering log file" ; + + $MS{"SortingIJ"} = "sorting IJ under Y" ; + $MS{"ConvertingHigh"} = "converting high ASCII values" ; + $MS{"ProcessingQuotes"} = "handling accented characters" ; + $MS{"ForcingFileType"} = "setting up filetype" ; + $MS{"UsingEps"} = "processing EPS-file" ; + $MS{"UsingTif"} = "processing TIF-file" ; + $MS{"UsingPdf"} = "processing PDF-file" ; + $MS{"UsingPng"} = "processing PNG-file" ; + $MS{"UsingJpg"} = "processing JPG-file" ; + $MS{"EpsToPdf"} = "convert EPS to PDF"; + $MS{"EpsPage"} = "setup EPS page"; + + $MS{"FilteringBoxes"} = "filtering overfull boxes" ; + $MS{"ApplyingCriterium"} = "applying overfull criterium" ; + $MS{"FilteringUnknown"} = "filtering unknown ..." ; + + $MS{"NoInputFile"} = "no input file given" ; + $MS{"NoOutputFile"} = "no output file generated" ; + $MS{"EmptyInputFile"} = "empty input file" ; + $MS{"NotYetImplemented"} = "not yet available" ; + + $MS{"Action"} = " action :" ; + $MS{"Option"} = " option :" ; + $MS{"Error"} = " error :" ; + $MS{"Remark"} = " remark :" ; + $MS{"SystemCall"} = " system call :" ; + $MS{"BadSystemCall"} = " bad system call :" ; + $MS{"MissingSubroutine"} = " missing subroutine :" ; + + $MS{"EmbeddedFiles"} = " embedded files :" ; + $MS{"BeginEndError"} = " b/e error in :" ; + $MS{"SynonymEntries"} = " synonym entries :" ; + $MS{"SynonymErrors"} = " bad entries :" ; + $MS{"RegisterEntries"} = " register entries :" ; + $MS{"RegisterErrors"} = " bad entries :" ; + $MS{"PassedCommands"} = " passed commands :" ; + + $MS{"MultiPagePdfFile"} = " too many pages :" ; + $MS{"MissingMediaBox"} = " missing mediabox :" ; + $MS{"MissingBoundingBox"} = " missing boundingbox :" ; + + $MS{"NOfDocuments"} = " document blocks :" ; + $MS{"NOfDefinitions"} = " definition blocks :" ; + $MS{"NOfSkips"} = " skipped blocks :" ; + $MS{"NOfSetups"} = " copied setups :" ; + $MS{"NOfTemplates"} = " copied templates :" ; + $MS{"NOfInfos"} = " copied helpinfos :" ; + $MS{"NOfFigures"} = " processed figures :" ; + $MS{"NOfBoxes"} = " overfull boxes :" ; + $MS{"NOfUnknown"} = " unknown ... :" ; + + $MS{"InputFile"} = " input file :" ; + $MS{"OutputFile"} = " output file :" ; + $MS{"FileType"} = " file type :" ; + $MS{"EpsFile"} = " eps file :" ; + $MS{"PdfFile"} = " pdf file :" ; + $MS{"TifFile"} = " tif file :" ; + $MS{"PngFile"} = " png file :" ; + $MS{"JpgFile"} = " jpg file :" ; + $MS{"MPFile"} = " metapost file :" ; + + $MS{"LoadedFilter"} = " loaded filter :" ; + $MS{"RemappedKeys"} = " remapped keys :" ; + $MS{"WrongFilterPath"} = " wrong filter path :" ; + + $MS{"Overfull"} = "overfull" ; + $MS{"Entries"} = "entries" ; + $MS{"References"} = "references" ; + + $MS{"ExtraProgram"} = " extra program :" ; + $MS{"PlugInInit"} = " plugin initialized :" ; + $MS{"PlugInReport"} = " plugin report :" ; + + } # end of english section + +#D \stopcompressdefinitions + +#D Showing the banner (name and version of the program) and +#D offering helpinfo is rather straightforward. + +sub ShowBanner + { Report("\n $Program\n") } + +sub ShowHelpInfo + { Report("HelpInfo") } + +#D The helpinfo is also saved in the hash table. This looks +#D like a waste of energy and space, but the program gains +#D readability. + +#D \startcompressdefinitions + +if ($UserInterface eq "nl") + + { # begin of dutch section + + $MS{"HelpInfo"} = + +" --references hulp file verwerken / tui->tuo \n" . +" --ij : IJ als Y sorteren \n" . +" --high : hoge ASCII waarden converteren \n" . +" --quotes : quotes converteren \n" . +" --tcxpath : tcx filter pad \n" . +" \n" . +" --purge(all) tijdelijke (klad) files verwijderen \n" . +" \n" . +" --documents documentatie file genereren / tex->ted \n" . +" --sources broncode file genereren / tex->tes \n" . +" --setups ConTeXt definities filteren / tex->texutil.tus \n" . +" --templates TeXEdit templates filteren / tex->tud \n" . +" --infos TeXEdit helpinfo filteren / tex->tud \n" . +" \n" . +" --figures eps figuren lijst genereren / *->texutil.tuf \n" . +" --epspage : voorbereiden voor pdf \n" . +" --epstopdf : omzetten naar pdf \n" . +" \n" . +" --logfile logfile filteren / log->$ProgramLog \n" . +" --box : overfull boxes controleren \n" . +" --criterium : overfull criterium in pt \n" . +" --unknown :onbekende ... controleren \n" ; + + } # end of dutch section + +elsif ($UserInterface eq "de") + + { # begin of german section + + $MS{"HelpInfo"} = + +" --references Verarbeiten der Hilfsdatei / tui->tuo \n" . +" --ij : Sortiere IJ als Y \n" . +" --high : Konvertiere hohe ASCII-Werte \n" . +" --quotes : Konvertiere akzentuierte Buchstaben \n" . +" --tcxpath : tcx Filter Path \n" . +" \n" . +" --purge(all) entferne temporaere ConTeXt-Dateien \n" . +" \n" . +" --documents Erstelle Dokumentationsdatei / tex->ted \n" . +" --sources Erstelle reine Quelltextdateien / tex->tes \n" . +" --setups Filtere ConTeXt-Definitionen / tex->texutil.tus\n" . +" --templates Filtere TeXEdit-templates / tex->tud \n" . +" --infos Filtere TeXEdit-helpinfo / tex->tud \n" . +" \n" . +" --figures Erstelle eps-Abbildungsliste / *->texutil.tuf \n" . +" --epspage : Bereite fuer pdf vor \n" . +" --epstopdf : Konvertiere zu pdf \n" . +" \n" . +" --logfile Filtere log-Datei / log->$ProgramLog \n" . +" --box : Ueberpruefe uebervolle Boxen \n" . +" --criterium : Uebervoll-Kriterium in pt \n" . +" --unknown : Ueberpruefe auf unbekannte ... \n" ; + + } # end of german section + +elsif ($UserInterface eq "it") + + { # begin of italian section GB: Hans, I need more space! + + $MS{"HelpInfo"} = + +" --references elabora file ausiliari / tui->tuo \n" . +" --ij : elenca IJ come Y \n" . +" --high : converti i valori ASCII alti \n" . +" --quotes : converti caratteri accentati \n" . +" --tcxpath : percorso del filtro tcx \n" . +" \n" . +" --purge(all) rimuovi i file temporanei ConTeXt \n" . +" \n" . +" --documents genera file di documentazione / tex->ted \n" . +" --sources genera solo sorgente / tex->tes \n" . +" --setups filtra definizioni ConTeXt / tex->texutil.tus \n" . +" --templates filtra modelli TeXEdit / tex->tud \n" . +" --infos filtra helpinfo TeXEdit / tex->tud \n" . +" \n" . +" --figures genera lista figure eps / *->texutil.tuf \n" . +" --epspage : prepara per pdf \n" . +" --epstopdf : converti in pdf \n" . +" \n" . +" --logfile filtra logfile / log->$ProgramLog \n" . +" --box : controlla overful boxes \n" . +" --criterium : criterio overfull in pt \n" . +" --unknown : controlla sconosciuti ... \n" ; + + } # end of italian section + +else + + { # begin of english section + + $MS{"HelpInfo"} = + +" --references process auxiliary file / tui->tuo \n" . +" --ij : sort IJ as Y \n" . +" --high : convert high ASCII values \n" . +" --quotes : convert quotes characters \n" . +" --tcxpath : tcx filter path \n" . +" \n" . +" --purge(all) clean up temporary context files \n" . +" \n" . +" --documents generate documentation file / tex->ted \n" . +" --sources generate source only file / tex->tes \n" . +" --setups filter ConTeXt definitions / tex->texutil.tus \n" . +" --templates filter TeXEdit templates / tex->tud \n" . +" --infos filter TeXEdit helpinfo / tex->tud \n" . +" \n" . +" --figures generate eps figure list / *->texutil.tuf \n" . +" --epspage : prepare for pdf \n" . +" --epstopdf : convert to pdf \n" . +" \n" . +" --logfile filter logfile / log->$ProgramLog \n" . +" --box : check overful boxes \n" . +" --criterium : overfull criterium in pt \n" . +" --unknown : check unknown ... \n" ; + + } # end of english section + +#D \stopcompressdefinitions + +#D In order to sort strings correctly, we have to sanitize +#D them. This is especially needed when we include \TEX\ +#D commands, quotes characters and compound word placeholders. +#D +#D \startopsomming[opelkaar] +#D \som \type{\name}: csnames are stripped +#D \som \type{{}}: are removed +#D \som \type{\"e}: and alike are translated into \type{"e} etc. +#D \som \type{"e}: is translated into an \type{e} and \type{b} etc. +#D \som \type{||}: becomes \type{-} +#D \som \type{\-}: also becomes \type{-} +#D \som \type{<*..>}: becomes \type{..} (internal XML entity) +#D \stopopsomming +#D +#D Of course other accented characters are handled too. The +#D appended string is responsible for decent sorting. +#D +#D \startPL +#D $TargetString = SanitizedString ( $SourceString ) ; +#D \stopPL +#D +#D The sort order depends on the ordering in array +#D \type{$ASCII}: + +$ASCII{"^"} = "a" ; $ASCII{'"'} = "b" ; $ASCII{"`"} = "c" ; +$ASCII{"'"} = "d" ; $ASCII{"~"} = "e" ; $ASCII{","} = "f" ; + +#sub SanitizedString +# { my ($string) = $_[0] ; +# if ($ProcessQuotes) +# { $string =~ s/\\([\^\"\`\'\~\,])/$1/gio ; +# $copied = $string ; +# $copied =~ s/([\^\"\`\'\~\,])([a-zA-Z])/$ASCII{$1}/gio ; +# $string =~ s/([\^\"\`\'\~\,])([a-zA-Z])/$2/gio ; +# $string=$string.$copied } +# $string =~ s/\\-|\|\|/\-/gio ; +# $string =~ s/\\[a-zA-Z]*| |\{|\}//gio ; +# return $string } + +#D YET UNDOCUMENTED + +my $SortN = 0 ; my @Filter ; + +# copied from texexec + +my @paths ; +my $kpsewhich = '' ; +my $pathslash = '/' ; if ($0 =~ /\\/) { $pathslash = "\\" } + +sub checked_path + { my $path = shift ; + if ((defined($path))&&($path ne '')) + { $path =~ s/[\/\\]/$pathslash/go ; + $path =~ s/[\/\\]*$//go ; + $path .= $pathslash } + else + { $path = '' } + return $path } + +if ($ENV{PATH} =~ /\;/) + { @paths = split(/\;/,$ENV{PATH}) } +else + { @paths = split(/\:/,$ENV{PATH}) } + +# until here. + +sub InitializeKeys + { my $filename = $ARGV[0] ; + return unless (open(TEX,"$filename.tex")) ; + for ($i=0;$i<=255;$i++) + { @Filter[$i] = $i } + if ($TcXPath eq '') + { foreach (@paths) + { my $p = checked_path($_) . 'kpsewhich' ; + if ((-e $p)||(-e $p . '.exe')) + { $kpsewhich = $p ; last } } } + $kpsewhich = "\"$kpsewhich\"" if ($kpsewhich =~ m/^[^\"].* /) ; + while (<TEX>) + { chomp ; + my $Filter ; + if (/^\%/) + { if (s/.*translat.*?=([\:\/0-9\-a-z]*)/$1/oi) + { my $Translation = $_ ; + if ($TcXPath ne '') + { $TcXPath = checked_path($TcXPath) ; + $Filter = "$TcXPath$pathslash$Translation.tcx" } + elsif ($kpsewhich ne '') + { $Filter = `$kpsewhich --format="web2c files" $Translation.tcx` ; + chomp $Filter } + else + { last } + if (open(ASC,$Filter)) + { Report ("LoadedFilter", $Translation) ; + while (<ASC>) + { if (/^(\d+)\s*(\d+)/) + { @Filter[$2] = $1 } } + close (ASC) } + elsif ($TcXPath ne '') + { Report ("WrongFilterPath", $TcXPath) } + last } } + else + { last } } + close (TEX) } + +sub HandleKey + { ++$SortN ; + $RestOfLine =~ s/\{(.*)\}/$1/o ; + my ($lan, $enc, $str, $chr, $map, $alf) = split(/\}\s*\{/, $RestOfLine) ; + if ($str =~ /^(\d+)/) { $str = ''.chr(@Filter[$1]) } + $map = chr(ord($MAP[$i])+128) ; + $STR[$SortN] = $str ; + $CHR[$SortN] = $chr ; + $MAP[$SortN] = $map ; +#print "$chr$map = $alf\n" ; +# $ALF{"$chr$map"} = $alf } + $ALF{"$map"} = $alf } + +sub FlushKeys + { Report ("RemappedKeys", $SortN) } + +sub SanitizedString + { my $string = my $original = shift ; + if ($SortN) + { my $copied = $string ; + for ($i=1;$i<=$SortN;$i++) + { my $s = $STR[$i] ; + my $c = $CHR[$i] ; + my $m = $MAP[$i] ; + # print "[$i $s $c $m]\n" ; + $string =~ s/($s)/$c/ge ; + $copied =~ s/($s)/$m/ge } + $string .= "\x00"; + $string .= $copied } + elsif ($ProcessQuotes) + { $string =~ s/\\([\^\"\`\'\~\,])/$1/gio ; + $copied = $string ; + $copied =~ s/([\^\"\`\'\~\,])([a-zA-Z])/$ASCII{$1}/gi ; + $string =~ s/([\^\"\`\'\~\,])([a-zA-Z])/$2/gio ; + $string .= "\x00"; + $string .= $copied } +# new and very experimental, will change +$string =~ s/\<\*(.*?)\>/\\$1 /go ; # reduce entities / will be table too +$string =~ s/\\getXMLentity\s*\{(.*?)\}/$1/gio ; # {tex} => tex +$string =~ s/\<[a-zA-Z\/].*?\>//go ; # remove elements +# so far + $string =~ s/\\-|\|\|/\-/gio ; + $string =~ s/\\[a-zA-Z]*| |\{|\}//gio ; # ? +#print "$original $string $copied\n" ; + return $string } + +#D This subroutine looks a bit complicated, which is due to the +#D fact that we want to sort for instance an accented \type{e} +#D after the plain \type{e}, so the imaginary words +#D +#D \starttypen +#D eerste +#D \"eerste +#D \"e\"erste +#D eerst\"e +#D \stoptypen +#D +#D come out in an acceptable order. + +#D We also have to deal with the typical \TEX\ sequences with +#D the double \type{^}'s, like \type{^^45}. These hexadecimal +#D coded characters are just converted. +#D +#D \startPL +#D $TargetString = HighConverted ( $SourceString ) ; +#D \stopPL + +sub HighConverted + { my ($string) = $_[0] ; + $string =~ s/\^\^([a-f0-9][a-f0-9])/chr hex($1)/geo ; + return $string } + +#D \extras +#D {references} +#D +#D \CONTEXT\ can handle many lists, registers (indexes), +#D tables of whatever and references. This data is collected +#D in one pass and processed in a second one. In between, +#D relevant data is saved in the file \type{\jobname.tui}. +#D This file also holds some additional information concerning +#D second pass optimizations. +#D +#D The main task of \TEXUTIL\ is to sort lists and registers +#D (indexes). The results are stored in again one file called +#D \type{\jobname.tuo}. +#D +#D Just for debugging purposes the nesting of files loaded +#D during the \CONTEXT\ run is stored. Of course this only +#D applies to files that are handled by the \CONTEXT\ file +#D structuring commands (projects, products, components and +#D environments). +#D +#D We have to handle the entries: +#D +#D \starttypen +#D f b {test} +#D f e {test} +#D \stoptypen +#D +#D and only report some status info at the end of the run. + +sub InitializeFiles + { $NOfFiles = 0 ; + $NOfBadFiles = 0 } + +sub HandleFile + { $RestOfLine =~ s/.*\{(.*)\}/$1/gio ; + ++$Files{$RestOfLine} } + +sub FlushFiles # hash needs to be sorted, else problem on macosx + { print TUO "%\n" . "% $Program / Files\n" . "%\n" ; + foreach $File (sort keys %Files) + { print TUO "% $File ($Files{$File})\n" } + print TUO "%\n" ; + $NOfFiles = keys %Files ; + Report("EmbeddedFiles", $NOfFiles) ; + foreach $File (sort keys %Files) + { unless (($Files{$File} % 2) eq 0) + { ++$NOfBadFiles ; + Report("BeginEndError", $File) } } } + +#D Commands don't need a special treatment. They are just +#D copied. Such commands are tagged by a \type{c}, like: +#D +#D \starttypen +#D c \thisisutilityversion{year.month.day} +#D c \twopassentry{class}{key}{value} +#D c \mainreference{prefix}{entry}{pagenumber}{realpage}{tag} +#D c \listentry{category}{tag}{number}{title}{pagenumber}{realpage} +#D c \initializevariable\realnumberofpages{number} +#D \stoptypen +#D +#D For historic reasons we check for the presense of the +#D backslash. + +my $NOfPositionsFound = 0 ; +my $TotalNOfPositions = 0 ; +my $TotalNOfMPgraphics = 0 ; + +my $SectionSeparator = ":" ; + +sub InitializeCommands + { print TUO "%\n" . "% $Program / Commands\n" . "%\n" ; + $NOfCommands = 0 } + +sub HandleCommand + { ++$NOfCommands ; + $RestOfLine =~ s/^\\//go ; + if ($RestOfLine =~ /^pospxy/o) + { ++$NOfPositionsFound } + elsif ($RestOfLine =~ /^initializevariable\\totalnofpositions\{(.*)\}/o) + { $TotalNOfPositions = $1 } + elsif ($RestOfLine =~ /^initializevariable\\totalnofMPgraphics\{(.*)\}/o) + { $TotalNOfMPgraphics = $1 } +# todo: reg how to +# elsif ($RestOfLine =~ /^thisissectionseparator\{(.*)\}/o) +# { $SectionSeparator = $1 } + elsif ($RestOfLine =~ /^thisisbytesequence\{(.*)\}/o) + { $RestOfLine =~ s/\^//go } + print TUO "\\$RestOfLine\n" } + +sub FlushCommands + { Report ("PassedCommands", $NOfCommands) } + +#D Experimental: Extra +#D +#D s p : extra programs + +my @ExtraPrograms = () ; + +sub InitializeExtra + { } + +sub HandleExtra + { if ($RestOfLine =~ /(.)\s+(.*)\s*$/o) + { if ($1 eq "p") + { my $str = $2 ; $str =~ s/^\{(.*)\}$/$1/o ; + push @ExtraPrograms,$str } } } + +sub FlushExtra + { print TUO "%\n" . "% $Program / System\n" . "%\n" ; + foreach $EP (@ExtraPrograms) + { print TUO "% extra program : $EP\n" } } + +sub RunExtraPrograms + { foreach $EP (@ExtraPrograms) + { Report ("ExtraProgram", $EP) ; + system($EP) } } + +#D Plugins +#D +#D test.pm: +#D +#D \starttypen +#D see plugtest.pm +#D \stoptypen +#D +#D utility format: +#D +#D \starttypen +#D p u {name} {data} {data} ... +#D \stoptypen + +my $pm_path ; + +BEGIN + { ## $pm_path = `kpsewhich --format="other text files" --progname=context texutil.pl` ; + ## chomp($pm_path) ; + # $pm_path =~ s/texutil\.pl.*// } + # $pm_path = $0 ; + # $pm_path =~ s/\\/\//o ; + # $pm_path =~ s/texutil\.pl.*//io ; + ## $pm_path =~ s/(.*)texutil.*?$/$1/i ; + $pm_path = "$FindBin::Bin/" ; + if ($pm_path eq "") { $pm_path = "./" } } + +use lib $pm_path ; + +my %UserPlugIns ; + +sub HandlePlugIn + { if ($RestOfLine =~ /\s*u\s*\{(.*?)\}\s*(.*)\s*/io) + { my $tag = $1 ; + my $arg = $2 ; + if (! defined($UserPlugIns{$tag})) + { $UserPlugIns{$tag} = 1 ; + eval("use $tag") ; + my $result = $tag->identify ; + if ($result ne "") + { Report ("PlugInInit", "$tag -> $result") } + else + { Report ("PlugInInit", $tag ) } + $tag->initialize() } + if (defined($UserPlugIns{$tag})) + { $arg =~ s/\{(.*)\}/$1/o ; + my @args = split(/\}\s*\{/o, $arg) ; + $tag->handle(@args) } } } + +sub FlushPlugIns + { foreach my $tag (keys %UserPlugIns) + { my @report = $tag->report ; + foreach $rep (@report) + { my ($key,$val) = split (/\s*\:\s*/,$rep) ; + if ($val ne "") + { Report ("PlugInReport", "$tag -> $key -> $val") } + else + { Report ("PlugInReport", "$tag -> $key") } } + $tag->process ; + print TUO "%\n" . "% $Program / " . $tag->identify . "\n" . "%\n" ; + foreach my $str ($tag->results) + { print TUO "\\plugincommand\{$str\}\n" } } } + +#D Synonyms are a sort of key||value pairs and are used for +#D ordered lists like abbreviations and units. +#D +#D \starttypen +#D s e {class}{sanitized key}{key}{associated data} +#D \stoptypen +#D +#D The sorted lists are saved as (surprise): +#D +#D \starttypen +#D \synonymentry{class}{sanitized key}{key}{associated data} +#D \stoptypen + +sub InitializeSynonyms + { $NOfSynonyms = 0 ; + $NOfBadSynonyms = 0 } + +#M \definieersynoniem [testname] [testnames] [\testmeaning] +#M +#M \stelsynoniemenin [testname] [criterium=alles] + +#D Let's first make clear what we can expect. Synonym +#D entries look like: +#D +#D \startbuffer +#D \testname [alpha] {\sl alpha} {a greek letter a} +#D \testname {alpha} {another a} +#D \testname [Beta] {\kap{beta}} {a greek letter b} +#D \testname {beta} {indeed another b} +#D \testname {gamma} {something alike g} +#D \testname {delta} {just a greek d} +#D \stopbuffer +#D +#D \typebuffer +#D +#D This not that spectacular list is to be sorted according +#D to the keys (names). \haalbuffer + +sub HandleSynonym + { ++$NOfSynonyms ; + ($SecondTag, $RestOfLine) = split(/ /, $RestOfLine, 2) ; + ($Class, $Key, $Entry, $Meaning) = split(/} \{/, $RestOfLine) ; + chop $Meaning ; + $Class = substr $Class, 1 ; + if ($Entry eq "") + { ++$NOfBadSynonyms } + else + { $SynonymEntry[$NOfSynonyms] = + join ($JOIN,$Class,$Key,$Entry,$Meaning) } } + +#D Depending on the settings\voetnoot{One can call for +#D all defined entries, call only the used ones, change +#D layout, attach (funny) commands etc.} a list of +#D {\em testnames} looks like: +#D +#D \plaatslijstmettestnames +#D +#D Watch the order in which these entries are sorted. + +sub FlushSynonyms + { print TUO "%\n" . "% $Program / Synonyms\n" . "%\n" ; + @SynonymEntry = sort { lc($a) cmp lc($b) } @SynonymEntry ; + $NOfSaneSynonyms = 0 ; + for ($n=1; $n<=$NOfSynonyms; $n++) + { # check normally not needed + if (($n==1)||($SynonymEntry[$n] ne $SynonymEntry[$n-1])) + { ($Class, $Key, $Entry, $Meaning) = + split(/$JOIN/, $SynonymEntry[$n]) ; + ++$NOfSaneSynonyms ; + print TUO "\\synonymentry{$Class}{$Key}{$Entry}{$Meaning}\n" } } + Report("SynonymEntries", $NOfSynonyms, "->", $NOfSaneSynonyms, "Entries") ; + if ($NOfBadSynonyms>0) + { Report("SynonymErrors", $NOfBadSynonyms) } } + +#D Register entries need a bit more care, especially when they +#D are nested. In the near future we will also handle page +#D ranges. +#D +#D \starttypen +#D r e {class}{tag}{sanitized key}{key}{pagenumber}{realpage} +#D r s {class}{tag}{sanitized key}{key}{string}{pagenumber} +#D r r {class}{tag}{sanitized key}{key}{string}{pagenumber} +#D \stoptypen +#D +#D The last one indicates the start of a range. + +#D The first one is the normal entry, the second one concerns +#D {\em see this or that} entries. Keys are sanitized, unless +#D the user supplies a sanitized key. To save a lot of +#D programming, all data concerning an entry is stored in one +#D string. Subentries are specified as: +#D +#D \starttypen +#D first&second&third +#D first+second+third +#D \stoptypen +#D +#D When these characters are needed for typesetting purposes, we +#D can also use the first character to specify the separator: +#D +#D \starttypen +#D &$x^2+y^2=r^2$ +#D +this \& that +#D \stoptypen +#D +#D Subentries are first unpacked and next stored in a +#D consistent way, which means that we can use both separators +#D alongside each other. We leave it to the reader to sort +#D out the dirty tricks. + +$SPLIT ="%%" ; +$JOIN ="__" ; + +sub InitializeRegisters + { $NOfEntries = 0 ; + $NOfBadEntries = 0 } + +$ProcessType = "" ; + +$RegStat{"f"} = 1 ; +$RegStat{"e"} = 2 ; # end up between from and to +$RegStat{"t"} = 3 ; +$RegStat{"s"} = 4 ; + +my $RegSep = "$SectionSeparator$SectionSeparator" ; + +sub HandleRegister # the } { makes sure that local {} is ok + { ($SecondTag, $RestOfLine) = split(/ /, $RestOfLine, 2) ; + ++$NOfEntries ; + #~ if ($SecondTag eq "s") + #~ { ($Class, $Location, $Key, $Entry, $SeeToo, $Page ) = + #~ split(/} \{/, $RestOfLine) ; + #~ chop $Page ; + #~ $Class = substr $Class, 1 ; + #~ $RealPage = 0 } + #~ else + #~ { ($Class, $Location, $Key, $Entry, $Page, $RealPage ) = + #~ split(/} \{/, $RestOfLine) ; + #~ chop $RealPage ; + #~ $Class = substr $Class, 1 ; + #~ $SeeToo = "" } + if ($SecondTag eq "s") + { if ($RestOfLine =~ /^\s*(.*?)\}\s\{(.*?)\}\s\{(.*?)\}\s\{(.*)\}\s\{(.*?)\}\s\{(.*?)\s*$/o) + { ($Class, $Location, $Key, $Entry, $SeeToo, $Page ) = ($1,$2,$3,$4,$5,$6) ; + chop $Page ; + $Class = substr $Class, 1 ; + $RealPage = 0 } + else + { return } } + else + { if ($RestOfLine =~ /^\s*(.*?)\}\s\{(.*?)\}\s\{(.*?)\}\s\{(.*)\}\s\{(.*?)\}\s\{(.*?)\s*$/o) + { ($Class, $Location, $Key, $Entry, $Page, $RealPage ) = ($1,$2,$3,$4,$5,$6) ; + chop $RealPage ; + $Class = substr $Class, 1 ; + $SeeToo = "" } + else + { return } } + $_ = $Key ; + if (/$RegSep/) + { ($PageHow,$Key) = split (/$RegSep/) } + else + { $PageHow = "" } + $_ = $Entry ; + if (/$RegSep/) + { ($TextHow,$Entry) = split (/$RegSep/) } + else + { $TextHow = "" } + # + if ($Key eq "") + { $Key = SanitizedString($Entry) } + if ($SortMethod ne '') + { $ProcessHigh = 0 } + if ($ProcessHigh) + { $Key = HighConverted($Key) } + $KeyTag = substr $Key, 0, 1 ; + if ($KeyTag eq "&") + { $Key =~ s/^\&//go ; + $Key =~ s/([^\\])\&/$1$SPLIT/go } + elsif ($KeyTag eq "+") + { $Key =~ s/^\+//go ; + $Key =~ s/([^\\])\+/$1$SPLIT/go } + else + { $Key =~ s/([^\\])\&/$1$SPLIT/go ; + $Key =~ s/([^\\])\+/$1$SPLIT/go } + $Key .= " " ; # so, "Word" comes for "Word Another Word" + $EntryTag = substr $Entry, 0, 1 ; + if ($EntryTag eq "&") + { $Entry =~ s/^\&//go ; + $Entry =~ s/([^\\])\&/$1$SPLIT/go } + elsif ($EntryTag eq "+") + { $Entry =~ s/^\+//go ; + $Entry =~ s/([^\\])\+/$1$SPLIT/go } + elsif ($KeyTag eq "&") + { $Entry =~ s/([^\\])\&/$1$SPLIT/go } + elsif ($KeyTag eq "+") + { $Entry =~ s/([^\\])\+/$1$SPLIT/go } + else + { $Entry =~ s/([^\\])\&/$1$SPLIT/go ; + $Entry =~ s/([^\\])\+/$1$SPLIT/go } + $Key =~ s/^([^a-zA-Z])/ $1/go ; + $Key =~ s/^\s*\{(.*)\}$SPLIT/$1$SPLIT/go ; ####### new + $Entry =~ s/^\{(.*)\}$SPLIT/$1$SPLIT/go ; ###### new + if ($ProcessIJ) { $Key =~ s/ij/yy/go } + $LCKey = lc $Key ; + $RegStatus = $RegStat{$SecondTag} ; + $RealPageNumber= sprintf("%6i",$RealPage) ; + $RegisterEntry[$NOfEntries] = + join($JOIN,$Class,$LCKey,$Key,$Entry,$TextHow,$RegStatus, + $RealPageNumber,$Location,$Page,$PageHow,$SeeToo) } + +#M \definieerregister [testentry] [testentries] + +#D The previous routine deals with entries like: +#D +#D \startbuffer +#D \testentry {alpha} +#D \testentry {beta} +#D \testentry {gamma} +#D \testentry {gamma} +#D \testentry {delta} +#D \testentry {epsilon} +#D \testentry {alpha+first} +#D \testentry {alpha+second} +#D \testentry {alpha+second} +#D \testentry {alpha+third} +#D \testentry {alpha+second+one} +#D \testentry {alpha+second+one} +#D \testentry {alpha+second+two} +#D \testentry {alpha+second+three} +#D \testentry {gamma+first+one} +#D \testentry {gamma+second} +#D \testentry {gamma+second+one} +#D +#D \testentry {alpha+fourth} +#D \testentry {&alpha&fourth} +#D \testentry {+alpha+fourth} +#D +#D \testentry [alpha+fourth] {alpha+fourth} +#D \testentry [&alpha&fourth&one] {&alpha&fourth&one} +#D \testentry [+alpha+fourth+two] {&alpha&fourth&two} +#D +#D \testentry {\kap{alpha}+fifth} +#D \testentry {\kap{alpha}+f\'ifth} +#D \testentry {\kap{alpha}+f"ifth} +#D +#D \testentry [&betaformula] {&$a^2+b^2=c^2$} +#D +#D \testentry {zeta \& more} +#D +#D \testentry [pagehowto::key]{texthowto::entry} +#D +#D % a very special case, when key has , and is constructed +#D +#D \testentry [pagehowto::{key}]{texthowto::{entry}} +#D +#D \stopbuffer +#D +#D \typebuffer +#D +#D \haalbuffer After being sorted, these entries are +#D turned into something \TEX\ using: + +$CollapseEntries = 0 ; + +$RegisterEntry[0] = ("") ; + +sub How + { return "$TextHow$RegSep" . "$_[0]" } + +sub FlushSavedLine + { if (($CollapseEntries)&&($SavedFrom ne "")) + { if ($SavedTo ne "") + { print TUO "\\registerfrom$SavedFrom" ; + print TUO "\\registerto$SavedTo" } + else + { print TUO "\\registerpage$SavedFrom" } } + $SavedHow = "" ; + $SavedFrom = "" ; + $SavedTo = "" ; + $SavedEntry = "" } + +sub FlushRegisters + { print TUO "%\n" . "% $Program / Registers\n" . "%\n" ; + @RegisterEntry = sort { lc($a) cmp lc($b) } @RegisterEntry ; + $NOfSaneEntries = 0 ; + $NOfSanePages = 0 ; + $LastPage = "" ; + $LastRealPage = "" ; + $AlfaClass = "" ; + $Alfa = "" ; + $PreviousA = "" ; + $PreviousB = "" ; + $PreviousC = "" ; + $ActualA = "" ; + $ActualB = "" ; + $ActualC = "" ; + + $SavedFrom = "" ; + $SavedTo = "" ; + $SavedEntry = "" ; + $SavedHow = "" ; + + for ($n=1 ; $n<=$NOfEntries ; ++$n) + { ($Class, $LCKey, $Key, $Entry, $TextHow, $RegisterState, + $RealPage, $Location, $Page, $PageHow, $SeeToo) = + split(/$JOIN/, $RegisterEntry[$n]) ; + $RealPage =~ s/^\s*//o ; + $TestAlfa = lc substr $Key, 0, 1 ; + # + if ($SortN) + { $AlfKey = $Key ; + $AlfKey =~ s/(.).*\x00(.).*/$2/o ; + if (defined($ALF{$AlfKey})) + { $TestAlfa = $ALF{$AlfKey} } } + # + if ((lc $TestAlfa ne lc $Alfa) or ($AlfaClass ne $Class)) + { # $Alfa= lc substr $Key, 0, 1 ; + $Alfa = $TestAlfa ; + $AlfaClass = $Class ; + if ($Alfa ne " ") + { FlushSavedLine ; + print TUO "\\registerentry{$Class}{$Alfa}\n" } } + ($ActualA, $ActualB, $ActualC ) = + split(/$SPLIT/, $Entry, 3) ; + unless ($ActualA) { $ActualA = "" } + unless ($ActualB) { $ActualB = "" } + unless ($ActualC) { $ActualC = "" } + if (How($ActualA) eq $PreviousA) + { $ActualA = "" } + else + { $PreviousA = How($ActualA) ; + $PreviousB = "" ; + $PreviousC = "" } + if (How($ActualB) eq $PreviousB) + { $ActualB = "" } + else + { $PreviousB = How($ActualB) ; + $PreviousC = "" } + if (How($ActualC) eq $PreviousC) + { $ActualC = "" } + else + { $PreviousC = How($ActualC) } + $Copied = 0 ; + if ($ActualA ne "") + { FlushSavedLine ; + print TUO "\\registerentrya{$Class}{$ActualA}\n" ; + $Copied = 1 } + if ($ActualB ne "") + { FlushSavedLine ; + print TUO "\\registerentryb{$Class}{$ActualB}\n" ; + $Copied = 1 } + if ($ActualC ne "") + { FlushSavedLine ; + print TUO "\\registerentryc{$Class}{$ActualC}\n" ; + $Copied = 1 } + if ($Copied) + { $NOfSaneEntries++ } + if ($RealPage eq 0) + { FlushSavedLine ; + print TUO "\\registersee{$Class}{$PageHow,$TextHow}{$SeeToo}{$Page}\n" ; + $LastPage = $Page ; + $LastRealPage = $RealPage } + else { +if (($SavedHow ne $PageHow) && ($PageHow ne "")) { + # last valid page attribute counts + $SavedHow = $PageHow ; +} + if (($Copied) || ! (($LastPage eq $Page) && ($LastRealPage eq $RealPage))) + { # print "$LastPage / $Page // $LastRealPage / $RealPage\n" ; + $NextEntry = "{$Class}{$PreviousA}{$PreviousB}{$PreviousC}{$PageHow,$TextHow}" ; + #~ $SavedLine = "{$Class}{$PageHow,$TextHow}{$Location}{$Page}{$RealPage}\n" ; + $SavedLine = "{$Class}{$SavedHow,$TextHow}{$Location}{$Page}{$RealPage}\n" ; + if ($RegisterState eq $RegStat{"f"}) + { FlushSavedLine ; + print TUO "\\registerfrom$SavedLine" } + elsif ($RegisterState eq $RegStat{"t"}) + { FlushSavedLine ; + print TUO "\\registerto$SavedLine" ; $SavedHow = '' ; } + else + { if ($CollapseEntries) + { if ($SavedEntry ne $NextEntry) + { $SavedFrom = $SavedLine } + else + { $SavedTo = $SavedLine } + $SavedEntry = $NextEntry } + else + { print TUO "\\registerpage$SavedLine" ; $SavedHow = '' ; } + } + ++$NOfSanePages ; + $LastPage = $Page ; + $LastRealPage = $RealPage } } } + +FlushSavedLine ; + + Report("RegisterEntries", $NOfEntries, "->", $NOfSaneEntries, "Entries", + $NOfSanePages, "References") ; + if ($NOfBadEntries>0) + { Report("RegisterErrors", $NOfBadEntries) } } + +#D As promised, we show the results: +#D +#D \plaatstestentry + +#D For debugging purposes we flush some status information. The +#D faster machines become, the more important this section will +#D be. The totals, when changed, force texexec to do a second pass. + +sub FlushData + { print TUO "%\n" . "% $Program / Status\n" . "%\n" ; + print TUO "" . + "% embedded files : $NOfFiles ($NOfBadFiles errors)\n" . + "% synonym entries : $NOfSynonyms ($NOfBadSynonyms errors)\n" . + "% register entries : $NOfEntries ($NOfBadEntries errors)\n" . + "% metapost graphics : $TotalNOfMPgraphics\n" . + "% position commands : $TotalNOfPositions " ; + if ($TotalNOfPositions) + { if ($NOfPositionsFound) + { print TUO "(resolved)\n" } + else + { print TUO "(unresolved)\n" } } + else + { print TUO "(not used)\n" } } + +#D The functionallity described on the previous few pages is +#D called upon in the main routine: + +sub NormalHandleReferences + { if ($InputFile eq "") + { Report("Error", "NoInputFile") } + else + { unless (open (TUI, "$InputFile.tui")) + { Report("Error", "EmptyInputFile", $InputFile) } + else + { Report("InputFile", "$InputFile.tui" ) ; + unlink "$InputFile.tmp" ; + rename "$InputFile.tuo", "$InputFile.tmp" ; + Report("OutputFile", "$InputFile.tuo" ) ; + open (TUO, ">$InputFile.tuo") ; + print TUO "%\n" . "% $Program / Commands\n" . "%\n" ; + while (<TUI>) + { $SomeLine = $_ ; + chomp $SomeLine ; + ($FirstTag, $RestOfLine) = split ' ', $SomeLine, 2 ; + if ($FirstTag eq "c") + { HandleCommand } + elsif ($FirstTag eq "s") + { HandleSynonym } + elsif ($FirstTag eq "r") + { HandleRegister } + elsif ($FirstTag eq "f") + { HandleFile } + elsif ($FirstTag eq "k") + { HandleKey } + elsif ($FirstTag eq "e") + { HandleExtra } + elsif ($FirstTag eq "p") + { HandlePlugIn } + elsif ($FirstTag eq "q") + { $ValidOutput = 0 ; + last } } + if ($ValidOutput) + { FlushCommands ; # already done during pass + FlushKeys ; + FlushRegisters ; + FlushSynonyms ; + FlushPlugIns ; + FlushFiles ; + FlushData ; + FlushExtra ; + close (TUO) ; + RunExtraPrograms } + else + { close (TUO) ; + unlink "$InputFile.tuo" ; + rename "$InputFile.tmp", "$InputFile.tuo" ; + Report ("Remark", "NoOutputFile") } } } } + +my $Suffix ; + +sub MergerHandleReferences + { unlink "texutil.tuo" ; + Report("OutputFile", "texutil.tuo" ) ; + open (TUO, ">texutil.tuo") ; + foreach $InputFile (@ARGV) + { ($InputFile, $Suffix) = split (/\./, $InputFile, 2) ; + unless (open (TUI, "$InputFile.tui")) + { Report("Error", "EmptyInputFile", $InputFile) } + else + { Report("InputFile", "$InputFile.tui" ) ; + while (<TUI>) + { $SomeLine = $_ ; + chomp $SomeLine ; + ($FirstTag, $RestOfLine) = split ' ', $SomeLine, 2 ; + if ($FirstTag eq "r") + { HandleRegister } } } } + if ($ValidOutput) + { FlushRegisters ; + close (TUO) } + else + { close (TUO) ; + unlink "texutil.tuo" ; + Report ("Remark", "NoOutputFile") } } + +# sub HandleReferences +# { Report("Action", "ProcessingReferences") ; +# if ($ProcessIJ ) +# { Report("Option", "SortingIJ") } +# if ($ProcessHigh) +# { Report("Option", "ConvertingHigh") } +# if ($ProcessQuotes) +# { Report("Option", "ProcessingQuotes") } +# if ($InputFile eq "") +# { Report("Error", "NoInputFile") } +# else +# { unless (open (TUI, "$InputFile.tui")) +# { Report("Error", "EmptyInputFile", $InputFile) } +# else +# { Report("InputFile", "$InputFile.tui" ) ; +# InitializeCommands ; +# InitializeRegisters ; +# InitializeSynonyms ; +# InitializeFiles ; +# $ValidOutput = 1 ; +# unlink "$InputFile.tmp" ; +# rename "$InputFile.tuo", "$InputFile.tmp" ; +# Report("OutputFile", "$InputFile.tuo" ) ; +# open (TUO, ">$InputFile.tuo") ; +# while (<TUI>) +# { $SomeLine = $_ ; +# chomp $SomeLine ; +# ($FirstTag, $RestOfLine) = split ' ', $SomeLine, 2 ; +# if ($FirstTag eq "c") +# { HandleCommand } +# elsif ($FirstTag eq "s") +# { HandleSynonym } +# elsif ($FirstTag eq "r") +# { HandleRegister } +# elsif ($FirstTag eq "f") +# { HandleFile } +# elsif ($FirstTag eq "q") +# { $ValidOutput = 0 ; +# last } } +# if ($ValidOutput) +# { FlushCommands ; # already done during pass +# FlushRegisters ; +# FlushSynonyms ; +# FlushFiles ; +# FlushData ; +# close (TUO) } +# else +# { close (TUO) ; +# unlink "$InputFile.tuo" ; +# rename "$InputFile.tmp", "$InputFile.tuo" ; +# Report ("Remark", "NoOutputFile") } } } } + +sub HandleReferences + { $Merging = @ARGV ; + $Merging = ($Merging>1) ; + if ($Merging) + { Report("Action", "MergingReferences") } + else + { Report("Action", "ProcessingReferences") } + if ($ProcessIJ ) + { Report("Option", "SortingIJ") } + if ($ProcessHigh) + { Report("Option", "ConvertingHigh") } + if ($ProcessQuotes) + { Report("Option", "ProcessingQuotes") } + InitializeKeys ; + InitializeCommands ; + InitializeExtra ; + InitializeRegisters ; + InitializeSynonyms ; + InitializeFiles ; + $ValidOutput = 1 ; + if ($Merging) + { MergerHandleReferences } + else + { NormalHandleReferences } } + +# moved to ctxtools +# +# sub HandleDocuments +# { my $files = @ARGV.join(' ') ; system("ctxtools $files") } + +#D \extras +#D {documents} +#D +#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 +#D \METAPOST\ files use \type{%} as a comment chacacter, while +#D \PERL\ uses \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. + +my $skippingbang = 0 ; + +sub HandleDocuments + { Report("Action", "GeneratingDocumentation") ; + if ($ProcessType ne "") + { Report("Option", "ForcingFileType", $ProcessType) } + if ($InputFile eq "") + { Report("Error", "NoInputFile") } + else + { CheckInputFiles ($InputFile) ; + foreach $FullName (@UserSuppliedFiles) + { ($FileName, $FileSuffix) = SplitFileName ($FullName) ; + unless ($FileSuffix) + { $FileSuffix = "tex" } + unless (-f "$FileName.$FileSuffix") + { next } + unless (open (TEX, "$FileName.$FileSuffix")) + { Report("Error", "EmptyInputFile", "$FileName.$FileSuffix" ) } + else + { Report("InputFile", "$FileName.$FileSuffix") ; + Report("OutputFile", "$FileName.ted") ; + open (TED, ">$FileName.ted") ; + $NOfDocuments = 0 ; + $NOfDefinitions = 0 ; + $NOfSkips = 0 ; + $SkipLevel = 0 ; + $InDocument = 0 ; + $InDefinition = 0 ; + $skippingbang = 0 ; + if ($ProcessType eq "") + { $FileType=lc $FileSuffix } + else + { $FileType=lc $ProcessType } + Report("FileType", $FileType) ; + # we need to signal to texexec what interfaec to use + my $firstline = <TEX> ; + if ($firstline =~ /^\%.*interface\=/) + { print TED $firstline } + else + { seek TEX, 0, 0 } + # so far + print TED "\\startmodule[type=$FileType]\n" ; + while (<TEX>) + { chomp ; + s/\s*$//o ; + if ($skippingbang) + { $skippingbang = 0 } + elsif (/^[%\#]D/) + { if ($SkipLevel == 0) + { if (length $_ < 3) + {$SomeLine = "" } + else # HH: added after that + {$SomeLine = substr $_, 3 } + if ($InDocument) + { print TED "$SomeLine\n" } + else + { if ($InDefinition) + { print TED "\\stopdefinition\n" ; + $InDefinition = 0 } + unless ($InDocument) + { print TED "\n\\startdocumentation\n" } + print TED "$SomeLine\n" ; + $InDocument = 1 ; + ++$NOfDocuments } } } + elsif (/^[%\#]M/) + { if ($SkipLevel == 0) + { $SomeLine = substr $_, 3 ; + print TED "$SomeLine\n" } } + elsif (/^[%\%]S B]/) + { ++$SkipLevel ; + ++$NOfSkips } + elsif (/^[%\%]S E]/) + { --$SkipLevel } + elsif (/^[%\#]/) + { } + elsif (/^eval \'\(exit \$\?0\)\' \&\& eval \'exec perl/o) + { $skippingbang = 1 } + elsif ($SkipLevel == 0) + { $InLocalDocument = $InDocument ; + $SomeLine = $_ ; + if ($InDocument) + { print TED "\\stopdocumentation\n" ; + $InDocument = 0 } + if (($SomeLine eq "") && ($InDefinition)) + { print TED "\\stopdefinition\n" ; + $InDefinition = 0 } + else + { if ($InDefinition) + { print TED "$SomeLine\n" } + elsif ($SomeLine ne "") + { print TED "\n" . "\\startdefinition\n" ; + $InDefinition = 1 ; + unless ($InLocalDocument) + { ++$NOfDefinitions } + print TED "$SomeLine\n" } } } } + if ($InDocument) + { print TED "\\stopdocumentation\n" } + if ($InDefinition) + { print TED "\\stopdefinition\n" } + print TED "\\stopmodule\n" ; + close (TED) ; + unless (($NOfDocuments) || ($NOfDefinitions)) + { unlink "$FileName.ted" } + Report ("NOfDocuments", $NOfDocuments) ; + Report ("NOfDefinitions", $NOfDefinitions) ; + Report ("NOfSkips", $NOfSkips) } } } } + +#D \extras +#D {sources} +#D +#D Documented sources can be stripped of documentation and +#D comments, although at the current processing speeds the +#D overhead of skipping the documentation at run time is +#D neglectable. Only lines beginning with a \type{%} are +#D stripped. The stripped files gets the suffix \type{tes}. + +sub HandleSources + { Report("Action", "GeneratingSources") ; + if ($InputFile eq "") + { Report("Error", "NoInputFile") } + else + { CheckInputFiles ($InputFile) ; + foreach $FullName (@UserSuppliedFiles) + { ($FileName, $FileSuffix) = SplitFileName ($FullName) ; + unless ($FileSuffix) + { $FileSuffix = "tex" } + unless (-f "$FileName.$FileSuffix") + { next } + unless (open (TEX, "$FileName.$FileSuffix")) + { Report("Error", "EmptyInputFile", "$FileName.$FileSuffix" ) } + else + { Report("InputFile", "$FileName.$FileSuffix") ; + Report("OutputFile", "$FileName.tes") ; + open (TES, ">$FileName.tes") ; + $EmptyLineDone = 1 ; + $FirstCommentDone = 0 ; + while (<TEX>) + { $SomeLine = $_ ; + chomp $SomeLine ; + if ($SomeLine eq "") + { unless ($FirstCommentDone) + { $FirstCommentDone = 1 ; + print TES + "\n% further documentation is removed\n\n" ; + $EmptyLineDone = 1 } + unless ($EmptyLineDone) + { print TES "\n" ; + $EmptyLineDone = 1 } } + elsif ($SomeLine =~ /^%/) + { unless ($FirstCommentDone) + { print TES "$SomeLine\n" ; + $EmptyLineDone = 0 } } + else + { print TES "$SomeLine\n" ; + $EmptyLineDone = 0 } } + close (TES) ; + unless ($FirstCommentDone) + { unlink "$FileName.tes" } } } } } + +#D \extras +#D {setups} +#D +#D All \CONTEXT\ commands are specified in a compact format +#D that can be used to generate quick reference tables and +#D cards. Such setups are preceded by \type{%S}. The setups +#D are collected in the file \type{texutil.tus}. + +sub HandleSetups + { Report("Action", "FilteringDefinitions" ) ; + if ($InputFile eq "") + { Report("Error", "NoInputFile") } + else + { SetOutputFile ("texutil.tus" ) ; + Report("OutputFile", $OutputFile) ; + open (TUS, ">$OutputFile") ; # always reset! + $NOfSetups = 0 ; + CheckInputFiles ($InputFile) ; + foreach $FullName (@UserSuppliedFiles) + { ($FileName, $FileSuffix) = SplitFileName ($FullName) ; + unless ($FileSuffix) + { $FileSuffix = "tex" } + unless (-f "$FileName.$FileSuffix") + { next } + unless (open (TEX, "$FileName.$FileSuffix")) + { Report("Error", "EmptyInputFile", "$FileName.$FileSuffix" ) } + else + { Report("InputFile", "$FileName.$FileSuffix") ; + print TUS "%\n" . "% File : $FileName.$FileSuffix\n" . "%\n" ; + while (<TEX>) + { $SomeLine = $_ ; + chomp $SomeLine ; + ($Tag, $RestOfLine) = split(/ /, $SomeLine, 2) ; + if ($Tag eq "%S") + { ++$NOfSetups ; + while ($Tag eq "%S") + { print TUS "$RestOfLine\n" ; + $SomeLine = <TEX> ; + chomp $SomeLine ; + ($Tag, $RestOfLine) = split(/ /, $SomeLine, 2) } + print TUS "\n" } } } } + close (TUS) ; + unless ($NOfSetups) + { unlink $OutputFile } + Report("NOfSetups", $NOfSetups) } } + +#D \extras +#D {templates, infos} +#D +#D From the beginning, the \CONTEXT\ source files contained +#D helpinfo and key||templates for \TEXEDIT. In fact, for a +#D long time, this was the only documentation present. More +#D and more typeset (interactive) documentation is replacing +#D this helpinfo, but we still support the traditional method. +#D This information is formatted like: +#D +#D \starttypen +#D %I n=Struts +#D %I c=\strut,\setnostrut,\setstrut,\toonstruts +#D %I +#D %I text +#D %I .... +#D %P +#D %I text +#D %I .... +#D \stoptypen +#D +#D Templates look like: +#D +#D \starttypen +#D %T n=kap +#D %T m=kap +#D %T a=k +#D %T +#D %T \kap{?} +#D \stoptypen +#D +#D The key||value pairs stand for {\em name}, {\em mnemonic}, +#D {\em key}. This information is copied to files with the +#D extension \type{tud}. + +sub HandleEditorCues + { if ($ProcessTemplates) + { Report("Action", "CopyingTemplates" ) } + if ($ProcessInfos) + {Report("Action", "CopyingInformation" ) } + if ($InputFile eq "") + { Report("Error", "NoInputFile") } + else + { CheckInputFiles ($InputFile) ; + foreach $FullName (@UserSuppliedFiles) + { ($FileName, $FileSuffix) = SplitFileName ($FullName) ; + if ($FileSuffix eq "") + { $FileSuffix = "tex" } + unless (-f "$FileName.$FileSuffix") + { next } + unless (open (TEX, "$FileName.$FileSuffix")) + { Report("Error", "EmptyInputFile", "$FileName.$FileSuffix" ) } + else + { Report("InputFile", "$FileName.$FileSuffix") ; + Report("OutputFile", "$FileName.tud") ; + open (TUD, ">$FileName.tud") ; + $NOfTemplates = 0 ; + $NOfInfos = 0 ; + while (<TEX>) + { $SomeLine = $_ ; + chomp $SomeLine ; + ($Tag, $RestOfLine) = split(/ /, $SomeLine, 2) ; + if (($Tag eq "%T") && ($ProcessTemplates)) + { ++$NOfTemplates ; + while ($Tag eq "%T") + { print TUD "$SomeLine\n" ; + $SomeLine = <TEX> ; + chomp $SomeLine ; + ($Tag, $RestOfLine) = split(/ /, $SomeLine, 2) } + print TUD "\n" } + elsif (($Tag eq "%I") && ($ProcessInfos)) + { ++$NOfInfos ; + while (($Tag eq "%I") || ($Tag eq "%P")) + { print TUD "$SomeLine\n" ; + $SomeLine = <TEX> ; + chomp $SomeLine ; + ($Tag, $RestOfLine) = split(/ /, $SomeLine, 2) } + print TUD "\n" } } + close (TUD) ; + unless (($NOfTemplates) || ($NOfInfos)) + { unlink "$FileName.tud" } + if ($ProcessTemplates) + { Report("NOfTemplates", $NOfTemplates) } + if ($ProcessInfos) + { Report("NOfInfos", $NOfInfos) } } } } } + +#D \extras +#D {figures} +#D +#D Directories can be scanned for illustrations in \EPS, \PDF, +#D \TIFF, \PNG\ or \JPG\ format. The resulting file \type{texutil.tuf} +#D contains entries like: +#D +#D \starttypen +#D \thisisfigureversion{year.month.day} +#D \presetfigure[file][...specifications...] +#D \stoptypen +#D +#D where the specifications are: +#D +#D \starttypen +#D [e=suffix,x=xoffset,y=yoffset,w=width,h=height,t=title,c=creator,s=size] +#D \stoptypen +#D +#D This data can be used when determining dimensions and +#D generate directories of illustrations. + +$DPtoCM = 2.54/72.0 ; +$INtoCM = 2.54 ; + +sub SaveFigurePresets + { my ($FNam, $FTyp, $FUni, $FXof, $FYof, $FWid, $FHei, $FTit, $FCre, $FSiz) = @_ ; + if ($ProcessVerbose) + { OpenTerminal ; + if ($FUni) + { print "n=$FNam t=$FTyp " . + (sprintf "x=%1.3fcm y=%1.3fcm ", $FXof, $FYof) . + (sprintf "w=%5.3fcm h=%5.3fcm\n", $FWid, $FHei) } + else + { print "n=$FNam t=$FTyp " . + "x=${FXof}bp y=${FYof}bp " . + "w=${FWid}bp h=${FHei}bp\n" } + CloseTerminal } + else + { ++$NOfFigures ; + $Figures[$NOfFigures] = "\\presetfigure[$FNam][e=$FTyp" ; + if ($FUni) + { $Figures[$NOfFigures] .= (sprintf ",w=%5.3fcm,h=%5.3fcm", $FWid, $FHei) } + else + { $Figures[$NOfFigures] .= ",w=${FWid}bp,h=${FHei}bp" } + if (($FXof!=0)||($FYof!=0)) + { if ($FUni) + { $Figures[$NOfFigures] .= (sprintf ",x=%1.3fcm,y=%1.3fcm", $FXof, $FYof) } + else + { $Figures[$NOfFigures] .= ",x=${FXof}bp,y=${FYof}bp" } } + if ($FTit) + { $Figures[$NOfFigures] .= ",t=\{$FTit\}" } + if ($FCre) + { $Figures[$NOfFigures] .= ",c=\{$FCre\}" } + $Figures[$NOfFigures] .= ",s=$FSiz]\n" } } + +#D The \EPS\ to \PDF\ conversion pipe to \GHOSTSCRIPT\ is +#D inspired by a script posted by Sebastian Ratz at the +#D \PDFTEX\ mailing list. Watch the bounding box check, we +#D use the values found in an earlier pass. + +sub ConvertEpsToEps + { my ( $SuppliedFileName , $LLX, $LLY, $URX, $URY ) = @_ ; + ($FileName, $FileSuffix) = SplitFileName ($SuppliedFileName) ; + if ($ProcessEpsToPdf) + { if ($dosish) { $gs = "gswin32c" } else { $gs = "gs" } + unlink "$FileName.pdf" ; + $GSCommandLine = "-q " . + "-sDEVICE=pdfwrite " . + "-dNOCACHE " . + "-dUseFlateCompression=true " . + "-dMaxSubsetPct=100 " . + "-sOutputFile=$FileName.pdf " . + "- -c " . + "quit " ; + open ( EPS, "| $gs $GSCommandLine") } + elsif ($PDFReady) + { return } + else + { open ( EPS, ">texutil.tmp" ) ; + binmode EPS } + open ( TMP , "$SuppliedFileName" ) ; + binmode TMP ; + $EpsBBOX = 0 ; + $EpsWidth = $URX - $LLX ; + $EpsHeight = $URY - $LLY ; + $EpsXOffset = 0 - $LLX ; + $EpsYOffset = 0 - $LLY ; + while (<TMP>) + { if (/%!PS/) + { s/(.*)%!PS/%!PS/o ; + print EPS $_ ; + last } } + while (<TMP>) + { if ((!$PDFReady)&&(/^%%(HiResB|ExactB|B)oundingBox:/o)) + { unless ($EpsBBOX) + { print EPS "%%PDFready: $Program\n" ; + print EPS "%%BoundingBox: 0 0 $EpsWidth $EpsHeight\n" ; + print EPS "<< /PageSize [$EpsWidth $EpsHeight] >> setpagedevice\n" ; + print EPS "gsave $EpsXOffset $EpsYOffset translate\n" ; + $EpsBBOX = 1 } } + elsif (/^%%EOF/o) # when final: (/^%%(EOF|Trailer)/o) + { last } + elsif (/^%%Trailer/o) + { last } + else + { print EPS $_ } } + close ( TMP ) ; + if (($EpsBBOX)&&(!$PDFReady)) + { print EPS "grestore\n%%EOF\n%%RestOfFileIgnored: $Program\n" ; + close ( EPS ) ; + Report ( "PdfFile", "$SuppliedFileName" ) ; + unless ($ProcessEpsToPdf) + { unlink "$SuppliedFileName" ; + rename "texutil.tmp", "$SuppliedFileName" } } + else + { close (EPS) } + unlink "texutil.tmp" } + +sub HandleEpsFigure + { my ($SuppliedFileName) = @_ ; + my ($Temp) = "" ; + if (-f $SuppliedFileName) + { ($FileName, $FileSuffix) = SplitFileName ($SuppliedFileName) ; + if ($FileSuffix ne "") + {#$Temp = $FileSuffix ; + #$Temp =~ s/[0-9]//go ; + #if ($Temp eq "") + if ($FileSuffix =~ /^[0-9]+$/o) + { $EpsFileName = $SuppliedFileName; + Report ( "MPFile", "$SuppliedFileName" ) } + elsif ((lc $FileSuffix ne "eps")&&(lc $FileSuffix ne "mps")) + { return } + else + { $EpsFileName = $SuppliedFileName; # $FileName + Report ( "EpsFile", "$SuppliedFileName" ) } + $EpsTitle = "" ; + $EpsCreator = "" ; + open ( EPS , $SuppliedFileName ) ; + binmode EPS ; + $EpsSize = -s EPS ; + $PDFReady = 0 ; + $MPSFound = 0 ; + $BBoxFound = 0 ; + while (<EPS>) + { $SomeLine = $_; + chomp $SomeLine ; + if (($BBoxFound) && ((substr $SomeLine,0,1) ne "%")) + { last } + if ($BBoxFound<2) + { if ($SomeLine =~ /^%%BoundingBox:(?!\s+\(atend\))/io) # atend (th) + { $EpsBBox = $SomeLine ; $BBoxFound = 1 ; next } + elsif ($SomeLine =~ /^%%HiResBoundingBox:/io) + { $EpsBBox = $SomeLine ; $BBoxFound = 2 ; next } + elsif ($SomeLine =~ /^%%ExactBoundingBox:/io) + { $EpsBBox = $SomeLine ; $BBoxFound = 3 ; next } } + if ($SomeLine =~ /^%%PDFready:/io) + { $PDFReady = 1 } + elsif ($SomeLine =~ /^%%Creator:/io) + { ($Tag, $EpsCreator) = split (/ /, $SomeLine, 2) ; + if ($EpsCreator =~ /MetaPost/io) + { $MPSFound = 1 } } + elsif ($SomeLine =~ /^%%Title:/io) + { ($Tag, $EpsTitle) = split (/ /, $SomeLine, 2) } } + close ( EPS ) ; + if ($BBoxFound) + { ($Tag, $LLX, $LLY, $URX, $URY, $RestOfLine) = split (/ /, $EpsBBox, 6 ) ; + $EpsHeight = ($URY-$LLY)*$DPtoCM ; + $EpsWidth = ($URX-$LLX)*$DPtoCM ; + $EpsXOffset = $LLX*$DPtoCM ; + $EpsYOffset = $LLY*$DPtoCM ; + if ($MPSFound) + { $EpsType = "mps" } + else + { $EpsType = "eps" } + SaveFigurePresets + ( $EpsFileName, $EpsType, 1, + $EpsXOffset, $EpsYOffset, $EpsWidth, $EpsHeight, + $EpsTitle, $EpsCreator, $EpsSize ) ; + if (($ProcessEpsPage) || ($ProcessEpsToPdf)) + { ConvertEpsToEps ( $SuppliedFileName, $LLX, $LLY, $URX, $URY ) } } + else + { Report ( "MissingBoundingBox", "$SuppliedFileName" ) } } } } + +#D The \PDF\ scanning does a similar job. This time we +#D search for a mediabox. I could have shared some lines +#D with the previous routines, but prefer readability. + +sub HandlePdfFigure + { my ( $SuppliedFileName ) = @_ ; + ($FileName, $FileSuffix) = SplitFileName ($SuppliedFileName) ; + if (lc $FileSuffix ne "pdf") + { return } + else + { $PdfFileName = $SuppliedFileName ; + Report ( "PdfFile", "$SuppliedFileName" ) } + open ( PDF , $SuppliedFileName ) ; + binmode PDF ; + $PdfSize = -s PDF ; + $MediaBoxFound = 0 ; + $MediaBox = 0 ; + $PageFound = 0 ; + $PagesFound = 0 ; + while (<PDF>) + { $SomeLine = $_ ; + chomp ($SomeLine) ; + if ($SomeLine =~ /\/Type\s*\/Pages/io) + { $PagesFound = 1 } + elsif ($SomeLine =~ /\/Type\s*\/Page/io) + { ++$PageFound ; + if ($PageFound>1) { last } } + if (($PageFound)||($PagesFound)) + { if (($MediaBoxFound < 2) && ($SomeLine =~ /\/ArtBox\s*\[/io)) + { $MediaBoxFound = 3 ; + $MediaBox = $SomeLine } + elsif (($MediaBoxFound < 2) && ($SomeLine =~ /\/CropBox\s*\[/io)) + { $MediaBoxFound = 2 ; + $MediaBox = $SomeLine } + elsif (($MediaBoxFound == 0) && ($SomeLine =~ /\/MediaBox\s*\[/io)) + { $MediaBoxFound = 1 ; + $MediaBox = $SomeLine } } } + close ( PDF ) ; + if ($PageFound>1) + { Report ( "MultiPagePdfFile", "$SuppliedFileName" ) } + if (($MediaBoxFound) && ($MediaBox)) + { my $D = "[0-9\-\.]" ; + $MediaBox =~ /\/(Media|Crop|Art)Box\s*\[\s*($D+)\s*($D+)\s*($D+)\s*($D+)/o ; + $LLX = $2 ; $LLY = $3 ; $URX = $4 ; $URY = $5 ; + $PdfHeight = ($URY-$LLY)*$DPtoCM ; + $PdfWidth = ($URX-$LLX)*$DPtoCM ; + $PdfXOffset = $LLX*$DPtoCM ; + $PdfYOffset = $LLY*$DPtoCM ; + SaveFigurePresets + ( $PdfFileName, "pdf", 1, + $PdfXOffset, $PdfYOffset, $PdfWidth, $PdfHeight, + "", "", $PdfSize ) } + else + { Report ( "MissingMediaBox", "$SuppliedFileName" ) } } + +#D A previous version of \TEXUTIL\ used \type{tifftags} or +#D \type{tiffinfo} for collecting the dimensions. However, +#D the current implementation does this job itself. + +sub TifGetByte + { my $B = 0 ; + read TIF, $B, 1 ; + return ord($B) } + +sub TifGetShort + { my $S = 0 ; + read TIF, $S, 2 ; + if ($TifLittleEndian) + { return (unpack ("v", $S)) } + else + { return (unpack ("n", $S)) } } + +sub TifGetLong + { my $L = 0 ; + read TIF, $L, 4 ; + if ($TifLittleEndian) + { return (unpack ("V", $L)) } + else + { return (unpack ("N", $L)) } } + +sub TifGetRational + { my ($N, $M) = (0,0) ; + $N = TifGetLong ; + $M = TifGetLong ; + return $N/$M } + +sub TifGetAscii + { my $S = "" ; + --$TifValues; + unless ($TifValues) + { return "" } + else + { read TIF, $S, $TifValues ; + return $S } } + +sub TifGetWhatever + { if ($_[0]==1) + { return TifGetByte } + elsif ($_[0]==2) + { return TifGetAscii } + elsif ($_[0]==3) + { return TifGetShort } + elsif ($_[0]==4) + { return TifGetLong } + elsif ($_[0]==5) + { return TifGetRational } + else + { return 0 } } + +sub TifGetChunk + { seek TIF, $TifNextChunk, 0 ; + $Length = TifGetShort ; + $TifNextChunk += 2 ; + for ($i=1; $i<=$Length; $i++) + { seek TIF, $TifNextChunk, 0 ; + $TifTag = TifGetShort ; + $TifType = TifGetShort ; + $TifValues = TifGetLong ; + if ($TifTag==256) + { $TifWidth = TifGetWhatever($TifType) } + elsif ($TifTag==257) + { $TifHeight = TifGetWhatever($TifType) } + elsif ($TifTag==296) + { $TifUnit = TifGetWhatever($TifType) } + elsif ($TifTag==282) + { seek TIF, TifGetLong, 0 ; + $TifHRes = TifGetWhatever($TifType) } + elsif ($TifTag==283) + { seek TIF, TifGetLong, 0 ; + $TifVRes = TifGetWhatever($TifType) } + elsif ($TifTag==350) + { seek TIF, TifGetLong, 0 ; + $TifCreator = TifGetWhatever($TifType) } + elsif ($TifTag==315) + { seek TIF, TifGetLong, 0 ; + $TifAuthor = TifGetWhatever($TifType) } + elsif ($TifTag==269) + { seek TIF, TifGetLong, 0 ; + $TifTitle = TifGetWhatever($TifType) } + $TifNextChunk += 12 } + seek TIF, $TifNextChunk, 0 ; + $TifNextChunk = TifGetLong ; + return ($TifNextChunk>0) } + +sub HandleTifFigure + { my ( $SuppliedFileName ) = @_ ; + ($FileName, $FileSuffix) = SplitFileName ($SuppliedFileName) ; + if (lc $FileSuffix ne "tif") + { return } + else + { $TifFile = $SuppliedFileName ; + if (open ( TIF, $TifFile )) { # { must be here, perl 5.003 bug + Report ( "TifFile", "$SuppliedFileName" ) ; + binmode TIF; + $TifWidth = 0 ; + $TifHeight = 0 ; + $TifTitle = "" ; + $TifAuthor = "" ; + $TifCreator = "" ; + $TifUnit = 0 ; + $TifHRes = 1 ; + $TifVRes = 1 ; + $TifSize = -s TIF ; + $TifByteOrder = "" ; + seek TIF, 0, 0 ; + read TIF, $TifByteOrder, 2 ; + $TifLittleEndian = ($TifByteOrder eq "II") ; + $TifTag = TifGetShort; + unless ($TifTag == 42) + { close ( TIF ) ; + return } + $TifNextChunk = TifGetLong ; + while (TifGetChunk) { } + if ($TifUnit==2) + { $TifMult = $INtoCM } + elsif ($TifUnit==3) + { $TifMult = 1 } + else + { $TifMult = 72 } + $TifWidth = ($TifWidth /$TifHRes)*$TifMult ; + $TifHeight = ($TifHeight/$TifVRes)*$TifMult ; + close ( TIF ) ; + SaveFigurePresets + ( $TifFile, "tif", $TifUnit, + 0, 0, $TifWidth, $TifHeight, + $TifTitle, $TifCreator, $TifSize ) } } } + +#D I first intended to use the public utility \type{pngmeta} +#D (many thanks to Taco for compiling it), but using this +#D utility to analyze lots of \PNG\ files, I tried to do a +#D similar job in \PERL. Here are the results: + +my ($PngSize, $PngWidth, $PngHeight) = (0,0,0) ; +my ($PngMult, $PngHRes, $PngVRes, $PngUnit) = (0,1,1,0) ; +my ($PngFile, $PngTitle, $PngAuthor, $PngCreator) = ("","","") ; +my ($PngNextChunk, $PngLength, $PngType) = (0,0,0) ; +my ($PngKeyword, $PngDummy) = ("","") ; + +my $PngSignature = chr(137) . chr(80) . chr(78) . chr(71) . + chr (13) . chr(10) . chr(26) . chr(10) ; +sub PngGetByte + { my ($B) = 0 ; + read PNG, $B, 1 ; + return (ord($B)) } + +sub PngGetLong + { my ($L) = 0 ; + read PNG, $L, 4 ; + return (unpack("N", $L)) } + +sub PngGetChunk + { if ($PngNextChunk<$PngSize) + { seek PNG, $PngNextChunk, 0 ; + $PngLength = PngGetLong ; + $PngNextChunk = $PngNextChunk + $PngLength + 12 ; + read PNG, $PngType, 4 ; + if ($PngType eq "") + { return 0 } + elsif ($PngType eq "IEND") + { return 0 } + elsif ($PngType eq "IHDR") + { $PngWidth = PngGetLong ; + $PngHeight = PngGetLong } + elsif ($PngType eq "pHYs") + { $PngHRes = PngGetLong ; + $PngVRes = PngGetLong ; + read PNG, $PngUnit, 1 } + elsif ($PngType eq "tEXt") + { read PNG, $PngKeyword, $PngLength ; + ($PngKeyword,$PngDummy) = split(/\x00/,$PngKeyword) ; + if ( $PngKeyword eq "Title") + { $PngTitle = $PngDummy } + elsif ( $PngKeyword eq "Author") + { $PngAuthor = $PngDummy } + elsif ( $PngKeyword eq "Software") + { $PngCreator = $PngDummy } } + return 1 } + else + { return 0 } } + +sub HandlePngFigure + { my ( $SuppliedFileName ) = @_ ; + ($FileName, $FileSuffix) = SplitFileName ($SuppliedFileName) ; + if (lc $FileSuffix ne "png") + { return } + else + { $PngFile = $SuppliedFileName ; + if (open ( PNG, $PngFile )) + { Report ( "PngFile", "$SuppliedFileName" ) } + $PngSize = 0 ; + $PngWidth = 0 ; + $PngHeight = 0 ; + $PngTitle = "" ; + $PngAuthor = "" ; + $PngCreator = "" ; + $PngUnit = 0 ; + $PngVRes = 1 ; + $PngHRes = 1 ; + $PngSig = "" ; + $PngSize = -s PNG ; + binmode PNG ; + seek PNG, 0, 0 ; + read PNG, $PngSig, 8; + unless ($PngSig eq $PngSignature) + { close ( PNG ) ; + return } + $PngNextChunk = 8 ; + while (PngGetChunk) { } + $PngWidth = ($PngWidth /$PngVRes) ; + $PngHeight = ($PngHeight/$PngHRes) ; + close ( PNG ) ; + SaveFigurePresets + ( $PngFile, "png", $PngUnit, + 0, 0, $PngWidth, $PngHeight, + $PngTitle, $PngCreator, $PngSize ) } } + +#D Well, we also offer \JPG\ scanning (actually \JFIF) +#D scanning. (I can recomend David Salomon's book on Data +#D Compression to those interested in the internals of +#D \JPG.) +#D +#D It took me some time to discover that the (sort of) +#D reference document I used had a faulty byte position table. +#D Nevertheless, when I was finaly able to grab the header, +#D Piet van Oostrum pointer me to the \PERL\ script of Alex +#D Knowles (and numerous other contributers), from which I +#D could deduce what segment contained the dimensions. + +my ($JpgSize, $JpgWidth, $JpgHeight) = (0,0,0) ; +my ($JpgMult, $JpgUnit, $JpgHRes, $JpgVRes) = (1,0,1,1) ; +my ($JpgFile, $JpgVersion, $JpgDummy) = ("",0,"") ; +my ($JpgSig, $JpgPos, $JpgLen, $JpgSoi, $JpgApp) = ("",0,0,0,0) ; + +my $JpgSignature = "JFIF" . chr(0) ; + +sub JpgGetByte + { my ($B) = 0 ; + read JPG, $B, 1 ; + return ( ord($B) ) } + +sub JpgGetInteger + { my ($I) = 0 ; + read JPG, $I, 2 ; + return (unpack("n", $I)) } + +sub HandleJpgFigure + { my ($SuppliedFileName) = @_ ; + ($FileName, $FileSuffix) = SplitFileName ($SuppliedFileName) ; + if (lc $FileSuffix ne "jpg") + { return } + else + { $JpgFile = $SuppliedFileName ; + Report ( "JpgFile", "$SuppliedFileName" ) } + open ( JPG, $JpgFile ) ; + binmode JPG ; + $JpgSignature = "JFIF" . chr(0) ; + $JpgSize = -s JPG ; + $JpgWidth = 0 ; + $JpgHeight = 0 ; + $JpgUnit = 0 ; + $JpgVRes = 1 ; + $JpgHRes = 1 ; + seek JPG, 0, 0 ; + read JPG, $JpgSig, 4 ; + unless ($JpgSig eq chr(255).chr(216).chr(255).chr(224)) + { close ( JPG ) ; + return } + $JpgLen = JpgGetInteger; + read JPG, $JpgSig, 5 ; + unless ($JpgSig eq $JpgSignature) + { close ( JPG ) ; + return } + $JpgUnit = JpgGetByte ; + $JpgVersion = JpgGetInteger ; + $JpgHRes = JpgGetInteger ; + $JpgVRes = JpgGetInteger ; + $JpgPos = $JpgLen + 4 ; + $JpgSoi = 255 ; + while () + { seek JPG, $JpgPos, 0 ; + $JpgSoi = JpgGetByte ; + $JpgApp = JpgGetByte ; + $JpgLen = JpgGetInteger ; + if ($JpgSoi!=255) + { last } + if (($JpgApp>=192) && ($JpgApp<=195)) # Found in the perl script. + { $JpgDummy = JpgGetByte ; # Found in the perl script. + $JpgHeight = JpgGetInteger ; # Found in the perl script. + $JpgWidth = JpgGetInteger } # Found in the perl script. + $JpgPos = $JpgPos + $JpgLen + 2 } + close ( JPG ) ; + if ($JpgUnit==1) + { $JpgMult = $INtoCM } + else + { $JpgMult = 1 } + $JpgHRes = 72 unless $JpgHRes>1 ; + $JpgVRes = 72 unless $JpgVRes>1 ; + $JpgWidth = ($JpgWidth/$JpgHRes)*$JpgMult ; + $JpgHeight = ($JpgHeight/$JpgVRes)*$JpgMult ; + close ( JPG ) ; + SaveFigurePresets + ( $JpgFile, "jpg", $JpgUnit, + 0, 0, $JpgWidth, $JpgHeight, + "", "", $JpgSize ) } + +#D Now we can handle figures! + +sub InitializeFigures + { $NOfFigures = 0 } + +sub FlushFigures + { SetOutputFile ("texutil.tuf") ; + open ( TUF, ">$OutputFile" ) ; + print TUF "%\n" . "% $Program / Figures\n" . "%\n" ; + print TUF "\\thisisfigureversion\{1996.06.01\}\n" . "%\n" ; + for ($n=1 ; $n<=$NOfFigures ; ++$n) + { print TUF $Figures[$n] } + close (TUF) ; + if ($NOfFigures) + { Report("OutputFile", $OutputFile ) } + else + { unlink $OutputFile } + Report ( "NOfFigures", $NOfFigures ) } + +sub DoHandleFigures + { my ($FigureSuffix, $FigureMethod) = @_ ; + if ($InputFile eq "") + { $InputFile = $FigureSuffix } + CheckInputFiles ($InputFile) ; + foreach $FileName (@UserSuppliedFiles) + { &{$FigureMethod} ( $FileName ) } } + +sub HandleFigures + { Report("Action", "GeneratingFigures" ) ; + foreach $FileType (@ARGV) + { if ($FileType=~/\.eps/io) + { Report("Option", "UsingEps") ; + if ($ProcessEpsToPdf) { Report("Option", "EpsToPdf") } + if ($ProcessEpsPage) { Report("Option", "EpsPage") } + last } } + foreach $FileType (@ARGV) + { if ($FileType=~/\.pdf/io) + { Report("Option", "UsingPdf") ; + last } } + foreach $FileType (@ARGV) + { if ($FileType=~/\.tif/io) + { Report("Option", "UsingTif") ; + #RunTifPrograms ; + last } } + foreach $FileType (@ARGV) + { if ($FileType=~/\.png/io) + { Report("Option", "UsingPng") ; + last } } + foreach $FileType (@ARGV) + { if ($FileType=~/\.jpg/io) + { Report("Option", "UsingJpg") ; + last } } + InitializeFigures ; + DoHandleFigures ("eps", "HandleEpsFigure") ; + DoHandleFigures ("pdf", "HandlePdfFigure") ; + DoHandleFigures ("tif", "HandleTifFigure") ; + DoHandleFigures ("png", "HandlePngFigure") ; + DoHandleFigures ("jpg", "HandleJpgFigure") ; + FlushFigures } + +#D \extras +#D {logfiles} +#D +#D This (poor man's) log file scanning routine filters +#D overfull box messages from a log file (\type{\hbox}, +#D \type{\vbox} or both). The collected problems are saved +#D in \type{$ProgramLog}. One can specify a selection +#D criterium. +#D +#D \CONTEXT\ reports unknown entities. These can also be +#D filtered. When using fast computers, or when processing +#D files in batch, one has to rely on the log files and/or +#D this filter. + +$Unknown = "onbekende verwijzing|" . + "unbekannte Referenz|" . + "unknown reference|" . + "dubbele verwijzing|" . + "duplicate reference|" . + "doppelte Referenz" ; + +sub FlushLogTopic + { unless ($TopicFound) + { $TopicFound = 1 ; + print ALL "\n% File: $FileName.log\n\n" } } + +sub HandleLogFile + { if ($ProcessBox) + { Report("Option", "FilteringBoxes", "(\\vbox & \\hbox)") ; + $Key = "[h|v]box" } + elsif ($ProcessHBox) + { Report("Option", "FilteringBoxes", "(\\hbox)") ; + $Key = "hbox" ; + $ProcessBox = 1 } + elsif ($ProcessVBox) + { Report("Option", "FilteringBoxes", "(\\vbox)") ; + $Key = "vbox" ; + $ProcessBox = 1 } + if (($ProcessBox) && ($ProcessCriterium)) + { Report("Option", "ApplyingCriterium") } + if ($ProcessUnknown) + { Report("Option", "FilteringUnknown") } + unless (($ProcessBox) || ($ProcessUnknown)) + { ShowHelpInfo ; + return } + Report("Action", "FilteringLogFile" ) ; + if ($InputFile eq "") + { Report("Error", "NoInputFile") } + else + { $NOfBoxes = 0 ; + $NOfMatching = 0 ; + $NOfUnknown = 0 ; + SetOutputFile ($ProgramLog) ; + Report("OutputFile", $OutputFile) ; + CheckInputFiles ($InputFile) ; + open ( ALL, ">$OutputFile" ) ; + foreach $FullName (@UserSuppliedFiles) + { ($FileName, $FileSuffix) = SplitFileName ($FullName) ; + if (! open (LOG, "$FileName.log")) + { Report("Error", "EmptyInputFile", "$FileName.$FileSuffix" ) } + elsif (-e "$FileName.tex") + { $TopicFound = 0 ; + Report("InputFile", "$FileName.log") ; + while (<LOG>) + { $SomeLine = $_ ; + chomp $SomeLine ; + if (($ProcessBox) && ($SomeLine =~ /Overfull \\$Key/)) + { ++$NOfBoxes ; + $SomePoints = $SomeLine ; + $SomePoints =~ s/.*\((.*)pt.*/$1/ ; + if ($SomePoints>=$ProcessCriterium) + { ++$NOfMatching ; + FlushLogTopic ; + print ALL "$SomeLine\n" ; + $SomeLine=<LOG> ; + print ALL $SomeLine } } + if (($ProcessUnknown) && ($SomeLine =~ /$Unknown/io)) + { ++$NOfUnknown ; + FlushLogTopic ; + print ALL "$SomeLine\n" } } } } + close (ALL) ; + unless (($NOfBoxes) ||($NOfUnknown)) + { unlink $OutputFile } + if ($ProcessBox) + { Report ( "NOfBoxes" , "$NOfBoxes", "->", $NOfMatching, "Overfull") } + if ($ProcessUnknown) + { Report ( "NOfUnknown", "$NOfUnknown") } } } + +#D Undocumented feature. +# +# obsolete, i.e now in ctxtools, so this will become: +# +# sub PurgeFiles { +# if ($PurgeAllFiles) { +# system("ctxtools --purge $ARGV[0]") ; +# } else { +# system("ctxtools --purge --all $ARGV[0]") ; +# } + +my $removedfiles = 0 ; +my $keptfiles = 0 ; +my $persistentfiles = 0 ; +my $reclaimedbytes = 0 ; + +sub RemoveContextFile + { my $filename = shift ; + my $filesize = -s $filename ; + unlink $filename ; + if (-e $filename) + { ++$persistentfiles ; + print " persistent : $filename\n" } + else + { ++$removedfiles ; $reclaimedbytes += $filesize ; + print " removed : $filename\n" } } + +sub KeepContextFile + { my $filename = shift ; + ++$keptfiles ; + print " kept : $filename\n" } + +my @dontaskprefixes = sort glob "mpx-*" ; push @dontaskprefixes , + ("tex-form.tex","tex-edit.tex","tex-temp.tex", + "texexec.tex","texexec.tui","texexec.tuo", + "texexec.ps","texexec.pdf","texexec.dvi", + "cont-opt.tex","cont-opt.bak") ; +my @dontasksuffixes = + ("mpgraph.mp","mpgraph.mpd","mpgraph.mpo","mpgraph.mpy", + "mprun.mp", "mprun.mpd", "mprun.mpo", "mprun.mpy", + "xlscript.xsl") ; +my @forsuresuffixes = + ("tui","tup","ted","tes","top", + "log","tmp","run","bck","rlg", + "mpt","mpx","mpd","mpo") ; +my @texonlysuffixes = + ("dvi","ps","pdf") ; +my @texnonesuffixes = + ("tuo","tub","top") ; + +if ($PurgeAllFiles) + { push @forsuresuffixes, @texnonesuffixes ; @texnonesuffixes = [] } + +sub PurgeFiles # no my in foreach + { my $pattern = $ARGV[0] ; my $strippedname, $basename ; + my @files = () ; + if ($pattern eq '') + { $pattern = "*.*" ; + @files = glob $pattern } + else + { $pattern = $ARGV[0] . "-*.*" ; + @files = glob $pattern ; + $pattern = $ARGV[0] . ".*" ; + push(@files,glob $pattern) } + @files = sort @files ; + print " purging files : $pattern\n\n" ; + foreach $file (@dontaskprefixes) + { if (-e $file) + { RemoveContextFile($file) } } + foreach $file (@dontasksuffixes) + { if (-e $file) + { RemoveContextFile($file) } } + foreach $suffix (@dontasksuffixes) + { foreach (@files) + { if (/$suffix$/i) + { RemoveContextFile($_) } } } + foreach $suffix (@forsuresuffixes) + { foreach (@files) + { if (/\.$suffix$/i) + { RemoveContextFile($_) } } } + foreach $file (@files) + { if ($file =~ /(.*?)\.\d+$/) + { $basename = $1 ; + if (($file =~ /mp(graph|run)/) || (-e "$basename.mp")) + { RemoveContextFile($file) } } } + foreach $suffix (@texnonesuffixes) + { foreach (@files) + { if (/(.*)\.$suffix$/i) + { if ((-e "$1.tex")||(-e "$1.xml")||(-e "$1.fo")) + { KeepContextFile($_) } + else + { $strippedname = $1 ; + $strippedname =~ s/\-[a-z]$//io ; + if ((-e "$strippedname.tex")||(-e "$strippedname.xml")) + { KeepContextFile($_." (potential result file)") } + else + { RemoveContextFile($_) } } } } } + if ($removedfiles||$keptfiles||$persistentfiles) + { print "\n" } + print " removed files : $removedfiles\n" ; + print " kept files : $keptfiles\n" ; + print " persistent files : $persistentfiles\n" ; + print " reclaimed bytes : $reclaimedbytes\n" } + +#D Another undocumented feature. +# +# obsolete, i.e now in pdftools, so this will become: +# +# sub AnalyzeFile +# { system("pdftools --analyze $ARGV[0]") } + +sub AnalyzeFile + { my $filename = $ARGV[0] ; + return unless (($filename =~ /\.pdf/)&&(-e $filename)) ; + my $filesize = -s $filename ; + print " analyzing file : $filename\n" ; + print " file size : $filesize\n" ; + open (PDF, $filename) ; + binmode PDF ; + my $Object = 0 ; + my $Annot = 0 ; + my $Link = 0 ; + my $Widget = 0 ; + my $Named = 0 ; + my $Script = 0 ; + my $Cross = 0 ; + while (<PDF>) + { while (/\d+\s+\d+\s+obj/go) { ++$Object } ; + while (/\/Type\s*\/Annot/go) { ++$Annot } ; + while (/\/GoToR\s*\/F/go) { ++$Cross } ; + while (/\/Subtype\s*\/Link/go) { ++$Link } ; + while (/\/Subtype\s*\/Widget/go) { ++$Widget } ; + while (/\/S\s*\/Named/go) { ++$Named } ; + while (/\/S\s*\/JavaScript/go) { ++$Script } } + close (PDF) ; + print " objects : $Object\n" ; + print " annotations : $Annot\n" ; + print " links : $Link ($Named named / $Script scripts / $Cross files)\n" ; + print " widgets : $Widget\n" } + +# moved to ctxtools +# +# sub FilterPages +# { system("ctxtools $ARGV{0]") } + +sub FilterPages # temp feature / no reporting + { my $filename = $ARGV[0] ; + return unless -f "$filename.pdf" ; + my $old = '' ; + my $n = 0 ; + if (open(PDF,"<$filename.pdf") && open(TUO,">>$filename.tuo")) + { binmode PDF ; + while (<PDF>) + { chomp ; + if (($_ eq '/Type /Page') && ($old =~ /^(\d+)\s+0\s+obj/o)) + { ++$n ; $p = $1 ; + print TUO "\\objectreference{PDFP}{$n}{$p}{$n}\n" } + else + { $old = $_ } } + close(PDF) ; + close(TUO) } } + + ShowBanner ; + +if ($UnknownOptions ) { ShowHelpInfo } # not yet done +elsif ($ProcessReferences) { HandleReferences } +elsif ($ProcessDocuments ) { HandleDocuments } +elsif ($ProcessSources ) { HandleSources } +elsif ($ProcessSetups ) { HandleSetups } +elsif ($ProcessTemplates ) { HandleEditorCues } +elsif ($ProcessInfos ) { HandleEditorCues } +elsif ($ProcessFigures ) { HandleFigures } +elsif ($ProcessLogFile ) { HandleLogFile } +elsif ($PurgeFiles ) { PurgeFiles } +elsif ($PurgeAllFiles ) { PurgeFiles } +elsif ($AnalyzeFile ) { AnalyzeFile } +elsif ($FilterPages ) { FilterPages } +elsif ($SciteApi ) { GenerateSciteApi } +elsif ($ProcessHelp ) { ShowHelpInfo } # redundant +else { ShowHelpInfo } + +#D So far. diff --git a/Master/texmf-dist/scripts/context/perl/utiplug.pm b/Master/texmf-dist/scripts/context/perl/utiplug.pm new file mode 100644 index 00000000000..2bc162e1e38 --- /dev/null +++ b/Master/texmf-dist/scripts/context/perl/utiplug.pm @@ -0,0 +1,30 @@ +package utiplug ; + +my @data ; +my @result ; + +sub utiplug::initialize + { @data = () } + +sub utiplug::process + { @data = sort @data ; + for (my $i=0; $i<@data; $i++) + { @result[$i] = "\\plugintest\{$i\}\{$data[$i]\}" } } + +sub utiplug::handle + { my ($self,$text,$rest) = @_ ; push @data, $text } + +sub utiplug::identify + { return "utiplug test plugin" } + +sub utiplug::report + { my $keys = @data ; + if ($keys) + { return ("done", "keys:$keys") } + else + { return ("nothing done") } } + +sub utiplug::results + { return @result } + +1 ; diff --git a/Master/texmf-dist/scripts/context/ruby/base/ctx.rb b/Master/texmf-dist/scripts/context/ruby/base/ctx.rb new file mode 100644 index 00000000000..852c3f7046e --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/ctx.rb @@ -0,0 +1,285 @@ +# module : base/ctx +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# todo: write systemcall for mpost to file so that it can be run +# faster + +# report ? + +require 'base/system' +require 'base/file' +require 'base/switch' # has needsupdate, bad place + +require 'rexml/document' + +class CtxRunner + + attr_reader :environments, :modules, :filters + + def initialize(jobname=nil,logger=nil) + if @logger = logger then + def report(str='') + @logger.report(str) + end + else + def report(str='') + puts(str) + end + end + @jobname = jobname + @ctxname = nil + @xmldata = nil + @prepfiles = Hash.new + @environments = Array.new + @modules = Array.new + @filters = Array.new + end + + def manipulate(ctxname=nil,defaultname=nil) + + if ctxname then + @ctxname = ctxname + @jobname = File.suffixed(@ctxname,'tex') unless @jobname + else + @ctxname = File.suffixed(@jobname,'ctx') if @jobname + end + + if not @ctxname then + report('provide ctx file') + return + end + + if not FileTest.file?(@ctxname) and defaultname and FileTest.file?(defaultname) then + @ctxname = defaultname + end + + if not FileTest.file?(@ctxname) then + report('provide ctx file') + return + end + + @xmldata = IO.read(@ctxname) + + unless @xmldata =~ /^.*<\?xml.*?\?>/moi then + report("ctx file #{@ctxname} is no xml file, skipping") + return + else + report("loading ctx file #{@ctxname}") + end + + begin + @xmldata = REXML::Document.new(@xmldata) + rescue + report('provide valid ctx file (xml error)') + return + else + include(@xmldata,'ctx:include','name') + end + + begin + variables = Hash.new + if @jobname then + variables['job'] = @jobname + end + REXML::XPath.each(@xmldata.root,"//ctx:value[@name='job']") do |val| + substititute(val,variables['job']) + end + REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:message") do |mes| + report("preprocessing: #{justtext(mes)}") + end + REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:environment") do |sty| + @environments << justtext(sty) + end + REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:module") do |mod| + @modules << justtext(mod) + end + REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:process/ctx:resources/ctx:filter") do |fil| + @filters << justtext(mod) + end + REXML::XPath.each(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:files") do |files| + REXML::XPath.each(files,"ctx:file") do |pattern| + preprocessor = pattern.attributes['processor'] + if preprocessor and not preprocessor.empty? then + pattern = justtext(pattern) + Dir.glob(pattern).each do |oldfile| + newfile = "#{oldfile}.prep" + if File.needsupdate(oldfile,newfile) then + begin + File.delete(newfile) + rescue + # hope for the best + end + # there can be a sequence of processors + preprocessor.split(',').each do |pp| + if command = REXML::XPath.first(@xmldata.root,"/ctx:job/ctx:preprocess/ctx:processors/ctx:processor[@name='#{pp}']") then + # a lie: no <?xml ...?> + command = REXML::Document.new(command.to_s) # don't infect original + command = command.elements["ctx:processor"] + report("preprocessing #{oldfile} using #{pp}") + REXML::XPath.each(command,"ctx:old") do |value| replace(value,oldfile) end + REXML::XPath.each(command,"ctx:new") do |value| replace(value,newfile) end + variables['old'] = oldfile + variables['new'] = newfile + REXML::XPath.each(command,"ctx:value") do |value| + if name = value.attributes['name'] then + substititute(value,variables[name.to_s]) + end + end + command = justtext(command) + report(command) + unless ok = System.run(command) then + report("error in preprocessing file #{oldfile}") + end + end + end + if FileTest.file?(newfile) then + File.syncmtimes(oldfile,newfile) + else + report("preprocessing #{oldfile} gave no #{newfile}") + end + else + report("#{oldfile} needs no preprocessing") + end + @prepfiles[oldfile] = FileTest.file?(newfile) + end + end + end + end + rescue + report("fatal error in preprocessing #{@ctxname}: #{$!}") + end + end + + def savelog(ctlname=nil) + unless ctlname then + if @jobname then + ctlname = File.suffixed(@jobname,'ctl') + elsif @ctxname then + ctlname = File.suffixed(@ctxname,'ctl') + else + return + end + end + if @prepfiles.length > 0 then + if log = File.open(ctlname,'w') then + log << "<?xml version='1.0' standalone='yes'?>\n\n" + log << "<ctx:preplist>\n" + @prepfiles.keys.sort.each do |prep| + log << "\t<ctx:prepfile done='#{yes_or_no(@prepfiles[prep])}'>#{File.basename(prep)}</ctx:prepfile>\n" + end + log << "</ctx:preplist>\n" + log.close + end + else + begin + File.delete(ctlname) + rescue + end + end + end + + private + + def include(xmldata,element='ctx:include',attribute='name') + loop do + begin + more = false + REXML::XPath.each(xmldata.root,element) do |e| + begin + name = e.attributes.get_attribute(attribute).to_s + name = e.text.to_s if name.empty? + name.strip! if name + if name and not name.empty? and FileTest.file?(name) then + if f = File.open(name,'r') and i = REXML::Document.new(f) then + report("including ctx file #{name}") + REXML::XPath.each(i.root,"*") do |ii| + xmldata.root.insert_after(e,ii) + more = true + end + end + else + report("no valid ctx inclusion file #{name}") + end + rescue Exception + # skip this file + ensure + xmldata.root.delete(e) + end + end + break unless more + rescue Exception + break # forget about inclusion + end + end + end + + private + + def yes_or_no(b) + if b then 'yes' else 'no' end + end + + private # copied from rlxtools.rb + + def justtext(str) + str = str.to_s + str.gsub!(/<[^>]*?>/o, '') + str.gsub!(/\s+/o, ' ') + str.gsub!(/</o, '<') + str.gsub!(/>/o, '>') + str.gsub!(/&/o, '&') + str.gsub!(/"/o, '"') + str.gsub!(/[\/\\]+/o, '/') + return str.strip + end + + def substititute(value,str) + if str then + begin + if value.attributes.key?('method') then + str = filtered(str.to_s,value.attributes['method'].to_s) + end + if str.empty? && value.attributes.key?('default') then + str = value.attributes['default'].to_s + end + value.insert_after(value,REXML::Text.new(str.to_s)) + rescue Exception + end + end + end + + def replace(value,str) + if str then + begin + value.insert_after(value,REXML::Text.new(str.to_s)) + rescue Exception + end + end + end + + def filtered(str,method) + str = str.to_s # to be sure + case method + when 'name' then # no path, no suffix + case str + when /^.*[\\\/](.+?)\..*?$/o then $1 + when /^.*[\\\/](.+?)$/o then $1 + when /^(.*)\..*?$/o then $1 + else str + end + when 'path' then if str =~ /^(.+)([\\\/])(.*?)$/o then $1 else '' end + when 'suffix' then if str =~ /^.*\.(.*?)$/o then $1 else '' end + when 'nosuffix' then if str =~ /^(.*)\..*?$/o then $1 else str end + when 'nopath' then if str =~ /^.*[\\\/](.*?)$/o then $1 else str end + else str + end + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/file.rb b/Master/texmf-dist/scripts/context/ruby/base/file.rb new file mode 100644 index 00000000000..42fb346c409 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/file.rb @@ -0,0 +1,147 @@ +# module : base/file +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +require 'ftools' + +class File + + def File.suffixed(name,sufa,sufb=nil) + if sufb then + if sufa.empty? then + unsuffixed(name) + ".#{sufb}" + else + unsuffixed(name) + "-#{sufa}.#{sufb}" + end + else + unsuffixed(name) + ".#{sufa}" + end + end + + def File.unsuffixed(name) + name.sub(/\.[^\.]*?$/o, '') + end + + def File.suffix(name,default='') + if name =~ /\.([^\.]*?)$/o then + $1 + else + default + end + end + + def File.splitname(name,suffix='') + if name =~ /^(.*)\.([^\.]*?)$/o then + [$1, $2] + else + [name, suffix] + end + end + +end + +class File + + def File.silentopen(name,method='r') + begin + f = File.open(name,method) + rescue + return nil + else + return f + end + end + + def File.silentread(name) + begin + data = IO.read(name) + rescue + return nil + else + return data + end + end + + def File.atleast?(name,n=0) + begin + size = FileTest.size(name) + rescue + return false + else + return size > n + end + end + + def File.appended(name,str='') + if FileTest.file?(name) then + begin + if f = File.open(name,'a') then + f << str + f.close + return true + end + rescue + end + end + return false + end + + def File.written(name,str='') + begin + if f = File.open(name,'w') then + f << str + f.close + return true + end + rescue + end + return false + end + + def File.silentdelete(filename) + begin File.delete(filename) ; rescue ; end + end + + def File.silentcopy(oldname,newname) + return if File.expand_path(oldname) == File.expand_path(newname) + begin File.copy(oldname,newname) ; rescue ; end + end + + def File.silentrename(oldname,newname) + # in case of troubles, we just copy the file; we + # maybe working over multiple file systems or + # apps may have mildly locked files (like gs does) + return if File.expand_path(oldname) == File.expand_path(newname) + begin File.delete(newname) ; rescue ; end + begin + File.rename(oldname,newname) + rescue + begin File.copy(oldname,newname) ; rescue ; end + end + end + +end + +class File + + # handles "c:\tmp\test.tex" as well as "/${TEMP}/test.tex") + + def File.unixfied(filename) + begin + str = filename.gsub(/\$\{*([a-z0-9\_]+)\}*/oi) do + if ENV.key?($1) then ENV[$1] else $1 end + end + str.gsub(/[\/\\]+/o, '/') + rescue + filename + end + end + +end + diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpse.rb b/Master/texmf-dist/scripts/context/ruby/base/kpse.rb new file mode 100644 index 00000000000..dc4898ffc58 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/kpse.rb @@ -0,0 +1,305 @@ +# module : base/kpse +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# rename this one to environment +# +# todo: web2c vs miktex module and include in kpse + +require 'rbconfig' + +# beware $engine is lowercase in kpse +# +# miktex has mem|fmt|base paths + +module Kpse + + @@located = Hash.new + @@paths = Hash.new + @@scripts = Hash.new + @@formats = ['tex','texmfscripts','other text files'] + @@progname = 'context' + @@ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + @@problems = false + @@tracing = false + @@distribution = 'web2c' + @@crossover = true + @@mswindows = Config::CONFIG['host_os'] =~ /mswin/ + + @@distribution = 'miktex' if ENV['PATH'] =~ /miktex[\\\/]bin/o + + @@usekpserunner = false || ENV['KPSEFAST'] == 'yes' + + require 'base/tool' if @@usekpserunner + + if @@crossover then + ENV.keys.each do |k| + case k + when /\_CTX\_KPSE\_V\_(.*?)\_/io then @@located[$1] = ENV[k].dup + when /\_CTX\_KPSE\_P\_(.*?)\_/io then @@paths [$1] = ENV[k].dup.split(';') + when /\_CTX\_KPSE\_S\_(.*?)\_/io then @@scripts[$1] = ENV[k].dup + end + end + end + + def Kpse.distribution + @@distribution + end + + def Kpse.miktex? + @@distribution == 'miktex' + end + + def Kpse.web2c? + @@distribution == 'web2c' + end + + def Kpse.inspect + @@located.keys.sort.each do |k| puts("located : #{k} -> #{@@located[k]}\n") end + @@paths .keys.sort.each do |k| puts("paths : #{k} -> #{@@paths [k]}\n") end + @@scripts.keys.sort.each do |k| puts("scripts : #{k} -> #{@@scripts[k]}\n") end + end + + def Kpse.found(filename, progname=nil, format=nil) + begin + tag = Kpse.key(filename) # all + if @@located.key?(tag) then + return @@located[tag] + elsif FileTest.file?(filename) then + setvariable(tag,filename) + return filename + elsif FileTest.file?(File.join(@@ownpath,filename)) then + setvariable(tag,File.join(@@ownpath,filename)) + return @@located[tag] + else + [progname,@@progname].flatten.compact.uniq.each do |prg| + [format,@@formats].flatten.compact.uniq.each do |fmt| + begin + tag = Kpse.key(filename,prg,fmt) + if @@located.key?(tag) then + return @@located[tag] + elsif p = Kpse.kpsewhich(filename,prg,fmt) then + setvariable(tag,p.chomp) + return @@located[tag] + end + rescue + end + end + end + setvariable(tag,filename) + return filename + end + rescue + filename + end + end + + def Kpse.kpsewhich(filename,progname,format) + Kpse.run("-progname=#{progname} -format=\"#{format}\" #{filename}") + end + + def Kpse.which + Kpse.kpsewhich + end + + def Kpse.run(arguments) + puts arguments if @@tracing + begin + if @@problems then + results = '' + else + if @@usekpserunner then + results = KpseRunner.kpsewhich(arguments).chomp + else + results = `kpsewhich #{arguments}`.chomp + end + end + rescue + puts "unable to run kpsewhich" if @@tracing + @@problems, results = true, '' + end + puts results if @@tracing + return results + end + + def Kpse.formatpaths + # maybe we should check for writeability + unless @@paths.key?('formatpaths') then + begin + setpath('formatpaths',run("--show-path=fmt").gsub(/\\/,'/').split(File::PATH_SEPARATOR)) + rescue + setpath('formatpaths',[]) + end + end + return @@paths['formatpaths'] + end + + def Kpse.key(filename='',progname='all',format='all') + [progname,format,filename].join('-') + end + + def Kpse.formatpath(engine='pdfetex',enginepath=true) + + # because engine support in distributions is not always + # as we expect, we need to check for it; + + # todo: miktex + + if miktex? then + return '.' + else + unless @@paths.key?(engine) then + # savedengine = ENV['engine'] + if ENV['TEXFORMATS'] && ! ENV['TEXFORMATS'].empty? then + # make sure that we have a lowercase entry + ENV['TEXFORMATS'] = ENV['TEXFORMATS'].sub(/\$engine/io,"\$engine") + # well, we will append anyway, so we could also strip it + # ENV['TEXFORMATS'] = ENV['TEXFORMATS'].sub(/\$engine/io,"") + end + # use modern method + if enginepath then + formatpath = run("--engine=#{engine} --show-path=fmt") + else + # ENV['engine'] = engine if engine + formatpath = run("--show-path=fmt") + end + # use ancient method + if formatpath.empty? then + if enginepath then + if @@mswindows then + formatpath = run("--engine=#{engine} --expand-path=\$TEXFORMATS") + else + formatpath = run("--engine=#{engine} --expand-path=\\\$TEXFORMATS") + end + end + # either no enginepath or failed run + if formatpath.empty? then + if @@mswindows then + formatpath = run("--expand-path=\$TEXFORMATS") + else + formatpath = run("--expand-path=\\\$TEXFORMATS") + end + end + end + # locate writable path + if ! formatpath.empty? then + formatpath.split(File::PATH_SEPARATOR).each do |fp| + fp.gsub!(/\\/,'/') + # remove funny patterns + fp.sub!(/^!!/,'') + fp.sub!(/\/+$/,'') + fp.sub!(/unsetengine/,if enginepath then engine else '' end) + if ! fp.empty? && (fp != '.') then + # strip (possible engine) and test for writeability + fpp = fp.sub(/#{engine}\/*$/,'') + if FileTest.directory?(fpp) && FileTest.writable?(fpp) then + # use this path + formatpath = fp.dup + break + end + end + end + end + # needed ! + begin File.makedirs(formatpath) ; rescue ; end ; + # fall back to current path + formatpath = '.' if formatpath.empty? || ! FileTest.writable?(formatpath) + # append engine but prevent duplicates + formatpath = File.join(formatpath.sub(/\/*#{engine}\/*$/,''), engine) if enginepath + begin File.makedirs(formatpath) ; rescue ; end ; + setpath(engine,formatpath) + # ENV['engine'] = savedengine + end + return @@paths[engine].first + end + end + + def Kpse.update + system('initexmf -u') if Kpse.miktex? + system('mktexlsr') + end + + # engine support is either broken of not implemented in some + # distributions, so we need to take care of it ourselves (without + # delays due to kpse calls); there can be many paths in the string + # + # in a year or so, i will drop this check + + def Kpse.fixtexmfvars(engine=nil) + ENV['ENGINE'] = engine if engine + texformats = if ENV['TEXFORMATS'] then ENV['TEXFORMATS'].dup else '' end + if texformats.empty? then + if engine then + if @@mswindows then + texformats = `kpsewhich --engine=#{engine} --expand-var=\$TEXFORMATS`.chomp + else + texformats = `kpsewhich --engine=#{engine} --expand-var=\\\$TEXFORMATS`.chomp + end + else + if @@mswindows then + texformats = `kpsewhich --expand-var=\$TEXFORMATS`.chomp + else + texformats = `kpsewhich --expand-var=\\\$TEXFORMATS`.chomp + end + end + end + if engine then + texformats.sub!(/unsetengine/,engine) + else + texformats.sub!(/unsetengine/,"\$engine") + end + if engine && (texformats =~ /web2c[\/\\].*#{engine}/o) then + # ok, engine is seen + return false + elsif texformats =~ /web2c[\/\\].*\$engine/io then + # shouldn't happen + return false + else + ENV['TEXFORMATS'] = texformats.gsub(/(web2c\/\{)(,\})/o) do + "#{$1}\$engine#{$2}" + end + if texformats !~ /web2c[\/\\].*\$engine/io then + ENV['TEXFORMATS'] = texformats.gsub(/web2c\/*/, "web2c/{\$engine,}") + end + return true + end + end + + def Kpse.runscript(name,filename=[],options=[]) + setscript(name,`texmfstart --locate #{name}`) unless @@scripts.key?(name) + cmd = "#{@@scripts[name]} #{[options].flatten.join(' ')} #{[filename].flatten.join(' ')}" + system(cmd) + end + + def Kpse.pipescript(name,filename=[],options=[]) + setscript(name,`texmfstart --locate #{name}`) unless @@scripts.key?(name) + cmd = "#{@@scripts[name]} #{[options].flatten.join(' ')} #{[filename].flatten.join(' ')}" + `#{cmd}` + end + + private + + def Kpse.setvariable(key,value) + @@located[key] = value + ENV["_CTX_K_V_#{key}_"] = @@located[key] if @@crossover + end + + def Kpse.setscript(key,value) + @@scripts[key] = value + ENV["_CTX_K_S_#{key}_"] = @@scripts[key] if @@crossover + end + + def Kpse.setpath(key,value) + @@paths[key] = [value].flatten.uniq.collect do |p| + p.sub(/^!!/,'').sub(/\/*$/,'') + end + ENV["_CTX_K_P_#{key}_"] = @@paths[key].join(';') if @@crossover + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb b/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb new file mode 100644 index 00000000000..52ab28d0fe7 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/kpsefast.rb @@ -0,0 +1,881 @@ +# module : base/kpsefast +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# todo: multiple cnf files + +class File + + def File.locate_file(path,name) + begin + files = Dir.entries(path) + if files.include?(name) then + fullname = File.join(path,name) + return fullname if FileTest.file?(fullname) + end + files.each do |p| + fullname = File.join(path,p) + if p != '.' and p != '..' and FileTest.directory?(fullname) and result = locate_file(fullname,name) then + return result + end + end + rescue + # bad path + end + return nil + end + + def File.glob_file(pattern) + return Dir.glob(pattern).first + end + +end + +class KPSEFAST + + # formats are an incredible inconsistent mess + + @@suffixes = Hash.new + @@formats = Hash.new + @@suffixmap = Hash.new + + @@suffixes['gf'] = ['.<resolution>gf'] # todo + @@suffixes['pk'] = ['.<resolution>pk'] # todo + @@suffixes['tfm'] = ['.tfm'] + @@suffixes['afm'] = ['.afm'] + @@suffixes['base'] = ['.base'] + @@suffixes['bib'] = ['.bib'] + @@suffixes['bst'] = ['.bst'] + @@suffixes['cnf'] = ['.cnf'] + @@suffixes['ls-R'] = ['ls-R', 'ls-r'] + @@suffixes['fmt'] = ['.fmt', '.efmt', '.efm', '.ofmt', '.ofm', '.oft', '.eofmt', '.eoft', '.eof', '.pfmt', '.pfm', '.epfmt', '.epf', '.xpfmt', '.xpf', '.afmt', '.afm'] + @@suffixes['map'] = ['.map'] + @@suffixes['mem'] = ['.mem'] + @@suffixes['mf'] = ['.mf'] + @@suffixes['mfpool'] = ['.pool'] + @@suffixes['mft'] = ['.mft'] + @@suffixes['mp'] = ['.mp'] + @@suffixes['mppool'] = ['.pool'] + @@suffixes['ocp'] = ['.ocp'] + @@suffixes['ofm'] = ['.ofm', '.tfm'] + @@suffixes['opl'] = ['.opl'] + @@suffixes['otp'] = ['.otp'] + @@suffixes['ovf'] = ['.ovf'] + @@suffixes['ovp'] = ['.ovp'] + @@suffixes['graphic/figure'] = ['.eps', '.epsi'] + @@suffixes['tex'] = ['.tex'] + @@suffixes['texpool'] = ['.pool'] + @@suffixes['PostScript header'] = ['.pro'] + @@suffixes['type1 fonts'] = ['.pfa', '.pfb'] + @@suffixes['vf'] = ['.vf'] + @@suffixes['ist'] = ['.ist'] + @@suffixes['truetype fonts'] = ['.ttf', '.ttc'] + @@suffixes['web'] = ['.web', '.ch'] + @@suffixes['cweb'] = ['.w', '.web', '.ch'] + @@suffixes['enc files'] = ['.enc'] + @@suffixes['cmap files'] = ['.cmap'] + @@suffixes['subfont definition files'] = ['.sfd'] + @@suffixes['lig files'] = ['.lig'] + @@suffixes['bitmap font'] = [] + @@suffixes['MetaPost support'] = [] + @@suffixes['TeX system documentation'] = [] + @@suffixes['TeX system sources'] = [] + @@suffixes['Troff fonts'] = [] + @@suffixes['dvips config'] = [] + @@suffixes['type42 fonts'] = [] + @@suffixes['web2c files'] = [] + @@suffixes['other text files'] = [] + @@suffixes['other binary files'] = [] + @@suffixes['misc fonts'] = [] + @@suffixes['opentype fonts'] = [] + @@suffixes['pdftex config'] = [] + @@suffixes['texmfscripts'] = [] + + # replacements + + @@suffixes['fmt'] = ['.fmt'] + @@suffixes['type1 fonts'] = ['.pfa', '.pfb', '.pfm'] + @@suffixes['tex'] = ['.tex', '.xml'] + @@suffixes['texmfscripts'] = ['rb','lua','py','pl'] + + @@suffixes.keys.each do |k| @@suffixes[k].each do |s| @@suffixmap[s] = k end end + + # TTF2TFMINPUTS + # MISCFONTS + # TEXCONFIG + # DVIPDFMINPUTS + # OTFFONTS + + @@formats['gf'] = '' + @@formats['pk'] = '' + @@formats['tfm'] = 'TFMFONTS' + @@formats['afm'] = 'AFMFONTS' + @@formats['base'] = 'MFBASES' + @@formats['bib'] = '' + @@formats['bst'] = '' + @@formats['cnf'] = '' + @@formats['ls-R'] = '' + @@formats['fmt'] = '' + @@formats['map'] = 'TEXFONTMAPS' + @@formats['mem'] = 'MPMEMS' + @@formats['mf'] = 'MFINPUTS' + @@formats['mfpool'] = 'MFPOOL' + @@formats['mft'] = '' + @@formats['mp'] = 'MPINPUTS' + @@formats['mppool'] = 'MPPOOL' + @@formats['ocp'] = 'OCPINPUTS' + @@formats['ofm'] = 'OFMFONTS' + @@formats['opl'] = 'OPLFONTS' + @@formats['otp'] = 'OTPINPUTS' + @@formats['ovf'] = 'OVFFONTS' + @@formats['ovp'] = 'OVPFONTS' + @@formats['graphic/figure'] = '' + @@formats['tex'] = 'TEXINPUTS' + @@formats['texpool'] = 'TEXPOOL' + @@formats['PostScript header'] = 'TEXPSHEADERS' + @@formats['type1 fonts'] = 'T1FONTS' + @@formats['vf'] = 'VFFONTS' + @@formats['ist'] = '' + @@formats['truetype fonts'] = 'TTFONTS' + @@formats['web'] = '' + @@formats['cweb'] = '' + @@formats['enc files'] = 'ENCFONTS' + @@formats['cmap files'] = 'CMAPFONTS' + @@formats['subfont definition files'] = 'SFDFONTS' + @@formats['lig files'] = 'LIGFONTS' + @@formats['bitmap font'] = '' + @@formats['MetaPost support'] = '' + @@formats['TeX system documentation'] = '' + @@formats['TeX system sources'] = '' + @@formats['Troff fonts'] = '' + @@formats['dvips config'] = '' + @@formats['type42 fonts'] = 'T42FONTS' + @@formats['web2c files'] = 'WEB2C' + @@formats['other text files'] = '' + @@formats['other binary files'] = '' + @@formats['misc fonts'] = '' + @@formats['opentype fonts'] = 'OPENTYPEFONTS' + @@formats['pdftex config'] = 'PDFTEXCONFIG' + @@formats['texmfscripts'] = 'TEXMFSCRIPTS' + + attr_accessor :progname, :engine, :format, :rootpath, :treepath, + :verbose, :remember, :scandisk, :diskcache, :renewcache + + @@cacheversion = '1' + + def initialize + @rootpath = '' + @treepath = '' + @progname = 'kpsewhich' + @engine = 'pdfetex' + @variables = Hash.new + @expansions = Hash.new + @files = Hash.new + @found = Hash.new + @kpsevars = Hash.new + @lsrfiles = Array.new + @cnffiles = Array.new + @verbose = true + @remember = true + @scandisk = true + @diskcache = true + @renewcache = false + @isolate = false + + @diskcache = false + @cachepath = nil + @cachefile = 'tmftools.log' + end + + # {$SELFAUTOLOC,$SELFAUTODIR,$SELFAUTOPARENT}{,{/share,}/texmf{-local,}/web2c} + # + # $SELFAUTOLOC : /usr/tex/bin/platform + # $SELFAUTODIR : /usr/tex/bin + # $SELFAUTOPARENT : /usr/tex + # + # since we live in scriptpath we need a slightly different method + + def load_cnf + ownpath = File.expand_path($0) + if ownpath.gsub!(/texmf.*?$/o, '') then + ENV['SELFAUTOPARENT'] = ownpath + else + ENV['SELFAUTOPARENT'] = '.' + end + unless @treepath.empty? then + unless @rootpath.empty? then + @treepath = @treepath.split(',').collect do |p| File.join(@rootpath,p) end.join(',') + end + ENV['TEXMF'] = @treepath + ENV['TEXMFCNF'] = File.join(@treepath.split(',').first,'texmf/web2c') + end + unless @rootpath.empty? then + ENV['TEXMFCNF'] = File.join(@rootpath,'texmf/web2c') + ENV['SELFAUTOPARENT'] = @rootpath + @isolate = true + end + filenames = Array.new + if ENV['TEXMFCNF'] and not ENV['TEXMFCNF'].empty? then + filenames << File.join(ENV['TEXMFCNF'],'texmf.cnf') + elsif ENV['SELFAUTOPARENT'] == '.' then + filenames << File.join('.','texmf.cnf') + else + ['texmf-local','texmf'].each do |tree| + filenames << File.join(ENV['SELFAUTOPARENT'],tree,'web2c','texmf.cnf') + end + end + # <root>/texmf/web2c/texmf.cnf + @rootpath = filenames.first + 3.times do + @rootpath = File.dirname(@rootpath) + end + filenames.collect! do |f| + f.gsub("\\", '/') + end + filenames.each do |fname| + if FileTest.file?(fname) and f = File.open(fname) then + @cnffiles << fname + while line = f.gets do + loop do + # concatenate lines ending with \ + break unless line.sub!(/\\\s*$/o) do + f.gets || '' + end + end + case line + when /^[\%\#]/o then + # comment + when /^\s*(.*?)\s*\=\s*(.*?)\s*$/o then + key, value = $1, $2 + unless @variables.key?(key) then + value.sub!(/\%.*$/,'') + value.sub!(/\~/, "$HOME") + @variables[key] = value + end + @kpsevars[key] = true + end + end + f.close + end + end + end + + def load_lsr + @lsrfiles = [] + simplified_list(expansion('TEXMF')).each do |p| + ['ls-R','ls-r'].each do |f| + filename = File.join(p,f) + if FileTest.file?(filename) then + @lsrfiles << [filename,File.size(filename)] + break + end + end + end + @files = Hash.new + if @diskcache then + ['HOME','TEMP','TMP','TMPDIR'].each do |key| + if ENV[key] then + if FileTest.directory?(ENV[key]) then + @cachepath = ENV[key] + @cachefile = [@rootpath.gsub(/[^A-Z0-9]/io, '-').gsub(/\-+/,'-'),File.basename(@cachefile)].join('-') + break + end + end + end + if @cachepath and not @renewcache and FileTest.file?(File.join(@cachepath,@cachefile)) then + begin + if f = File.open(File.join(@cachepath,@cachefile)) then + cacheversion = Marshal.load(f) + if cacheversion == @@cacheversion then + lsrfiles = Marshal.load(f) + if lsrfiles == @lsrfiles then + @files = Marshal.load(f) + end + end + f.close + end + rescue + @files = Hash.new + end + end + end + return if @files.size > 0 + @lsrfiles.each do |filedata| + filename, filesize = filedata + filepath = File.dirname(filename) + begin + path = '.' + data = IO.readlines(filename) + if data[0].chomp =~ /% ls\-R \-\- filename database for kpathsea\; do not change this line\./io then + data.each do |line| + case line + when /^[a-zA-Z0-9]/o then + line.chomp! + if @files[line] then + @files[line] << path + else + @files[line] = [path] + end + when /^\.\/(.*?)\:$/o then + path = File.join(filepath,$1) + end + end + end + rescue + # sorry + end + end + if @diskcache and @cachepath and f = File.open(File.join(@cachepath,@cachefile),'wb') then + f << Marshal.dump(@@cacheversion) + f << Marshal.dump(@lsrfiles) + f << Marshal.dump(@files) + f.close + end + end + + def expand_variables + @expansions = Hash.new + if @isolate then + @variables['TEXMFCNF'] = ENV['TEXMFCNF'].dup + @variables['SELFAUTOPARENT'] = ENV['SELFAUTOPARENT'].dup + else + ENV.keys.each do |e| + if e =~ /^([a-zA-Z]+)\_(.*)\s*$/o then + @expansions["#{$1}.#{$2}"] = ENV[e].dup + else + @expansions[e] = ENV[e].dup + end + end + end + @variables.keys.each do |k| + @expansions[k] = @variables[k].dup unless @expansions[k] + end + loop do + busy = false + @expansions.keys.each do |k| + @expansions[k].gsub!(/\$([a-zA-Z0-9\_\-]*)/o) do + busy = true + @expansions[$1] || '' + end + @expansions[k].gsub!(/\$\{([a-zA-Z0-9\_\-]*)\}/o) do + busy = true + @expansions[$1] || '' + end + end + break unless busy + end + @expansions.keys.each do |k| + @expansions[k] = @expansions[k].gsub("\\", '/') + end + end + + def variable(name='') + (name and not name.empty? and @variables[name.sub('$','')]) or '' + end + + def expansion(name='') + (name and not name.empty? and @expansions[name.sub('$','')]) or '' + end + + def variable?(name='') + name and not name.empty? and @variables.key?(name.sub('$','')) + end + + def expansion?(name='') + name and not name.empty? and @expansions.key?(name.sub('$','')) + end + + def simplified_list(str) + lst = str.gsub(/^\{/o,'').gsub(/\}$/o,'').split(",") + lst.collect do |l| + l.sub(/^[\!]*/,'').sub(/[\/\\]*$/o,'') + end + end + + def original_variable(variable) + if variable?("#{@progname}.#{variable}") then + variable("#{@progname}.#{variable}") + elsif variable?(variable) then + variable(variable) + else + '' + end + end + + def expanded_variable(variable) + if expansion?("#{variable}.#{@progname}") then + expansion("#{variable}.#{@progname}") + elsif expansion?(variable) then + expansion(variable) + else + '' + end + end + + def original_path(filename='') + _expanded_path_(original_variable(var_of_format_or_suffix(filename)).split(";")) + end + + def expanded_path(filename='') + _expanded_path_(expanded_variable(var_of_format_or_suffix(filename)).split(";")) + end + + def _expanded_path_(pathlist) + i, n = 0, 0 + pathlist.collect! do |mainpath| + mainpath.gsub(/([\{\}])/o) do + if $1 == "{" then + i += 1 ; n = i if i > n ; "<#{i}>" + else + i -= 1 ; "</#{i+1}>" + end + end + end + n.times do |i| + loop do + more = false + newlist = [] + pathlist.each do |path| + unless path.sub!(/^(.*?)<(#{n-i})>(.*?)<\/\2>(.*?)$/) do + pre, mid, post = $1, $3, $4 + mid.gsub!(/\,$/,',.') + mid.split(',').each do |m| + more = true + if m == '.' then + newlist << "#{pre}#{post}" + else + newlist << "#{pre}#{m}#{post}" + end + end + end then + newlist << path + end + end + if more then + pathlist = [newlist].flatten # copy -) + else + break + end + end + end + pathlist = pathlist.uniq.collect do |path| + p = path.gsub(/^\/+/o) do '' end + # p.gsub!(/(.)\/\/(.)/o) do "#{$1}/#{$2}" end + # p.gsub!(/\/\/+$/o) do '//' end + p.gsub!(/\/\/+/o) do '//' end + p + end + pathlist + end + + # todo: ignore case + + def var_of_format(str) + @@formats[str] || '' + end + + def var_of_suffix(str) # includes . + if @@suffixmap.key?(str) then @@formats[@@suffixmap[str]] else '' end + end + + def var_of_format_or_suffix(str) + if @@formats.key?(@format) then + @@formats[@format] + elsif @@suffixmap.key?(File.extname(str)) then # extname includes . + @@formats[@@suffixmap[File.extname(str)]] # extname includes . + else + '' + end + end + +end + +class KPSEFAST + + # test things + + def list_variables(kpseonly=true) + @variables.keys.sort.each do |k| + if kpseonly then + puts("#{k} = #{@variables[k]}") if @kpsevars[k] + else + puts("#{if @kpsevars[k] then 'K' else 'E' end} #{k} = #{@variables[k]}") + end + end + end + + def list_expansions(kpseonly=true) + @expansions.keys.sort.each do |k| + if kpseonly then + puts("#{k} = #{@expansions[k]}") if @kpsevars[k] + else + puts("#{if @kpsevars[k] then 'K' else 'E' end} #{k} = #{@expansions[k]}") + end + end + end + + def list_lsr + puts("files = #{@files.size}") + end + + def set_test_patterns + @variables["KPSE_TEST_PATTERN_A"] = "foo/{1,2}/bar//" + @variables["KPSE_TEST_PATTERN_B"] = "!!x{A,B{1,2}}y" + @variables["KPSE_TEST_PATTERN_C"] = "x{A,B//{1,2}}y" + @variables["KPSE_TEST_PATTERN_D"] = "x{A,B//{1,2,}}//y" + end + + def show_test_patterns + ['A','B','D'].each do |i| + puts "" + puts @variables ["KPSE_TEST_PATTERN_#{i}"] + puts "" + puts expand_path("KPSE_TEST_PATTERN_#{i}").split(File::PATH_SEPARATOR) + puts "" + end + end + +end + +class KPSEFAST + + # kpse stuff + + def expand_braces(str) # output variable and brace expansion of STRING. + _expanded_path_(original_variable(str).split(";")).join(File::PATH_SEPARATOR) + end + + def expand_path(str) # output complete path expansion of STRING. + _expanded_path_(expanded_variable(str).split(";")).join(File::PATH_SEPARATOR) + end + + def expand_var(str) # output variable expansion of STRING. + expanded_variable(str) + end + + def show_path(str) # output search path for file type NAME + expanded_path(var_of_format(str)).join(File::PATH_SEPARATOR) + end + + def var_value(str) # output the value of variable $STRING. + original_variable(str) + end + +end + +class KPSEFAST + + def find_file(filename) + find_files(filename,true) + end + + def find_files(filename,first=false) + if @remember then + stamp = "#{filename}--#{@format}--#{@engine}--#{@progname}" + return @found[stamp] if @found.key?(stamp) + end + pathlist = expanded_path(filename) + result = [] + filelist = if @files.key?(filename) then @files[filename].uniq else nil end + done = false + pathlist.each do |path| + doscan = if path =~ /^\!\!/o then false else true end + recurse = if path =~ /\/\/$/o then true else false end + pathname = path.dup + pathname.gsub!(/^\!+/o, '') + done = false + if not done and filelist then + # checking for exact match + if filelist.include?(pathname) then + result << pathname + done = true + end + if not done and recurse then + # checking for fuzzy // + pathname.gsub!(/\/+$/o, '/.*') + # pathname.gsub!(/\/\//o,'/[\/]*/') + pathname.gsub!(/\/\//o,'/.*?/') + re = /^#{pathname}/ + filelist.each do |f| + if re =~ f then + result << f # duplicates will be filtered later + done = true + end + break if done + end + end + end + if not done and doscan then + # checking for path itself + pname = pathname.sub(/\.\*$/,'') + if not pname =~ /\*/o and FileTest.file?(File.join(pname,filename)) then + result << pname + done = true + end + end + break if done and first + end + if not done and @scandisk then + pathlist.each do |path| + pathname = path.dup + unless pathname.gsub!(/^\!+/o, '') then # !! prevents scan + recurse = pathname.gsub!(/\/+$/o, '') + complex = pathname.gsub!(/\/\//o,'/*/') + if recurse then + if complex then + if ok = File.glob_file("#{pathname}/**/#{filename}") then + result << File.dirname(ok) + done = true + end + elsif ok = File.locate_file(pathname,filename) then + result << File.dirname(ok) + done = true + end + elsif complex then + if ok = File.glob_file("#{pathname}/#{filename}") then + result << File.dirname(ok) + done = true + end + elsif FileTest.file?(File.join(pathname,filename)) then + result << pathname + done = true + end + break if done and first + end + end + end + result = result.uniq.collect do |pathname| + File.join(pathname,filename) + end + @found[stamp] = result if @remember + return result # redundant + end + +end + +class KPSEFAST + + class FileData + attr_accessor :tag, :name, :size, :date + def initialize(tag=0,name=nil,size=nil,date=nil) + @tag, @name, @size, @date = tag, name, size, date + end + def FileData.sizes(a) + a.collect do |aa| + aa.size + end + end + def report + case @tag + when 1 + "deleted | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}" + when 2 + "present | #{@size.to_s.rjust(8)} | #{@date.strftime('%m/%d/%Y %I:%M')} | #{@name}" + when 3 + "obsolete | #{' '*8} | #{' '*16} | #{@name}" + end + end + end + + def analyze_files(filter='',strict=false,sort='',delete=false) + puts("command line = #{ARGV.join(' ')}") + puts("number of files = #{@files.size}") + puts("filter pattern = #{filter}") + puts("loaded cnf files = #{@cnffiles.join(' ')}") + puts('') + if filter.gsub!(/^not:/,'') then + def the_same(filter,filename) + not filter or filter.empty? or /#{filter}/ !~ filename + end + else + def the_same(filter,filename) + not filter or filter.empty? or /#{filter}/ =~ filename + end + end + @files.keys.each do |name| + if @files[name].size > 1 then + data = Array.new + @files[name].each do |path| + filename = File.join(path,name) + # if not filter or filter.empty? or /#{filter}/ =~ filename then + if the_same(filter,filename) then + if FileTest.file?(filename) then + if delete then + data << FileData.new(1,filename,File.size(filename),File.mtime(filename)) + begin + File.delete(filename) if delete + rescue + end + else + data << FileData.new(2,filename,File.size(filename),File.mtime(filename)) + end + else + # data << FileData.new(3,filename) + end + end + end + if data.length > 1 then + if strict then + # if data.collect do |d| d.size end.uniq! then + # data.sort! do |a,b| b.size <=> a.size end + # data.each do |d| puts d.report end + # puts '' + # end + data.sort! do |a,b| + if a.size and b.size then + b.size <=> a.size + else + 0 + end + end + bunch = Array.new + done = false + data.each do |d| + if bunch.size == 0 then + bunch << d + elsif bunch[0].size == d.size then + bunch << d + else + if bunch.size > 1 then + bunch.each do |b| + puts b.report + end + done = true + end + bunch = [d] + end + end + puts '' if done + else + case sort + when 'size' then data.sort! do |a,b| a.size <=> b.size end + when 'revsize' then data.sort! do |a,b| b.size <=> a.size end + when 'date' then data.sort! do |a,b| a.date <=> b.date end + when 'revdate' then data.sort! do |a,b| b.date <=> a.date end + end + data.each do |d| puts d.report end + puts '' + end + end + end + end + end + +end + +module KpseRunner + + @@kpse = nil + + def KpseRunner.kpsewhich(arg='') + options, arguments = split_args(arg) + unless @@kpse then + @@kpse = KPSEFAST.new + @@kpse.load_cnf + @@kpse.progname = options['progname'] || '' + @@kpse.engine = options['engine'] || '' + @@kpse.format = options['format'] || '' + @@kpse.expand_variables + @@kpse.load_lsr + else + @@kpse.progname = options['progname'] || '' + @@kpse.engine = options['engine'] || '' + @@kpse.format = options['format'] || '' + @@kpse.expand_variables + end + if option = options['expand-braces'] and not option.empty? then + @@kpse.expand_braces(option) + elsif option = options['expand-path'] and not option.empty? then + @@kpse.expand_path(option) + elsif option = options['expand-var'] and not option.empty? then + @@kpse.expand_var(option) + elsif option = options['show-path'] and not option.empty? then + @@kpse.show_path(option) + elsif option = options['var-value'] and not option.empty? then + @@kpse.expand_var(option) + elsif arguments.size > 0 then + files = Array.new + arguments.each do |option| + if file = @@kpse.find_file(option) and not file.empty? then + files << file + end + end + files.join("\n") + else + '' + end + end + + def KpseRunner.kpsereset + @@kpse = nil + end + + private + + def KpseRunner.split_args(arg) + vars, args = Hash.new, Array.new + arg.gsub!(/([\"\'])(.*?)\1/o) do + $2.gsub(' ','<space/>') + end + arg = arg.split(/\s+/o) + arg.collect! do |a| + a.gsub('<space/>',' ') + end + arg.each do |a| + if a =~ /^(.*?)\=(.*?)$/o then + k, v = $1, $2 + vars[k.sub(/^\-+/,'')] = v + else + args << a + end + end + # puts vars.inspect + # puts args.inspect + return vars, args + end + +end + +if false then + + k = KPSEFAST.new # (root) + k.set_test_patterns + k.load_cnf + k.expand_variables + k.load_lsr + + k.show_test_patterns + # puts k.list_variables + # puts k.list_expansions + # k.list_lsr + # puts k.expansion("$TEXMF") + # puts k.expanded_path("TEXINPUTS","context") + + # k.progname, k.engine, k.format = 'context', 'pdfetex', 'tfm' + # k.scandisk = false # == must_exist + # k.expand_variables + + # 10.times do |i| puts k.find_file('texnansi-lmr10.tfm') end + + # puts "expand braces $TEXMF" + # puts k.expand_braces("$TEXMF") + # puts "expand path $TEXMF" + # puts k.expand_path("$TEXMF") + # puts "expand var $TEXMF" + # puts k.expand_var("$TEXMF") + # puts "expand path $TEXMF" + # puts k.show_path('tfm') + # puts "expand value $TEXINPUTS" + # puts k.var_value("$TEXINPUTS") + # puts "expand value $TEXINPUTS.context" + # puts k.var_value("$TEXINPUTS.context") + + exit + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/logger.rb b/Master/texmf-dist/scripts/context/ruby/base/logger.rb new file mode 100644 index 00000000000..2526cdb0e25 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/logger.rb @@ -0,0 +1,104 @@ +# module : base/logger +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +require 'thread' + +# The next calls are valid: + +# @log.report('a','b','c', 'd') +# @log.report('a','b',"c #{d}") +# @log.report("a b c #{d}") + +# Keep in mind that "whatever #{something}" is two times faster than +# 'whatever ' + something or ['whatever',something].join and that +# when verbosity is not needed the following is much faster too: + +# @log.report('a','b','c', 'd') if @log.verbose? +# @log.report('a','b',"c #{d}") if @log.verbose? +# @log.report("a b c #{d}") if @log.verbose? + +# The last three cases are equally fast when verbosity is turned off. + +# Under consideration: verbose per instance + +class Logger + + @@length = 0 + @@verbose = false + + def initialize(tag=nil,length=0,verbose=false) + @tag = tag || '' + @@verbose = @@verbose || verbose + @@length = @tag.length if @tag.length > @@length + @@length = length if length > @@length + end + + def report(*str) + begin + case str.length + when 0 + print("\n") + return true + when 1 + message = str.first + else + message = [str].flatten.collect{|s| s.to_s}.join(' ').chomp + end + if @tag.empty? then + print("#{message}\n") + else + # try to avoid too many adjustments + @tag = @tag.ljust(@@length) unless @tag.length == @@length + print("#{@tag} | #{message}\n") + end + rescue + end + return true + end + + def reportlines(*str) + unless @tag.empty? then + @tag = @tag.ljust(@@length) unless @tag.length == @@length + end + report([str].flatten.collect{|s| s.gsub(/\n/,"\n#{@tag} | ")}.join(' ')) + end + + def debug(*str) + report(str) if @@verbose + end + + def error(*str) + if ! $! || $!.to_s.empty? then + report(str) + else + report(str,$!) + end + end + + def verbose + @@verbose = true + end + + def silent + @@verbose = false + end + + def verbose? + @@verbose + end + + # attr_reader :tag + + # alias fatal error + # alias info debug + # alias warn debug + # alias debug? :verbose? + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/pdf.rb b/Master/texmf-dist/scripts/context/ruby/base/pdf.rb new file mode 100644 index 00000000000..d8cbf9e0523 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/pdf.rb @@ -0,0 +1,51 @@ +module PDFview + + @files = Hash.new + + def PDFview.open(*list) + begin + [*list].flatten.each do |file| + filename = fullname(file) + if FileTest.file?(filename) then + result = `pdfopen --file #{filename} 2>&1` + @files[filename] = true + end + end + rescue + end + end + + def PDFview.close(*list) + [*list].flatten.each do |file| + filename = fullname(file) + begin + if @files.key?(filename) then + result = `pdfclose --file #{filename} 2>&1` + else + closeall + return + end + rescue + end + @files.delete(filename) + end + end + + def PDFview.closeall + begin + result = `pdfclose --all 2>&1` + rescue + end + @files.clear + end + + def PDFview.fullname(name) + name + if name =~ /\.pdf$/ then '' else '.pdf' end + end + +end + +# PDFview.open("t:/document/show-exa.pdf") +# PDFview.open("t:/document/show-gra.pdf") +# PDFview.close("t:/document/show-exa.pdf") +# PDFview.close("t:/document/show-gra.pdf") diff --git a/Master/texmf-dist/scripts/context/ruby/base/state.rb b/Master/texmf-dist/scripts/context/ruby/base/state.rb new file mode 100644 index 00000000000..f57231592a7 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/state.rb @@ -0,0 +1,75 @@ +require "md5" + +# todo: register omissions per file + +class FileState + + def initialize + @states = Hash.new + @omiter = Hash.new + end + + def reset + @states.clear + @omiter.clear + end + + def register(filename,omit=nil) + unless @states.key?(filename) then + @states[filename] = Array.new + @omiter[filename] = omit + end + @states[filename] << checksum(filename,@omiter[filename]) + end + + def update(filename=nil) + [filename,@states.keys].flatten.compact.uniq.each do |fn| + register(fn) + end + end + + def inspect(filename=nil) + result = '' + [filename,@states.keys].flatten.compact.uniq.sort.each do |fn| + if @states.key?(fn) then + result += "#{fn}: #{@states[fn].inspect}\n" + end + end + result + end + + def changed?(filename) + if @states.key?(filename) then + n = @states[filename].length + if n>1 then + changed = @states[filename][n-1] != @states[filename][n-2] + else + changed = true + end + else + changed = true + end + return changed + end + + def checksum(filename,omit=nil) + sum = '' + begin + if FileTest.file?(filename) && (data = IO.read(filename)) then + data.gsub!(/\n.*?(#{[omit].flatten.join('|')}).*?\n/ms,"\n") if omit + sum = MD5.new(data).hexdigest.upcase + end + rescue + sum = '' + end + return sum + end + + def stable? + @states.keys.each do |s| + return false if changed?(s) + end + return true + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/switch.rb b/Master/texmf-dist/scripts/context/ruby/base/switch.rb new file mode 100644 index 00000000000..64d518bd42f --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/switch.rb @@ -0,0 +1,605 @@ +# module : base/switch +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# we cannot use getoptlong because we want to be more +# tolerant; also we want to be case insensitive (2002). + +# we could make each option a class itself, but this is +# simpler; also we can put more in the array + +# beware: regexps/o in methods are optimized globally + +require "rbconfig" + +$mswindows = Config::CONFIG['host_os'] =~ /mswin/ +$separator = File::PATH_SEPARATOR + +class String + + def has_suffix?(suffix) + self =~ /\.#{suffix}$/i + end + +end + +# may move to another module + +class File + + def File.needsupdate(oldname,newname) + begin + if $mswindows then + return File.stat(oldname).mtime > File.stat(newname).mtime + else + return File.stat(oldname).mtime != File.stat(newname).mtime + end + rescue + return true + end + end + + def File.syncmtimes(oldname,newname) + begin + if $mswindows then + # does not work (yet) + t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here + File.utime(0,t,oldname,newname) + else + t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here + File.utime(0,t,oldname,newname) + end + rescue + end + end + + def File.timestamp(name) + begin + "#{File.stat(name).mtime}" + rescue + return 'unknown' + end + end + +end + +# main thing + +module CommandBase + + # this module can be used as a mixin in a command handler + + $stdout.sync = true + + def initialize(commandline,logger,banner) + @commandline, @logger, @banner = commandline, logger, banner + @forcenewline, @versiondone = false, false + version if @commandline.option('version') + end + + def reportlines(*str) + @logger.reportlines(str) + end + + # only works in 1.8 + # + # def report(*str) + # @logger.report(str) + # end + # + # def version # just a bit of playing with defs + # report(@banner.join(' - ')) + # def report(*str) + # @logger.report + # @logger.report(str) + # def report(*str) + # @logger.report(str) + # end + # end + # def version + # end + # end + + def report(*str) + initlogger ; @logger.report(str) + end + + def debug(*str) + initlogger ; @logger.debug(str) + end + + def error(*str) + initlogger ; @logger.error(str) + end + + def initlogger + if @forcenewline then + @logger.report + @forcenewline = false + end + end + + def logger + @logger + end + + def version # just a bit of playing with defs + unless @versiondone then + report(@banner.join(' - ')) + @forcenewline = true + @versiondone = true + end + end + + def help + version # is nilled when already given + @commandline.helpkeys.each do |k| + if @commandline.help?(k) then + kstr = ('--'+k).ljust(@commandline.helplength+2) + message = @commandline.helptext(k) + message = '' if message == CommandLine::NOHELP + message = message.split(/\s*\n\s*/) + loop do + report("#{kstr} #{message.shift}") + kstr = ' '*kstr.length + break if message.length == 0 + end + end + end + end + + def option(key) + @commandline.option(key) + end + def oneof(*key) + @commandline.oneof(*key) + end + + def globfiles(pattern='*',suffix=nil) + @commandline.setarguments([pattern].flatten) + if files = findfiles(suffix) then + @commandline.setarguments(files) + else + @commandline.setarguments + end + end + + private + + def findfiles(suffix=nil) + + if @commandline.arguments.length>1 then + return @commandline.arguments + else + pattern = @commandline.argument('first') + pattern = '*' if pattern.empty? + if suffix && ! pattern.match(/\..+$/o) then + suffix = '.' + suffix + pattern += suffix unless pattern =~ /#{suffix}$/ + end + # not {} safe + pattern = '**/' + pattern if @commandline.option('recurse') + files = Dir[pattern] + if files && files.length>0 then + return files + else + pattern = @commandline.argument('first') + if FileTest.file?(pattern) then + return [pattern] + else + report("no files match pattern #{pattern}") + return nil + end + end + end + + end + + def globbed(pattern,recurse=false) + + files = Array.new + pattern.split(' ').each do |p| + if recurse then + if p =~ /^(.*)(\/.*?)$/i then + p = $1 + '/**' + $2 + else + p = '**/' + p + end + p.gsub!(/[\\\/]+/, '/') + end + files.push(Dir.glob(p)) + end + files.flatten.sort do |a,b| + pathcompare(a,b) + end + end + + def pathcompare(a,b) + + aa, bb = a.split('/'), b.split('/') + if aa.length == bb.length then + aa.each_index do |i| + if aa[i]<bb[i] then + return -1 + elsif aa[i]>bb[i] then + return +1 + end + end + return 0 + else + return aa.length <=> bb.length + end + + end + +end + +class CommandLine + + VALUE, FLAG = 1, 2 + NOHELP = 'no arguments' + + def initialize(prefix='-') + + @registered = Array.new + @options = Hash.new + @unchecked = Hash.new + @arguments = Array.new + @original = ARGV.join(' ') + @helptext = Hash.new + @mandated = Hash.new + @provided = Hash.new + @prefix = prefix + @actions = Array.new + + # The quotes in --switch="some value" get lost in ARGV, so we need to do some trickery here. + + @original = '' + ARGV.each do |a| + aa = a.strip.gsub(/^([#{@prefix}]+\w+\=)([^\"].*?\s+.*[^\"])$/) do + $1 + "\"" + $2 + "\"" + end + @original += if @original.empty? then '' else ' ' end + aa + end + + end + + def setarguments(args=[]) + @arguments = if args then args else [] end + end + + def register(option,shortcut,kind,default=false,action=false,helptext='') + if kind == FLAG then + @options[option] = default + elsif not default then + @options[option] = '' + else + @options[option] = default + end + @registered.push([option,shortcut,kind]) + @mandated[option] = false + # @provided[option] = false + @helptext[option] = helptext + @actions.push(option) if action + end + + def registerflag(option,default=false,helptext='') + if default.class == String then + register(option,'',FLAG,false,false,default) + else + register(option,'',FLAG,false,false,helptext) + end + end + + def registervalue(option,default='',helptext='') + register(option,'',VALUE,default,false,helptext) + end + + def registeraction(option,helptext='') + register(option,'',FLAG,false,true,helptext) + end + + def registermandate(*option) + [*option].each do |o| + [o].each do |oo| + @mandated[oo] = true + end + end + end + + def actions + a = @actions.delete_if do |t| + ! option(t) + end + if a && a.length>0 then + return a + else + return nil + end + end + + def action + @actions.each do |t| + return t if option(t) + end + return nil + end + + def forgotten + @mandated.keys.sort - @provided.keys.sort + end + + def registerhelp(option,text='') + @helptext['unknown'] = if text.empty? then option else text end + end + + def helpkeys(option='.*') + @helptext.keys.sort.grep(/#{option}/) + end + + def helptext(option) + @helptext.fetch(option,'') + end + + def help?(option) + @helptext[option] && ! @helptext[option].empty? + end + + def helplength + n = 0 + @helptext.keys.each do |h| + n = h.length if h.length>n + end + return n + end + + def expand + + # todo : '' or false, depending on type + # @options.clear + # @arguments.clear + + dirtyvalue(@original).split(' ').each do |arg| + case arg + when /^[#{@prefix}][#{@prefix}](.+?)\=(.*?)$/ then locatedouble($1,$2) + when /^[#{@prefix}][#{@prefix}](.+?)$/ then locatedouble($1,false) + when /^[#{@prefix}](.)\=(.)$/ then locatesingle($1,$2) + when /^[#{@prefix}](.+?)$/ then locateseries($1,false) + when /^[\+\-]+/o then # do nothing + else + arguments.push(arg) + end + end + + @options or @unchecked or @arguments + + end + + def extend (str) + @original = @original + ' ' + str + end + + def replace (str) + @original = str + end + + def show + # print "-- options --\n" + @options.keys.sort.each do |key| + print "option: #{key} -> #{@options[key]}\n" + end + # print "-- arguments --\n" + @arguments.each_index do |key| + print "argument: #{key} -> #{@arguments[key]}\n" + end + end + + def option(str,default=nil) + if @options.key?(str) then + @options[str] + elsif default then + default + else + @options[str] + end + end + + def checkedoption(str,default='') + if @options.key?(str) then + if @options[str].empty? then default else @options[str] end + else + default + end + end + + def foundoption(str,default='') + str = str.split(',') if str.class == String + str.each do |s| + return str if @options.key?(str) + end + return default + end + + def oneof(*key) + [*key].flatten.compact.each do |k| + return true if @options.key?(k) && @options[k] + end + return false + end + + def setoption(str,value) + @options[str] = value + end + + def getoption(str,value='') # value ? + @options[str] + end + + def argument(n=0) + if n.class == String then + case n + when 'first' then argument(0) + when 'second' then argument(1) + when 'third' then argument(2) + else + argument(0) + end + elsif @arguments[n] then + @arguments[n] + else + '' + end + end + + # a few local methods, cannot be defined nested (yet) + + private + + def dirtyvalue(value) + if value then + value.gsub(/([\"\'])(.*?)\1/) do + $2.gsub(/\s+/o, "\xFF") + end + else + '' + end + end + + def cleanvalue(value) + if value then + # value.sub(/^([\"\'])(.*?)\1$/) { $2.gsub(/\xFF/o, ' ') } + value.gsub(/\xFF/o, ' ') + else + '' + end + end + + def locatedouble(key, value) + + foundkey, foundkind = nil, nil + + @registered.each do |option, shortcut, kind| + if option == key then + foundkey, foundkind = option, kind + break + end + end + unless foundkey then + @registered.each do |option, shortcut, kind| + n = 0 + if option =~ /^#{key}/i then + case n + when 0 + foundkey, foundkind = option, kind + n = 1 + when 1 + # ambiguous matches, like --fix => --fixme --fixyou + foundkey, foundkind = nil, nil + break + end + end + end + end + if foundkey then + @provided[foundkey] = true + if foundkind == VALUE then + @options[foundkey] = cleanvalue(value) + else + @options[foundkey] = true + end + else + if value.class == FalseClass then + @unchecked[key] = true + else + @unchecked[key] = cleanvalue(value) + end + end + + end + + def locatesingle(key, value) + + @registered.each do |option, shortcut, kind| + if shortcut == key then + @provided[option] = true + @options[option] = if kind == VALUE then '' else cleanvalue(value) end + break + end + end + + end + + def locateseries(series, value) + + series.each do |key| + locatesingle(key,cleanvalue(value)) + end + + end + + public + + attr_reader :arguments, :options, :original, :unchecked + +end + +# options = CommandLine.new +# +# options.register("filename", "f", CommandLine::VALUE) +# options.register("request" , "r", CommandLine::VALUE) +# options.register("verbose" , "v", CommandLine::FLAG) +# +# options.expand +# options.extend(str) +# options.show +# +# c = CommandLine.new +# +# c.registervalue('aaaa') +# c.registervalue('test') +# c.registervalue('zzzz') +# +# c.registerhelp('aaaa','some aaaa to enter') +# c.registerhelp('test','some text to enter') +# c.registerhelp('zzzz','some zzzz to enter') +# +# c.registermandate('test') +# +# c.expand +# +# class CommandLine +# +# def showhelp (banner,*str) +# if helpkeys(*str).length>0 +# print banner +# helpkeys(*str).each do |h| +# print helptext(h) + "\n" +# end +# true +# else +# false +# end +# end +# +# def showmandate(banner) +# if forgotten.length>0 +# print banner +# forgotten.each do |f| +# print helptext(f) + "\n" +# end +# true +# else +# false +# end +# end +# +# end +# +# c.showhelp("you can provide:\n\n") +# c.showmandate("you also need to provide:\n\n") diff --git a/Master/texmf-dist/scripts/context/ruby/base/system.rb b/Master/texmf-dist/scripts/context/ruby/base/system.rb new file mode 100644 index 00000000000..ed8c2756e2b --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/system.rb @@ -0,0 +1,102 @@ +# module : base/system +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +require "rbconfig" + +module System + + @@mswindows = Config::CONFIG['host_os'] =~ /mswin/ + @@binpaths = ENV['PATH'].split(File::PATH_SEPARATOR) + @@binsuffixes = if $mswindows then ['.exe','.com','.bat'] else ['','.sh','.csh'] end + @@located = Hash.new + @@binnames = Hash.new + + if @@mswindows then + @@binnames['ghostscript'] = ['gswin32c.exe','gs.cmd','gs.bat'] + @@binnames['imagemagick'] = ['imagemagick.exe','convert.exe'] + @@binnames['inkscape'] = ['inkscape.exe'] + else + @@binnames['ghostscript'] = ['gs'] + @@binnames['imagemagick'] = ['convert'] + @@binnames['inkscape'] = ['inkscape'] + end + + + def System.null + if @@mswindows then 'nul' else '/dev/null/' end + end + + def System.binnames(str) + if @@binnames.key?(str) then + @@binnames[str] + else + [str] + end + end + + def System.locatedprogram(program) + if @@located.key?(program) then + return @@located[program] + else + System.binnames(program).each do |binname| + if binname =~ /\..*$/io then + @@binpaths.each do |path| + if FileTest.file?(str = File.join(path,binname)) then + return @@located[program] = str + end + end + end + binname.gsub!(/\..*$/io, '') + @@binpaths.each do |path| + @@binsuffixes.each do |suffix| + if FileTest.file?(str = File.join(path,"#{binname}#{suffix}")) then + return @@located[program] = str + end + end + end + end + end + return @@located[program] = "texmfstart #{program}" + end + + def System.command(program,arguments='') + if program =~ /^(.*?) (.*)$/ then + program = System.locatedprogram($1) + ' ' + $2 + else + program = System.locatedprogram(program) + end + program = program + ' ' + arguments if ! arguments.empty? + program.gsub!(/\s+/io, ' ') + program.gsub!(/(\.\/)+/io, '') + program.gsub!(/\\/io, '/') + return program + end + + def System.run(program,arguments='',pipe=false,collect=false) + if pipe then + if collect then + `#{System.command(program,arguments)} 2>&1` + else + `#{System.command(program,arguments)}` + end + else + system(System.command(program,arguments)) + end + end + + def System.pipe(program,arguments='',collect=false) + System.run(program,arguments,true) + end + + def System.safepath(path) + if path.match(/ /o) then "\"#{path}\"" else path end + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/tex.rb b/Master/texmf-dist/scripts/context/ruby/base/tex.rb new file mode 100644 index 00000000000..2cc9d25422e --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/tex.rb @@ -0,0 +1,1495 @@ +# module : base/tex +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# todo: write systemcall for mpost to file so that it can be run +# faster + +# report ? + +require 'base/variables' +require 'base/kpse' +require 'base/system' +require 'base/state' +require 'base/pdf' +require 'base/file' +require 'base/ctx' + +class String + + def standard? + begin + self == 'standard' + rescue + false + end + end + +end + +class Array + + def standard? + begin + self.include?('standard') + rescue + false + end + end + +end + +class TEX + + # The make-part of this class was made on a rainy day while listening + # to "10.000 clowns on a rainy day" by Jan Akkerman. Unfortunately the + # make method is not as swinging as this live cd. + + include Variables + + @@texengines = Hash.new + @@mpsengines = Hash.new + @@backends = Hash.new + @@runoptions = Hash.new + @@texformats = Hash.new + @@mpsformats = Hash.new + @@prognames = Hash.new + @@texmakestr = Hash.new + @@texprocstr = Hash.new + @@mpsmakestr = Hash.new + @@mpsprocstr = Hash.new + + @@texmethods = Hash.new + @@mpsmethods = Hash.new + + ['tex','pdftex','pdfetex','standard'] .each do |e| @@texengines[e] = 'pdfetex' end + ['aleph','omega'] .each do |e| @@texengines[e] = 'aleph' end + ['xetex'] .each do |e| @@texengines[e] = 'xetex' end + + ['metapost','mpost','standard'] .each do |e| @@mpsengines[e] = 'mpost' end + + ['pdfetex','pdftex','pdf','pdftex','standard'] .each do |b| @@backends[b] = 'pdftex' end + ['dvipdfmx','dvipdfm','dpx','dpm'] .each do |b| @@backends[b] = 'dvipdfmx' end + ['xetex','xtx'] .each do |b| @@backends[b] = 'xetex' end + ['dvips','ps'] .each do |b| @@backends[b] = 'dvips' end + ['dvipsone'] .each do |b| @@backends[b] = 'dvipsone' end + ['acrobat','adobe','distiller'] .each do |b| @@backends[b] = 'acrobat' end + + # todo norwegian (no) + + ['plain'] .each do |f| @@texformats[f] = 'plain' end + ['cont-en','en','english','context','standard'].each do |f| @@texformats[f] = 'cont-en' end + ['cont-nl','nl','dutch'] .each do |f| @@texformats[f] = 'cont-nl' end + ['cont-de','de','german'] .each do |f| @@texformats[f] = 'cont-de' end + ['cont-it','it','italian'] .each do |f| @@texformats[f] = 'cont-it' end + ['cont-cz','cz','czech'] .each do |f| @@texformats[f] = 'cont-cz' end + ['cont-ro','ro','romanian'] .each do |f| @@texformats[f] = 'cont-ro' end + ['cont-uk','uk','brittish'] .each do |f| @@texformats[f] = 'cont-uk' end + ['mptopdf'] .each do |f| @@texformats[f] = 'mptopdf' end + + ['latex'] .each do |f| @@texformats[f] = 'latex.ltx' end + + ['plain','mpost'] .each do |f| @@mpsformats[f] = 'plain' end + ['metafun','context','standard'] .each do |f| @@mpsformats[f] = 'metafun' end + + ['pdfetex','aleph','omega'] .each do |p| @@prognames[p] = 'context' end + ['mpost'] .each do |p| @@prognames[p] = 'metafun' end + + ['plain','default','standard','mptopdf'] .each do |f| @@texmethods[f] = 'plain' end + ['cont-en','cont-nl','cont-de','cont-it', + 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texmethods[f] = 'context' end + ['latex'] .each do |f| @@texmethods[f] = 'latex' end + + ['plain','default','standard'] .each do |f| @@mpsmethods[f] = 'plain' end + ['metafun'] .each do |f| @@mpsmethods[f] = 'metafun' end + + @@texmakestr['plain'] = "\\dump" + @@mpsmakestr['plain'] = "\\dump" + + ['cont-en','cont-nl','cont-de','cont-it', + 'cont-cz','cont-ro','cont-uk'] .each do |f| @@texprocstr[f] = "\\emergencyend" end + + @@runoptions['xetex'] = ['--no-pdf'] + + @@booleanvars = [ + 'batchmode', 'nonstopmode', 'fast', 'fastdisabled', 'silentmode', 'final', + 'paranoid', 'notparanoid', 'nobanner', 'once', 'allpatterns', + 'nompmode', 'nomprun', 'automprun', + 'nomapfiles', 'local', + 'arrange', 'noarrange', + 'forcexml', 'foxet', + 'mpyforce', 'forcempy', + 'forcetexutil', 'texutil', + 'globalfile', 'autopath', + 'purge', 'purgeall', 'autopdf', 'simplerun', 'verbose', + 'nooptionfile' + ] + @@stringvars = [ + 'modefile', 'result', 'suffix', 'response', 'path', + 'filters', 'usemodules', 'environments', 'separation', 'setuppath', + 'arguments', 'input', 'output', 'randomseed', 'modes', 'filename', + 'modefile', 'ctxfile' + ] + @@standardvars = [ + 'mainlanguage', 'bodyfont', 'language' + ] + @@knownvars = [ + 'engine', 'distribution', 'texformats', 'mpsformats', 'progname', 'interface', + 'runs', 'backend' + ] + + @@extrabooleanvars = [] + @@extrastringvars = [] + + def booleanvars + [@@booleanvars,@@extrabooleanvars].flatten + end + def stringvars + [@@stringvars,@@extrastringvars].flatten + end + def standardvars + @@standardvars + end + def knownvars + @@knownvars + end + + def setextrastringvars(vars) + @@extrastringvars << vars + end + def setextrabooleanvars(vars) + @@extrabooleanvars << vars + end + + @@temprunfile = 'texexec' + @@temptexfile = 'texexec.tex' + + def initialize(logger=nil) + if @logger = logger then + def report(str='') + @logger.report(str) + end + else + def report(str='') + puts(str) + end + end + @cleanups = Array.new + @variables = Hash.new + @startuptime = Time.now + # options + booleanvars.each do |k| + setvariable(k,false) + end + stringvars.each do |k| + setvariable(k,'') + end + standardvars.each do |k| + setvariable(k,'standard') + end + setvariable('distribution', Kpse.distribution) + setvariable('texformats', defaulttexformats) + setvariable('mpsformats', defaultmpsformats) + setvariable('progname', 'context') + setvariable('interface', 'standard') + setvariable('engine', 'standard') # replaced by tex/mpsengine + setvariable('backend', 'pdftex') + setvariable('runs', '8') + setvariable('randomseed', rand(1440).to_s) + # files + setvariable('files', []) + # defaults + setvariable('texengine', 'standard') + setvariable('mpsengine', 'standard') + setvariable('backend', 'standard') + end + + def runtime + Time.now - @startuptime + end + + def reportruntime + report("runtime: #{runtime}") + end + + def inspect(name=nil) + if ! name || name.empty? then + name = [booleanvars,stringvars,standardvars,knownvars] + end + [name].flatten.each do |n| + if str = getvariable(n) then + unless (str.class == String) && str.empty? then + report("option '#{n}' is set to '#{str}'") + end + end + end + end + + def tempfilename(suffix='') + @@temprunfile + if suffix.empty? then '' else ".#{suffix}" end + end + + def cleanup + @cleanups.each do |name| + begin + File.delete(name) if FileTest.file?(name) + rescue + report("unable to delete #{name}") + end + end + end + + def cleanuptemprunfiles + begin + Dir.glob("#{@@temprunfile}*").each do |name| + if File.file?(name) && (File.splitname(name)[1] !~ /(pdf|dvi)/o) then + begin File.delete(name) ; rescue ; end + end + end + rescue + end + end + + def backends() @@backends.keys.sort end + + def texengines() @@texengines.keys.sort end + def mpsengines() @@mpsengines.keys.sort end + def texformats() @@texformats.keys.sort end + def mpsformats() @@mpsformats.keys.sort end + + def defaulttexformats() ['en','nl','mptopdf'] end + def defaultmpsformats() ['metafun'] end + + def texmakeextras(format) @@texmakestr[format] || '' end + def mpsmakeextras(format) @@mpsmakestr[format] || '' end + def texprocextras(format) @@texprocstr[format] || '' end + def mpsprocextras(format) @@mpsprocstr[format] || '' end + + def texmethod(format) @@texmethods[str] || @@texmethods['standard'] end + def mpsmethod(format) @@mpsmethods[str] || @@mpsmethods['standard'] end + + def runoptions(engine) + if @@runoptions.key?(engine) then @@runoptions[engine].join(' ') else '' end + end + + # private + + def cleanuplater(name) + begin + @cleanups.push(File.expand_path(name)) + rescue + @cleanups.push(name) + end + end + + def openedfile(name) + begin + f = File.open(name,'w') + rescue + report("file '#{File.expand_path(name)}' cannot be opened for writing") + return nil + else + cleanuplater(name) if f + return f + end + end + + def prefixed(format,engine) + case engine + when /etex|eetex|pdfetex|pdfeetex|pdfxtex|xpdfetex|eomega|aleph|xetex/io then + "*#{format}" + else + format + end + end + + def quoted(str) + if str =~ /^[^\"].* / then "\"#{str}\"" else str end + end + + def getarrayvariable(str='') + str = getvariable(str) + if str.class == String then str.split(',') else str.flatten end + end + + def validtexformat(str) validsomething(str,@@texformats) end + def validmpsformat(str) validsomething(str,@@mpsformats) end + def validtexengine(str) validsomething(str,@@texengines) end + def validmpsengine(str) validsomething(str,@@mpsengines) end + + def validtexmethod(str) [validsomething(str,@@texmethods)].flatten.first end + def validmpsmethod(str) [validsomething(str,@@mpsmethods)].flatten.first end + + def validsomething(str,something) + if str then + list = [str].flatten.collect do |s| + something[s] + end .compact.uniq + if list.length>0 then + if str.class == String then list.first else list end + else + false + end + else + false + end + end + + def validbackend(str) + if str && @@backends.key?(str) then + @@backends[str] + else + @@backends['standard'] + end + end + + def validprogname(str,engine='standard') + if str && @@prognames.key?(str) then + @@prognames[str] + elsif (engine != 'standard') && @@prognames.key?(engine) then + @@prognames[engine] + else + str + end + end + + # we no longer support the & syntax + + def formatflag(engine=nil,format=nil) + case getvariable('distribution') + when 'standard' then prefix = "--fmt" + when /web2c/io then prefix = web2cformatflag(engine) + when /miktex/io then prefix = "--undump" + else return "" + end + if format then + # if engine then + # "#{prefix}=#{engine}/#{format}" + # else + "#{prefix}=#{format}" + # end + else + prefix + end + end + + def web2cformatflag(engine=nil) + # funny that we've standardized on the fmt suffix (at the cost of + # upward compatibility problems) but stuck to the bas/mem/fmt flags + if engine then + case validmpsengine(engine) + when /mpost/ then "--mem" + when /mfont/ then "--bas" + else "--fmt" + end + else + "--fmt" + end + end + + def prognameflag(progname=nil) + case getvariable('distribution') + when 'standard' then prefix = "--progname" + when /web2c/io then prefix = "--progname" + when /miktex/io then prefix = "--alias" + else return "" + end + if progname then + if progname = validprogname(progname) then + "#{prefix}=#{progname}" + else + "" + end + else + prefix + end + end + + def iniflag() # should go to kpse and kpse should become texenv + if Kpse.miktex? then + "-initialize" + else + "--ini" + end + end + def tcxflag(file="natural.tcx") + if Kpse.miktex? then + "-tcx=#{file}" + else + "--translate-file=#{file}" + end + end + + def filestate(file) + File.mtime(file).strftime("%d/%m/%Y %H:%M:%S") + end + + # will go to context/process context/listing etc + + def contextversion # ook elders gebruiken + filename = Kpse.found('context.tex') + version = 'unknown' + begin + if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+)\}/) then + version = $1 + end + rescue + end + return version + end + + def makeformats + if getvariable('fast') then + report('using existing database') + else + report('updating file database') + Kpse.update + end + # goody + if getvariable('texformats') == 'standard' then + setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + end + # prepare + texformats = validtexformat(getarrayvariable('texformats')) + mpsformats = validmpsformat(getarrayvariable('mpsformats')) + texengine = validtexengine(getvariable('texengine')) + mpsengine = validmpsengine(getvariable('mpsengine')) + # save current path + savedpath = Dir.getwd + # generate tex formats + if texformats && texengine && (progname = validprogname(getvariable('progname'),texengine)) then + report("using tex engine #{texengine}") + texformatpath = if getvariable('local') then '.' else Kpse.formatpath(texengine,true) end + # can be empty, to do + report("using tex format path #{texformatpath}") + begin + Dir.chdir(texformatpath) + rescue + end + if texformats.length > 0 then + makeuserfile + makeresponsefile + end + texformats.each do |texformat| + report("generating tex format #{texformat}") + command = [quoted(texengine),prognameflag(progname),iniflag,tcxflag,prefixed(texformat,texengine),texmakeextras(texformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + end + else + texformatpath = '' + end + # generate mps formats + if mpsformats && mpsengine && (progname = validprogname(getvariable('progname'),mpsengine)) then + report("using mp engine #{mpsengine}") + mpsformatpath = if getvariable('local') then '.' else Kpse.formatpath(mpsengine,false) end + report("using mps format path #{mpsformatpath}") + begin + Dir.chdir(mpsformatpath) + rescue + end + mpsformats.each do |mpsformat| + report("generating mps format #{mpsformat}") + command = [quoted(mpsengine),prognameflag(progname),iniflag,tcxflag,mpsformat,mpsmakeextras(mpsformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + end + else + mpsformatpath = '' + end + # check for problems + report("tex engine path: #{texformatpath}") unless texformatpath.empty? + report("mps engine path: #{mpsformatpath}") unless mpsformatpath.empty? + [['fmt','tex'],['mem','mps']].each do |f| + [[texformatpath,'global'],[mpsformatpath,'global'],[savedpath,'current']].each do |p| + begin + Dir.chdir(p[0]) + rescue + else + Dir.glob("*.#{f[0]}").each do |file| + report("#{f[1]}format: #{filestate(file)} > #{File.expand_path(file)}") + end + end + end + end + # to be sure, go back to current path + begin + Dir.chdir(savedpath) + rescue + end + # finalize + cleanup + reportruntime + end + + def checkcontext + + # todo : report texmf.cnf en problems + + # basics + report("current distribution: #{Kpse.distribution}") + report("context source date: #{contextversion}") + formatpaths = Kpse.formatpaths + globpattern = "**/{#{formatpaths.join(',')}}/*/*.{fmt,efmt,ofmt,xfmt,mem}" + report("format path: #{formatpaths.join(' ')}") + # utilities + report('start of analysis') + results = Array.new + ['texexec','texutil','ctxtools'].each do |program| + result = `texmfstart #{program} --help` + result.sub!(/.*?(#{program}[^\n]+)\n.*/mi) do $1 end + results.push("#{result}") + end + # formats + cleanuptemprunfiles + if formats = Dir.glob(globpattern) then + formats.sort.each do |name| + cleanuptemprunfiles + if f = open(tempfilename('tex'),'w') then + # kind of aleph-run-out-of-par safe + f << "\\starttext\n" + f << " \\relax test \\relax\n" + f << "\\stoptext\n" + f << "\\endinput\n" + f.close + if FileTest.file?(tempfilename('tex')) then + format = File.basename(name) + engine = if name =~ /(pdfetex|aleph|xetex)[\/\\]#{format}/ then $1 else '' end + if engine.empty? then + engineflag = "" + else + engineflag = "--engine=#{$1}" + end + case format + when /cont\-([a-z]+)/ then + interface = $1.sub(/cont\-/,'') + results.push('') + results.push("testing interface #{interface}") + flags = ['--process','--batch','--once',"--interface=#{interface}",engineflag] + # result = Kpse.pipescript('newtexexec',tempfilename,flags) + result = runtexexec([tempfilename], flags, 1) + if FileTest.file?("#{@@temprunfile}.log") then + logdata = IO.read("#{@@temprunfile}.log") + if logdata =~ /^\s*This is (.*?)[\s\,]+(.*?)$/mois then + if validtexengine($1.downcase) then + results.push("#{$1} #{$2.gsub(/\(format.*$/,'')}".strip) + end + end + if logdata =~ /^\s*(ConTeXt)\s+(.*int:\s+[a-z]+.*?)\s*$/mois then + results.push("#{$1} #{$2}".gsub(/\s+/,' ').strip) + end + else + results.push("format #{format} does not work") + end + when /metafun/ then + # todo + when /mptopdf/ then + # todo + end + else + results.push("error in creating #{tempfilename('tex')}") + end + end + cleanuptemprunfiles + end + end + report('end of analysis') + report + results.each do |line| + report(line) + end + cleanuptemprunfiles + + end + + private + + def makeuserfile + language = getvariable('language') + mainlanguage = getvariable('mainlanguage') + bodyfont = getvariable('bodyfont') + if f = openedfile("cont-fmt.tex") then + f << "\\unprotect" + case language + when 'all' then + f << "\\preloadallpatterns\n" + when '' then + f << "% no language presets\n" + when 'standard' + f << "% using defaults\n" + else + languages = language.split(',') + languages.each do |l| + f << "\\installlanguage[\\s!#{l}][\\c!state=\\v!start]\n" + end + mainlanguage = languages.first + end + unless mainlanguage == 'standard' then + f << "\\setupcurrentlanguage[\\s!#{mainlanguage}]\n"; + end + unless bodyfont == 'standard' then + # ~ will become obsolete when lmr is used + f << "\\definetypescriptsynonym[cmr][#{bodyfont}]" + # ~ is already obsolete for some years now + f << "\\definefilesynonym[font-cmr][font-#{bodyfont}]\n" + end + f << "\\protect\n" + f << "\\endinput\n" + f.close + end + end + + def makeresponsefile + interface = getvariable('interface') + if f = openedfile("mult-def.tex") then + case interface + when 'standard' then + f << "% using default response interface" + else + f << "\\def\\currentresponses\{#{interface}\}\n" + end + f << "\\endinput\n" + f.close + end + end + + private # will become baee/context + + @@preamblekeys = [ + ['tex','texengine'], + ['program','texengine'], + ['translate','tcxfilter'], + ['tcx','tcxfilter'], + ['output','backend'], + ['mode','mode'], + ['ctx','ctxfile'], + ['version','contextversion'], + ['format','texformat'], + ['interface','texformat'] + ] + + def scantexpreamble(filename) + begin + if FileTest.file?(filename) and tex = File.open(filename) then + while str = tex.gets and str.chomp! do + if str =~ /^\%\s*(.*)/o then + vars = Hash.new + $1.split(/\s+/o).each do |s| + k, v = s.split('=') + vars[k] = v + end + @@preamblekeys.each do |v| + setvariable(v[1],vars[v[0]]) if vars.key?(v[0]) + end + else + break + end + end + tex.close + end + rescue + # well, let's not worry too much + end + end + + def scantexcontent(filename) + if FileTest.file?(filename) and tex = File.open(filename) then + while str = tex.gets do + case str.chomp + when /^\%/o then + # next + when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then + setvariable('texformat','nl') ; break + when /\\(stelle|verwende|umgebung|benutze)/o then + setvariable('texformat','de') ; break + when /\\(stel|gebruik|omgeving)/o then + setvariable('texformat','nl') ; break + when /\\(use|setup|environment)/o then + setvariable('texformat','en') ; break + when /\\(usa|imposta|ambiente)/o then + setvariable('texformat','it') ; break + when /(height|width|style)=/o then + setvariable('texformat','en') ; break + when /(hoehe|breite|schrift)=/o then + setvariable('texformat','de') ; break + when /(hoogte|breedte|letter)=/o then + setvariable('texformat','nl') ; break + when /(altezza|ampiezza|stile)=/o then + setvariable('texformat','it') ; break + when /externfiguur/o then + setvariable('texformat','nl') ; break + when /externalfigure/o then + setvariable('texformat','en') ; break + when /externeabbildung/o then + setvariable('texformat','de') ; break + when /figuraesterna/o then + setvariable('texformat','it') ; break + end + end + tex.close + end + + end + + private # will become base/context + + def pushresult(filename,resultname) + fname = File.unsuffixed(filename) + rname = File.unsuffixed(resultname) + if ! rname.empty? && (rname != fname) then + report("outputfile #{rname}") + ['tuo','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed(fname,s),File.suffixed('texexec',s)) + end + ['tuo'].each do |s| + File.silentrename(File.suffixed(rname,s),File.suffixed(fname,s)) if FileTest.file?(File.suffixed(rname,s)) + end + end + end + + def popresult(filename,resultname) + fname = File.unsuffixed(filename) + rname = File.unsuffixed(resultname) + if ! rname.empty? && (rname != fname) then + report("renaming #{fname} to #{rname}") + ['tuo','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed(fname,s),File.suffixed(rname,s)) + end + report("restoring #{fname}") + unless $fname == 'texexec' then + ['tuo','log','dvi','pdf'].each do |s| + File.silentrename(File.suffixed('texexec',s),File.suffixed(fname,s)) + end + end + end + end + + def makestubfile(rawname,forcexml=false) + if tmp = File.open(File.suffixed(rawname,'run'),'w') then + tmp << "\\starttext\n" + if forcexml then + if FileTest.file?(rawname) && (xml = File.open(rawname)) then + xml.each do |line| + case line + when /<\?context\-directive\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*?)\s*\?>/o then + category, key, value, rest = $1, $2, $3, $4 + case category + when 'job' then + case key + when 'control' then + setvariable(value,if rest.empty? then true else rest end) + when 'mode', 'modes' then + tmp << "\\enablemode[#{value}]\n" + when 'stylefile', 'environment' then + tmp << "\\environment #{value}\n" + when 'module' then + tmp << "\\usemodule[#{value}]\n" + when 'interface' then + contextinterface = value + end + end + when /<[a-z]+/io then # beware of order, first pi test + break + end + end + xml.close + end + tmp << "\\processXMLfilegrouped{#{rawname}}\n" + else + tmp << "\\processfile{#{rawname}}\n" + end + tmp << "\\stoptext\n" + tmp.close + return "run" + else + return File.splitname(rawname)[1] + end + end + +end + +class TEX + + def processtex # much to do: mp, xml, runs etc + setvariable('texformats',[getvariable('interface')]) unless getvariable('interface').empty? + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing document '#{filename}'") + processfile + end + reportruntime + end + + def processmptex + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing graphic '#{filename}'") + runtexmp(filename) + end + reportruntime + end + + def processmpxtex + getarrayvariable('files').each do |filename| + setvariable('filename',filename) + report("processing text of graphic '#{filename}'") + processmpx(filename,true) + end + reportruntime + end + + def deleteoptionfile(rawname) + begin + File.delete(File.suffixed(rawname,'top')) + rescue + end + end + + def makeoptionfile(rawname, jobname, jobsuffix, finalrun, fastdisabled, kindofrun) + # jobsuffix = orisuffix + if topname = File.suffixed(rawname,'top') and opt = File.open(topname,'w') then + # local handies + opt << "\% #{topname}\n" + opt << "\\unprotect\n" + opt << "\\setupsystem[\\c!n=#{kindofrun}]\n" + opt << "\\def\\MPOSTformatswitch\{#{prognameflag('metafun')} #{formatflag('mpost')}=\}\n" + if getvariable('batchmode') then + opt << "\\batchmode\n" + end + if getvariable('nonstopmode') then + opt << "\\nonstopmode\n" + end + if getvariable('paranoid') then + opt << "\\def\\maxreadlevel{1}\n" + end + if (str = File.unixfied(getvariable('modefile'))) && ! str.empty? then + opt << "\\readlocfile{#{str}}{}{}\n" + end + if (str = File.unixfied(getvariable('result'))) && ! str.empty? then + opt << "\\setupsystem[file=#{str}]\n" + elsif (str = getvariable('suffix')) && ! str.empty? then + opt << "\\setupsystem[file=#{jobname}.#{str}]\n" + end + if (str = File.unixfied(getvariable('path'))) && ! str.empty? then + opt << "\\usepath[#{str}]\n" unless str.empty? + end + if (str = getvariable('mainlanguage').downcase) && ! str.empty? && ! str.standard? then + opt << "\\setuplanguage[#{str}]\n" + end + if str = validbackend(getvariable('backend')) then + opt << "\\setupoutput[#{str}]\n" + end + if getvariable('color') then + opt << "\\setupcolors[\\c!state=\\v!start]\n" + end + if getvariable('nompmode') || getvariable('nomprun') || getvariable('automprun') then + opt << "\\runMPgraphicsfalse\n" + end + if getvariable('fast') && ! getvariable('fastdisabled') then + opt << "\\fastmode\n" + end + if getvariable('silentmode') then + opt << "\\silentmode\n" + end + if (str = getvariable('separation')) && ! str.empty? then + opt << "\\setupcolors[\\c!split=#{str}]\n" + end + if (str = getvariable('setuppath')) && ! str.empty? then + opt << "\\setupsystem[\\c!directory=\{#{str}\}]\n" + end + if (str = getvariable('paperformat')) && ! str.empty? && ! str.standard? then + if str =~ /^([a-z]+\d+)([a-z]+\d+)$/io then # A5A4 A4A3 A2A1 ... + opt << "\\setuppapersize[#{$1.upcase}][#{$2.upcase}]\n" + else # ...*... + pf = str.upcase.split(/[x\*]/o) + pf << pf[0] if pd.size == 1 + opt << "\\setuppapersize[#{pf[0]}][#{pf[1]}]\n" + end + end + if (str = getvariable('background')) && ! str.empty? then + opt << "\\defineoverlay[whatever][{\\externalfigure[#{str}][\\c!factor=\\v!max]}]\n" + opt << "\\setupbackgrounds[\\v!page][\\c!background=whatever]\n" + end + if getvariable('centerpage') then + opt << "\\setuplayout[\\c!location=\\v!middle,\\c!marking=\\v!on]\n" + end + if getvariable('nomapfiles') then + opt << "\\disablemapfiles\n" + end + if getvariable('noarrange') then + opt << "\\setuparranging[\\v!disable]\n" + elsif getvariable('arrange') then + arrangement = Array.new + if finalrun then + arrangement << "\\v!doublesided" unless getvariable('noduplex') + case printformat + when '' then arrangement << "\\v!normal" + when /.*up/oi then arrangement << "\\v!rotated" + when /.*down/oi then arrangement << ["2DOWN","\\v!rotated"] + when /.*side/oi then arrangement << ["2SIDE","\\v!rotated"] + end + else + arrangement << "\\v!disable" + end + opt << "\\setuparranging[#{arrangement.flatten.join(',')}]\n" if arrangement.size > 0 + end + if (str = getvariable('modes')) && ! str.empty? then + opt << "\\enablemode[#{modes}]\n" + end + if (str = getvariable('arguments')) && ! str.empty? then + opt << "\\setupenv[#{str}]\n" + end + if (str = getvariable('randomseed')) && ! str.empty? then + opt << "\\setupsystem[\\c!random=#{str}]\n" + end + if (str = getvariable('input')) && ! str.empty? then + opt << "\\setupsystem[inputfile=#{str}]\n" + else + opt << "\\setupsystem[inputfile=#{rawname}]\n" + end + if (str = getvariable('pages')) && ! str.empty? then + if str.downcase == 'odd' then + opt << "\\chardef\\whichpagetoshipout=1\n" + elsif str.downcase == 'even' then + opt << "\\chardef\\whichpagetoshipout=2\n" + else + pagelist = Array.new + str.split(/\,/).each do |page| + pagerange = page.split(/(\:|\.\.)/o ) + if pagerange.size > 1 then + pagerange.first.to_i.upto(pagerange.last.to_i) do |p| + pagelist << p.to_s + end + else + pagelist << page + end + end + opt << "\\def\\pagestoshipout\{pagelist.join(',')\}\n"; + end + end + opt << "\\protect\n"; + begin getvariable('filters' ).split(',').uniq.each do |f| opt << "\\useXMLfilter[#{f}]\n" end ; rescue ; end + begin getvariable('usemodules' ).split(',').uniq.each do |m| opt << "\\usemodule[#{m}]\n" end ; rescue ; end + begin getvariable('environments').split(',').uniq.each do |e| opt << "\\environment #{e}\n" end ; rescue ; end + # this will become: + # begin getvariable('environments').split(',').uniq.each do |e| opt << "\\useenvironment[#{e}]\n" end ; rescue ; end + opt << "\\endinput\n" + opt.close + end + end + + def takeprecautions + ENV['MPXCOMAND'] = '0' # else loop + if getvariable('paranoid') then + ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 'f' + ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'p' + ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'p' + elsif getvariable('notparanoid') then + ENV['SHELL_ESCAPE'] = ENV['SHELL_ESCAPE'] || 't' + ENV['OPENOUT_ANY'] = ENV['OPENOUT_ANY'] || 'a' + ENV['OPENIN_ANY'] = ENV['OPENIN_ANY'] || 'a' + end + if ENV['OPENIN_ANY'] && (ENV['OPENIN_ANY'] == 'p') then # first test redundant + setvariable('paranoid', true) + end + if ENV.key?('SHELL_ESCAPE') && (ENV['SHELL_ESCAPE'] == 'f') then + setvariable('automprun',true) + end + ['TXRESOURCES','MPRESOURCES','MFRESOURCES'].each do |res| + [getvariable('runpath'),getvariable('path')].each do |pat| + unless pat.empty? then + if ENV.key?(res) then + ENV[res] = if ENV[res].empty? then pat else pat + ":" + ENV[res] end + else + ENV[res] = pat + end + end + end + end + end + + def runtex(filename) + texengine = validtexengine(getvariable('texengine')) + texformat = validtexformat(getarrayvariable('texformats').first) + progname = validprogname(getvariable('progname')) + report("tex engine: #{texengine}") + report("tex format: #{texformat}") + report("progname: #{progname}") + if texengine && texformat && progname then + command = [quoted(texengine),prognameflag(progname),formatflag(texengine,texformat),runoptions(texengine),filename,texprocextras(texformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + else + false + end + end + + def runmp(filename) + mpsengine = validmpsengine(getvariable('mpsengine')) + mpsformat = validmpsformat(getarrayvariable('mpsformats').first) + progname = validprogname(getvariable('progname')) + if mpsengine && mpsformat && progname then + command = [quoted(mpsengine),prognameflag(progname),formatflag(mpsengine,mpsformat),runoptions(mpsengine),filename,mpsprocextras(mpsformat)].join(' ') + report(command) if getvariable('verbose') + system(command) + else + false + end + end + + def runtexmp(filename,filetype='') + mpfile = File.suffixed(filename,filetype,'mp') + if File.atleast?(mpfile,25) then + # first run needed + File.silentdelete(File.suffixed(mpfile,'mpt')) + doruntexmp(mpfile,false) + mpgraphics = checkmpgraphics(mpfile) + mplabels = checkmplabels(mpfile) + if mpgraphics || mplabels then + # second run needed + doruntexmp(mpfile,mplabels) + end + end + end + + def runtexmpjob(filename,filetype='') + mpfile = File.suffixed(filename,filetype,'mp') + if File.atleast?(mpfile,25) && (data = File.silentread(mpfile)) then + textranslation = if data =~ /^\%\s+translate.*?\=([\w\d\-]+)/io then $1 else '' end + mpjobname = if data =~ /collected graphics of job \"(.+?)\"/io then $1 else '' end + if ! mpjobname.empty? and File.unsuffixed(filename) =~ /#{mpjobname}/ then # don't optimize + options = Array.new + options.push("--mptex") + options.push("--nomp") + options.push("--mpyforce") if getvariable('forcempy') || getvariable('mpyforce') + options.push("--translate=#{textranslation}") unless textranslation.empty? + options.push("--batch") if getvariable('batchmode') + options.push("--nonstop") if getvariable('nonstopmode') + options.push("--output=ps") + return runtexexec(mpfile,options,2) + end + end + return false + end + + def runtexutil(filename=[], options=['--ref','--ij','--high'], old=false) + filename.each do |fname| + if old then + Kpse.runscript('texutil',fname,options) + else + begin + logger = Logger.new('TeXUtil') + if tu = TeXUtil::Converter.new(logger) and tu.loaded(fname) then + tu.saved if tu.processed + end + rescue + Kpse.runscript('texutil',fname,options) + end + end + end + end + + # 1=tex 2=mptex 3=mpxtex + + def runtexexec(filename=[], options=[], mode=nil) + begin + if mode and job = TEX.new(@logger) then + options.each do |option| + if option=~ /^\-*(.*?)\=(.*)$/o then + job.setvariable($1,$2) + else + job.setvariable(option,true) + end + end + job.setvariable("files",filename) + case mode + when 1 then job.processtex + when 2 then job.processmptex + when 3 then job.processmpxtex + end + job.inspect && Kpse.inspect if getvariable('verbose') + return true + else + Kpse.runscript('texexec',filename,options) + end + rescue + Kpse.runscript('texexec',filename,options) + end + end + + def fixbackendvars(backend) + ENV['backend'] = backend ; + ENV['progname'] = backend unless validtexengine(backend) + ENV['TEXFONTMAPS'] = ".;\$TEXMF/fonts/map/{#{backend},pdftex,dvips,}//" + end + + def runbackend(rawname) + case validbackend(getvariable('backend')) + when 'dvipdfmx' then + fixbackendvars('dvipdfm') + system("dvipdfmx -d 4 #{File.unsuffixed(rawname)}") + when 'xetex' then + fixbackendvars('xetex') + system("xdv2pdf #{File.suffixed(jrawname,'xdv')}") + when 'dvips' then + fixbackendvars('dvips') + mapfiles = '' + begin + if tuifile = File.suffixed(rawname,'tui') and FileTest.file?(tuifile) then + IO.read(tuifile).scan(/^c \\usedmapfile\{.\}\{(.*?)\}\s*$/o) do + mapfiles += "-u +#{$1} " ; + end + end + rescue + mapfiles = '' + end + system("dvips #{mapfiles} #{File.unsuffixed(rawname)}") + when 'pdftex' then + # no need for postprocessing + else + report("no postprocessing needed") + end + end + + def processfile + + takeprecautions + + rawname = getvariable('filename') + + jobname = getvariable('filename') + suffix = getvariable('suffix') + result = getvariable('result') + + runonce = getvariable('once') + finalrun = getvariable('final') || (getvariable('arrange') && ! getvariable('noarrange')) + globalfile = getvariable('globalfile') + + if getvariable('autopath') then + jobname = File.basename(jobname) + inppath = File.dirname(jobname) + else + inppath = '' + end + + jobname, jobsuffix = File.splitname(jobname,'tex') + + jobname = File.unixfied(jobname) + inppath = File.unixfied(inppath) + result = File.unixfied(result) + + orisuffix = jobsuffix # still needed ? + + setvariable('nomprun',true) if orisuffix == 'mpx' # else cylic run + + PDFview.closeall if getvariable('autopdf') + + forcexml = jobsuffix.match(/^(xml|fo|fox|rlg|exa)$/io) # nil or match + + dummyfile = false + + # fuzzy code snippet: (we kunnen kpse: prefix gebruiken) + + unless FileTest.file?(File.suffixed(jobname,jobsuffix)) then + if FileTest.file?(rawname + '.tex') then + jobname = rawname.dup + jobsuffix = 'tex' + end + end + + # we can have funny names, like 2005.10.10 (given without suffix) + + rawname = jobname + '.' + jobsuffix + + unless FileTest.file?(rawname) then + inppath.split(',').each do |ip| + break if dummyfile = FileTest.file?(File.join(ip,rawname)) + end + end + + # preprocess files + + ctx = CtxRunner.new(rawname,@logger) + if getvariable('ctxfile').empty? then + ctx.manipulate(File.suffixed(rawname,'ctx'),'jobname.ctx') + else + ctx.manipulate(File.suffixed(getvariable('ctxfile'),'ctx')) + end + ctx.savelog(File.suffixed(rawname,'ctl')) + + envs = ctx.environments + mods = ctx.modules + + # merge environment and module specs + + envs << getvariable('environments') unless getvariable('environments').empty? + mods << getvariable('modules') unless getvariable('modules') .empty? + + envs = envs.uniq.join(',') + mods = mods.uniq.join(',') + + report("using environments #{envs}") if envs.length > 0 + report("using modules #{mods}") if mods.length > 0 + + setvariable('environments', envs) + setvariable('modules', mods) + + # end of preprocessing and merging + + jobsuffix = makestubfile(rawname,forcexml) if dummyfile || forcexml + + if globalfile || FileTest.file?(rawname) then + + if not dummyfile and not globalfile then + scantexpreamble(rawname) + scantexcontent(rawname) if getvariable('texformats').standard? + end + + result = File.suffixed(rawname,suffix) unless suffix.empty? + + pushresult(rawname,result) + + method = validtexmethod(validtexformat(getvariable('texformats'))) + + report("tex processing method: #{method}") + + case method + + when 'context' then + if getvariable('simplerun') || runonce then + makeoptionfile(rawname,jobname,orisuffix,true,true,3) unless getvariable('nooptionfile') + ok = runtex(rawname) + if ok then + ok = runtexutil(rawname) if getvariable('texutil') || getvariable('forcetexutil') + runbackend(rawname) + popresult(rawname,result) + end + File.silentdelete(File.suffixed(rawname,'tmp')) + File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp')) + else + mprundone, ok, stoprunning = false, true, false + texruns, nofruns = 0, getvariable('runs').to_i + state = FileState.new + ['tub','tuo'].each do |s| + state.register(File.suffixed(rawname,s)) + end + if getvariable('automprun') then # check this + ['mprun','mpgraph'].each do |s| + state.register(File.suffixed(rawname,s,'mp'),'randomseed') + end + end + while ! stoprunning && (texruns < nofruns) && ok do + texruns += 1 + report("TeX run #{texruns}") + if texruns == 1 then + makeoptionfile(rawname,jobname,orisuffix,false,false,1) unless getvariable('nooptionfile') + else + makeoptionfile(rawname,jobname,orisuffix,false,false,2) unless getvariable('nooptionfile') + end + ok = runtex(File.suffixed(rawname,jobsuffix)) + if ok && (nofruns > 1) then + unless getvariable('nompmode') then + mprundone = runtexmpjob(rawname, "mpgraph") + mprundone = runtexmpjob(rawname, "mprun") + end + ok = runtexutil(rawname) + state.update + stoprunning = state.stable? + end + end + ok = runtexutil(rawname) if (nofruns == 1) && getvariable('texutil') + if ok && finalrun && (nofruns > 1) then + makeoptionfile(rawname,jobname,orisuffix,true,finalrun,4) unless getvariable('nooptionfile') + report("final TeX run #{texruns}") + ok = runtex(File.suffixed(rawname,jobsuffix)) + end + ['tmp','top'].each do |s| # previous tuo file / runtime option file + File.silentdelete(File.suffixed(rawname,s)) + end + File.silentrename(File.suffixed(rawname,'top'),File.suffixed(rawname,'tmp')) + if ok then + runbackend(rawname) + popresult(rawname,result) + end + end + + Kpse.runscript('ctxtools',rawname,'--purge') if getvariable('purge') + Kpse.runscript('ctxtools',rawname,'--purgeall') if getvariable('purgeall') + + when 'latex' then + + ok = runtex(rawname) + + else + + ok = runtex(rawname) + + end + + if (dummyfile or forcexml) and FileTest.file?(rawname) then + begin + File.delete(File.suffixed(rawname,'run')) + rescue + report("unable to delete stub file") + end + end + + if ok and getvariable('autopdf') then + PDFview.open(File.suffixed(if result.empty? then rawname else result end,'pdf')) + end + + end + + end + + # mp specific + + def doruntexmp(mpname,mergebe=true,context=true) + texfound = false + mpbetex = Hash.new + mpfile = File.suffixed(mpname,'mp') + mpcopy = File.suffixed(mpname,'copy','mp') + setvariable('mp.file',mpfile) + setvariable('mp.line','') + setvariable('mp.error','') + if mpdata = File.silentread(mpfile) then + mpdata.gsub!(/^\#.*\n/o,'') + File.silentrename(mpfile,mpcopy) + texfound = mergebe || mpdata =~ /btex .*? etex/o + if mp = File.silentopen(mpfile,'w') then + mpdata.gsub!(/(btex.*?)\;(.*?etex)/o) do "#{$1}@@@#{$2}" end + mpdata.gsub!(/(\".*?)\;(.*?\")/o) do "#{$1}@@@#{$2}" end + mpdata.gsub!(/\;/o, "\;\n") + mpdata.gsub!(/\n+/o, "\n") + mpdata.gsub!(/(btex.*?)@@@(.*?etex)/o) do "#{$1}\;#{$2}" end + mpdata.gsub!(/(\".*?)@@@(.*?\")/o) do "#{$1};#{$2}" end + if mergebe then + mpdata.gsub!(/beginfig\s*\((\d+)\)\s*\;(.*?)endfig\s*\;/o) do + n, str = $1, $2 + if str =~ /(.*?)(verbatimtex.*?etex)\s*\;(.*)/o then + "beginfig(#{n})\;\n$1$2\;\n#{mpbetex(n)}\n$3\;endfig\;\n" + else + "beginfig(#{n})\;\n#{mpbetex(n)}\n#{str}\;endfig\;\n" + end + end + end + unless mpdata =~ /beginfig\s*\(\s*0\s*\)/o then + mp << mpbetex[0] if mpbetex.key?(0) + end + mp << mpdata # ?? + mp << "\n" + mp << "end" + mp << "\n" + mp.close + end + processmpx(mpname) if texfound + if getvariable('batchmode') then + options = ' --interaction=batch' + elsif getvariable('nonstopmode') then + options = ' --interaction=nonstop' + else + options = '' + end + # todo plain|mpost|metafun + ok = runmp(mpname) + if f = File.silentopen(File.suffixed(mpfile,'log')) then + while str = f.gets do + if str =~ /^l\.(\d+)\s(.*?)\n/o then + setvariable('mp.line',$1) + setvariable('mp.error',$2) + break + end + end + f.close + end + File.silentrename(mpfile,"mptrace.tmp") + File.silentrename(mpcopy, mpfile) + end + end + + def processmpx(mpname,context=true) + mpname = File.suffixed(mpname,'mp') + if File.atleast?(mpname,10) && (data = File.silentread(mpname)) then + begin + if data =~ /(btex|etex|verbatimtex)/o then + mptex = File.suffixed(mpname,'temp','tex') + mpdvi = File.suffixed(mpname,'temp','dvi') + mplog = File.suffixed(mpname,'temp','log') + mpmpx = File.suffixed(mpname,'temp','mpx') + ok = system("mpto #{mpname} > #{mptex}") + if ok && File.appended(mptex, "\\end\n") then + if context then + ok = RunConTeXtFile(mptex) + else + ok = RunSomeTeXFile(mptex) + end + ok = ok && FileTest.file?(mpdvi) && system("dvitomp #{mpdvi} #{mpmpx}") + [mptex,mpdvi,mplog].each do |mpfil| + File.silentdelete(mpfil) + end + end + end + rescue + # error in processing mpx file + end + end + end + + def checkmpgraphics(mpname) + mpoptions = '' + if getvariable('makempy') then + mpoptions += " --makempy " + end + if getvariable('mpyforce') || getvariable('forcempy') then + mpoptions += " --force " + else + mponame = File.suffixed(mpname,'mpo') + mpyname = File.suffixed(mpname,'mpy') + return false unless File.atleast?(mponame,32) + mpochecksum = State.new.checksum(mponame) + return false if mpochecksum.empty? + # where does the checksum get into the file? + # maybe let texexec do it? + # solution: add one if not present or update when different + if f = File.open(mpyname) then + str = f.gets.chomp + f.close + if str =~ /^\%\s*mpochecksum\s*\:\s*(\d+)/o then + return false if mpochecksum == $1 + end + end + end + return Kpse.runscript('makempy',mpname) + end + + def checkmplabels(mpname) + mpname = File.suffixed(mpname,'mpt') + if File.atleast?(mpname,10) && (mp = File.open(mpname)) then + labels = Hash.new + while str = mp.gets do + if str =~ /%\s*setup\s*:\s*(.*)/o then + t = $1 + else + t = '' + end + if str =~ /%\s*figure\s*(\d+)\s*:\s*(.*)/o then + unless t.empty? then + labels[$1] += "#{t}\n" + t = '' + end + labels[$1] += "$2\n" + end + end + mp.close + return labels if labels.size>0 + end + return nil + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/texutil.rb b/Master/texmf-dist/scripts/context/ruby/base/texutil.rb new file mode 100644 index 00000000000..c279bcc97a6 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/texutil.rb @@ -0,0 +1,872 @@ +require "base/file" +require "base/logger" + +class String + + # real dirty, but inspect does a pretty good escaping but + # unfortunately puts quotes around the string so we need + # to strip these + + # def escaped + # self.inspect[1,self.inspect.size-2] + # end + + def escaped + str = self.inspect ; str[1,str.size-2] + end + + def splitdata + if self =~ /^\s*(.*?)\s*\{(.*)\}\s*$/o then + first, second = $1, $2 + if first.empty? then + [second.split(/\} \{/o)].flatten + else + [first.split(/\s+/o)] + [second.split(/\} \{/o)] + end + else + [] + end + end + +end + +class Logger + def banner(str) + report(str) + return "%\n% #{str}\n%\n" + end +end + +class TeXUtil + + class Plugin + + # we need to reset module data for each run; persistent data is + # possible, just don't reinitialize the data structures that need + # to be persistent; we reset afterwards becausethen we know what + # plugins are defined + + def initialize(logger) + @plugins = Array.new + @logger = logger + end + + def reset(name) + if @plugins.include?(name) then + begin + eval("#{name}").reset(@logger) + rescue Exception + @logger.report("fatal error in resetting plugin") + end + else + @logger.report("no plugin #{name}") + end + end + + def resets + @plugins.each do |p| + reset(p) + end + end + + def register(name, file=nil) # maybe also priority + if file then + begin + require("#{file.downcase.sub(/\.rb$/,'')}.rb") + rescue Exception + @logger.report("no plugin file #{file} for #{name}") + else + @plugins.push(name) + end + else + @plugins.push(name) + end + return self + end + + def reader(name, data=[]) + if @plugins.include?(name) then + begin + eval("#{name}").reader(@logger,data.flatten) + rescue Exception + @logger.report("fatal error in plugin reader #{name} (#{$!})") + end + else + @logger.report("no plugin #{name}") + end + end + + def readers(data=[]) + @plugins.each do |p| + reader(p,data.flatten) + end + end + + def writers(handle) + @plugins.each do |p| + begin + eval("#{p}").writer(@logger,handle) + rescue Exception + @logger.report("fatal error in plugin writer #{p} (#{$!})") + end + end + end + + def processors + @plugins.each do |p| + begin + eval("#{p}").processor(@logger) + rescue Exception + @logger.report("fatal error in plugin processor #{p} (#{$!})") + end + end + end + + end + + class Sorter + + def initialize(max=12) + @rep, @map, @exp, @div = Hash.new, Hash.new, Hash.new, Hash.new + @max = max + @rexa, @rexb = nil, nil + end + + def replacer(from,to='') # and expand + @max = [@max,to.length+1].max if to + @rep[from.escaped] = to || '' + end + + # sorter.reducer('ch', 'c') + # sorter.reducer('ij', 'y') + + def reducer(from,to='') + @max = [@max,to.length+1].max if to + @map[from] = to || '' + end + + # sorter.expander('aeligature', 'ae') + # sorter.expander('ijligature', 'y') + + def expander(from,to=nil) + from, to = converted(from), converted(to) + @max = [@max,to.length+1].max if to + @exp[from] = to || from || '' + end + + def division(from,to=nil) + from, to = converted(from), converted(to) + @max = [@max,to.length+1].max if to + @div[from] = to || from || '' + end + + # shortcut("\\ab\\cd\\e\\f", 'iacute') + # shortcut("\\\'\\i", 'iacute') + # shortcut("\\\'i", 'iacute') + # shortcut("\\\"e", 'ediaeresis') + # shortcut("\\\'o", 'oacute') + + def shortcut(from,to) + replacer(from,to) + expander(to) + end + + def prepare + if @rep.size > 0 then + @rexa = /(#{@rep.keys.join('|')})/ # o + else + @rexa = nil + end + if @map.size > 0 then + # watch out, order of match matters + @rexb = /(\\[a-zA-Z]+|#{@map.keys.join('|')}|.)\s*/ # o + else + @rexb = /(\\[a-zA-Z]+|.)\s*/o + end + end + + def remap(str) + s = str.dup + s.gsub!(/(\d+)/o) do + $1.rjust(10,'a') # rest is b .. k + end + if @rexa then + s.gsub!(@rexa) do + @rep[$1.escaped] + end + end + if @rexb then + s.gsub!(@rexb) do + token = $1.sub(/\\/o, '') + if @exp.key?(token) then + @exp[token].ljust(@max,' ') + elsif @map.key?(token) then + @map[token].ljust(@max,' ') + else + '' + end + end + end + s + end + + def preset(shortcuts=[],expansions=[],reductions=[],divisions=[]) + # maybe we should move this to sort-def.tex + 'a'.upto('z') do |c| expander(c) ; division(c) end + expander('1','b') ; expander('2','c') ; expander('3','e') ; expander('4','f') + expander('5','g') ; expander('6','h') ; expander('7','i') ; expander('8','i') + expander('9','j') ; expander('0','a') ; expander('-','-') ; + # end potential move + shortcuts.each do |s| shortcut(s[0],s[1]) end + expansions.each do |e| expander(e[0],e[1]) end + reductions.each do |r| reducer(r[0],r[1]) end + divisions.each do |d| division(d[0],d[1]) end + end + + def simplify(str) + s = str.dup + # ^^ + # s.gsub!(/\^\^([a-f0-9][a-f0-9])/o, $1.hex.chr) + # \- || + s.gsub!(/(\\\-|\|\|)/o) do '-' end + # {} + s.gsub!(/\{\}/o) do '' end + # <*..> (internal xml entity) + s.gsub!(/<\*(.*?)>/o) do $1 end + # entities + s.gsub!(/\\getXMLentity\s*\{(.*?)\}/o) do $1 end + # elements + s.gsub!(/\<.*?>/o) do '' end + # what to do with xml and utf-8 + # \"e etc + # unknown \cs + s.gsub!(/\\[a-z][a-z]+\s*\{(.*?)\}/o) do $1 end + return s + end + + def getdivision(str) + @div[str] || str + end + + def division?(str) + @div.key?(str) + end + + private + + def converted(str) + if str then + str.gsub(/([\+\-]*\d+)/o) do + n = $1.to_i + if n > 0 then + 'z'*n + elsif n < 0 then + '-'*(-n) # '-' precedes 'a' + else + '' + end + end + else + nil + end + end + + end + + class Plugin + + module MyFiles + + @@files = Hash.new + + def MyFiles::reset(logger) + @@files = Hash.new + end + + def MyFiles::reader(logger,data) + case data[0] + when 'b', 'e' then + if @@files.key?(data[1]) then + @@files[data[1]] += 1 + else + @@files[data[1]] = 1 + end + end + end + + def MyFiles::writer(logger,handle) + handle << logger.banner("loaded files: #{@@files.size}") + @@files.keys.sort.each do |k| + handle << "% > #{k} #{@@files[k]/2}\n" + end + end + + def MyFiles::processor(logger) + @@files.keys.sort.each do |k| + unless (@@files[k] % 2) == 0 then + logger.report("check loading of file #{k}, begin/end problem") + end + end + end + + end + + end + + class Plugin + + module MyCommands + + @@commands = [] + + def MyCommands::reset(logger) + @@commands = [] + end + + def MyCommands::reader(logger,data) + @@commands.push(data.shift+data.collect do |d| "\{#{d}\}" end.join) + end + + def MyCommands::writer(logger,handle) + handle << logger.banner("commands: #{@@commands.size}") + @@commands.each do |c| + handle << "#{c}\n" + end + end + + def MyCommands::processor(logger) + end + + end + + end + + class Plugin + + module MyExtras + + @@programs = [] + + def MyExtras::reset(logger) + @@programs = [] + end + + def MyExtras::reader(logger,data) + case data[0] + when 'p' then + @@programs.push(data[1]) if data[0] + end + end + + def MyExtras::writer(logger,handle) + handle << logger.banner("programs: #{@@programs.size}") + @@programs.each do |p| + handle << "% #{p} (#{@@programs[p.to_i]})\n" + end + end + + def MyExtras::processor(logger) + @@programs.each do |p| + cmd = "texmfstart #{@@programs[p.to_i]}" + logger.report("running #{cmd}") + system(cmd) + end + end + + end + + end + + class Plugin + + module MySynonyms + + class Synonym + + @@debug = false + @@debug = true + + def initialize(t, c, k, d) + @type, @command, @key, @sortkey, @data = t, c, k, k, d + end + + attr_reader :type, :command, :key, :data + attr_reader :sortkey + attr_writer :sortkey + + def build(sorter) + @sortkey = sorter.remap(sorter.simplify(@key.downcase)) + if @sortkey.empty? then + @sortkey = sorter.remap(@command.downcase) + end + end + + def <=> (other) + @sortkey <=> other.sortkey + end + + def Synonym.flush(list,handle) + if @@debug then + list.each do |entry| + handle << "% [#{entry.sortkey}]\n" + end + end + list.each do |entry| + handle << "\\synonymentry{#{entry.type}}{#{entry.command}}{#{entry.key}}{#{entry.data}}\n" + end + end + + end + + @@synonyms = Hash.new + + def MySynonyms::reset(logger) + @@synonyms = Hash.new + end + + def MySynonyms::reader(logger,data) + if data[0] == 'e' then + @@synonyms[data[1]] = Array.new unless @@synonyms.key?(data[1]) + @@synonyms[data[1]].push(Synonym.new(data[1],data[2],data[3],data[4])) + end + end + + def MySynonyms::writer(logger,handle) + if @@synonyms.size > 0 then + @@synonyms.keys.sort.each do |s| + handle << logger.banner("synonyms: #{s} #{@@synonyms[s].size}") + Synonym.flush(@@synonyms[s],handle) + end + end + end + + def MySynonyms::processor(logger) + sorter = Sorter.new + sorter.preset(eval("MyKeys").shortcuts,eval("MyKeys").expansions,eval("MyKeys").reductions,eval("MyKeys").divisions) + sorter.prepare + @@synonyms.keys.each do |s| + @@synonyms[s].each_index do |i| + @@synonyms[s][i].build(sorter) + end + @@synonyms[s] = @@synonyms[s].sort + end + end + + end + + end + + class Plugin + + module MyRegisters + + class Register + + @@debug = false + @@debug = true + + @@howto = /^(.*?)\:\:(.*)$/o + @@split = ' && ' + + def initialize(state, t, l, k, e, s, p, r) + @state, @type, @location, @key, @entry, @seetoo, @page, @realpage = state, t, l, k, e, s, p, r + if @key =~ @@howto then @pagehowto, @key = $1, $2 else @pagehowto = '' end + if @entry =~ @@howto then @texthowto, @entry = $1, $2 else @texthowto = '' end + @key = @entry.dup if @key.empty? + @sortkey = @key.dup + @nofentries, @nofpages = 0, 0 + end + + attr_reader :state, :type, :location, :key, :entry, :seetoo, :page, :realpage, :texthowto, :pagehowto + attr_reader :sortkey + attr_writer :sortkey + + def build(sorter) + @entry, @key = [@entry, @key].collect do |target| + # +a+b+c &a&b&c a+b+c a&b&c + case target[0,1] + when '&' then target = target.sub(/^./o,'').gsub(/([^\\])\&/o) do "#{$1}#{@@split}" end + when '+' then target = target.sub(/^./o,'').gsub(/([^\\])\+/o) do "#{$1}#{@@split}" end + else target = target .gsub(/([^\\])[\&\+]/o) do "#{$1}#{@@split}" end + end + # {a}{b}{c} + # if target =~ /^\{(.*)\}$/o then + # $1.split(/\} \{/o).join(@@split) # space between } { is mandate + # else + target + # end + end + @sortkey = sorter.simplify(@key) + @sortkey = @sortkey.split(@@split).collect do |c| sorter.remap(c) end.join(@@split) + # if ($Key eq "") { $Key = SanitizedString($Entry) } + # if ($ProcessHigh){ $Key = HighConverted($Key) } + @sortkey = [ + @sortkey.downcase, + @sortkey, + @texthowto.ljust(10,' '), + @state, + @realpage.rjust(6,' '), + @pagehowto + ].join(@@split) + end + + def <=> (other) + @sortkey <=> other.sortkey + end + + # more module like + + @@savedhowto, @@savedfrom, @@savedto, @@savedentry = '', '', '', '', '' + @@collapse = false + + def Register.flushsavedline(handle) + if @@collapse && ! @@savedfrom.empty? then + if ! @@savedto.empty? then + handle << "\\registerfrom#{@@savedfrom}" + handle << "\\registerto#{@@savedto}" + else + handle << "\\registerpage#{@@savedfrom}" + end + end + @@savedhowto, @@savedfrom, @@savedto, @@savedentry = '', '', '', '' + end + + def Register.flush(list,handle,sorter) + # a bit messy, quite old mechanism, maybe some day ... + # alphaclass can go, now flushed per class + if list.size > 0 then + @nofentries, @nofpages = 0, 0 + current, previous, howto = Array.new, Array.new, Array.new + lastpage, lastrealpage = '', '' + alphaclass, alpha = '', '' + @@savedhowto, @@savedfrom, @@savedto, @@savedentry = '', '', '', '' + if @@debug then + list.each do |entry| + handle << "% [#{entry.sortkey.gsub(/#{@@split}/o,'] [')}]\n" + end + end + list.each do |entry| + if entry.sortkey =~ /^(\S+)/o then + if sorter.division?($1) then + testalpha = sorter.getdivision($1) + else + testalpha = entry.sortkey[0,1].downcase + end + else + testalpha = entry.sortkey[0,1].downcase + end + if testalpha != alpha.downcase or alphaclass != entry.class then + alpha = testalpha + alphaclass = entry.class + if alpha != ' ' then + flushsavedline(handle) + if alpha =~ /^[a-zA-Z]$/o then + character = alpha.dup + elsif alpha.length > 1 then + # character = "\\getvalue\{#{alpha}\}" + character = "\\#{alpha}" + else + character = "\\#{alpha}" + end + handle << "\\registerentry{#{entry.type}}{#{character}}\n" + end + end + current = [entry.entry.split(@@split),'','',''].flatten + howto = current.collect do |e| + e + '::' + entry.texthowto + end + if howto[0] == previous[0] then + current[0] = '' + else + previous[0] = howto[0].dup + previous[1] = '' + previous[2] = '' + end + if howto[1] == previous[1] then + current[1] = '' + else + previous[1] = howto[1].dup + previous[2] = '' + end + if howto[2] == previous[2] then + current[2] = '' + else + previous[2] = howto[2].dup + end + copied = false + unless current[0].empty? then + Register.flushsavedline(handle) + handle << "\\registerentrya{#{entry.type}}{#{current[0]}}\n" + copied = true + end + unless current[1].empty? then + Register.flushsavedline(handle) + handle << "\\registerentryb{#{entry.type}}{#{current[1]}}\n" + copied = true + end + unless current[2].empty? then + Register.flushsavedline(handle) + handle << "\\registerentryc{#{entry.type}}{#{current[2]}}\n" + copied = true + end + @nofentries += 1 if copied + if entry.realpage.to_i == 0 then + Register.flushsavedline(handle) + handle << "\\registersee{#{entry.type}}{#{entry.pagehowto},#{entry.texthowto}}{#{entry.seetoo}}{#{entry.page}}\n" ; + lastpage, lastrealpage = entry.page, entry.realpage + elsif @@savedhowto != entry.pagehowto and ! entry.pagehowto.empty? then + @@savedhowto = entry.pagehowto + end + if copied || ! ((lastpage == entry.page) && (lastrealpage == entry.realpage)) then + nextentry = "{#{entry.type}}{#{previous[0]}}{#{previous[1]}}{#{previous[2]}}{#{entry.pagehowto},#{entry.texthowto}}" + savedline = "{#{entry.type}}{#{@@savedhowto},#{entry.texthowto}}{#{entry.location}}{#{entry.page}}{#{entry.realpage}}" + if entry.state == 1 then # from + Register.flushsavedline(handle) + handle << "\\registerfrom#{savedline}\n" + elsif entry.state == 3 then # to + Register.flushsavedline(handle) + handle << "\\registerto#{savedline}\n" + @@savedhowto = '' # test + elsif @@collapse then + if savedentry != nextentry then + savedFrom = savedline + else + savedTo, savedentry = savedline, nextentry + end + else + handle << "\\registerpage#{savedline}\n" + @@savedhowto = '' # test + end + @nofpages += 1 + lastpage, lastrealpage = entry.page, entry.realpage + end + end + Register.flushsavedline(handle) + end + end + + end + + @@registers = Hash.new + @@sorter = Sorter.new + + def MyRegisters::reset(logger) + @@registers = Hash.new + @@sorter = Sorter.new + end + + def MyRegisters::reader(logger,data) + case data[0] + when 'f' then + @@registers[data[1]] = Array.new unless @@registers.key?(data[1]) + @@registers[data[1]].push(Register.new(1,data[1],data[2],data[3],data[4],nil,data[5],data[6])) + when 'e' then + @@registers[data[1]] = Array.new unless @@registers.key?(data[1]) + @@registers[data[1]].push(Register.new(2,data[1],data[2],data[3],data[4],nil,data[5],data[6])) + when 't' then + @@registers[data[1]] = Array.new unless @@registers.key?(data[1]) + @@registers[data[1]].push(Register.new(3,data[1],data[2],data[3],data[4],nil,data[5],data[6])) + when 's' then + @@registers[data[1]] = Array.new unless @@registers.key?(data[1]) + @@registers[data[1]].push(Register.new(4,data[1],data[2],data[3],data[4],data[5],data[6],nil)) + end + end + + def MyRegisters::writer(logger,handle) + if @@registers.size > 0 then + @@registers.keys.sort.each do |s| + handle << logger.banner("registers: #{s} #{@@registers[s].size}") + Register.flush(@@registers[s],handle,@@sorter) + # report("register #{@@registers[s].class}: #{@@registers[s].@nofentries} entries and #{@@registers[s].@nofpages} pages") + end + end + end + + def MyRegisters::processor(logger) + @@sorter.preset(eval("MyKeys").shortcuts,eval("MyKeys").expansions,eval("MyKeys").reductions,eval("MyKeys").divisions) + @@sorter.prepare + @@registers.keys.each do |s| + @@registers[s].each_index do |i| + @@registers[s][i].build(@@sorter) + end + @@registers[s] = @@registers[s].sort + end + end + + end + + end + + class Plugin + + module MyPlugins + + @@plugins = nil + + def MyPlugins::reset(logger) + @@plugins = nil + end + + def MyPlugins::reader(logger,data) + @@plugins = Plugin.new(logger) unless @@plugins + case data[0] + when 'r' then + logger.report("registering plugin #{data[1]}") + @@plugins.register(data[1],data[2]) + when 'd' then + begin + @@plugins.reader(data[1],data[2,data.length-1]) + rescue + @@plugins.reader(data[1],['error']) + end + end + end + + def MyPlugins::writer(logger,handle) + @@plugins.writers(handle) if @@plugins + end + + def MyPlugins::processor(logger) + @@plugins.processors if @@plugins + end + + end + + end + + class Plugin + + module MyKeys + + @@shortcuts = Array.new + @@expansions = Array.new + @@reductions = Array.new + @@divisions = Array.new + + def MyKeys::shortcuts + @@shortcuts + end + def MyKeys::expansions + @@expansions + end + def MyKeys::reductions + @@reductions + end + def MyKeys::divisions + @@divisions + end + + def MyKeys::reset(logger) + @@shortcuts = Array.new + @@expansions = Array.new + @@reductions = Array.new + end + + def MyKeys::reader(logger,data) + key = data.shift + grp = data.shift # language code, todo + case key + when 's' then @@shortcuts.push(data) + when 'e' then @@expansions.push(data) + when 'r' then @@reductions.push(data) + when 'd' then @@divisions.push(data) + end + end + + def MyKeys::writer(logger,handle) + end + + def MyKeys::processor(logger) + logger.report("shortcuts : #{@@shortcuts.size}") # logger.report(@@shortcuts.inspect) + logger.report("expansions: #{@@expansions.size}") # logger.report(@@expansions.inspect) + logger.report("reductions: #{@@reductions.size}") # logger.report(@@reductions.inspect) + logger.report("divisions : #{@@divisions.size}") # logger.report(@@divisions.inspect) + end + + end + + end + + class Converter + + def initialize(logger=nil) + if @logger = logger then + def report(str) + @logger.report(str) + end + def banner(str) + @logger.banner(str) + end + else + @logger = self + def report(str) + puts(str) + end + def banner(str) + puts(str) + end + end + @filename = 'texutil' + @fatalerror = false + @plugins = Plugin.new(@logger) + ['MyFiles', 'MyCommands', 'MySynonyms', 'MyRegisters', 'MyExtras', 'MyPlugins', 'MyKeys'].each do |p| + @plugins.register(p) + end + end + + def loaded(filename) + begin + report("parsing file #{filename}") + if f = open(File.suffixed(filename,'tui')) then + f.each do |line| + case line.chomp + when /^f (.*)$/o then @plugins.reader('MyFiles', $1.splitdata) + when /^c (.*)$/o then @plugins.reader('MyCommands', [$1]) + when /^e (.*)$/o then @plugins.reader('MyExtras', $1.splitdata) + when /^s (.*)$/o then @plugins.reader('MySynonyms', $1.splitdata) + when /^r (.*)$/o then @plugins.reader('MyRegisters',$1.splitdata) + when /^p (.*)$/o then @plugins.reader('MyPlugins', $1.splitdata) + when /^x (.*)$/o then @plugins.reader('MyKeys', $1.splitdata) + else report("unknown entry #{line[0,1]} in line #{line.chomp}") + end + end + f.close + end + rescue + report("fatal error in parsing #{filename}") + @filename = 'texutil' + else + @filename = filename + end + end + + def processed + @plugins.processors + return true # for the moment + end + + def saved(filename=@filename) + if @fatalerror then + report("fatal error, no tuo file saved") + else + begin + if f = File.open(File.suffixed(filename,'tuo'),'w') then + @plugins.writers(f) + f.close + end + rescue + report("fatal error when saving file (#{$!})") + else + report("tuo file saved") + end + end + @plugins.resets + end + + def reset + @plugins.resets + end + + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/tool.rb b/Master/texmf-dist/scripts/context/ruby/base/tool.rb new file mode 100644 index 00000000000..7f40e773d0c --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/tool.rb @@ -0,0 +1,306 @@ +# module : base/tool +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +require 'timeout' +require 'socket' +require 'rbconfig' + +module Tool + + $constructedtempdir = '' + + def Tool.constructtempdir(create,mainpath='',fallback='') + begin + mainpath += '/' unless mainpath.empty? + timeout(5) do + begin + t = Time.now + u = t.usec.to_s % [1..2] [0..3] + pth = t.strftime("#{mainpath}%Y%m%d-%H%M%S-#{u}-#{Process.pid}") + if pth == $constructedtempdir + # sleep(0.01) + retry + end + Dir.mkdir(pth) if create + $constructedtempdir = pth + return pth + rescue + # sleep(0.01) + retry + end + end + rescue TimeoutError + # ok + rescue + # ok + end + unless fallback.empty? + begin + pth = "#{mainpath}#{fallback}" + mkdir(pth) if create + $constructedtempdir = path + return pth + rescue + return '.' + end + else + return '.' + end + + end + + def Tool.findtempdir(*vars) + constructtempdir(false,*vars) + end + + def Tool.maketempdir(*vars) + constructtempdir(true,*vars) + end + + # print maketempdir + "\n" + # print maketempdir + "\n" + # print maketempdir + "\n" + # print maketempdir + "\n" + # print maketempdir + "\n" + + + def Tool.ruby_platform + case RUBY_PLATFORM + when /(mswin|bccwin|mingw|cygwin)/i then 'mswin' + when /(linux)/i then 'linux' + when /(netbsd|unix)/i then 'unix' + when /(darwin|rhapsody|nextstep)/i then 'macosx' + else 'unix' + end + end + + $defaultlineseparator = $/ # $RS in require 'English' + + def Tool.file_platform(filename) + + begin + if f = open(filename,'rb') then + str = f.read(4000) + str.gsub!(/(.*?)\%\!PS/mo, "%!PS") # don't look into preamble crap + f.close + nn = str.count("\n") + nr = str.count("\r") + if nn>nr then + return 2 + elsif nn<nr then + return 3 + else + return 1 + end + else + return 0 + end + rescue + return 0 + end + + end + + def Tool.path_separator + return File::PATH_SEPARATOR + end + + def Tool.line_separator(filename) + + case file_platform(filename) + when 1 then return $defaultlineseparator + when 2 then return "\n" + when 3 then return "\r" + else return $defaultlineseparator + end + + end + + def Tool.default_line_separator + $defaultlineseparator + end + + def Tool.simplefilename(old) + + return old unless test(?f,old) + + new = old.downcase + new.gsub!(/[^A-Za-z0-9\-\.\\\/]/o) do # funny chars + '-' + end + if old =~ /[a-zA-Z]\:/o + # seems like we have a dos/windows drive prefix, so roll back + new.sub!(/^(.)\-/) do + $1 + ':' + end + end + new.gsub!(/(.+?)\.(.+?)(\..+)$/o) do # duplicate . + $1 + '-' + $2 + $3 + end + new.gsub!(/\-+/o) do # duplicate - + '-' + end + new + + end + + if Config::CONFIG['host_os'] =~ /mswin/ then + + require 'Win32API' + + GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', ['P','P','N'], 'N') + GetLongPathName = Win32API.new('kernel32', 'GetLongPathName', ['P','P','N'], 'N') + + def Tool.dowith_pathname (filename,filemethod) + filename.gsub!(/\\/o,'/') + case filename + when /\;/o then + # could be a path spec + return filename + when /\s+/o then + # danger lurking + buffer = ' ' * 260 + length = filemethod.call(filename,buffer,buffer.size) + if length>0 then + return buffer.slice(0..length-1) + else + # when the path or file does not exist, nothing is returned + # so we try to handle the path separately from the basename + basename = File.basename(filename) + pathname = File.dirname(filename) + length = filemethod.call(pathname,buffer,260) + if length>0 then + return buffer.slice(0..length-1) + '/' + basename + else + return filename + end + end + else + # no danger + return filename + end + end + + def Tool.shortpathname(filename) + dowith_pathname(filename,GetShortPathName) + end + + def Tool.longpathname(filename) + dowith_pathname(filename,GetLongPathName) + end + + else + + def Tool.shortpathname(filename) + filename + end + + def Tool.longpathname(filename) + filename + end + + end + + # print shortpathname("C:/Program Files/ABBYY FineReader 6.0/matrix.str")+ "!\n" + # print shortpathname("C:/Program Files/ABBYY FineReader 6.0/matrix.strx")+ "!\n" + + def Tool.checksuffix(old) + + return old unless test(?f,old) + + new = old + + unless new =~ /\./io # no suffix + f = open(filename,'rb') + if str = f.gets + case str + when /^\%\!PS/io + # logging.report(filename, 'analyzed as EPS') + new = new + '.eps' + when /^\%PDF/io + # logging.report(filename, 'analyzed as PDF') + new = new + '.pdf' + else + # logging.report(filename, 'fallback as TIF') + new = new + '.tif' + end + end + f.close + end + + new.sub!(/\.jpeg$/io) do + '.jpg' + end + new.sub!(/\.tiff$/io) do + '.tif' + end + new.sub!(/\.ai$/io) do + '.eps' + end + new.sub!(/\.ai(.*?)$/io) do + '-' + $1 + '.eps' + end + new + + end + + def Tool.cleanfilename(old,logging=nil) + + return old unless test(?f,old) + + new = checksuffix(simplefilename(old)) + unless new == old + begin + File.rename(old,new) + logging.report("renaming fuzzy name #{old} to #{new}") unless logging + return old + rescue + logging.report("unable to rename fuzzy name #{old} to #{new}") unless logging + end + end + return new + + end + + def Tool.preventduplicates(old,logging=nil) + + return false unless test(?f,old) + + if old =~ /\.(tif|jpg|png|tiff)$/io + suffix = $1 + new = old + newn, news = new.split('.') + if test(?e,'newn.eps') + new = newn + '-' + suffix + '.' + suffix + begin + File.rename(old,new) + logging.report("renaming duplicate #{old} to #{new}") unless logging + return true + rescue + logging.report("unable to rename duplicate #{old} to #{new}") unless logging + end + end + end + return false + + end + + def Tool.servername + host = Socket::gethostname + begin + Socket::gethostbyname(host)[0] + rescue + host + end + end + + # print file_platform(ARGV[0]) + +end diff --git a/Master/texmf-dist/scripts/context/ruby/base/variables.rb b/Master/texmf-dist/scripts/context/ruby/base/variables.rb new file mode 100644 index 00000000000..5cbc5ba538e --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/base/variables.rb @@ -0,0 +1,45 @@ +# module : base/variables +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/tool','tool'].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/tool' + +module Variables + + def setvariable(key,value='') + @variables[key] = value + end + + def replacevariable(key,value='') + @variables[key] = value if @variables.key?(key) + end + + def getvariable(key,default='') + if @variables.key?(key) then @variables[key] else default end + end + + def checkedvariable(str,default='') + if @variables.key?(key) then + if @variables[key].empty? then default else @variables[key] end + else + default + end + end + + def report(*str) + @logger.report(*str) + end + + def debug(*str) + @logger.debug(str) + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/concheck.rb b/Master/texmf-dist/scripts/context/ruby/concheck.rb new file mode 100644 index 00000000000..bf09bbdc8da --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/concheck.rb @@ -0,0 +1,461 @@ +# Program : concheck (tex & context syntax checker) +# Copyright : PRAGMA ADE / Hasselt NL / www.pragma-ade.com +# Author : Hans Hagen +# Version : 1.1 / 2003.08.18 + +# remarks: +# +# - the error messages are formatted like tex's messages so that scite can see them +# - begin and end tags are only tested on a per line basis because we assume clean sources +# - maybe i'll add begin{something} ... end{something} checking + +# # example validation file +# +# begin interface en +# +# 1 text +# 4 Question +# 0 endinput +# 0 setupsomething +# 0 chapter +# +# end interface en + +# nicer + +# class Interface + + # def initialize (language = 'unknown') + # @valid = Array.new + # @language = language + # end + + # def register (left, right) + # @valid.push([left,right]) + # end + +# end + +# $interfaces = Hash.new + +# $interfaces['en'] = Interface.new('english') +# $interfaces['nl'] = Interface.new('dutch') + +# $interfaces['en'].add('\\\\start','\\\\stop') +# $interfaces['en'].add('\\\\begin','\\\\end') +# $interfaces['en'].add('\\\\Start','\\\\Stop') +# $interfaces['en'].add('\\\\Begin','\\\\End') + +# $interfaces['nl'].add('\\\\start','\\\\stop') +# $interfaces['nl'].add('\\\\beginvan','\\\\eindvan') +# $interfaces['nl'].add('\\\\Start','\\\\Stop') +# $interfaces['nl'].add('\\\\BeginVan','\\\\Eindvan') + +# rest todo + +$valid = Hash.new + +$valid['en'] = Array.new +$valid['nl'] = Array.new + +#$valid['en'].push(['','']) +$valid['en'].push(['\\\\start','\\\\stop']) +$valid['en'].push(['\\\\begin','\\\\end']) +$valid['en'].push(['\\\\Start','\\\\Stop']) +$valid['en'].push(['\\\\Begin','\\\\End']) + +#$valid['nl'].push(['','']) +$valid['nl'].push(['\\\\start','\\\\stop']) +$valid['nl'].push(['\\\\beginvan','\\\\eindvan']) +$valid['nl'].push(['\\\\Start','\\\\Stop']) +$valid['nl'].push(['\\\\BeginVan','\\\\Eindvan']) + +$valid_tex = "\\\\end\(input|insert|csname|linechar|graf|buffer|strut\)" +$valid_mp = "(enddef||end||endinput)" + +$start_verbatim = Hash.new +$stop_verbatim = Hash.new + +$start_verbatim['en'] = '\\\\starttyping' +$start_verbatim['nl'] = '\\\\starttypen' + +$stop_verbatim['en'] = '\\\\stoptyping' +$stop_verbatim['nl'] = '\\\\stoptypen' + +def message(str, filename=nil, line=nil, column=nil) + if filename then + if line then + if column then + puts("error in file #{filename} at line #{line} in column #{column}: #{str}\n") + else + puts("error in file #{filename} at line #{line}: #{str}\n") + end + else + puts("file #{filename}: #{str}\n") + end + else + puts(str+"\n") + end +end + +def load_file (filename='') + begin + data = IO.readlines(filename) + data.collect! do |d| + if d =~ /^\s*%/o then + '' + elsif d =~ /(.*?[^\\])%.*$/o then + $1 + else + d + end + end + rescue + message("provide proper filename") + return nil + end + # print data.to_s + "\n" + return data +end + +def guess_interface(data) + if data.first =~ /^%.*interface\=(.*)\s*/ then + return $1 + else + data.each do |line| + case line + when /\\(starttekst|stoptekst|startonderdeel|startdocument|startoverzicht)/o then return 'nl' + when /\\(stelle|verwende|umgebung|benutze)/o then return 'de' + when /\\(stel|gebruik|omgeving)/ then return 'nl' + when /\\(use|setup|environment)/ then return 'en' + when /\\(usa|imposta|ambiente)/ then return 'it' + when /(height|width|style)=/ then return 'en' + when /(hoehe|breite|schrift)=/ then return 'de' + when /(hoogte|breedte|letter)=/ then return 'nl' + when /(altezza|ampiezza|stile)=/ then return 'it' + when /externfiguur/ then return 'nl' + when /externalfigure/ then return 'en' + when /externeabbildung/ then return 'de' + when /figuraesterna/ then return 'it' + end + end + return 'en' + end +end + +def cleanup_data(data, interface='en') + verbatim = 0 + re_start = /^\s*#{$start_verbatim[interface]}/ + re_stop = /^\s*#{$stop_verbatim[interface]}/ + data.collect! do |d| + if d =~ re_start then + verbatim += 1 + if verbatim>1 then + '' + else + d + end + elsif d =~ re_stop then + verbatim -= 1 + if verbatim>0 then + '' + else + d + end + elsif verbatim > 0 then + '' + else + d + end + end + return data +end + +def load_valid(data, interface=nil) + if data && (data.first =~ /^%.*valid\=(.*)\s*/) + filename = $1 + filename = '../' + filename unless test(?f,filename) + filename = '../' + filename unless test(?f,filename) + if test(?f,filename) then + interface = guess_interface(data) unless interface + if $valid.has_key?(interface) then + interface = $valid[interface] + else + interface = $valid['en'] + end + begin + message("loading validation file",filename) + validkeys = Hash.new + line = 1 + IO.readlines(filename).each do |l| + if l =~ /\s+[\#\%]/io then + # ignore line + elsif l =~ /^\s*(begin|end)\s+interface\s+([a-z][a-z])/o then + # not yet supported + elsif l =~ /^\s*(\d+)\s+([a-zA-Z]*)$/o then + type, key = $1.to_i, $2.strip + if interface[type] then + validkeys[interface[type].first+key] = true + validkeys[interface[type].last+key] = true + else + error_message(filename,line,nil,'wrong definition') + end + end + line += 1 + end + if validkeys then + message("#{validkeys.length} validation keys loaded",filename) + end + return validkeys + rescue + message("invalid validation file",filename) + end + else + message("unknown validation file", filename) + end + else + message("no extra validation file specified") + end + return nil +end + +def some_chr_error(data, filename, left, right) + levels = Array.new + for line in 0..data.length-1 do + str = data[line] + column = 0 + while column<str.length do + case str[column].chr + when "\%" then + break + when "\\" then + column += 2 + when left then + levels.push([line,column]) + column += 1 + when right then + if levels.pop + column += 1 + else + message("missing #{left} for #{right}",filename,line+1,column+1) + return true + end + else + column += 1 + end + end + end + if levels && levels.length>0 then + levels.each do |l| + column = l.pop + line = l.pop + message("missing #{right} for #{left}",filename,line+1,column+1) + end + return true + else + return false + end +end + +def some_wrd_error(data, filename, start, stop, ignore) + levels = Array.new + len = 0 + re_start = /[^\%]*(#{start})([a-zA-Z]*)/ + re_stop = /[^\%]*(#{stop})([a-zA-Z]*)/ + re_ignore = /#{ignore}.*/ + str_start = start.gsub(/\\+/,'\\') + str_stop = stop.gsub(/\\+/,'\\') + line = 0 + while line<data.length do + dataline = data[line].split(/[^\\A-Za-z]/) + if dataline.length>0 then + # todo: more on one line + dataline.each do |dataword| + case dataword + when re_ignore then + # just go on + when re_start then + levels.push([line,$2]) + # print ' '*levels.length + '>' + $2 + "\n" + when re_stop then + # print ' '*levels.length + '<' + $2 + "\n" + if levels && levels.last && (levels.last[1] == $2) then + levels.pop + elsif levels && levels.last then + message("#{str_stop}#{levels.last[1]} expected instead of #{str_stop}#{$2}",filename,line+1) + return true + else + message("missing #{str_start}#{$2} for #{str_stop}#{$2}",filename,line+1) + return true + end + else + # just go on + end + end + end + line += 1 + end + if levels && levels.length>0 then + levels.each do |l| + text = l.pop + line = l.pop + message("missing #{str_stop}#{text} for #{str_start}#{text}",filename,line+1) + end + return true + else + return false + end +end + +def some_sym_error (data, filename, symbol,template=false) + saved = Array.new + inside = false + level = 0 + for line in 0..data.length-1 do + str = data[line] + column = 0 + while column<str.length do + case str[column].chr + when "[" then + level += 1 if template + when "]" then + level -= 1 if template && level > 0 + when "\%" then + break + when "\\" then + column += 1 + when symbol then + if level == 0 then + inside = ! inside + saved = [line,column] + else + # we're in some kind of template or so + end + else + # go on + end + column += 1 + end + end + if inside && saved && level == 0 then + column = saved.pop + line = saved.pop + message("missing #{symbol} for #{symbol}",filename,line+1) + return true + else + return false + end +end + +def some_key_error(data, filename, valid) + return if (! valid) || (valid.length == 0) + error = false + # data.foreach do |line| ... end + for line in 0..data.length-1 do + data[line].scan(/\\([a-zA-Z]+)/io) do + unless valid.has_key?($1) then + message("unknown command \\#{$1}",filename,line+1) + error = true + end + end + end + return error +end + +# todo : language dependent + +def check_file_tex (filename) + if data = load_file(filename) then + message("checking tex file", filename) + interface = guess_interface(data) + valid = load_valid(data,interface) + data = cleanup_data(data,interface) + # data.each do |d| print d end + $valid[interface].each do |v| + return false if some_wrd_error(data, filename, v[0], v[1] ,$valid_tex) + end + # return false if some_wrd_error(data, filename, '\\\\start' , '\\\\stop' , $valid_tex) + # return false if some_wrd_error(data, filename, '\\\\Start' , '\\\\Stop' , $valid_tex) + # return false if some_wrd_error(data, filename, '\\\\beginvan', '\\\\eindvan', $valid_tex) + # return false if some_wrd_error(data, filename, '\\\\begin' , '\\\\end|\\\\eind', $valid_tex) + return false if some_sym_error(data, filename, '$', false) + return false if some_sym_error(data, filename, '|', true) + return false if some_chr_error(data, filename, '{', '}') + return false if some_chr_error(data, filename, '[', ']') + return false if some_chr_error(data, filename, '(', ')') + return false if some_key_error(data, filename, valid) + message("no errors in tex code", filename) + return true + else + return false + end +end + +def check_file_mp (filename) + if data = load_file(filename) then + message("checking metapost file", filename) + interface = guess_interface(data) + valid = load_valid(data,interface) + $valid[interface].each do |v| + return false if some_wrd_error(data, filename, v[0], v[1] ,$valid_tex) + end + # return false if some_wrd_error(data, filename, '', 'begin', 'end', $valid_mp) + return false if some_chr_error(data, filename, '{', '}') + return false if some_chr_error(data, filename, '[', ']') + return false if some_chr_error(data, filename, '(', ')') + return false if some_key_error(data, filename, valid) + message("no errors in metapost code", filename) + return true + else + return true + end +end + +def check_file_text(filename='') + if data = load_file(filename) then + for line in 0..data.length-1 do + # case data[line] + # when /\s([\:\;\,\.\?\!])/ then + # message("space before #{$1}",filename,line+1) + # when /\D([\:\;\,\.\?\!])\S/ then + # message("no space after #{$1}",filename,line+1) + # end + if data[line] =~ /\s([\:\;\,\.\?\!])/ then + message("space before #{$1}",filename,line+1) + else + data[line].gsub!(/\[.*?\]/o, '') + data[line].gsub!(/\(.*?\)/o, '') + data[line].gsub!(/\[.*?$/o, '') + data[line].gsub!(/^.*?\]/o, '') + if data[line] =~ /\D([\:\;\,\.\?\!])\S/ then + message("no space after #{$1}",filename,line+1) + end + end + end + end +end + +def check_file(filename='') + case filename + when '' then + message("provide filename") + return false + when /\.tex$/i then + return check_file_tex(filename) # && check_file_text(filename) + when /\.mp$/i then + return check_file_mp(filename) + else + message("only tex and metapost files are checked") + return false + end +end + +if filename = ARGV[0] then + if check_file(filename) then + exit 0 + else + exit 1 + end +else + exit 1 +end diff --git a/Master/texmf-dist/scripts/context/ruby/ctxtools.rb b/Master/texmf-dist/scripts/context/ruby/ctxtools.rb new file mode 100644 index 00000000000..94e6e735a20 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/ctxtools.rb @@ -0,0 +1,1405 @@ +#!/usr/bin/env ruby + +# program : ctxtools +# copyright : PRAGMA Advanced Document Engineering +# version : 2004-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# This script will harbor some handy manipulations on context +# related files. + +# todo: move scite here +# +# todo: move kpse call to kpse class/module + +banner = ['CtxTools', 'version 1.2.2', '2004/2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' +require 'base/system' + +require 'rexml/document' +require 'ftools' +require 'kconv' + +exit if defined?(REQUIRE2LIB) + +class String + + def i_translate(element, attribute, category) + self.gsub!(/(<#{element}.*?#{attribute}=)([\"\'])(.*?)\2/) do + if category.key?($3) then + # puts "#{element} #{$3} -> #{category[$3]}\n" if element == 'cd:inherit' + # puts "#{element} #{$3} => #{category[$3]}\n" if element == 'cd:command' + "#{$1}#{$2}#{category[$3]}#{$2}" + else + # puts "#{element} #{$3} -> ?\n" if element == 'cd:inherit' + # puts "#{element} #{$3} => ?\n" if element == 'cd:command' + "#{$1}#{$2}#{$3}#{$2}" # unchanged + end + end + end + + def i_load(element, category) + self.scan(/<#{element}.*?name=([\"\'])(.*?)\1.*?value=\1(.*?)\1/) do + category[$2] = $3 + end + end + +end + +class Commands + + include CommandBase + + public + + def touchcontextfile + dowithcontextfile(1) + end + + def contextversion + dowithcontextfile(2) + end + + private + + def dowithcontextfile(action) + maincontextfile = 'context.tex' + unless FileTest.file?(maincontextfile) then + begin + maincontextfile = `kpsewhich -progname=context #{maincontextfile}`.chomp + rescue + maincontextfile = '' + end + end + unless maincontextfile.empty? then + case action + when 1 then touchfile(maincontextfile) + when 2 then reportversion(maincontextfile) + end + end + + end + + def touchfile(filename) + + if FileTest.file?(filename) then + if data = IO.read(filename) then + timestamp = Time.now.strftime('%Y.%m.%d') + prevstamp = '' + begin + data.gsub!(/\\contextversion\{(\d+\.\d+\.\d+)\}/) do + prevstamp = $1 + "\\contextversion{#{timestamp}}" + end + rescue + else + begin + File.delete(filename+'.old') + rescue + end + begin + File.copy(filename,filename+'.old') + rescue + end + begin + if f = File.open(filename,'w') then + f.puts(data) + f.close + end + rescue + end + end + if prevstamp.empty? then + report("#{filename} is not updated, no timestamp found") + else + report("#{filename} is updated from #{prevstamp} to #{timestamp}") + end + end + else + report("#{filename} is not found") + end + + end + + def reportversion(filename) + + version = 'unknown' + begin + if FileTest.file?(filename) && IO.read(filename).match(/\\contextversion\{(\d+\.\d+\.\d+)\}/) then + version = $1 + end + rescue + end + if @commandline.option("pipe") then + print version + else + report("context version: #{version}") + end + + end + +end + +class Commands + + include CommandBase + + public + + def jeditinterface + editinterface('jedit') + end + + def bbeditinterface + editinterface('bbedit') + end + + def sciteinterface + editinterface('scite') + end + + def rawinterface + editinterface('raw') + end + + private + + def editinterface(type='raw') + + return unless FileTest.file?("cont-en.xml") + + interfaces = @commandline.arguments + + if interfaces.empty? then + interfaces = ['en', 'cz','de','it','nl','ro'] + end + + interfaces.each do |interface| + begin + collection = Hash.new + mappings = Hash.new + if f = open("keys-#{interface}.xml") then + while str = f.gets do + if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+value=\"(.*?)\".*?\>/o then + mappings[$1] = $2 + end + end + f.close + if f = open("cont-en.xml") then + while str = f.gets do + if str =~ /\<cd\:command\s+name=\"(.*?)\"\s+type=\"environment\".*?\>/o then + collection["start#{mappings[$1]}"] = '' + collection["stop#{mappings[$1]}"] = '' + elsif str =~ /\<cd\:command\s+name=\"(.*?)\".*?\>/o then + collection["#{mappings[$1]}"] = '' + end + end + f.close + case type + when 'jedit' then + if f = open("context-jedit-#{interface}.xml", 'w') then + f.puts("<?xml version='1.0'?>\n\n") + f.puts("<!DOCTYPE MODE SYSTEM 'xmode.dtd'>\n\n") + f.puts("<MODE>\n") + f.puts(" <RULES>\n") + f.puts(" <KEYWORDS>\n") + collection.keys.sort.each do |name| + f.puts(" <KEYWORD2>\\#{name}</KEYWORD2>\n") unless name.empty? + end + f.puts(" </KEYWORDS>\n") + f.puts(" </RULES>\n") + f.puts("</MODE>\n") + f.close + end + when 'bbedit' then + if f = open("context-bbedit-#{interface}.xml", 'w') then + f.puts("<?xml version='1.0'?>\n\n") + f.puts("<key>BBLMKeywordList</key>\n") + f.puts("<array>\n") + collection.keys.sort.each do |name| + f.puts(" <string>\\#{name}</string>\n") unless name.empty? + end + f.puts("</array>\n") + f.close + end + when 'scite' then + if f = open("cont-#{interface}-scite.properties", 'w') then + i = 0 + f.write("keywordclass.macros.context.#{interface}=") + collection.keys.sort.each do |name| + unless name.empty? then + if i==0 then + f.write("\\\n ") + i = 5 + else + i = i - 1 + end + f.write("#{name} ") + end + end + f.write("\n") + f.close + end + else # raw + collection.keys.sort.each do |name| + puts("\\#{name}\n") unless name.empty? + end + end + end + end + end + end + + end + +end + +class Commands + + include CommandBase + + public + + def translateinterface + + # since we know what kind of file we're dealing with, + # we do it quick and dirty instead of using rexml or + # xslt + + interfaces = @commandline.arguments + + if interfaces.empty? then + interfaces = ['cz','de','it','nl','ro'] + else + interfaces.delete('en') + end + + interfaces.flatten.each do |interface| + + variables, constants, strings, list, data = Hash.new, Hash.new, Hash.new, '', '' + + keyfile, intfile, outfile = "keys-#{interface}.xml", "cont-en.xml", "cont-#{interface}.xml" + + report("generating #{keyfile}") + + begin + one = "texexec --make --alone --all #{interface}" + two = "texexec --batch --silent --interface=#{interface} x-set-01" + if @commandline.option("force") then + system(one) + system(two) + elsif not system(two) then + system(one) + system(two) + end + rescue + end + + unless File.file?(keyfile) then + report("no #{keyfile} generated") + next + end + + report("loading #{keyfile}") + + begin + list = IO.read(keyfile) + rescue + list = empty + end + + if list.empty? then + report("error in loading #{keyfile}") + next + end + + list.i_load('cd:variable', variables) + list.i_load('cd:constant', constants) + list.i_load('cd:command' , strings) + # list.i_load('cd:element' , strings) + + report("loading #{intfile}") + + begin + data = IO.read(intfile) + rescue + data = empty + end + + if data.empty? then + report("error in loading #{intfile}") + next + end + + report("translating interface en to #{interface}") + + data.i_translate('cd:string' , 'value', strings) + data.i_translate('cd:variable' , 'value', variables) + data.i_translate('cd:parameter', 'name' , constants) + data.i_translate('cd:constant' , 'type' , variables) + data.i_translate('cd:variable' , 'type' , variables) + data.i_translate('cd:inherit' , 'name' , strings) + # data.i_translate('cd:command' , 'name' , strings) + + report("saving #{outfile}") + + begin + if f = File.open(outfile, 'w') then + f.write(data) + f.close + end + rescue + end + + end + + end + +end + +class Commands + + include CommandBase + + public + + def purgefiles(all=false) + + pattern = @commandline.arguments + purgeall = @commandline.option("all") || all + recurse = @commandline.option("recurse") + + $dontaskprefixes.push(Dir.glob("mpx-*")) + $dontaskprefixes.flatten! + $dontaskprefixes.sort! + + if purgeall then + $forsuresuffixes.push($texnonesuffixes) + $texnonesuffixes = [] + $forsuresuffixes.flatten! + end + + if ! pattern || pattern.empty? then + globbed = if recurse then "**/*.*" else "*.*" end + files = Dir.glob(globbed) + report("purging files : #{globbed}") + else + pattern.each do |pat| + globbed = if recurse then "**/#{pat}-*.*" else "#{pat}-*.*" end + files = Dir.glob(globbed) + globbed = if recurse then "**/#{pat}.*" else "#{pat}.*" end + files.push(Dir.glob(globbed)) + end + report("purging files : #{pattern.join(' ')}") + end + files.flatten! + files.sort! + + $dontaskprefixes.each do |file| + removecontextfile(file) + end + $dontasksuffixes.each do |suffix| + files.each do |file| + removecontextfile(file) if file =~ /#{suffix}$/i + end + end + $forsuresuffixes.each do |suffix| + files.each do |file| + removecontextfile(file) if file =~ /\.#{suffix}$/i + end + end + files.each do |file| + if file =~ /(.*?)\.\d+$/o then + basename = $1 + if file =~ /mp(graph|run)/o || FileTest.file?("#{basename}.mp") then + removecontextfile($file) + end + end + end + $texnonesuffixes.each do |suffix| + files.each do |file| + if file =~ /(.*)\.#{suffix}$/i then + if FileTest.file?("#{$1}.tex") || FileTest.file?("#{$1}.xml") || FileTest.file?("#{$1}.fo") then + keepcontextfile(file) + else + strippedname = $1.gsub(/\-[a-z]$/io, '') + if FileTest.file?("#{strippedname}.tex") || FileTest.file?("#{strippedname}.xml") then + keepcontextfile("#{file} (potential result file)") + else + removecontextfile(file) + end + end + end + end + end + + files = Dir.glob("*.*") + $dontasksuffixes.each do |suffix| + files.each do |file| + removecontextfile(file) if file =~ /^#{suffix}$/i + end + end + + if $removedfiles || $keptfiles || $persistentfiles then + report("removed files : #{$removedfiles}") + report("kept files : #{$keptfiles}") + report("persistent files : #{$persistentfiles}") + report("reclaimed bytes : #{$reclaimedbytes}") + end + + end + + def purgeallfiles + purgefiles(true) # for old times sake + end + + private + + $removedfiles = 0 + $keptfiles = 0 + $persistentfiles = 0 + $reclaimedbytes = 0 + + $dontaskprefixes = [ + # "tex-form.tex", "tex-edit.tex", "tex-temp.tex", + "texexec.tex", "texexec.tui", "texexec.tuo", + "texexec.ps", "texexec.pdf", "texexec.dvi", + "cont-opt.tex", "cont-opt.bak" + ] + $dontasksuffixes = [ + "mp(graph|run)\\.mp", "mp(graph|run)\\.mpd", "mp(graph|run)\\.mpo", "mp(graph|run)\\.mpy", + "mp(graph|run)\\.\\d+", + "xlscript\\.xsl" + ] + $forsuresuffixes = [ + "tui", "tup", "ted", "tes", "top", + "log", "tmp", "run", "bck", "rlg", + "mpt", "mpx", "mpd", "mpo" + ] + $texonlysuffixes = [ + "dvi", "ps", "pdf" + ] + $texnonesuffixes = [ + "tuo", "tub", "top" + ] + + def removecontextfile (filename) + if filename && FileTest.file?(filename) then + begin + filesize = FileTest.size(filename) + File.delete(filename) + rescue + report("problematic : #{filename}") + else + if FileTest.file?(filename) then + $persistentfiles += 1 + report("persistent : #{filename}") + else + $removedfiles += 1 + $reclaimedbytes += filesize + report("removed : #{filename}") + end + end + end + end + + def keepcontextfile (filename) + if filename && FileTest.file?(filename) then + $keptfiles += 1 + report("not removed : #{filename}") + end + end + +end + +#D Documentation can be woven into a source file. The next +#D routine generates a new, \TEX\ ready file with the +#D documentation and source fragments properly tagged. The +#D documentation is included as comment: +#D +#D \starttypen +#D %D ...... some kind of documentation +#D %M ...... macros needed for documenation +#D %S B begin skipping +#D %S E end skipping +#D \stoptypen +#D +#D The most important tag is \type {%D}. Both \TEX\ and \METAPOST\ +#D files use \type{%} as a comment chacacter, while \PERL, \RUBY\ +#D and alike use \type{#}. Therefore \type{#D} is also handled. +#D +#D The generated file gets the suffix \type{ted} and is +#D structured as: +#D +#D \starttypen +#D \startmodule[type=suffix] +#D \startdocumentation +#D \stopdocumentation +#D \startdefinition +#D \stopdefinition +#D \stopmodule +#D \stoptypen +#D +#D Macro definitions specific to the documentation are not +#D surrounded by start||stop commands. The suffix specifaction +#D can be overruled at runtime, but defaults to the file +#D extension. This specification can be used for language +#D depended verbatim typesetting. + +class Commands + + include CommandBase + + public + + def documentation + files = @commandline.arguments + processtype = @commandline.option("type") + files.each do |fullname| + if fullname =~ /(.*)\.(.+?)$/o then + filename, filesuffix = $1, $2 + else + filename, filesuffix = fullname, 'tex' + end + filesuffix = 'tex' if filesuffix.empty? + fullname, resultname = "#{filename}.#{filesuffix}", "#{filename}.ted" + if ! FileTest.file?(fullname) + report("empty input file #{fullname}") + elsif ! tex = File.open(fullname) + report("invalid input file #{fullname}") + elsif ! ted = File.open(resultname,'w') then + report("unable to openresult file #{resultname}") + else + report("input file : #{fullname}") + report("output file : #{resultname}") + nofdocuments, nofdefinitions, nofskips = 0, 0, 0 + skiplevel, indocument, indefinition, skippingbang = 0, false, false, false + if processtype.empty? then + filetype = filesuffix.downcase + else + filetype = processtype.downcase + end + report("filetype : #{filetype}") + # we need to signal to texexec what interface to use + firstline = tex.gets + if firstline =~ /^\%.*interface\=/ then + ted.puts(firstline) + else + tex.rewind # seek(0) + end + ted.puts("\\startmodule[type=#{filetype}]\n") + while str = tex.gets do + if skippingbang then + skippingbang = false + else + str.chomp! + str.sub!(/\s*$/o, '') + case str + when /^[%\#]D/io then + if skiplevel == 0 then + someline = if str.length < 3 then "" else str[3,str.length-1] end + if indocument then + ted.puts("#{someline}\n") + else + if indefinition then + ted.puts("\\stopdefinition\n") + indefinition = false + end + unless indocument then + ted.puts("\n\\startdocumentation\n") + end + ted.puts("#{someline}\n") + indocument = true + nofdocuments += 1 + end + end + when /^[%\#]M/io then + if skiplevel == 0 then + someline = if str.length < 3 then "" else str[3,str.length-1] end + ted.puts("#{someline}\n") + end + when /^[%\%]S B/io then + skiplevel += 1 + nofskips += 1 + when /^[%\%]S E/io then + skiplevel -= 1 + when /^[%\#]/io then + #nothing + when /^eval \'\(exit \$\?0\)\' \&\& eval \'exec perl/o then + skippingbang = true + else + if skiplevel == 0 then + inlocaldocument = indocument + someline = str + if indocument then + ted.puts("\\stopdocumentation\n") + indocument = false + end + if someline.empty? && indefinition then + ted.puts("\\stopdefinition\n") + indefinition = false + elsif indefinition then + ted.puts("#{someline}\n") + elsif ! someline.empty? then + ted.puts("\n\\startdefinition\n") + indefinition = true + unless inlocaldocument then + nofdefinitions += 1 + ted.puts("#{someline}\n") + end + end + end + end + end + end + if indocument then + ted.puts("\\stopdocumentation\n") + end + if indefinition then + ted.puts("\\stopdefinition\n") + end + ted.puts("\\stopmodule\n") + ted.close + + if nofdocuments == 0 && nofdefinitions == 0 then + begin + File.delete(resultname) + rescue + end + end + report("documentation sections : #{nofdocuments}") + report("definition sections : #{nofdefinitions}") + report("skipped sections : #{nofskips}") + end + end + end + +end + +#D This feature was needed when \PDFTEX\ could not yet access page object +#D numbers (versions prior to 1.11). + +class Commands + + include CommandBase + + public + + def filterpages # temp feature / no reporting + filename = @commandline.argument('first') + filename.sub!(/\.([a-z]+?)$/io,'') + pdffile = "#{filename}.pdf" + tuofile = "#{filename}.tuo" + if FileTest.file?(pdffile) then + begin + prevline, n = '', 0 + if (pdf = File.open(pdffile)) && (tuo = File.open(tuofile,'a')) then + report('filtering page object numbers') + pdf.binmode + while line = pdf.gets do + line.chomp + # typical pdftex search + if (line =~ /\/Type \/Page/o) && (prevline =~ /^(\d+)\s+0\s+obj/o) then + p = $1 + n += 1 + tuo.puts("\\objectreference{PDFP}{#{n}}{#{p}}{#{n}}\n") + else + prevline = line + end + end + end + pdf.close + tuo.close + report("number of pages : #{n}") + rescue + report("fatal error in filtering pages") + end + end + end + +end + +# This script is used to generate hyphenation pattern files +# that suit ConTeXt. One reason for independent files is that +# over the years too many uncommunicated changes took place +# as well that inconsistency in content, naming, and location +# in the texmf tree takes more time than I'm willing to spend +# on it. Pattern files are normally shipped for LaTeX (and +# partially plain). A side effect of independent files is that +# we can make them encoding independent. +# +# Maybe I'll make this hyptools.tex + +class Language + + include CommandBase + + def initialize(commandline=nil, language='en', filenames=nil, encoding='ec') + @commandline= commandline + @language = language + @filenames = filenames + @remapping = Array.new + @unicode = Hash.new + @encoding = encoding + @data = '' + @read = '' + preload_accents() + preload_unicode() if @commandline.option('utf8') + case @encoding.downcase + when 't1', 'ec', 'cork' then preload_vector('ec') + when 'y', 'texnansi' then preload_vector('texnansi') + end + end + + def report(str) + if @commandline then + @commandline.report(str) + else + puts("#{str}\n") + end + end + + def remap(from, to) + @remapping.push([from,to]) + end + + def load(filenames=@filenames) + begin + if filenames then + @filenames.each do |fileset| + [fileset].flatten.each do |filename| + begin + if fname = located(filename) then + data = IO.read(fname) + @data += data.gsub(/\%.*$/, '') + data.gsub!(/(\\patterns|\\hyphenation)\s*\{.*/mo) do '' end + @read += "\n% preamble of file #{fname}\n\n#{data}\n" + report("file #{fname} is loaded") + break # next fileset + end + rescue + report("file #{filename} is not readable") + end + end + end + end + rescue + end + end + + def valid? + ! @data.empty? + end + + def convert + if @data then + n = 0 + @remapping.each do |k| + @data.gsub!(k[0]) do + # report("#{k[0]} => #{k[1]}") + n += 1 + k[1] + end + end + report("#{n} changes in patterns and exceptions") + if @commandline.option('utf8') then + n = 0 + @data.gsub!(/\[(.*?)\]/o) do + n += 1 + @unicode[$1] || $1 + end + report("#{n} unicode utf8 entries") + end + return true + else + return false + end + end + + def comment(str) + str.gsub!(/^\n/o, '') + str.chomp! + if @commandline.option('xml') then + "<!-- #{str.strip} -->\n\n" + else + "% #{str.strip}\n\n" + end + end + + def content(tag, str) + lst = str.split(/\s+/) + lst.collect! do |l| + l.strip + end + if lst.length>0 then + lst = "\n#{lst.join("\n")}\n" + else + lst = "" + end + if @commandline.option('xml') then + lst.gsub!(/\[(.*?)\]/o) do + "&#{$1};" + end + "<#{tag}>#{lst}</#{tag}>\n\n" + else + "\\#{tag} \{#{lst}\}\n\n" + end + end + + def banner + if @commandline.option('xml') then + "<?xml version='1.0' standalone='yes' ?>\n\n" + end + end + + def triggerunicode + if @commandline.option('utf8') then + "% xetex needs utf8 encoded patterns and for patterns\n" + + "% coded as such we need to enable this regime when\n" + + "% not in xetex; this code will be moved into context\n" + + "% as soon as we've spread the generic patterns\n" + + "\n" + + "\\ifx\\XeTeXversion\\undefined \\else\n" + + " \\ifx\\enableregime\\undefined \\else\n" + + " \\enableregime[utf]\n" + + " \\fi\n" + + "\\fi\n" + + "\n" + end + end + + def save + xml = @commandline.option("xml") + + patname = "lang-#{@language}.pat" + hypname = "lang-#{@language}.hyp" + rmename = "lang-#{@language}.rme" + logname = "lang-#{@language}.log" + + desname = "lang-all.xml" + + @data.gsub!(/\\[nc]\{(.+?)\}/) do $1 end + @data.gsub!(/\{\}/) do '' end + @data.gsub!(/\n+/mo) do "\n" end + @read.gsub!(/\n+/mo) do "\n" end + + description = '' + commentfile = rmename.dup + + begin + desfile = `kpsewhich -progname=context #{desname}`.chomp + if f = File.new(desfile) then + if doc = REXML::Document.new(f) then + if e = REXML::XPath.first(doc.root,"/descriptions/description[@language='#{@language}']") then + description = e.to_s + end + end + end + rescue + description = '' + else + unless description.empty? then + commentfile = desname.dup + str = "<!-- copied from lang-all.xml\n\n" + str << "<?xml version='1.0' standalone='yes'?>\n\n" + str << description.chomp + str << "\n\nend of copy -->\n" + str.gsub!(/^/io, "% ") unless @commandline.option('xml') + description = comment("begin description data") + description << str + "\n" + description << comment("end description data") + report("description found for language #{@language}") + end + end + + begin + if description.empty? || @commandline.option('log') then + if f = File.open(logname,'w') then + report("saving #{@remapping.length} remap patterns in #{logname}") + @remapping.each do |m| + f.puts("#{m[0].inspect} => #{m[1]}\n") + end + f.close + end + else + File.delete(logname) if FileTest.file?(logname) + end + rescue + end + + begin + if description.empty? || @commandline.option('log') then + if f = File.open(rmename,'w') then + data = @read.dup + data.gsub!(/(\s*\n\s*)+/mo, "\n") + f << comment("comment copied from public hyphenation files}") + f << comment("source of data: #{@filenames.join(' ')}") + f << comment("begin original comment") + f << "#{data}\n" + f << comment("end original comment") + f.close + report("comment saved in file #{rmename}") + else + report("file #{rmename} is not writable") + end + else + File.delete(rmename) if FileTest.file?(rmename) + end + rescue + end + + begin + if f = File.open(patname,'w') then + data = '' + @data.scan(/\\patterns\s*\{\s*(.*?)\s*\}/m) do + report("merging patterns") + data += $1 + "\n" + end + data.gsub!(/(\s*\n\s*)+/mo, "\n") + + f << banner + f << comment("context pattern file, see #{commentfile} for original comment") + f << comment("source of data: #{@filenames.join(' ')}") + f << description + f << comment("begin pattern data") + f << triggerunicode + f << content('patterns', data) + f << comment("end pattern data") + f.close + report("patterns saved in file #{patname}") + else + report("file #{patname} is not writable") + end + rescue + report("problems with file #{patname}") + end + + begin + if f = File.open(hypname,'w') then + data = '' + @data.scan(/\\hyphenation\s*\{\s*(.*?)\s*\}/m) do + report("merging exceptions") + data += $1 + "\n" + end + data.gsub!(/(\s*\n\s*)+/mo, "\n") + f << banner + f << comment("context hyphenation file, see #{commentfile} for original comment") + f << comment("source of data: #{@filenames.join(' ')}") + f << description + f << comment("begin hyphenation data") + f << triggerunicode + f << content('hyphenation', data) + f << comment("end hyphenation data") + f.close + report("exceptions saved in file #{hypname}") + else + report("file #{hypname} is not writable") + end + rescue + report("problems with file #{hypname}") + end + end + + def process + load + if valid? then + convert + save + else + report("aborted due to missing files") + end + end + + def Language::generate(commandline, language='', filenames='', encoding='ec') + if ! language.empty? && ! filenames.empty? then + commandline.report("processing language #{language}") + commandline.report("") + language = Language.new(commandline,language,filenames,encoding) + language.load + language.convert + language.save + commandline.report("") + end + end + + private + + def located(filename) + begin + fname = `kpsewhich -progname=context #{filename}`.chomp + if FileTest.file?(fname) then + report("using file #{fname}") + return fname + else + report("file #{filename} is not present") + return nil + end + rescue + report("file #{filename} cannot be located using kpsewhich") + return nil + end + end + + def preload_accents + + begin + if filename = located("enco-acc.tex") then + if data = IO.read(filename) then + report("preloading accent conversions") + data.scan(/\\defineaccent\s*\\*(.+?)\s*\{*(.+?)\}*\s*\{\\(.+?)\}/o) do + one, two, three = $1, $2, $3 + one.gsub!(/[\`\~\!\^\*\_\-\+\=\:\;\"\'\,\.\?]/o) do + "\\#{one}" + end + remap(/\\#{one} #{two}/, "[#{three}]") + remap(/\\#{one}#{two}/, "[#{three}]") unless one =~ /[a-zA-Z]/o + remap(/\\#{one}\{#{two}\}/, "[#{three}]") + end + end + end + rescue + end + + end + + def preload_unicode + + # \definecharacter Agrave {\uchar0{192}} + + begin + if filename = located("enco-uc.tex") then + if data = IO.read(filename) then + report("preloading unicode conversions") + data.scan(/\\definecharacter\s*(.+?)\s*\{\\uchar\{*(\d+)\}*\s*\{(\d+)\}/o) do + one, two, three = $1, $2.to_i, $3.to_i + @unicode[one] = [(two*256 + three)].pack("U") + end + end + end + rescue + report("error in loading unicode mapping (#{$!})") + end + + end + + def preload_vector(encoding='') + + # funny polish + + case @language + when 'pl' then + remap(/\/a/, "[aogonek]") ; remap(/\/A/, "[Aogonek]") + remap(/\/c/, "[cacute]") ; remap(/\/C/, "[Cacute]") + remap(/\/e/, "[eogonek]") ; remap(/\/E/, "[Eogonek]") + remap(/\/l/, "[lstroke]") ; remap(/\/L/, "[Lstroke]") + remap(/\/n/, "[nacute]") ; remap(/\/N/, "[Nacute]") + remap(/\/o/, "[oacute]") ; remap(/\/O/, "[Oacute]") + remap(/\/s/, "[sacute]") ; remap(/\/S/, "[Sacute]") + remap(/\/x/, "[zacute]") ; remap(/\/X/, "[Zacute]") + remap(/\/z/, "[zdotaccent]") ; remap(/\/Z/, "[Zdotaccent]") + when 'sl' then + remap(/\"c/,"[ccaron]") ; remap(/\"C/,"[Ccaron]") + remap(/\"s/,"[scaron]") ; remap(/\"S/,"[Scaron]") + remap(/\"z/,"[zcaron]") ; remap(/\"Z/,"[Zcaron]") + when 'da' then + remap(/X/, "[aeligature]") + remap(/Y/, "[ostroke]") + remap(/Z/, "[aring]") + when 'ca' then + remap(/\\c\{.*?\}/, "") + when 'de', 'deo' then + remap(/\\c\{.*?\}/, "") + remap(/\\n\{\}/, "") + remap(/\\3/, "[ssharp]") + remap(/\\9/, "[ssharp]") + remap(/\"a/, "[adiaeresis]") + remap(/\"o/, "[odiaeresis]") + remap(/\"u/, "[udiaeresis]") + when 'fr' then + remap(/\\ae/, "[adiaeresis]") + remap(/\\oe/, "[odiaeresis]") + when 'la' then + # \lccode`'=`' somewhere else, todo + remap(/\\c\{.*?\}/, "") + remap(/\\a\s*/, "[aeligature]") + remap(/\\o\s*/, "[oeligature]") + else + end + + if ! encoding.empty? then + begin + filename = `kpsewhich -progname=context enco-#{encoding}.tex` + if data = IO.read(filename.chomp) then + report("preloading #{encoding} character mappings") + data.scan(/\\definecharacter\s*([a-zA-Z]+)\s*(\d+)\s*/o) do + name, number = $1, $2 + remap(/\^\^#{sprintf("%02x",number)}/, "[#{name}]") + end + end + rescue + end + end + + end + +end + +class Commands + + include CommandBase + + public + + @@languagedata = Hash.new + + def patternfiles + language = @commandline.argument('first') + if (language == 'all') || language.empty? then + languages = @@languagedata.keys.sort + elsif @@languagedata.key?(language) then + languages = [language] + else + languages = [] + end + languages.each do |language| + encoding = @@languagedata[language][0] || '' + files = @@languagedata[language][1] || [] + Language::generate(self,language,files,encoding) + end + end + + private + + # todo: filter the fallback list from context + + # The first entry in the array is the encoding which will be used + # when interpreting th eraw patterns. The second entry is a list of + # filesets (string|aray), each first match of a set is taken. + + @@languagedata['ba' ] = [ 'ec' , ['bahyph.tex'] ] + @@languagedata['ca' ] = [ 'ec' , ['cahyph.tex'] ] + @@languagedata['cy' ] = [ 'ec' , ['cyhyph.tex'] ] + @@languagedata['cz' ] = [ 'ec' , ['czhyphen.tex','czhyphen.ex'] ] + @@languagedata['de' ] = [ 'ec' , ['dehyphn.tex'] ] + @@languagedata['deo'] = [ 'ec' , ['dehypht.tex'] ] + @@languagedata['da' ] = [ 'ec' , ['dkspecial.tex','dkcommon.tex'] ] + # elhyph.tex + @@languagedata['es' ] = [ 'ec' , ['eshyph.tex'] ] + @@languagedata['fi' ] = [ 'ec' , ['ethyph.tex'] ] + @@languagedata['fi' ] = [ 'ec' , ['fihyph.tex'] ] + @@languagedata['fr' ] = [ 'ec' , ['frhyph.tex'] ] + # ghyphen.readme ghyph31.readme grphyph + @@languagedata['hr' ] = [ 'ec' , ['hrhyph.tex'] ] + @@languagedata['hu' ] = [ 'ec' , ['huhyphn.tex'] ] + @@languagedata['en' ] = [ 'default' , [['ushyphmax.tex','ushyph.tex','hyphen.tex']] ] + # inhyph.tex + @@languagedata['is' ] = [ 'ec' , ['ishyph.tex'] ] + @@languagedata['it' ] = [ 'ec' , ['ithyph.tex'] ] + @@languagedata['la' ] = [ 'ec' , ['lahyph.tex'] ] + # mnhyph + @@languagedata['nl' ] = [ 'ec' , ['nehyph96.tex'] ] + @@languagedata['no' ] = [ 'ec' , ['nohyph.tex'] ] + # oldgrhyph.tex + @@languagedata['pl' ] = [ 'ec' , ['plhyph.tex'] ] + @@languagedata['pt' ] = [ 'ec' , ['pthyph.tex'] ] + @@languagedata['ro' ] = [ 'ec' , ['rohyph.tex'] ] + @@languagedata['sl' ] = [ 'ec' , ['sihyph.tex'] ] + @@languagedata['sk' ] = [ 'ec' , ['skhyphen.tex','skhyphen.ex'] ] + # sorhyph.tex / upper sorbian + # srhyphc.tex / cyrillic + @@languagedata['sv' ] = [ 'ec' , ['svhyph.tex'] ] + @@languagedata['tr' ] = [ 'ec' , ['tkhyph.tex'] ] + @@languagedata['uk' ] = [ 'default' , [['ukhyphen.tex','ukhyph.tex']] ] + +end + +class Commands + + include CommandBase + + def dpxmapfiles + + force = @commandline.option("force") + + texmfroot = @commandline.argument('first') + texmfroot = '.' if texmfroot.empty? + maproot = "#{texmfroot}/fonts/map/pdftex/context" + + if File.directory?(maproot) then + if files = Dir.glob("#{maproot}/*.map") and files.size > 0 then + files.each do |pdffile| + next if File.basename(pdffile) == 'pdftex.map' + pdffile = File.expand_path(pdffile) + dpxfile = File.expand_path(pdffile.sub(/pdftex/i,'dvipdfm')) + unless pdffile == dpxfile then + begin + if data = File.read(pdffile) then + report("< #{File.basename(pdffile)} - pdf(e)tex") + n = 0 + data = data.collect do |line| + if line =~ /^[\%\#]+/mo then + '' + else + encoding = if line =~ /([a-z0-9\-]+)\.enc/io then $1 else '' end + fontfile = if line =~ /([a-z0-9\-]+)\.(pfb|ttf)/io then $1 else nil end + metrics = if line =~ /^([a-z0-9\-]+)[\s\<]+/io then $1 else nil end + slant = if line =~ /\"([\d\.]+)\s+SlantFont\"/io then "-s #{$1}" else '' end + if metrics && encoding && fontfile then + n += 1 + "#{metrics} #{encoding} #{fontfile} #{slant}" + else + '' + end + end + end + data.delete_if do |line| + line.gsub(/\s+/,'').empty? + end + begin + if force then + if n > 0 then + File.makedirs(File.dirname(dpxfile)) + if f = File.open(dpxfile,'w') then + report("> #{File.basename(dpxfile)} - dvipdfm(x) - #{n}") + f.puts(data) + f.close + else + report("? #{File.basename(dpxfile)} - dvipdfm(x)") + end + else + report("- #{File.basename(dpxfile)} - dvipdfm(x)") + begin File.delete(dpxname) ; rescue ; end + end + else + report(". #{File.basename(dpxfile)} - dvipdfm(x) - #{n}") + end + rescue + report("error in saving dvipdfm file") + end + else + report("error in loading pdftex file") + end + rescue + report("error in processing pdftex file") + end + end + end + if force then + begin + report("regenerating database for #{texmfroot}") + system("mktexlsr #{texmfroot}") + rescue + end + end + else + report("no mapfiles found in #{maproot}") + end + else + report("provide proper texmfroot") + end + + end + +end + +class Commands + + include CommandBase + + # usage : ctxtools --listentities entities.xml + # document: <!DOCTYPE something SYSTEM "entities.xml"> + + def flushentities(handle,entities,doctype=nil) # 'stylesheet' + tab = if doctype then "\t" else "" end + handle.puts("<!DOCTYPE #{doctype} [") if doctype + entities.keys.sort.each do |k| + handle.puts("#{tab}<!ENTITY #{k} \"\&\##{entities[k]};\">") + end + handle.puts("]>") if doctype + end + + def listentities + + # filename = `texmfstart tmftools.rb --progname=context enco-uc.tex`.chomp + filename = `kpsewhich --progname=context enco-uc.tex`.chomp + outputname = @commandline.argument('first') + + if filename and not filename.empty? and FileTest.file?(filename) then + entities = Hash.new + IO.readlines(filename).each do |line| + if line =~ /\\definecharacter\s+([a-zA-Z]+)\s+\{\\uchar\{*(\d+)\}*\{(\d+)\}\}/o then + name, low, high = $1, $2.to_i, $3.to_i + entities[name] = low*256 + high + end + end + if outputname and not outputname.empty? then + if f = File.open(outputname,'w') then + flushentities(f,entities) + f.close + else + flushentities($stdout,entities) + end + else + flushentities($stdout,entities) + end + end + + end + +end + + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registeraction('touchcontextfile', 'update context version') +commandline.registeraction('contextversion', 'report context version') + +commandline.registeraction('jeditinterface', 'generate jedit syntax files [--pipe]') +commandline.registeraction('bbeditinterface', 'generate bbedit syntax files [--pipe]') +commandline.registeraction('sciteinterface', 'generate scite syntax files [--pipe]') +commandline.registeraction('rawinterface', 'generate raw syntax files [--pipe]') + +commandline.registeraction('translateinterface', 'generate interface files (xml) [nl de ..]') +commandline.registeraction('purgefiles', 'remove temporary files [--all --recurse] [basename]') + +commandline.registeraction('documentation', 'generate documentation [--type=] [filename]') + +commandline.registeraction('filterpages') # no help, hidden temporary feature +commandline.registeraction('purgeallfiles') # no help, compatibility feature + +commandline.registeraction('patternfiles', 'generate pattern files [--all --xml --utf8] [languagecode]') + +commandline.registeraction('dpxmapfiles', 'convert pdftex mapfiles to dvipdfmx [--force] [texmfroot]') +commandline.registeraction('listentities', 'create doctype entity definition from enco-uc.tex') + +commandline.registervalue('type','') + +commandline.registerflag('recurse') +commandline.registerflag('force') +commandline.registerflag('pipe') +commandline.registerflag('all') +commandline.registerflag('xml') +commandline.registerflag('log') +commandline.registerflag('utf8') + +# general + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'help') diff --git a/Master/texmf-dist/scripts/context/ruby/fcd_start.rb b/Master/texmf-dist/scripts/context/ruby/fcd_start.rb new file mode 100644 index 00000000000..8ac48f79e8c --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/fcd_start.rb @@ -0,0 +1,453 @@ +# Hans Hagen / PRAGMA ADE / 2005 / www.pragma-ade.com +# +# Fast Change Dir +# +# This is a kind of variant of the good old ncd +# program. This script uses the same indirect cmd +# trick as Erwin Waterlander's wcd program. +# +# === windows: fcd.cmd === +# +# @echo off +# ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9 +# if exist "%HOME%/fcd_stage.cmd" call %HOME%/fcd_stage.cmd +# +# === linux: fcd (fcd.sh) === +# +# !/usr/bin/env sh +# ruby -S fcd_start.rb $1 $2 $3 $4 $5 $6 $7 $8 $9 +# if test -f "$HOME/fcd_stage.sh" ; then +# . $HOME/fcd_stage.sh ; +# fi; +# +# === +# +# On linux, one should source the file: ". fcd args" in order +# to make the chdir persistent. +# +# You can create a stub with: +# +# ruby fcd_start.rb --stub --verbose +# +# usage: +# +# fcd --make t:\ +# fcd --add f:\project +# fcd [--find] whatever +# fcd [--find] whatever c (c being a list entry) +# fcd [--find] whatever . (last choice with this pattern) +# fcd --list + +require 'rbconfig' + +class FastCD + + @@rootpath = nil + + ['HOME','TEMP','TMP','TMPDIR'].each do |key| + if ENV[key] then + if FileTest.directory?(ENV[key]) then + @@rootpath = ENV[key] + break + end + end + end + + exit unless @@rootpath + + @@mswindows = Config::CONFIG['host_os'] =~ /mswin/ + @@maxlength = 26 + + require 'Win32API' if @@mswindows + + if @@mswindows then + @@stubcode = [ + '@echo off', + '', + 'if not exist "%HOME%" goto temp', + '', + ':home', + '', + 'ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9', + '', + 'if exist "%HOME%\fcd_stage.cmd" call %HOME%\fcd_stage.cmd', + 'goto end', + '', + ':temp', + '', + 'ruby -S fcd_start.rb %1 %2 %3 %4 %5 %6 %7 %8 %9', + '', + 'if exist "%TEMP%\fcd_stage.cmd" call %TEMP%\fcd_stage.cmd', + 'goto end', + '', + ':end' + ].join("\n") + else + @@stubcode = [ + '#!/usr/bin/env sh', + '', + 'ruby -S fcd_start.rb $1 $2 $3 $4 $5 $6 $7 $8 $9', + '', + 'if test -f "$HOME/fcd_stage.sh" ; then', + ' . $HOME/fcd_stage.sh ;', + 'fi;' + ].join("\n") + end + + @@selfpath = File.dirname($0) + @@datafile = File.join(@@rootpath,'fcd_state.dat') + @@histfile = File.join(@@rootpath,'fcd_state.his') + @@cdirfile = File.join(@@rootpath,if @@mswindows then 'fcd_stage.cmd' else 'fcd_stage.sh' end) + @@stubfile = File.join(@@selfpath,if @@mswindows then 'fcd.cmd' else 'fcd' end) + + def initialize(verbose=false) + @list = Array.new + @hist = Hash.new + @result = Array.new + @pattern = '' + @result = '' + @verbose = verbose + if f = File.open(@@cdirfile,'w') then + f << "#{if @@mswindows then 'rem' else '#' end} no dir to change to" + f.close + else + report("unable to create stub #{@@cdirfile}") + end + end + + def filename(name) + File.join(@@root,name) + end + + def report(str,verbose=@verbose) + puts(">> #{str}") if verbose + end + + def flush(str,verbose=@verbose) + print(str) if verbose + end + + def clear + if FileTest.file?(@@histfile) + begin + File.delete(@@histfile) + rescue + report("error in deleting history file '#{@histfile}'") + else + report("history file '#{@histfile}' is deleted") + end + else + report("no history file '#{@histfile}'") + end + end + + def scan(dir='.') + begin + [dir].flatten.sort.uniq.each do |dir| + begin + Dir.chdir(dir) + report("scanning '#{dir}'") + # flush(">> ") + Dir.glob("**/*").each do |d| + if FileTest.directory?(d) then + @list << File.expand_path(d) + # flush(".") + end + end + # flush("\n") + @list = @list.sort.uniq + report("#{@list.size} entries found") + rescue + report("unknown directory '#{dir}'") + end + end + rescue + report("invalid dir specification ") + end + end + + def save + begin + if f = File.open(@@datafile,'w') then + @list.each do |l| + f.puts(l) + end + f.close + report("#{@list.size} status bytes saved in #{@@datafile}") + else + report("unable to save status in #{@@datafile}") + end + rescue + report("error in saving status in #{@@datafile}") + end + end + + def remember + if @hist[@pattern] == @result then + # no need to save result + else + begin + if f = File.open(@@histfile,'w') then + @hist[@pattern] = @result + @hist.keys.each do |k| + f.puts("#{k} #{@hist[k]}") + end + f.close + report("#{@hist.size} history entries saved in #{@@histfile}") + else + report("unable to save history in #{@@histfile}") + end + rescue + report("error in saving history in #{@@histfile}") + end + end + end + + def load + begin + @list = IO.read(@@datafile).split("\n") + report("#{@list.length} status bytes loaded from #{@@datafile}") + rescue + report("error in loading status from #{@@datafile}") + end + begin + IO.readlines(@@histfile).each do |line| + if line =~ /^(.*?)\s+(.*)$/i then + @hist[$1] = $2 + end + end + report("#{@hist.length} history entries loaded from #{@@histfile}") + rescue + report("error in loading history from #{@@histfile}") + end + end + + def show + begin + puts("directories:") + puts("\n") + if @list.length > 0 then + @list.each do |l| + puts(l) + end + else + puts("no entries") + end + puts("\n") + puts("history:") + puts("\n") + if @hist.length > 0 then + @hist.keys.sort.each do |h| + puts("#{h} >> #{@hist[h]}") + end + else + puts("no entries") + end + rescue + end + end + + def find(pattern=nil) + begin + if pattern = [pattern].flatten.first then + if pattern.length > 0 and @pattern = pattern then + @result = @list.grep(/\/#{@pattern}$/i) + if @result.length == 0 then + @result = @list.grep(/\/#{@pattern}[^\/]*$/i) + end + end + end + rescue + end + end + + def chdir(dir) + begin + if dir then + if f = File.open(@@cdirfile,'w') then + if @@mswindows then + f.puts("cd /d #{dir.gsub('/','\\')}") + else + f.puts("cd #{dir.gsub("\\",'/')}") + end + end + @result = dir + report("changing to #{dir}",true) + else + report("not changing dir") + end + rescue + end + end + + def choose(args=[]) + unless @pattern.empty? then + begin + case @result.size + when 0 then + report("dir '#{@pattern}' not found",true) + when 1 then + chdir(@result[0]) + else + list = @result.dup + begin + if answer = args[1] then # assignment & test + if answer == '.' and @hist.key?(@pattern) then + if FileTest.directory?(@hist[@pattern]) then + print("last choice ") + chdir(@hist[@pattern]) + return + end + else + index = answer[0] - ?a + if dir = list[index] then + chdir(dir) + return + end + end + end + rescue + end + loop do + print("\n") + list.each_index do |i| + if i < @@maxlength then + puts("#{(i+?a).chr} #{list[i]}") + else + puts("\n there are #{list.length-@@maxlength} entries more") + break + end + end + print("\n>> ") + if answer = wait then + if answer >= ?a and answer <= ?z then + index = answer - ?a + if dir = list[index] then + print("#{answer.chr} ") + chdir(dir) + elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then + print("last choice ") + chdir(@hist[@pattern]) + else + print("quit\n") + end + break + elsif list.length >= @@maxlength then + @@maxlength.times do |i| list.shift end + print("next set") + print("\n") + elsif @hist.key?(@pattern) and FileTest.directory?(@hist[@pattern]) then + print("last choice ") + chdir(@hist[@pattern]) + break + else + print("quit\n") + break + end + end + end + end + rescue + # report($!) + end + end + end + + def wait + begin + $stdout.flush + return getc + rescue + return nil + end + end + + def getc + begin + if @@mswindows then + ch = Win32API.new('crtdll','_getch',[],'L').call + else + system('stty raw -echo') + ch = $stdin.getc + system('stty -raw echo') + end + rescue + ch = nil + end + return ch + end + + def check + unless FileTest.file?(@@stubfile) then + report("creating stub #{@@stubfile}") + begin + if f = File.open(@@stubfile,'w') then + f.puts(@@stubcode) + f.close + end + rescue + report("unable to create stub #{@@stubfile}") + else + unless @mswindows then + begin + File.chmod(0755,@@stubfile) + rescue + report("unable to change protections on #{@@stubfile}") + end + end + end + else + report("stub #{@@stubfile} already present") + end + end + +end + +verbose, action, args = false, :find, Array.new + +usage = "fcd [--make|add|show|find] [--verbose] [pattern]" + +ARGV.each do |a| + case a + when '-v', '--verbose' then verbose = true + when '-m', '--make' then action = :make + when '-c', '--clear' then action = :clear + when '-a', '--add' then action = :add + when '-s', '--show' then action = :show + when '-l', '--list' then action = :show + when '-f', '--find' then action = :find + when '--stub' then action = :stub + when '-h', '--help' then puts "usage: #{usage}" ; exit + when /^\-\-.*/ then puts "unknown switch: #{a}" + "\n" + "usage: #{usage}" ; exit + else args << a + end +end + +$stdout.sync = true + +fcd = FastCD.new(verbose) + +fcd.report("Fast Change Dir / version 1.0") + +case action + when :make then + fcd.clear + fcd.scan(args) + fcd.save + when :clear then + fcd.clear + when :add then + fcd.load + fcd.scan(args) + fcd.save + when :show then + fcd.load + fcd.show + when :find then + fcd.load + fcd.find(args) + fcd.choose(args) + fcd.remember + when :stub + fcd.check +end diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb new file mode 100644 index 00000000000..807cad10ccd --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/graphics/gs.rb @@ -0,0 +1,641 @@ +# module : graphics/gs +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/variables','../variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end +# ['base/system', '../system', 'system' ].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/variables' +require 'base/system' + +class GhostScript + + include Variables + + @@pstopdfoptions = [ + 'AntiAliasColorImages', + 'AntiAliasGrayImages', + 'AntiAliasMonoImages', + 'ASCII85EncodePages', + 'AutoFilterColorImages', + 'AutoFilterGrayImages', + 'AutoPositionEPSFiles', + 'AutoRotatePages', + 'Binding', + 'ColorConversionStrategy', + 'ColorImageDepth', + 'ColorImageDownsampleThreshold', + 'ColorImageDownsampleType', + 'ColorImageFilter', + 'ColorImageResolution', + 'CompatibilityLevel', + 'CompressPages', + #'ConvertCMYKImagesToRGB', # buggy + #'ConvertImagesToIndexed', # buggy + 'CreateJobTicket', + 'DetectBlends', + 'DoThumbnails', + 'DownsampleColorImages', + 'DownsampleGrayImages', + 'DownsampleMonoImages', + 'EmbedAllFonts', + 'EncodeColorImages', + 'EncodeGrayImages', + 'EncodeMonoImages', + 'EndPage', + 'FirstPage', + 'GrayImageDepth', + 'GrayImageDownsampleThreshold', + 'GrayImageDownsampleType', + 'GrayImageFilter', + 'GrayImageResolution', + 'MaxSubsetPct', + 'MonoImageDepth', + 'MonoImageDownsampleThreshold', + 'MonoImageDownsampleType', + 'MonoImageFilter', + 'MonoImageResolution', + 'Optimize', + 'ParseDCSComments', + 'ParseDCSCommentsForDocInfo', + 'PreserveCopyPage', + 'PreserveEPSInfo', + 'PreserveHalftoneInfo', + 'PreserveOPIComments', + 'PreserveOverprintSettings', + 'SubsetFonts', + 'UseFlateCompression' + ] + + @@methods = Hash.new + + @@methods['raw'] = '1' + @@methods['bound'] = '2' + @@methods['bounded'] = '2' + @@methods['crop'] = '3' + @@methods['cropped'] = '3' + @@methods['down'] = '4' + @@methods['downsample'] = '4' + @@methods['downsampled'] = '4' + @@methods['simplify'] = '5' + @@methods['simplified'] = '5' + + @@tempfile = 'gstemp' + @@pstempfile = @@tempfile + '.ps' + @@pdftempfile = @@tempfile + '.pdf' + + @@bboxspec = '\s*([\-\d\.]+)' + '\s+([\-\d\.]+)'*3 + + def initialize(logger=nil) + + unless logger then + puts('gs class needs a logger') + exit + end + + @variables = Hash.new + @psoptions = Hash.new + @logger = logger + + setvariable('profile', 'gsprofile.ini') + setvariable('pipe', true) + setvariable('method', 2) + setvariable('force', false) + setvariable('colormodel', 'cmyk') + setvariable('inputfile', '') + setvariable('outputfile', '') + + @@pstopdfoptions.each do |key| + @psoptions[key] = '' + end + + reset + + end + + def reset + @llx = @lly = @ulx = @uly = 0 + @oldbbox = [@llx,@lly,@urx,@ury] + @width = @height = @xoffset = @yoffset = @offset = 0 + @rs = Tool.default_line_separator + end + + def supported?(filename) + psfile?(filename) || pdffile?(filename) + end + + def psfile?(filename) + filename =~ /\.(eps|epsf|ps|ai\d*)$/io + end + + def pdffile?(filename) + filename =~ /\.(pdf)$/io + end + + def setpsoption(key,value) + @psoptions[key] = value unless value.empty? + end + + def setdimensions (llx,lly,urx,ury) + @oldbbox = [llx,lly,urx,ury] + @llx, @lly = llx.to_f-@offset, lly.to_f-@offset + @urx, @ury = urx.to_f+@offset, ury.to_f+@offset + @width, @height = @urx - @llx, @ury - @lly + @xoffset, @yoffset = 0 - @llx, 0 - @lly + end + + def setoffset (offset=0) + @offset = offset.to_f + setdimensions(@llx,@lly,@urx,@ury) if dimensions? + end + + def resetdimensions + setdimensions(0,0,0,0) + end + + def dimensions? + (@width>0) && (@height>0) + end + + def convert + + inpfile = getvariable('inputfile') + + if inpfile.empty? then + report('no inputfile specified') + return false + end + + unless FileTest.file?(inpfile) then + report("unknown input file #{inpfile}") + return false + end + + outfile = getvariable('outputfile') + + if outfile.empty? then + outfile = inpfile + outfile = outfile.sub(/^.*[\\\/]/,'') + end + + outfile = outfile.sub(/\.(pdf|eps|ps|ai)/i, "") + resultfile = outfile + '.pdf' + setvariable('outputfile', resultfile) + + # flags + + saveprofile(getvariable('profile')) + + begin + gsmethod = method(getvariable('method')).to_i + report("conversion method #{gsmethod}") + rescue + gsmethod = 1 + report("fallback conversion method #{gsmethod}") + end + + debug('piping data') if getvariable('pipe') + + ok = false + begin + case gsmethod + when 0, 1 then ok = convertasis(inpfile,resultfile) + when 2 then ok = convertbounded(inpfile,resultfile) + when 3 then ok = convertcropped(inpfile,resultfile) + when 4 then ok = downsample(inpfile,resultfile,'screen') + when 5 then ok = downsample(inpfile,resultfile,'prepress') + else report("invalid conversion method #{gsmethod}") + end + rescue + report("job aborted due to some error #{$!}") + begin + File.delete(resultfile) if test(?e,resultfile) + rescue + report("unable to delete faulty #{resultfile}") + end + ok = false + ensure + deleteprofile(getvariable('profile')) + File.delete(@@pstempfile) if test(?e,@@pstempfile) + File.delete(@@pdftempfile) if test(?e,@@pdftempfile) + end + return ok + end + + # private + + def method (str) + if @@methods.key?(str) then + @@methods[str] + else + str + end + end + + def pdfmethod? (str) + case method(str).to_i + when 4, 5 then return true + end + return false + end + + def pdfprefix (str) + case method(str).to_i + when 4 then return 'lowres-' + when 5 then return 'normal-' + end + return '' + end + + def psmethod? (str) + ! pdfmethod?(str) + end + + def insertprofile (flags) + for key in flags.keys do + replacevariable("flag.#{key}", flags[key]) + end + end + + def deleteprofile (filename) + begin + File.delete(filename) if FileTest.file?(filename) + rescue + end + end + + def saveprofile (filename) + return if filename.empty? || ! (ini = open(filename,"w")) + @@pstopdfoptions.each do |k| + str = @psoptions[k] + # beware, booleans are translated, but so are yes/no which is dangerous + if str.class == String then + if ! str.empty? && (str != 'empty') then + str.sub!(/(.+)\-/io, '') + str = "/" + str unless str =~ /^(true|false|none|[\d\.\-\+]+)$/ + ini.puts("-d#{k}=#{str}\n") + end + end + end + ini.close + debug("gs profile #{filename} saved") + end + + def gsstream # private + if getvariable('pipe') then '-' else @@pstempfile end + end + + def gscolorswitch + case getvariable('colormodel') + when 'cmyk' then '-dProcessColorModel=/DeviceCMYK ' + when 'rgb' then '-dProcessColorModel=/DeviceRGB ' + when 'gray' then '-dProcessColorModel=/DeviceGRAY ' + else + '' + end + end + + def gsdefaults + defaults = '' + begin + defaults << '-dAutoRotatePages=/None ' if @psoptions['AutoRotatePages'].empty? + rescue + defaults << '-dAutoRotatePages=/None ' + end + return defaults + end + + def convertasis (inpfile, outfile) + + report("converting #{inpfile} as-is") + + @rs = Tool.line_separator(inpfile) + debug("platform mac") if @rs == "\r" + + arguments = '' + arguments << "\@gsprofile.ini " + arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH " + arguments << "#{gsdefaults} " + arguments << "#{gscolorswitch} " + arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit " + + debug("ghostscript: #{arguments}") + unless ok = System.run('ghostscript',arguments) then + begin + report("removing file #{outfile}") + File.delete(outfile) if FileTest.file?(outfile) + rescue + debug("file #{outfile} may be invalid") + end + end + return ok + + end + + def convertbounded (inpfile, outfile) + + report("converting #{inpfile} bounded") + + begin + return false if FileTest.file?(outfile) && (! File.delete(outfile)) + rescue + return false + end + + arguments = '' + arguments << "\@gsprofile.ini " + arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER" + arguments << "#{gscolorswitch} " + arguments << "#{gsdefaults} " + arguments << "-sOutputFile=#{outfile} #{gsstream} -c quit " + + debug("ghostscript: #{arguments}") + debug('opening input file') + + @rs = Tool.line_separator(inpfile) + debug("platform mac") if @rs == "\r" + + return false unless tmp = open(inpfile, 'rb') + + debug('opening pipe/file') + + if getvariable('pipe') then + + return false unless eps = IO.popen(System.command('ghostscript',arguments),'wb') + debug('piping data') + unless pipebounded(tmp,eps) then + debug('something went wrong in the pipe') + File.delete(outfile) if test(?e,outfile) + end + debug('closing pipe') + eps.close_write + + else + + return false unless eps = File.open(@@pstempfile, 'wb') + + debug('copying data') + + if pipebounded(tmp,eps) then + eps.close + debug('processing temp file') + begin + ok = System.run('ghostscript',arguments) + rescue + ok = false + # debug("fatal error: #{$!}") + ensure + end + else + eps.close + ok = false + end + + unless ok then + begin + report('no output file due to error') + File.delete(outfile) if test(?e,outfile) + rescue + # debug("fatal error: #{$!}") + debug('file',outfile,'may be invalid') + end + end + + debug('deleting temp file') + begin + File.delete(@@pstempfile) if test(?e,@@pstempfile) + rescue + end + + end + + tmp.close + return FileTest.file?(outfile) + + end + + # hm, strange, no execute here, todo ! ! ! + + def getdimensions (inpfile) + + # -dEPSFitPage and -dEPSCrop behave weird (don't work) + + arguments = "-sDEVICE=bbox -dSAFER -dNOPAUSE -dBATCH #{inpfile}" + + debug("ghostscript: #{arguments}") + + begin + bbox = System.run('ghostscript',arguments,true,true) + rescue + bbox = '' + end + + resetdimensions + + debug('bbox spec', bbox) + + if bbox =~ /(Exact|HiRes)BoundingBox:#{@@bboxspec}/mois then + debug("high res bbox #{$2} #{$3} #{$4} #{$5}") + setdimensions($2,$3,$4,$5) + elsif bbox =~ /BoundingBox:#{@@bboxspec}/mois + debug("low res bbox #{$1} #{$2} #{$3} #{$4}") + setdimensions($1,$2,$3,$4) + end + + return dimensions? + + end + + def convertcropped (inpfile, outfile) + + report("converting #{inpfile} cropped") + + convertbounded(inpfile, @@pdftempfile) + + return unless test(?e,@@pdftempfile) + + arguments = " --offset=#{@offset} #{@@pdftempfile} #{outfile}" + + unless ok = System.run('cropcrap',arguments) then + report('cropping failed') + begin + File.delete(outfile) + rescue + end + begin + File.move(@@pdftempfile,outfile) + rescue + File.copy(@@pdftempfile,outfile) + File.delete(@@pdftempfile) + end + end + + return ok + + end + + def pipebounded (eps, out) + + epsbbox, skip, buffer = false, false, '' + + while str = eps.gets(rs=@rs) do + if str =~ /^%!PS/oi then + debug("looks like a valid ps file") + break + elsif str =~ /%PDF\-\d+\.\d+/oi then + debug("looks like a pdf file, so let\'s quit") + return false + end + end + + debug('locating boundingbox') + + # why no BeginData check + + eps.rewind + + while str = eps.gets(rs=@rs) do + case str + when /^%%Page:/io then + break + when /^%%(Crop|HiResBounding|ExactBounding)Box:#{@@bboxspec}/moi then + debug('high res boundingbox found') + setdimensions($2,$3,$4,$5) + break + when /^%%BoundingBox:#{@@bboxspec}/moi then + debug('low res boundingbox found') + setdimensions($1,$2,$3,$4) + end + end + + debug('no boundingbox found') if @width == 0 + + eps.rewind + + while str = eps.gets(rs=@rs) do + if str.sub!(/^(.*)%!PS/moi, "%!PS") then + debug("removing pre banner data") + out.puts(str) + break + end + end + + while str = eps.gets(rs=@rs) do + if skip then + skip = false if str =~ /^%+(EndData|EndPhotoshop|BeginProlog).*$/o + out.puts(str) if $1 == "BeginProlog" + elsif str =~ /^%(BeginPhotoshop)\:\s*\d+.*$/o then + skip = true + elsif str =~ /^%%/mos then + if ! epsbbox && str =~ /^%%(Page:|EndProlog)/io then + out.puts(str) if $1 == "EndProlog" + debug('faking papersize') + out.puts("<< /PageSize [#{@width} #{@height}] >> setpagedevice\n") + out.puts("gsave #{@xoffset} #{@yoffset} translate\n") + epsbbox = true + elsif str =~ /^%%BeginBinary\:\s*\d+\s*$/o then + debug('copying binary data') + out.puts(str) + while str = eps.gets(rs=@rs) + if str =~ /^%%EndBinary\s*$/o then + out.puts(str) + else + out.write(str) + end + end + elsif str =~ /^%AI9\_PrivateDataBegin/o then + debug('ignore private ai crap') + break + elsif str =~ /^%%EOF/o then + debug('ignore post eof crap') + break + # elsif str =~ /^%%PageTrailer/o then + # debug('ignoring post page trailer crap') + # break + elsif str =~ /^%%Trailer/o then + debug('ignoring post trailer crap') + break + elsif str =~ /^%%Creator.*Illustrator.*$/io then + debug('getting rid of problematic creator spec') + str = "% Creator: Adobe Illustrator ..." + out.puts(str) + elsif str =~ /^%%AI.*(PaperRect|Margin)/io then + debug('removing AI paper crap') + elsif str =~ /^%%AI.*Version.*$/io then + debug('removing dangerous version info') + elsif str =~ /^(%+AI.*Thumbnail.*)$/o then + debug('skipping AI thumbnail') + skip = true + else + out.puts(str) + end + else + out.puts(str) + end + end + + debug('done, sending EOF') + + out.puts "grestore\n%%EOF\n" + + # ok = $? == 0 + # report('process aborted, broken pipe, fatal error') unless ok + # return ok + + return true + + end + + def downsample (inpfile, outfile, method='screen') + + # gs <= 8.50 + + report("downsampling #{inpfile}") + + doit = true + unless getvariable('force') then + begin + if f = File.open(inpfile) then + f.binmode + while doit && (data = f.gets) do + if data =~ /\/ArtBox\s*\[\s*[\d\.]+\s+[\d\.]+\s+[\d\.]+\s+[\d\.]+\s*\]/io then + doit = false + end + end + f.close + end + rescue + end + end + + if doit then + arguments = '' + arguments << "-dPDFSETTINGS=/#{method} -dEmbedAllFonts=true " + arguments << "#{gscolorswitch} " + arguments << "#{gsdefaults} " + arguments << "-q -sDEVICE=pdfwrite -dNOPAUSE -dNOCACHE -dBATCH -dSAFER " + arguments << "-sOutputFile=#{outfile} #{inpfile} -c quit " + unless ok = System.run('ghostscript',arguments) then + begin + File.delete(outfile) if FileTest.file?(outfile) + report("removing file #{outfile}") + rescue + debug("file #{outfile} may be invalid") + end + end + return ok + else + report("crop problem, straight copying #{inpfile}") + File.copy(inpfile,outfile) + return false + end + + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb b/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb new file mode 100644 index 00000000000..4495c3070f4 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/graphics/inkscape.rb @@ -0,0 +1,103 @@ +# module : graphics/inkscape +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end +# ['graphics/gs','gs'].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/variables' +require 'base/system' +require 'graphics/gs' + +class InkScape + + include Variables + + def initialize(logger=nil) + + unless logger then + puts('inkscape class needs a logger') + exit + end + + @variables = Hash.new + @logger = logger + + reset + + end + + def reset + # nothing yet + end + + def supported?(filename) + filename =~ /.*\.(svg|svgz)/io + end + + def convert(logfile=System.null) + + inpfilename = getvariable('inputfile').dup + outfilename = getvariable('outputfile').dup + outfilename = inpfilename.dup if outfilename.empty? + outfilename.gsub!(/(\.[^\.]*?)$/, ".pdf") + tmpfilename = outfilename.gsub(/(\.[^\.]*?)$/, ".ps") + + if inpfilename.empty? || outfilename.empty? then + report("no filenames given") + return false + end + if inpfilename == outfilename then + report("filenames must differ (#{inpfilename} #{outfilename})") + return false + end + unless FileTest.file?(inpfilename) then + report("unknown file #{inpfilename}") + return false + end + + report("converting #{inpfilename} to #{tmpfilename}") + + # we need to redirect the error info else we get a pop up console + + resultpipe = "--without-gui --print=\">#{tmpfilename}\" 2>#{logfile}" + + arguments = [resultpipe,inpfilename].join(' ').gsub(/\s+/,' ') + + ok = true + begin + debug("inkscape: #{arguments}") + # should work + # ok = System.run('inkscape',arguments) # does not work here + # but 0.40 only works with this: + ok = system("inkscape #{arguments}") + # and 0.41 fails with everything + rescue + report("aborted due to error") + return false + else + return false unless ok + end + + ghostscript = GhostScript.new(@logger) + + ghostscript.setvariable('inputfile',tmpfilename) + ghostscript.setvariable('outputfile',outfilename) + + report("converting #{tmpfilename} to #{outfilename}") + + ghostscript.convert + + begin + File.delete(tmpfilename) + rescue + end + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb b/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb new file mode 100644 index 00000000000..f59087bdf44 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/graphics/magick.rb @@ -0,0 +1,161 @@ +# module : graphics/inkscape +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# ['base/variables','variables'].each do |r| begin require r ; rescue Exception ; else break ; end ; end + +require 'base/variables' + +class ImageMagick + + include Variables + + def initialize(logger=nil) + + unless logger then + puts('magick class needs a logger') + exit + end + + @variables = Hash.new + @logger = logger + + reset + + end + + def reset + ['compression','depth','colorspace','quality'].each do |key| + setvariable(key) + end + end + + def supported?(filename) # ? pdf + filename =~ /.*\.(png|gif|tif|tiff|jpg|jpeg|eps|ai\d*)/io + end + + def convert(suffix='pdf') + + inpfilename = getvariable('inputfile').dup + outfilename = getvariable('outputfile').dup + outfilename = inpfilename.dup if outfilename.empty? + outfilename.gsub!(/(\.[^\.]*?)$/, ".#{suffix}") + + if inpfilename.empty? || outfilename.empty? then + report("no filenames given") + return false + end + if inpfilename == outfilename then + report("filenames must differ (#{inpfilename} #{outfilename})") + return false + end + unless FileTest.file?(inpfilename) then + report("unknown file #{inpfilename}") + return false + end + + if inpfilename =~ /\.tif+$/io then + tmpfilename = 'temp.png' + arguments = "#{inpfilename} #{tmpfilename}" + begin + debug("imagemagick: #{arguments}") + ok = System.run('imagemagick',arguments) + rescue + report("aborted due to error") + return false + else + return false unless ok + end + inpfilename = tmpfilename + end + + compression = depth = colorspace = quality = '' + + if getvariable('compression') =~ /(zip|jpeg)/o then + compression = " -compress #{$1}" + end + if getvariable('depth') =~ /(8|16)/o then + depth = "-depth #{$1}" + end + if getvariable('colorspace') =~ /(gray|rgb|cmyk)/o then + colorspace = "-colorspace #{$1}" + end + case getvariable('quality') + when 'low' then quality = '-quality 0' + when 'medium' then quality = '-quality 75' + when 'high' then quality = '-quality 100' + end + + report("converting #{inpfilename} to #{outfilename}") + + arguments = [compression,depth,colorspace,quality,inpfilename,outfilename].join(' ').gsub(/\s+/,' ') + + begin + debug("imagemagick: #{arguments}") + ok = System.run('imagemagick',arguments) + rescue + report("aborted due to error") + return false + else + return ok + end + + end + + def autoconvert + + inpfilename = getvariable('inputfile') + outfilename = getvariable('outputfile') + + if inpfilename.empty? || ! FileTest.file?(inpfilename) then + report("missing file #{inpfilename}") + return + end + + outfilename = inpfilename.dup if outfilename.empty? + tmpfilename = 'temp.jpg' + + reset + + megabyte = 1024*1024 + + ok = false + + if FileTest.size(inpfilename)>2*megabyte + setvariable('compression','zip') + ok = convert + else + setvariable('compression','jpeg') + if FileTest.size(inpfilename)>10*megabyte then + setvariable('quality',85) + elsif FileTest.size(inpfilename)>5*megabyte then + setvariable('quality',90) + else + setvariable('quality',95) + end + report("auto quality #{getvariable('quality')}") + setvariable('outputfile', tmpfilename) + ok = convert('jpg') + setvariable('inputfile', tmpfilename) + setvariable('outputfile', outfilename) + ok = convert + begin + File.delete(tmpfilename) + rescue + report("#{tmpfilename} cannot be deleted") + end + end + + reset + + return ok + + end + +end diff --git a/Master/texmf-dist/scripts/context/ruby/mpstools.rb b/Master/texmf-dist/scripts/context/ruby/mpstools.rb new file mode 100644 index 00000000000..6dc0c35ad0c --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/mpstools.rb @@ -0,0 +1,5 @@ +# todo + +puts("This program is yet unfinished, for the moment it just calls 'mptopdf'.\n\n") + +system("texmfstart mptopdf #{ARGV.join(' ')}") diff --git a/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb b/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb new file mode 100644 index 00000000000..3c1636cf2bc --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/newimgtopdf.rb @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby + +# program : newimgtopdf +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +unless defined? ownpath + ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' + +require 'graphics/magick' + +banner = ['ImgToPdf', 'version 1.1.1', '2002-2005', 'PRAGMA ADE/POD'] + +class Commands + + include CommandBase + + # nowadays we would force a directive, but + # for old times sake we handle default usage + + def main + filename = @commandline.argument('first') + + if filename.empty? then + help + else + convert + end + end + + # actions + + def convert + + magick = Magick.new(session) + + ['compression','depth','colorspace','quality','inputpath','outputpath'].each do |v| + magick.setvariable(v,@commandline.option(v)) + end + + @commandline.arguments.each do |fullname| + magick.setvariable('inputfile',fullname) + magick.setvariable('outputfile',fullname.gsub(/(\..*?$)/io, '.pdf')) + if @commandline.option('auto') then + magick.autoconvert + else + magick.convert + end + end + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registerflag('auto') + +commandline.registervalue('compression') +commandline.registervalue('depth') +commandline.registervalue('colorspace') +commandline.registervalue('quality') + +commandline.registervalue('inputpath') +commandline.registervalue('outputpath') + + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registeraction('convert', 'convert image into pdf') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'main') diff --git a/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb b/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb new file mode 100644 index 00000000000..898f987cbce --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/newpstopdf.rb @@ -0,0 +1,530 @@ +#!/usr/bin/env ruby + +# program : pstopdf +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +banner = ['PsToPdf', 'version 2.0.0', '2002-2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'') + $: << ownpath +end + +# todo: paden/prefix in magick and inkscape +# todo: clean up method handling (pass strings, no numbers) +# --method=crop|bounded|raw|... +# --resolution=low|normal|medium|high|printer|print|screen|ebook|default +# + downward compatible flag handling + +require 'base/switch' +require 'base/tool' +require 'base/logger' + +require 'graphics/gs' +require 'graphics/magick' +require 'graphics/inkscape' + +require 'rexml/document' + +exit if defined?(REQUIRE2LIB) + +class Commands + + include CommandBase + + # nowadays we would force a directive, but + # for old times sake we handle default usage + + def main + filename = @commandline.argument('first') + pattern = @commandline.option('pattern') + globfiles(pattern) if filename.empty? && ! pattern.empty? + filename = @commandline.argument('first') + if filename.empty? then + help + elsif filename =~ /\.exa$/ then + request + else + convert + end + end + + # actions + + def convert + + ghostscript = GhostScript.new(logger) + magick = ImageMagick.new(logger) + inkscape = InkScape.new(logger) + + outpath = @commandline.option('outputpath') + unless outpath.empty? then + begin + File.expand_path(outpath) + outpath = File.makedirs(outpath) unless FileTest.directory?(outpath) + rescue + # sorry + end + end + + @commandline.arguments.each do |filename| + + filename = Tool.cleanfilename(filename,@commandline) + inppath = @commandline.option('inputpath') + if inppath.empty? then + inppath = '.' + fullname = filename # avoid duplicate './' + else + fullname = File.join(inppath,filename) + end + if FileTest.file?(fullname) then + handle_whatever(ghostscript,inkscape,magick,filename) + else + report("file #{fullname} does not exist") + end + + end + + end + + def request + + # <exa:request> + # <exa:application> + # <exa:command>pstopdf</exa:command> + # <exa:filename>E:/tmp/demo.ps</exa:filename> + # </exa:application> + # <exa:data> + # <exa:variable label='gs:DoThumbnails'>false</exa:variable> + # <exa:variable label='gs:ColorImageDepth'>-1</exa:variable> + # </exa:data> + # </exa:request> + + ghostscript = GhostScript.new(logger) + magick = ImageMagick.new(logger) + inkscape = InkScape.new(logger) + + dataname = @commandline.argument('first') || '' + filename = @commandline.argument('second') || '' + + if dataname.empty? || ! FileTest.file?(dataname) then + report('provide valid exa file') + return + else + begin + request = REXML::Document.new(File.new(dataname)) + rescue + report('provide valid exa file (xml error)') + return + end + end + if filename.empty? then + begin + if filename = REXML::XPath.first(request.root,"exa:request/exa:application/exa:filename/text()") then + filename = filename.to_s + else + report('no filename found in exa file') + return + end + rescue + filename = '' + end + end + if filename.empty? then + report('provide valid filename') + return + elsif ! FileTest.file?(filename) then + report("invalid filename #{filename}") + return + end + + [ghostscript,inkscape,magick].each do |i| + i.setvariable('inputfile',filename) + end + + # set ghostscript variables + REXML::XPath.each(request.root,"/exa:request/exa:data/exa:variable") do |v| + begin + if (key = v.attributes['label']) and (value = v.text.to_s) then + case key + when /gs[\:\.](var[\:\.])*(offset)/io then ghostscript.setoffset(value) + when /gs[\:\.](var[\:\.])*(method)/io then ghostscript.setvariable('method',value) + when /gs[\:\.](var[\:\.])*(.*)/io then ghostscript.setpsoption($2,value) + end + end + rescue + end + end + + # no inkscape and magick variables (yet) + + handle_whatever(ghostscript,inkscape,magick,filename) + + end + + def watch + + ghostscript = GhostScript.new(logger) + magick = ImageMagick.new(logger) + inkscape = InkScape.new(logger) + + pathname = commandline.option('watch') + + unless pathname and not pathname.empty? then + report('empty watchpath is not supported') + exit + end + + if pathname == '.' then + report("watchpath #{pathname} is not supported") + exit + end + + if FileTest.directory?(pathname) then + if Dir.chdir(pathname) then + report("watching path #{pathname}") + else + report("unable to change to path #{pathname}") + exit + end + else + report("invalid path #{pathname}") + exit + end + + waiting = false + + loop do + + if waiting then + report("waiting #{getvariable('delay')}") + waiting = false + sleep(getvariable('delay').to_i) + end + + files = Dir.glob("**/*.*") + + if files and files.length > 0 then + + files.each do |fullname| + + next unless fullname + + if FileTest.directory?(fullname) then + debug('skipping path', fullname) + next + end + + unless magick.supported(fullname) then + debug('not supported', fullname) + next + end + + if (! FileTest.file?(fullname)) || (FileTest.size(fullname) < 100) then + debug("skipping small crap file #{fullname}") + next + end + + debug("handling file #{fullname}") + + begin + next unless File.rename(fullname,fullname) # access trick + rescue + next # being written + end + + fullname = Tool.cleanfilename(fullname,@commandline) + + fullname.gsub!(/\\/io, '/') + + filename = File.basename(fullname) + filepath = File.dirname(fullname) + + next if filename =~ /gstemp.*/io + + if filepath !~ /(result|done|raw|crop|bound|bitmap)/io then + begin + File.makedirs(filepath+'/raw') + File.makedirs(filepath+'/bound') + File.makedirs(filepath+'/crop') + File.makedirs(filepath+'/bitmap') + debug("creating prefered input paths on #{filepath}") + rescue + debug("creating input paths on #{filepath} failed") + end + end + + if filepath =~ /^(.*\/|)(done|result)$/io then + debug("skipping file #{fullname}") + else + report("start processing file #{fullname}") + if filepath =~ /^(.*\/*)(raw|crop|bound)$/io then + donepath = $1 + 'done' + resultpath = $1 + 'result' + case $2 + when 'raw' then method = 1 + when 'bound' then method = 2 + when 'crop' then method = 3 + else method = 2 + end + report("forcing method #{method}") + else + method = 2 + donepath = filepath + '/done' + resultpath = filepath + '/result' + report("default method #{method}") + end + + begin + File.makedirs(donepath) + File.makedirs(resultpath) + rescue + report('result path creation fails') + end + + if FileTest.directory?(donepath) && FileTest.directory?(resultpath) then + + resultname = resultpath + '/' + filename.sub(/\..*$/,'') + '.pdf' + + @commandline.setoption('inputpath', filepath) + @commandline.setoption('outputpath', resultpath) + @commandline.setoption('method', method) + + if ghostscript.psfile?(fullname) then + handle_ghostscript(ghostscript,filename) + else + handle_magick(magick,filename) + end + + sleep(1) # calm down + + if FileTest.file?(fullname) then + begin + File.copy(fullname,donepath + '/' + filename) + File.delete(fullname) + rescue + report('cleanup fails') + end + end + + end + + end + + end + + end + + waiting = true + end + + end + + private + + def handle_whatever(ghostscript,inkscape,magick,filename) + if ghostscript.psfile?(filename) then + # report("processing ps file #{filename}") + ghostscript.setvariable('pipe',false) if @commandline.option('nopipe') + # ghostscript.setvariable('pipe',not @commandline.option('nopipe')) + ghostscript.setvariable('colormodel',@commandline.option('colormodel')) + ghostscript.setvariable('offset',@commandline.option('offset')) + handle_ghostscript(ghostscript,filename) + elsif ghostscript.pdffile?(filename) && ghostscript.pdfmethod?(@commandline.option('method')) then + # report("processing pdf file #{filename}") + handle_ghostscript(ghostscript,filename) + elsif inkscape.supported?(filename) then + # report("processing non ps/pdf file #{filename}") + handle_inkscape(inkscape,filename) + elsif magick.supported?(filename) then + # report("processing non ps/pdf file #{filename}") + handle_magick(magick,filename) + end + end + + def handle_magick(magick,filename) + + report("converting non-ps file #{filename} into pdf") + + inppath = @commandline.option('inputpath') + outpath = @commandline.option('outputpath') + + inppath = inppath + '/' if not inppath.empty? + outpath = outpath + '/' if not outpath.empty? + + prefix = @commandline.option('prefix') + suffix = @commandline.option('suffix') + + inpfilename = "#{inppath}#{filename}" + outfilename = "#{outpath}#{prefix}#{filename.sub(/\.(.*?)$/, '')}#{suffix}.pdf" + + magick.setvariable('inputfile' , inpfilename) + magick.setvariable('outputfile', outfilename) + + magick.autoconvert + + end + + def handle_inkscape(inkscape,filename) + + report("converting svg(z) file #{filename} into pdf") + + inppath = @commandline.option('inputpath') + outpath = @commandline.option('outputpath') + + inppath = inppath + '/' if not inppath.empty? + outpath = outpath + '/' if not outpath.empty? + + prefix = @commandline.option('prefix') + suffix = @commandline.option('suffix') + + inpfilename = "#{inppath}#{filename}" + outfilename = "#{outpath}#{prefix}#{filename.sub(/\.(.*?)$/, '')}#{suffix}.pdf" + + inkscape.setvariable('inputfile' , inpfilename) + inkscape.setvariable('outputfile', outfilename) + + if @commandline.option('verbose') || @commandline.option('debug') then + logname = filename.gsub(/\.[^\.]*?$/, '.log') + report("log info saved in #{logname}") + inkscape.convert(logname) # logname ook doorgeven + else + inkscape.convert + end + + end + + def handle_ghostscript(ghostscript,filename) + + ghostscript.reset + + method = ghostscript.method(@commandline.option('method')) + force = ghostscript.method(@commandline.option('force')) + + ghostscript.setvariable('method', method) + ghostscript.setvariable('force', force) + + # report("conversion method #{method}") + + inppath = @commandline.option('inputpath') + outpath = @commandline.option('outputpath') + + inppath = inppath + '/' if not inppath.empty? + outpath = outpath + '/' if not outpath.empty? + + prefix = @commandline.option('prefix') + suffix = @commandline.option('suffix') + + ok = false + + if ghostscript.pdfmethod?(method) then + + report("converting pdf file #{filename} into pdf") + + if prefix.empty? && suffix.empty? && inppath.empty? && outpath.empty? then + prefix = ghostscript.pdfprefix(method) + end + + if ghostscript.pdffile?(filename) then + + filename = filename.sub(/\.pdf$/, '') + + inpfilename = "#{inppath}#{filename}.pdf" + outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf" + + ghostscript.setvariable('inputfile' ,inpfilename) + ghostscript.setvariable('outputfile',outfilename) + + if FileTest.file?(inpfilename) then + ok = ghostscript.convert + else + report("no file found #{filename}") + end + + else + report("no pdf file #{filename}") + end + + elsif ghostscript.psfile?(filename) then + + if filename =~ /(.*)\.(.*?)$/io then + filename, filesuffix = $1, $2 + else + filesuffix = 'eps' + end + + report("converting #{filesuffix} (ps) into pdf") + + inpfilename = "#{inppath}#{filename}.#{filesuffix}" + outfilename = "#{outpath}#{prefix}#{filename}#{suffix}.pdf" + + ghostscript.setvariable('inputfile' , inpfilename) + ghostscript.setvariable('outputfile', outfilename) + + if FileTest.file?(inpfilename) then + ok = ghostscript.convert + if ! ok && FileTest.file?(outfilename) then + begin + File.delete(outfilename) + rescue + end + end + else + report("no file with name #{filename} found") + end + + else + report('file must be of type eps/ps/ai/pdf') + end + + return ok + + end + +end + +# ook pdf -> pdf onder optie 0, andere kleurruimte + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registerflag('debug') +commandline.registerflag('verbose') +commandline.registerflag('nopipe') + +commandline.registervalue('method',2) +commandline.registervalue('offset',0) + +commandline.registervalue('prefix') +commandline.registervalue('suffix') + +commandline.registervalue('inputpath') +commandline.registervalue('outputpath') + +commandline.registerflag('watch') +commandline.registerflag('force') + +commandline.registervalue('delay',2) + +commandline.registervalue('colormodel','cmyk') +commandline.registervalue('pattern','') + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registeraction('convert', 'convert ps into pdf') +commandline.registeraction('request', 'handles exa request file') +commandline.registeraction('watch', 'watch folders for conversions (untested)') + +commandline.expand + +logger.verbose if (commandline.option('verbose') || commandline.option('debug')) + +Commands.new(commandline,logger,banner).send(commandline.action || 'main') diff --git a/Master/texmf-dist/scripts/context/ruby/newtexexec.rb b/Master/texmf-dist/scripts/context/ruby/newtexexec.rb new file mode 100644 index 00000000000..27f8eeed96f --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/newtexexec.rb @@ -0,0 +1,609 @@ +banner = ['TeXExec', 'version 6.1.0', '1997-2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' +require 'base/variables' +require 'base/system' + +require 'base/tex' +require 'base/texutil' + +require 'ftools' # needed ? + +require 'base/kpse' # needed ? +# require 'base/pdf' # needed ? +require 'base/state' # needed ? +require 'base/file' # needed ? + +class Commands + + include CommandBase + + def make + if job = TEX.new(logger) then + prepare(job) + # bonus, overloads language switch ! + job.setvariable('language','all') if @commandline.option('all') + if @commandline.arguments.length > 0 then + if @commandline.arguments.first == 'all' then + job.setvariable('texformats',job.defaulttexformats) + job.setvariable('mpsformats',job.defaultmpsformats) + else + job.setvariable('texformats',@commandline.arguments) + job.setvariable('mpsformats',@commandline.arguments) + end + end + job.makeformats + job.inspect && Kpse.inspect if @commandline.option('verbose') + end + end + + def check + if job = TEX.new(logger) then + job.checkcontext + job.inspect && Kpse.inspect if @commandline.option('verbose') + end + end + + def main + if @commandline.arguments.length>0 then + process + else + help + end + end + + def process + if job = TEX.new(logger) then + job.setvariable('files',@commandline.arguments) + prepare(job) + job.processtex + job.inspect && Kpse.inspect if @commandline.option('verbose') + end + end + + def mptex + if job = TEX.new(logger) then + job.setvariable('files',@commandline.arguments) + prepare(job) + job.processmptex + job.inspect && Kpse.inspect if @commandline.option('verbose') + end + end + + def mpxtex + if job = TEX.new(logger) then + job.setvariable('files',@commandline.arguments) + prepare(job) + job.processmpxtex + job.inspect && Kpse.inspect if @commandline.option('verbose') + end + end + + # hard coded goodies # to be redone as s-ctx-.. with vars passed as such + + def listing + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + if files.length > 0 then + if f = File.open(job.tempfilename('tex'),'w') then + backspace = @commandline.checkedoption('backspace', '1.5cm') + topspace = @commandline.checkedoption('topspace', '1.5cm') + pretty = @commandline.option('pretty') + f << "% interface=english\n" + f << "\\setupbodyfont[11pt,tt]\n" + f << "\\setuplayout\n" + f << " [topspace=#{topspace},backspace=#{backspace},\n" + f << " header=0cm,footer=1.5cm,\n" + f << " width=middle,height=middle]\n" + f << "\\setuptyping[lines=yes]\n" + f << "\\setuptyping[option=color]\n" if pretty + f << "\\starttext\n"; + files.each do |filename| + report("list file: #{filename}") + cleanname = cleantexfilename(filename).downcase + f << "\\page\n" + f << "\\setupfootertexts[\\tttf #{cleanname}][\\tttf \\pagenumber]\n" + f << "\\typefile{#{filename}}\n" + end + f << "\\stoptext\n" + f.close + job.setvariable('interface','english') + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + else + report('no files to list') + end + else + report('no files to list') + end + job.cleanuptemprunfiles + end + end + + def figures + # this one will be redone using rlxtools + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + if files.length > 0 then + if f = File.open(job.tempfilename('tex'),'w') then + job.runtexutil(files,"--figures", true) + figures = @commandline.checkedoption('method', 'a').downcase + paperoffset = @commandline.checkedoption('paperoffset', '0pt') + backspace = @commandline.checkedoption('backspace', '1.5cm') + topspace = @commandline.checkedoption('topspace', '1.5cm') + boxtype = @commandline.checkedoption('boxtype','') + f << "% format=english\n"; + f << "\\setuplayout\n"; + f << " [topspace=#{topspace},backspace=#{backspace},\n" + f << " header=1.5cm,footer=0pt,\n"; + f << " width=middle,height=middle]\n"; + if @commandline.option('fullscreen') then + f << "\\setupinteraction\n"; + f << " [state=start]\n"; + f << "\\setupinteractionscreen\n"; + f << " [option=max]\n"; + end + boxtype += "box" unless boxtype.empty? || (boxtype =~ /box$/io) + f << "\\starttext\n"; + f << "\\showexternalfigures[alternative=#{figures},offset=#{paperoffset},size=#{boxtype}]\n"; + f << "\\stoptext\n"; + f.close + job.setvariable('interface','english') + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + File.silentdelete('texutil.tuf') + else + report('no figures to show') + end + else + report('no figures to show') + end + job.cleanuptemprunfiles + end + end + + def modules + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + msuffixes = ['tex','mp','pl','pm','rb'] + if files.length > 0 then + files.each do |fname| + fnames = Array.new + if FileTest.file?(fname) then + fnames << fname + else + msuffixes.each do |fsuffix| + fnames << File.suffixed(fname,fsuffix) + end + end + fnames.each do |ffname| + if msuffixes.include?(File.splitname(ffname)[1]) && FileTest.file?(ffname) then + if mod = File.open(job.tempfilename('tex'),'w') then + # will become a call to ctxtools + job.runtexutil(ffname,"--documents", true) + if ted = File.silentopen(File.suffixed(ffname,'ted')) then + firstline = ted.gets + if firstline =~ /interface=/o then + mod << firstline + else + mod << "% interface=en\n" + end + ted.close + else + mod << "% interface=en\n" + end + mod << "\\usemodule[abr-01,mod-01]\n" + mod << "\\def\\ModuleNumber{1}\n" + mod << "\\starttext\n" + # todo: global file too + mod << "\\readlocfile{#{File.suffixed(ffname,'ted')}}{}{}\n" + mod << "\\stoptext\n" + mod.close + job.setvariable('interface','english') # redundant + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + ["dvi", "pdf","tuo"].each do |s| + File.silentrename(job.tempfilename(s),File.suffixed(ffname,s)); + end + end + end + end + end + else + report('no modules to process') + end + job.cleanuptemprunfiles + end + end + + def arrange + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + if files.length > 0 then + if f = File.open(job.tempfilename('tex'),'w') then + emptypages = @commandline.checkedoption('addempty', '') + paperoffset = @commandline.checkedoption('paperoffset', '0cm') + textwidth = @commandline.checkedoption('textwidth', '0cm') + backspace = @commandline.checkedoption('backspace', '0cm') + topspace = @commandline.checkedoption('topspace', '0cm') + f << "\\definepapersize\n" + f << " [offset=#{paperoffset}]\n" + f << "\\setuplayout\n" + f << " [backspace=#{backspace},\n" + f << " topspace=#{topspace},\n" + f << " marking=on,\n" if @commandline.option('marking') + f << " width=middle,\n" + f << " height=middle,\n" + f << " location=middle,\n" + f << " header=0pt,\n" + f << " footer=0pt]\n" + unless @commandline.option('noduplex') then + f << "\\setuppagenumbering\n" + f << " [alternative=doublesided]\n" + end + f << "\\starttext\n" + files.each do |filename| + report("arranging file #{filename}") + f << "\\insertpages\n" + f << " [#{filename}]\n" + f << " [#{addempty}]\n" unless addempty.empty? + f << " [width=#{textwidth}]\n" + end + f << "\\stoptext\n" + f.close + job.setvariable('interface','english') + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + else + report('no files to arrange') + end + else + report('no files to arrange') + end + job.cleanuptemprunfiles + end + end + + def select + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + if files.length > 0 then + if f = File.open(job.tempfilename('tex'),'w') then + selection = @commandline.checkedoption('selection', '') + paperoffset = @commandline.checkedoption('paperoffset', '0cm') + textwidth = @commandline.checkedoption('textwidth', '0cm') + backspace = @commandline.checkedoption('backspace', '0cm') + topspace = @commandline.checkedoption('topspace', '0cm') + paperformat = @commandline.checkedoption('paperformat', 'A4*A4').split(/[\*x]/o) + from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4' + if from == 'fit' or to == 'fit' then + f << "\\getfiguredimensions[#{files.first}]\n" + if from == 'fit' then + f << "\\expanded{\\definepapersize[from-fit][width=\\figurewidth,height=\\figureheight]}\n" + from = 'from-fit' + end + if to == 'fit' then + f << "\\expanded{\\definepapersize[to-fit][width=\\figurewidth,height=\\figureheight]}\n" + to = 'to-fit' + end + end + job.setvariable('paperformat','') # else overloaded later on + f << "\\setuppapersize[#{from}][#{to}]\n" + f << "\\definepapersize\n"; + f << " [offset=#{paperoffset}]\n"; + f << "\\setuplayout\n"; + f << " [backspace=#{backspace},\n"; + f << " topspace=#{topspace},\n"; + f << " marking=on,\n" if @commandline.option('marking') + f << " width=middle,\n"; + f << " height=middle,\n"; + f << " location=middle,\n"; + f << " header=0pt,\n"; + f << " footer=0pt]\n"; + f << "\\setupexternalfigures\n"; + f << " [directory=]\n"; + f << "\\starttext\n"; + unless selection.empty? then + f << "\\filterpages\n" + f << " [#{files.first}][#{selection}][width=#{textwidth}]\n" + end + f << "\\stoptext\n" + f.close + job.setvariable('interface','english') + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + else + report('no files to selectt') + end + else + report('no files to select') + end + job.cleanuptemprunfiles + end + end + + def copy + copyortrim(false,'copy') + end + + def trim + copyortrim(true,'trim') + end + + def copyortrim(trim=false) + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + if files.length > 0 then + if f = File.open(job.tempfilename('tex'),'w') then + scale = @commandline.checkedoption('scale') + scale = (scale * 1000).to_i if scale < 10 + paperoffset = @commandline.checkedoption('paperoffset', '0cm') + f << "\\starttext\n" + files.each do |filename| + result = @commandline.checkedoption('result','texexec') + if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then + report("copying file: #{filename}") + f << "\\getfiguredimensions\n" + f << " [#{filename}]\n" + f << " [page=1" + f << ",\n size=trimbox" if trim + f << "]\n" + f << "\\definepapersize\n" + f << " [copy]\n" + f << " [width=\\naturalfigurewidth,\n" + f << " height=\\naturalfigureheight]\n" + f << "\\setuppapersize\n" + f << " [copy][copy]\n" + f << "\\setuplayout\n" + f << " [page]\n" + f << "\\setupexternalfigures\n" + f << " [directory=]\n" + f << "\\copypages\n" + f << " [#[filename}]\n" + f << " [scale=#{scale},\n" + f << " marking=on,\n" if @commandline.option('markings') + f << " size=trimbox,\n" if trim + f << " offset=#{paperoffset}]\n" + end + end + f << "\\stoptext\n" + f.close + job.setvariable('interface','english') + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + else + report("no files to #{what}") + end + else + report("no files to #{what}") + end + job.cleanuptemprunfiles + end + end + + def combine + if job = TEX.new(logger) then + prepare(job) + job.cleanuptemprunfiles + files = @commandline.arguments.sort + if files.length > 0 then + if f = File.open(job.tempfilename('tex'),'w') then + paperoffset = @commandline.checkedoption('paperoffset', '0cm') + combination = @commandline.checkedoption('combination','2*2').split(/[\*x]/o) + paperformat = @commandline.checkedoption('paperoffset', 'A4*A4').split(/[\*x]/o) + nx, ny = combination[0] || '2', combination[1] || combination[0] || '2' + from, to = paperformat[0] || 'A4', paperformat[1] || paperformat[0] || 'A4' + f << "\\setuppapersize[#{from}][#{to}]\n" + f << "\\setuplayout\n" + f << " [topspace=#{paperoffset},\n" + f << " backspace=#{paperoffset},\n" + f << " header=0pt,\n" + f << " footer=1cm,\n" + f << " width=middle,\n" + f << " height=middle]\n" + if @commandline.option('nobanner') then + f << "\\setuplayout\n" + f << " [footer=0cm]\n" + end + f << "\\setupexternalfigures\n" + f << " [directory=]\n" + f << "\\starttext\n" + files.each do |filename| + result = @commandline.checkedoption('result','texexec') + if (filename !~ /^texexec/io) && (filename !~ /^#{result}/) then + report("combination file: #{filename}") + cleanname = cleantexfilename(filename).downcase + f << "\\setupfootertexts\n" + f << " [\\tttf #{cleanname}\\quad\\quad\\currentdate\\quad\\quad\\pagenumber]\n" + f << "\\combinepages[#{filename}][nx=#{nx},ny=#{ny}]\n" + f << "\\page\n" + end + end + f << "\\stoptext\n" + f.close + job.setvariable('interface','english') + job.setvariable('simplerun',true) + # job.setvariable('nooptionfile',true) + job.setvariable('files',[job.tempfilename]) + job.processtex + else + report('no files to list') + end + else + report('no files to list') + end + job.cleanuptemprunfiles + end + end + + private + + def prepare(job) + + job.booleanvars.each do |k| + job.setvariable(k,@commandline.option(k)) + end + job.stringvars.each do |k| + job.setvariable(k,@commandline.option(k)) + end + job.standardvars.each do |k| + job.setvariable(k,@commandline.option(k)) + end + job.knownvars.each do |k| + job.setvariable(k,@commandline.option(k)) unless @commandline.option(k).empty? + end + + if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then + job.setvariable('texengine',str) + elsif @commandline.oneof('pdfetex','pdftex','pdf') then + job.setvariable('texengine','pdfetex') + elsif @commandline.oneof('xetex','xtx') then + job.setvariable('texengine','xetex') + elsif @commandline.oneof('aleph') then + job.setvariable('texengine','aleph') + else + job.setvariable('texengine','standard') + end + + if (str = @commandline.option('backend')) && ! str.standard? && ! str.empty? then + job.setvariable('backend',str) + elsif @commandline.oneof('pdfetex','pdftex','pdf') then + job.setvariable('backend','pdftex') + elsif @commandline.oneof('dvipdfmx','dvipdfm','dpx','dpm') then + job.setvariable('backend','dvipdfmx') + elsif @commandline.oneof('xetex','xtx') then + job.setvariable('backend','xetex') + elsif @commandline.oneof('aleph') then + job.setvariable('backend','dvipdfmx') + elsif @commandline.oneof('dvips','ps') then + job.setvariable('backend','dvips') + else + job.setvariable('backend','standard') + end + + if (str = @commandline.option('engine')) && ! str.standard? && ! str.empty? then + job.setvariable('mpsengine',@commandline.option('engine')) + else + job.setvariable('mpsengine','standard') + end + + end + + def cleantexfilename(filename) + filename.gsub(/([\$\_\#])/) do "\\$1" end.gsub(/([\~])/) do "\\string$1" end + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registeraction('make', 'make formats') +commandline.registeraction('check', 'check versions') +commandline.registeraction('process', 'process file') +commandline.registeraction('mptex', 'process mp file') +commandline.registeraction('mpxtex', 'process mpx file') + +commandline.registeraction('listing', 'list of file content') +commandline.registeraction('figures', 'generate overview of figures') +commandline.registeraction('modules', 'generate module documentation') +commandline.registeraction('arrange', 'impose pages (booklets)') +commandline.registeraction('select', 'select pages from file(s)') +commandline.registeraction('copy', 'copy pages from file(s)') +commandline.registeraction('trim', 'trim pages from file(s)') +commandline.registeraction('combine', 'combine multiple pages') + +@@extrastringvars = [ + 'pages', 'background', 'backspace', 'topspace', 'boxtype', 'tempdir', + 'printformat', 'paperformat', 'method', 'scale', 'selection', + 'combination', 'paperoffset', 'textwidth', 'addempty', 'logfile', + 'startline', 'endline', 'startcolumn', 'endcolumn', 'scale' +] + +@@extrabooleanvars = [ + 'centerpage', 'noduplex', 'color', 'pretty', + 'fullscreen', 'screensaver', 'markings' +] + +if job = TEX.new(logger) then + + job.setextrastringvars(@@extrastringvars) + job.setextrabooleanvars(@@extrabooleanvars) + + job.booleanvars.each do |k| + commandline.registerflag(k) + end + job.stringvars.each do |k| + commandline.registervalue(k,'') + end + job.standardvars.each do |k| + commandline.registervalue(k,'standard') + end + job.knownvars.each do |k| + commandline.registervalue(k,'') + end + +end + +# todo: register flags -> first one true + +commandline.registerflag('pdf') +commandline.registerflag('pdftex') +commandline.registerflag('pdfetex') + +commandline.registerflag('dvipdfmx') +commandline.registerflag('dvipdfm') +commandline.registerflag('dpx') +commandline.registerflag('dpm') + +commandline.registerflag('dvips') +commandline.registerflag('ps') + +commandline.registerflag('xetex') +commandline.registerflag('xtx') + +commandline.registerflag('aleph') + +commandline.registerflag('all') +commandline.registerflag('fast') + +# generic + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registerflag('verbose') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'main') diff --git a/Master/texmf-dist/scripts/context/ruby/newtexutil.rb b/Master/texmf-dist/scripts/context/ruby/newtexutil.rb new file mode 100644 index 00000000000..af003a8e7b2 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/newtexutil.rb @@ -0,0 +1,96 @@ +banner = ['TeXUtil ', 'version 9.1.0', '1997-2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' +require 'base/file' +require 'base/texutil' + +class Commands + + include CommandBase + + def references + filename = @commandline.argument('first') + if not filename.empty? and FileTest.file?(File.suffixed(filename,'tuo')) then + if tu = TeXUtil::Converter.new(logger) and tu.loaded(filename) then + tu.saved if tu.processed + end + end + end + + def main + if @commandline.arguments.length>0 then + references + else + help + end + end + + def purgefiles + system("texmfstart ctxtools --purge #{@commandline.argument.join(' ')}") + end + + def purgeallfiles + system("texmfstart ctxtools --purgeall #{@commandline.argument.join(' ')}") + end + + def documentation + system("texmfstart ctxtools --document #{@commandline.argument.join(' ')}") + end + + def analyzefile + system("texmfstart pdftools --analyze #{@commandline.argument.join(' ')}") + end + + def filterpages # obsolete + system("texmfstart ctxtools --purge #{@commandline.argument.join(' ')}") + end + + def figures + report("this code is not yet converted from perl to ruby") + end + + def logfile + report("this code is not yet converted from perl to ruby") + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +# main feature + +commandline.registeraction('references', 'convert tui file into tuo file') + +# todo features + +commandline.registeraction('figures', 'generate figure dimensions file') +commandline.registeraction('logfile', 'filter essential log messages') + +# backward compatibility features + +commandline.registeraction('purgefiles', 'remove most temporary files') +commandline.registeraction('purgeallfiles', 'remove all temporary files') +commandline.registeraction('documentation', 'generate documentation file from source') +commandline.registeraction('analyzefile', 'analyze pdf file') + +# old feature, not needed any longer due to extension of pdftex + +commandline.registeraction('filterpages') + +# generic features + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registerflag('verbose') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'main') diff --git a/Master/texmf-dist/scripts/context/ruby/rlxtools.rb b/Master/texmf-dist/scripts/context/ruby/rlxtools.rb new file mode 100644 index 00000000000..7962474eb10 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/rlxtools.rb @@ -0,0 +1,262 @@ +#!/usr/bin/env ruby + +# program : rlxtools +# copyright : PRAGMA Advanced Document Engineering +# version : 2004-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +banner = ['RlxTools', 'version 1.0.1', '2004/2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' +require 'base/system' +require 'base/kpse' + +require 'ftools' +require 'rexml/document' + +class Commands + + include CommandBase + + # <?xml version='1.0 standalone='yes'?> + # <rl:manipulators> + # <rl:manipulator name='lowres' suffix='pdf'> + # <rl:step> + # texmfstart + # --verbose + # --iftouched=<rl:value name='path'/>/<rl:value name='file'/>,<rl:value name='path'/>/<rl:value name='prefix'/><rl:value name='file'/> + # pstopdf + # --method=5 + # --inputpath=<rl:value name='path'/> + # --outputpath=<rl:value name='path'/>/<rl:value name='prefix'/> + # <rl:value name='file'/> + # </rl:step> + # </rl:manipulator> + # </rl:manipulators> + # + # <?xml version='1.0' standalone='yes'?> + # <rl:library> + # <rl:usage> + # <rl:type>figure</rl:type> + # <rl:state>found</rl:state> + # <rl:file>cow.pdf</rl:file> + # <rl:suffix>pdf</rl:suffix> + # <rl:path>.</rl:path> + # <rl:conversion>lowres</rl:conversion> + # <rl:prefix>lowres/</rl:prefix> + # <rl:width>276.03125pt</rl:width> + # <rl:height>200.75pt</rl:height> + # </rl:usage> + # </r:library> + + def manipulate + + procname = @commandline.argument('first') || '' + filename = @commandline.argument('second') || '' + + procname = Kpse.found(procname) + + if procname.empty? || ! FileTest.file?(procname) then + report('provide valid manipulator file') + elsif filename.empty? || ! FileTest.file?(filename) then + report('provide valid resource log file') + else + begin + data = REXML::Document.new(File.new(filename)) + rescue + report('provide valid resource log file (xml error)') + return + end + begin + proc = REXML::Document.new(File.new(procname)) + rescue + report('provide valid manipulator file (xml error)') + return + end + report("manipulator file: #{procname}") + report("resourcelog file: #{filename}") + begin + nofrecords, nofdone = 0, 0 + REXML::XPath.each(data.root,"/rl:library/rl:usage") do |usage| + nofrecords += 1 + variables = Hash.new + usage.elements.each do |e| + variables[e.name] = e.text.to_s + end + report("processing record #{nofrecords} (#{variables['file'] || 'noname'}: #{variables.size} entries)") + if conversion = variables['conversion'] then + report("testing for conversion #{conversion}") + if suffix = variables['suffix'] then + if file = variables['file'] then + report("conversion #{conversion} for suffix #{suffix} for file #{file}") + else + report("conversion #{conversion} for suffix #{suffix}") + end + pattern = "@name='#{conversion}' and @suffix='#{suffix}'" + if steps = REXML::XPath.first(proc.root,"/rl:manipulators/rl:manipulator[#{pattern}]") then + localsteps = steps.deep_clone + ['rl:old','rl:new'].each do |tag| + REXML::XPath.each(localsteps,tag) do |extras| + REXML::XPath.each(extras,"rl:value") do |value| + if name = value.attributes['name'] then + substititute(value,variables[name.to_s]) + end + end + end + end + old = REXML::XPath.first(localsteps,"rl:old") + new = REXML::XPath.first(localsteps,"rl:new") + if old && new then + old, new = justtext(old.to_s), justtext(new.to_s) + variables['old'], variables['new'] = old, new + begin + [old,new].each do |d| + File.makedirs(File.dirname(d)) + end + rescue + report("error during path creation") + end + report("old file #{old}") + report("new file #{new}") + level = if File.needsupdate(old,new) then 2 else 0 end + else + level = 1 + end + if level>0 then + REXML::XPath.each(localsteps,"rl:step") do |command| + REXML::XPath.each(command,"rl:old") do |value| + replace(value,old) + end + REXML::XPath.each(command,"rl:new") do |value| + replace(value,new) + end + REXML::XPath.each(command,"rl:value") do |value| + if name = value.attributes['name'] then + substititute(value,variables[name.to_s]) + end + end + str = justtext(command.to_s) + # str.gsub!(/(\.\/)+/io, '') + report("command #{str}") + System.run(str) unless @commandline.option('test') + report("synchronizing #{old} and #{new}") + File.syncmtimes(old,new) if level > 1 + nofdone += 1 + end + else + report("no need for a manipulation") + end + else + report("no manipulator found") + end + else + report("no suffix specified") + end + else + report("no conversion needed") + end + end + if nofdone > 0 then + jobname = filename.gsub(/\.(.*?)$/,'') # not 'tuo' here + tuoname = jobname + '.tuo' + if FileTest.file?(tuoname) && (f = File.open(tuoname,'a')) then + f.puts("%\n% number of rlx manipulations: #{nofdone}\n") + f.close + end + end + rescue + report('error in manipulating files') + end + begin + logname = "#{filename}.log" + File.delete(logname) if FileTest.file?(logname) + File.copy(filename,logname) + rescue + end + end + + end + + private + + def justtext(str) + str = str.to_s + str.gsub!(/<[^>]*?>/o, '') + str.gsub!(/\s+/o, ' ') + str.gsub!(/</o, '<') + str.gsub!(/>/o, '>') + str.gsub!(/&/o, '&') + str.gsub!(/"/o, '"') + str.gsub!(/[\/\\]+/o, '/') + return str.strip + end + + def substititute(value,str) + if str then + begin + if value.attributes.key?('method') then + str = filtered(str.to_s,value.attributes['method'].to_s) + end + if str.empty? && value.attributes.key?('default') then + str = value.attributes['default'].to_s + end + value.insert_after(value,REXML::Text.new(str.to_s)) + rescue Exception + end + end + end + + def replace(value,str) + if str then + begin + value.insert_after(value,REXML::Text.new(str.to_s)) + rescue Exception + end + end + end + + def filtered(str,method) + + str = str.to_s # to be sure + case method + when 'name' then # no path, no suffix + case str + when /^.*[\\\/](.+?)\..*?$/o then $1 + when /^.*[\\\/](.+?)$/o then $1 + when /^(.*)\..*?$/o then $1 + else str + end + when 'path' then if str =~ /^(.+)([\\\/])(.*?)$/o then $1 else '' end + when 'suffix' then if str =~ /^.*\.(.*?)$/o then $1 else '' end + when 'nosuffix' then if str =~ /^(.*)\..*?$/o then $1 else str end + when 'nopath' then if str =~ /^.*[\\\/](.*?)$/o then $1 else str end + else str + end + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registeraction('manipulate', ' [--test] manipulatorfile resourselog') + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registerflag('test') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'help') diff --git a/Master/texmf-dist/scripts/context/ruby/texmfstart.rb b/Master/texmf-dist/scripts/context/ruby/texmfstart.rb new file mode 100644 index 00000000000..dc166bf92c3 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/texmfstart.rb @@ -0,0 +1,847 @@ +#!/usr/bin/env ruby + +# program : texmfstart +# copyright : PRAGMA Advanced Document Engineering +# version : 1.5.5 - 2003/2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# info : j.hagen@xs4all.nl +# www : www.pragma-pod.com / www.pragma-ade.com + +# no special requirements, i.e. no exa modules/classes used + +# texmfstart [switches] filename [optional arguments] +# +# ruby2exe texmfstart --help -> avoids stub test +# +# Of couse I can make this into a nice class, which i'll undoubtely will +# do when I feel the need. In that case it will be part of a bigger game. + +# turning this into a service would be nice, so some day ... + +# --locate => provides location +# --exec => exec instead of system +# --iftouched=a,b => only if timestamp a<>b +# +# file: path: bin: + +# texmfstart --exec bin:scite *.tex + +# we don't depend on other libs + +$ownpath = File.expand_path(File.dirname($0)) unless defined? $ownpath + +require "rbconfig" + +$mswindows = Config::CONFIG['host_os'] =~ /mswin/ +$separator = File::PATH_SEPARATOR +$version = "1.6.2" + +if $mswindows then + + require "win32ole" + require "Win32API" + +end + +exit if defined?(REQUIRE2LIB) + +$stdout.sync = true +$stderr.sync = true + +$applications = Hash.new +$suffixinputs = Hash.new +$predefined = Hash.new + +$suffixinputs['pl'] = 'PERLINPUTS' +$suffixinputs['rb'] = 'RUBYINPUTS' +$suffixinputs['py'] = 'PYTHONINPUTS' +$suffixinputs['lua'] = 'LUAINPUTS' +$suffixinputs['jar'] = 'JAVAINPUTS' +$suffixinputs['pdf'] = 'PDFINPUTS' + +$predefined['texexec'] = 'texexec.pl' +$predefined['texutil'] = 'texutil.pl' +$predefined['texfont'] = 'texfont.pl' + +$predefined['mptopdf'] = 'mptopdf.pl' +$predefined['pstopdf'] = 'pstopdf.rb' + +$predefined['examplex'] = 'examplex.rb' +$predefined['concheck'] = 'concheck.rb' + +$predefined['textools'] = 'textools.rb' +$predefined['tmftools'] = 'tmftools.rb' +$predefined['ctxtools'] = 'ctxtools.rb' +$predefined['rlxtools'] = 'rlxtools.rb' +$predefined['pdftools'] = 'pdftools.rb' +$predefined['mpstools'] = 'mpstools.rb' +$predefined['exatools'] = 'exatools.rb' +$predefined['xmltools'] = 'xmltools.rb' + + +if ENV['TEXMFSTART_MODE'] = 'experimental' then + $predefined['texexec'] = 'newtexexec.rb' + $predefined['pstopdf'] = 'newpstopdf.rb' +end + +$scriptlist = 'rb|pl|py|lua|jar' +$documentlist = 'pdf|ps|eps|htm|html' + +$editor = ENV['EDITOR'] || ENV['editor'] || 'scite' + +$crossover = true # to other tex tools, else only local + +$applications['unknown'] = '' +$applications['perl'] = $applications['pl'] = 'perl' +$applications['ruby'] = $applications['rb'] = 'ruby' +$applications['python'] = $applications['py'] = 'python' +$applications['lua'] = $applications['lua'] = 'lua' +$applications['java'] = $applications['jar'] = 'java' + +if $mswindows then + $applications['pdf'] = ['',"pdfopen --page #{$page} --file",'acroread'] + $applications['html'] = ['','netscape','mozilla','opera','iexplore'] + $applications['ps'] = ['','gview32','gv','gswin32','gs'] +else + $applications['pdf'] = ["pdfopen --page #{$page} --file",'acroread'] + $applications['html'] = ['netscape','mozilla','opera'] + $applications['ps'] = ['gview','gv','gs'] +end + +$applications['htm'] = $applications['html'] +$applications['eps'] = $applications['ps'] + +if $mswindows then + + GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', ['P','P','N'], 'N') + GetLongPathName = Win32API.new('kernel32', 'GetLongPathName', ['P','P','N'], 'N') + + def dowith_pathname (filename,filemethod) + filename = filename.gsub(/\\/o,'/') # no gsub! because filename can be frozen + case filename + when /\;/o then + # could be a path spec + return filename + when /\s+/o then + # danger lurking + buffer = ' ' * 260 + length = filemethod.call(filename,buffer,buffer.size) + if length>0 then + return buffer.slice(0..length-1) + else + # when the path or file does not exist, nothing is returned + # so we try to handle the path separately from the basename + basename = File.basename(filename) + pathname = File.dirname(filename) + length = filemethod.call(pathname,buffer,260) + if length>0 then + return buffer.slice(0..length-1) + '/' + basename + else + return filename + end + end + else + # no danger + return filename + end + end + + def longpathname (filename) + dowith_pathname(filename,GetLongPathName) + end + + def shortpathname (filename) + dowith_pathname(filename,GetShortPathName) + end + +else + + def longpathname (filename) + filename + end + + def shortpathname (filename) + filename + end + +end + +class File + + def File.needsupdate(oldname,newname) + begin + if $mswindows then + return File.stat(oldname).mtime > File.stat(newname).mtime + else + return File.stat(oldname).mtime != File.stat(newname).mtime + end + rescue + return true + end + end + + def File.timestamp(name) + begin + "#{File.stat(name).mtime}" + rescue + return 'unknown' + end + end + + def File.syncmtimes(oldname,newname) + begin + if $mswindows then + # does not work (yet) + else + t = File.mtime(oldname) # i'm not sure if the time is frozen, so we do it here + File.utime(0,t,oldname,newname) + end + rescue + end + end + +end + +def hashed (arr=[]) + arg = if arr.class == String then arr.split(' ') else arr.dup end + hsh = Hash.new + if arg.length > 0 + hsh['arguments'] = '' + done = false + arg.each do |s| + if done then + hsh['arguments'] += ' ' + s + else + kvl = s.split('=') + if kvl[0].sub!(/^\-+/,'') then + hsh[kvl[0]] = if kvl.length > 1 then kvl[1] else true end + else + hsh['file'] = s + done = true + end + end + end + end + return hsh +end + +def launch(filename) + if $browser && $mswindows then + filename = filename.gsub(/\.[\/\\]/) do + Dir.getwd + '/' + end + report("launching #{filename}") + ie = WIN32OLE.new("InternetExplorer.Application") + ie.visible = true + ie.navigate(filename) + return true + else + return false + end +end + +def expanded(arg) # no "other text files", too restricted + arg.gsub(/(env|environment)\:([a-zA-Z\-\_\.0-9]+)/o) do + method, original, resolved = $1, $2, '' + if resolved = ENV[original] then + report("environment variable #{original} expands to #{resolved}") unless $report + resolved + else + report("environment variable #{original} cannot be resolved") unless $report + original + end + end . gsub(/(kpse|loc|file|path)\:([a-zA-Z\-\_\.0-9]+)/o) do # was: \S + method, original, resolved = $1, $2, '' + if $program && ! $program.empty? then + pstrings = ["-progname=#{$program}"] + else + pstrings = ['','progname=context'] + end + # auto suffix with texinputs as fall back + if ENV["_CTX_K_V_#{original}_"] then + resolved = ENV["_CTX_K_V_#{original}_"] + report("environment provides #{original} as #{resolved}") unless $report + resolved + else + pstrings.each do |pstr| + if resolved.empty? then + command = "kpsewhich #{pstr} #{original}" + report("running #{command}") + begin + resolved = `#{command}`.chomp + rescue + resolved = '' + end + end + # elsewhere in the tree + if resolved.empty? then + command = "kpsewhich #{pstr} -format=\"other text files\" #{original}" + report("running #{command}") + begin + resolved = `#{command}`.chomp + rescue + resolved = '' + end + end + end + if resolved.empty? then + original = File.dirname(original) if method =~ /path/ + report("#{original} is not resolved") unless $report + ENV["_CTX_K_V_#{original}_"] = original if $crossover + original + else + resolved = File.dirname(resolved) if method =~ /path/ + report("#{original} is resolved to #{resolved}") unless $report + ENV["_CTX_K_V_#{original}_"] = resolved if $crossover + resolved + end + end + end +end + +def runcommand(command) + if $locate then + command = command.split(' ').collect do |c| + if c =~ /\//o then + begin + cc = File.expand_path(c) + c = cc if FileTest.file?(cc) + rescue + end + end + c + end . join(' ') + print command # to stdout and no newline + elsif $execute then + report("using 'exec' instead of 'system' call: #{command}") + begin + Dir.chdir($path) if ! $path.empty? + rescue + report("unable to chdir to: #{$path}") + end + exec(command) + else + report("using 'system' call: #{command}") + begin + Dir.chdir($path) if ! $path.empty? + rescue + report("unable to chdir to: #{$path}") + end + system(command) + end +end + +def runoneof(application,fullname,browserpermitted) + if browserpermitted && launch(fullname) then + return true + else + report("starting #{$filename}") unless $report + output("\n") if $report && $verbose + applications = $applications[application] + if applications.class == Array then + if $report then + output([fullname,expanded($arguments)].join(' ')) + return true + else + applications.each do |a| + return true if runcommand([a,fullname,expanded($arguments)].join(' ')) + end + end + elsif applications.empty? then + if $report then + output([fullname,expanded($arguments)].join(' ')) + return true + else + return runcommand([fullname,expanded($arguments)].join(' ')) + end + else + if $report then + output([applications,fullname,expanded($arguments)].join(' ')) + return true + else + return runcommand([applications,fullname,expanded($arguments)].join(' ')) + end + end + return false + end +end + +def report(str) + $stderr.puts(str) if $verbose +end + +def output(str) + $stderr.puts(str) +end + +def usage + print "version : #{$version} - 2003/2005 - www.pragma-ade.com\n" + print("\n") + print("usage : texmfstart [switches] filename [optional arguments]\n") + print("\n") + print("switches : --verbose --report --browser --direct --execute --locate --iftouched\n") + print(" --program --file --page --arguments --batch --edit --report --clear\n") + print(" --make --lmake --wmake --path --stubpath --indirect --before --after\n") + print("\n") + print("example : texmfstart pstopdf.rb cow.eps\n") + print(" texmfstart --locate examplex.rb\n") + print(" texmfstart --execute examplex.rb\n") + print(" texmfstart --browser examplap.pdf\n") + print(" texmfstart showcase.pdf\n") + print(" texmfstart --page=2 --file=showcase.pdf\n") + print(" texmfstart --program=yourtex yourscript.pl arg-1 arg-2\n") + print(" texmfstart --direct xsltproc kpse:somefile.xsl somefile.xml\n") + print(" texmfstart bin:xsltproc env:somepreset path:somefile.xsl somefile.xml\n") + print(" texmfstart --iftouched=normal,lowres downsample.rb normal lowres\n") + print(" texmfstart texmfstart bin:scite kpse:texmf.cnf\n") + print(" texmfstart texmfstart --exec bin:scite *.tex\n") + print(" texmfstart texmfstart --edit texmf.cnf\n") +end + +# somehow registration does not work out (at least not under windows) + +def tag(name) + if $crossover then "_CTX_K_S_#{name}_" else "TEXMFSTART.#{name}" end +end + +def registered?(filename) + return ENV[tag(filename)] != nil +end + +def registered(filename) + return ENV[tag(filename)] +end + +def register(filename,fullname) + if fullname && ! fullname.empty? then # && FileTest.file?(fullname) + ENV[tag(filename)] = fullname + report("registering '#{filename}' as '#{fullname}'") + return true + else + return false + end +end + +def find(filename,program) + filename = filename.sub(/script:/o, '') # so we have bin: and script: and nothing + if $predefined.key?(filename) then + report("expanding '#{filename}' to '#{$predefined[filename]}'") + filename = $predefined[filename] + end + if registered?(filename) then + report("already located '#{filename}'") + return registered(filename) + end + # create suffix list + if filename =~ /^(.*)\.(.+)$/ then + filename = $1 + suffixlist = [$2] + else + suffixlist = [$scriptlist.split('|'),$documentlist.split('|')].flatten + end + # first we honor a given path + if filename =~ /[\\\/]/ then + report("trying to honor '#{filename}'") + suffixlist.each do |suffix| + fullname = filename+'.'+suffix + if FileTest.file?(fullname) && register(filename,fullname) + return shortpathname(fullname) + end + end + end + filename.sub!(/^.*[\\\/]/, '') + # next we look at the current path and the callerpath + [['.','current'],[$ownpath,'caller'],[registered("THREAD"),'thread']].each do |p| + if p && ! p.empty? then + suffixlist.each do |suffix| + fname = "#{filename}.#{suffix}" + fullname = File.expand_path(File.join(p[0],fname)) + report("locating '#{fname}' in #{p[1]} path '#{p[0]}'") + if FileTest.file?(fullname) && register(filename,fullname) then + report("'#{fname}' located in #{p[1]} path") + return shortpathname(fullname) + end + end + end + end + # now we consult environment settings + fullname = nil + suffixlist.each do |suffix| + begin + break unless $suffixinputs[suffix] + environment = ENV[$suffixinputs[suffix]] || ENV[$suffixinputs[suffix]+".#{$program}"] + if ! environment || environment.empty? then + begin + environment = `kpsewhich -expand-path=\$#{$suffixinputs[suffix]}`.chomp + rescue + environment = nil + else + if environment && ! environment.empty? then + report("using kpsewhich variable #{$suffixinputs[suffix]}") + end + end + elsif environment && ! environment.empty? then + report("using environment variable #{$suffixinputs[suffix]}") + end + if environment && ! environment.empty? then + environment.split($separator).each do |e| + e.strip! + e = '.' if e == '\.' # somehow . gets escaped + e += '/' unless e =~ /[\\\/]$/ + fullname = e + filename + '.' + suffix + report("testing '#{fullname}'") + if FileTest.file?(fullname) then + break + else + fullname = nil + end + end + end + rescue + report("environment string '#{$suffixinputs[suffix]}' cannot be used to locate '#{filename}'") + fullname = nil + else + return shortpathname(fullname) if register(filename,fullname) + end + end + return shortpathname(fullname) if register(filename,fullname) + # then we fall back on kpsewhich + suffixlist.each do |suffix| + # TDS script scripts location as per 2004 + if suffix =~ /(#{$scriptlist})/ then + begin + report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (1)") + fullname = `kpsewhich -progname=#{program} -format=texmfscripts #{filename}.#{suffix}`.chomp + rescue + report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (1)") + fullname = nil + else + return shortpathname(fullname) if register(filename,fullname) + end + end + # old TDS location: .../texmf/context/... + begin + report("using 'kpsewhich' to locate '#{filename}' in suffix space '#{suffix}' (2)") + fullname = `kpsewhich -progname=#{program} -format="other text files" #{filename}.#{suffix}`.chomp + rescue + report("kpsewhich cannot locate '#{filename}' in suffix space '#{suffix}' (2)") + fullname = nil + else + return shortpathname(fullname) if register(filename,fullname) + end + end + return shortpathname(fullname) if register(filename,fullname) + # let's take a look at the path + paths = ENV['PATH'].split($separator) + suffixlist.each do |s| + paths.each do |p| + report("checking #{p} for suffix #{s}") + if FileTest.file?(File.join(p,"#{filename}.#{s}")) then + fullname = File.join(p,"#{filename}.#{s}") + return shortpathname(fullname) if register(filename,fullname) + end + end + end + # bad luck, we need to search the tree ourselves + if (suffixlist.length == 1) && (suffixlist.first =~ /(#{$documentlist})/) then + report("aggressively locating '#{filename}' in document trees") + begin + texroot = `kpsewhich -expand-var=$SELFAUTOPARENT`.chomp + rescue + texroot = '' + else + texroot.sub!(/[\\\/][^\\\/]*?$/, '') + end + if not texroot.empty? then + sffxlst = suffixlist.join(',') + begin + report("locating '#{filename}' in document tree '#{texroot}/doc*'") + if (result = Dir.glob("#{texroot}/doc*/**/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then + fullname = result[0] + end + rescue + report("locating '#{filename}.#{suffix}' in tree '#{texroot}' aborted") + end + end + return shortpathname(fullname) if register(filename,fullname) + end + report("aggressively locating '#{filename}' in tex trees") + begin + textrees = `kpsewhich -expand-var=$TEXMF`.chomp + rescue + textrees = '' + end + if not textrees.empty? then + textrees.gsub!(/[\{\}\!]/, '') + textrees = textrees.split(',') + if (suffixlist.length == 1) && (suffixlist.first =~ /(#{$documentlist})/) then + speedup = ['doc**','**'] + else + speedup = ['**'] + end + sffxlst = suffixlist.join(',') + speedup.each do |speed| + textrees.each do |tt| + tt.gsub!(/[\\\/]$/, '') + if FileTest.directory?(tt) then + begin + report("locating '#{filename}' in tree '#{tt}/#{speed}/#{filename}.{#{sffxlst}}'") + if (result = Dir.glob("#{tt}/#{speed}/#{filename}.{#{sffxlst}}")) && result && result[0] && FileTest.file?(result[0]) then + fullname = result[0] + break + end + rescue + report("locating '#{filename}' in tree '#{tt}' aborted") + next + end + end + end + break if fullname && ! fullname.empty? + end + end + if register(filename,fullname) then + return shortpathname(fullname) + else + return '' + end +end + +def run(fullname) + if ! fullname || fullname.empty? then + report("the file '#{$filename}' is not found") + elsif FileTest.file?(fullname) then + begin + case fullname + when /\.(#{$scriptlist})$/ then + return runoneof($1,fullname,false) + when /\.(#{$documentlist})$/ then + return runoneof($1,fullname,true) + else + return runoneof('unknown',fullname,false) + end + rescue + report("starting '#{$filename}' in program space '#{$program}' fails") + end + else + report("the file '#{$filename}' in program space '#{$program}' is not accessible") + end + return false +end + +def direct(fullname) + begin + return runcommand([fullname.sub(/^(bin|binary)\:/, ''),expanded($arguments)].join(' ')) + rescue + return false + end +end + +def edit(filename) + begin + return runcommand([$editor,expanded(filename),expanded($arguments)].join(' ')) + rescue + return false + end +end + +def make(filename,windows=false,linux=false) + basename = filename.dup + basename.sub!(/\.[^.]+?$/, '') + basename.sub!(/^.*[\\\/]/, '') + basename = $stubpath + '/' + basename unless $stubpath.empty? + if basename == filename then + report('nothing made') + else + program = nil + if filename =~ /[\\\/]/ && filename =~ /\.(#{$scriptlist})$/ then + program = $applications[$1] + end + filename = "\"#{filename}\"" if filename =~ /\s/ + program = 'texmfstart' if $indirect || ! program || program.empty? + begin + if windows && f = open(basename+'.bat','w') then + f.binmode + f.write("@echo off\015\012") + f.write("#{program} #{filename} %*\015\012") + f.close + report("windows stub '#{basename}.bat' made") + elsif linux && f = open(basename,'w') then + f.binmode + f.write("#!/bin/sh\012") + f.write("#{program} #{filename} $@\012") + f.close + report("unix stub '#{basename}' made") + end + rescue + report("failed to make stub '#{basename}'") + else + return true + end + end + return false +end + +def process(&block) + + if $iftouched then + files = $directives['iftouched'].split(',') + oldname, newname = files[0], files[1] + if oldname && newname && File.needsupdate(oldname,newname) then + report("file #{oldname}: #{File.timestamp(oldname)}") + report("file #{newname}: #{File.timestamp(newname)}") + report("file is touched, processing started") + yield + File.syncmtimes(oldname,newname) + else + report("file #{oldname} is untouched") + end + else + yield + end + +end + +def checktree(tree) + unless tree.empty? then + begin + setuptex = File.join(tree,'setuptex.tmf') + if FileTest.file?(setuptex) then + report('') + report("tex tree : #{setuptex}") + ENV['TEXPATH'] = tree.sub(/\/+$/,'') # + '/' + ENV['TMP'] = ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || ENV['HOME'] + case RUBY_PLATFORM + when /(mswin|bccwin|mingw|cygwin)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-mswin' + when /(linux)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-linux' + when /(darwin|rhapsody|nextstep)/i then ENV['TEXOS'] = ENV['TEXOS'] || 'texmf-macosx' + # when /(netbsd|unix)/i then # todo + else # todo + end + ENV['TEXMFOS'] = "#{ENV['TEXPATH']}/#{ENV['TEXOS']}" + report('') + report("preset : TEXPATH => #{ENV['TEXPATH']}") + report("preset : TEXOS => #{ENV['TEXOS']}") + report("preset : TEXMFOS => #{ENV['TEXMFOS']}") + report("preset : TMP => #{ENV['TMP']}") + report('') + IO.readlines(File.join(tree,'setuptex.tmf')).each do |line| + case line + when /^[\#\%]/ then + # comment + when /^(.*?)\s+\=\s+(.*)\s*$/ then + k, v = $1, $2 + ENV[k] = v.gsub(/\%(.*?)\%/) do + ENV[$1] || '' + end + report("user set : #{k} => #{ENV[k]}") + end + end + else + report("no setup file '#{setuptex}'") + end + rescue + end + end +end + +def execute(arguments) + + arguments = arguments.split(/\s+/) if arguments.class == String + + $directives = hashed(arguments) + + $help = $directives['help'] || false + $batch = $directives['batch'] || false + $filename = $directives['file'] || '' + $program = $directives['program'] || 'context' + $direct = $directives['direct'] || false + $edit = $directives['edit'] || false + $page = $directives['page'] || 0 + $browser = $directives['browser'] || false + $report = $directives['report'] || false + $verbose = $directives['verbose'] || (ENV['_CTX_VERBOSE_'] =~ /(y|yes|t|true|on)/io) || false + $arguments = $directives['arguments'] || '' + $execute = $directives['execute'] || $directives['exec'] || false + $locate = $directives['locate'] || false + + $path = $directives['path'] || '' + $tree = $directives['tree'] || '' + + + $make = $directives['make'] || false + $unix = $directives['unix'] || false + $windows = $directives['windows'] || false + $stubpath = $directives['stubpath'] || '' + $indirect = $directives['indirect'] || false + + $before = $directives['before'] || '' + $after = $directives['after'] || '' + + $iftouched = $directives['iftouched'] || false + + $openoffice = $directives['oo'] || false + + $crossover = false if $directives['clear'] + + ENV['_CTX_VERBOSE_'] = 'yes' if $verbose + + if $openoffice then + if ENV['OOPATH'] then + if FileTest.directory?(ENV['OOPATH']) then + report("using open office python") + if $mswindows then + $applications['python'] = $applications['py'] = "\"#{File.join(ENV['OOPATH'],'program','python.bat')}\"" + else + $applications['python'] = $applications['py'] = File.join(ENV['OOPATH'],'python') + end + report("python path #{$applications['python']}") + else + report("environment variable 'OOPATH' does not exist") + end + else + report("environment variable 'OOPATH' is not set") + end + end + + if $help || ! $filename || $filename.empty? then + usage + checktree($tree) + elsif $batch && $filename && ! $filename.empty? then + # todo, take commands from file and avoid multiple starts and checks + else + report("texmfstart version #{$version}") + checktree($tree) + if $make then + if $windows then + make($filename,true,false) + elsif $unix then + make($filename,false,true) + else + make($filename,$mswindows,!$mswindows) + end + elsif $browser && $filename =~ /^http\:\/\// then + launch($filename) + else + begin + process do + if $direct || $filename =~ /^bin\:/ then + direct($filename) + elsif $edit && ! $editor.empty? then + edit($filename) + else # script: or no prefix + command = find(shortpathname($filename),$program) + register("THREAD",File.dirname(File.expand_path(command))) + run(command) + end + end + rescue + report('fatal error in starting process') + end + end + end + +end + +execute(ARGV) diff --git a/Master/texmf-dist/scripts/context/ruby/texsync.rb b/Master/texmf-dist/scripts/context/ruby/texsync.rb new file mode 100644 index 00000000000..22b7d46c0fe --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/texsync.rb @@ -0,0 +1,207 @@ +#!/usr/bin/env ruby + +# program : texsync +# copyright : PRAGMA Advanced Document Engineering +# version : 2003-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# For the moment this script only handles the 'minimal' context +# distribution. In due time I will add a few more options, like +# synchronization of the iso image. + +banner = ['TeXSync', 'version 1.1.1', '2002/2004', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/]\w*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' +# require 'base/tool' + +require 'rbconfig' + +class Commands + + include CommandBase + + @@formats = ['en','nl','de','cz','it','ro'] + @@always = ['metafun','mptopdf','en','nl'] + @@rsync = 'rsync -r -z -c --progress --stats "--exclude=*.fmt" "--exclude=*.efmt" "--exclude=*.mem"' + + @@kpsewhich = Hash.new + + @@kpsewhich['minimal'] = 'SELFAUTOPARENT' + @@kpsewhich['context'] = 'TEXMFLOCAL' + @@kpsewhich['documentation'] = 'TEXMFLOCAL' + @@kpsewhich['unknown'] = 'SELFAUTOPARENT' + + def update + + report + + return unless destination = getdestination + + texpaths = gettexpaths + address = option('address') + user = option('user') + tree = option('tree') + force = option('force') + + ok = true + begin + report("synchronizing '#{tree}' from '#{address}' to '#{destination}'") + report + if texpaths then + texpaths.each do |path| + report("synchronizing path '#{path}' of '#{tree}' from '#{address}' to '#{destination}'") + command = "#{rsync} #{user}@#{address}::#{tree}/#{path} #{destination}/{path}" + ok = ok && system(command) if force + end + else + command = "#{@@rsync} #{user}@#{address}::#{tree} #{destination}" + ok = system(command) if force + end + rescue + report("error in running rsync") + ok = false + ensure + if force then + if ok then + if option('make') then + report("generating tex and metapost formats") + report + @@formats.delete_if do |f| + begin + `kpsewhich cont-#{f}`.chomp.empty? + rescue + end + end + str = [@@formats,@@always].flatten.uniq.join(' ') + begin + system("texexec --make --alone #{str}") + rescue + report("unable to generate formats '#{str}'") + else + report + end + else + report("regenerate the formats files if needed") + end + else + report("error in synchronizing '#{tree}'") + end + else + report("provide --force to execute '#{command}'") unless force + end + end + + end + + def list + + report + + address = option('address') + user = option('user') + result = nil + + begin + report("fetching list of trees from '#{address}'") + command = "#{@@rsync} #{user}@#{address}::" + if option('force') then + result = `#{command}`.chomp + else + report("provide --force to execute '#{command}'") + end + rescue + result = nil + else + if result then + report("available trees:") + report + reportlines(result) + end + ensure + report("unable to fetch list") unless result + end + + end + + private + + def gettexpaths + if option('full') then + texpaths = ['texmf','texmf-local','texmf-fonts','texmf-mswin','texmf-linux','texmf-macos'] + elsif option('terse') then + texpaths = ['texmf','texmf-local','texmf-fonts'] + case Config::CONFIG['host_os'] # or: Tool.ruby_platform + when /mswin/ then texpaths.push('texmf-mswin') + when /linux/ then texpaths.push('texmf-linux') + when /darwin/ then texpaths.push('texmf-macosx') + end + else + texpaths = nil + end + texpaths + end + + def getdestination + if (destination = option('destination')) && ! destination.empty? then + begin + if @@kpsewhich.key?(destination) then + destination = @@kpsewhich[option('tree')] || @@kpsewhich['unknown'] + destination = `kpsewhich --expand-var=$#{destination}`.chomp + elsif ! FileTest.directory?(destination) then + destination = nil + end + rescue + report("unable to determine destination tex root") + else + if ! destination || destination.empty? then + report("no destination is specified") + elsif not FileTest.directory?(destination) then + report("invalid destination '#{destination}'") + elsif not FileTest.writable?(destination) then + report("destination '#{destination}' is not writable") + else + report("using destination '#{destination}'") + return destination + end + end + else + report("unknown destination") + end + return nil + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registeraction('update', 'update installed tree') +commandline.registeraction('list', 'list available trees') + +commandline.registerflag('terse', 'download as less as possible (esp binaries)') +commandline.registerflag('full', 'download everything (all binaries)') +commandline.registerflag('force', 'confirm action') +commandline.registerflag('make', 'remake formats') + +commandline.registervalue('address', 'www.pragma-ade.com', 'adress of repository (www.pragma-ade)') +commandline.registervalue('user', 'guest', 'user account (guest)') +commandline.registervalue('tree', 'tex', 'tree to synchronize (tex)') +commandline.registervalue('destination', nil, 'destination of tree (kpsewhich)') + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'help') diff --git a/Master/texmf-dist/scripts/context/ruby/textools.rb b/Master/texmf-dist/scripts/context/ruby/textools.rb new file mode 100644 index 00000000000..78982f175a1 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/textools.rb @@ -0,0 +1,885 @@ +#!/usr/bin/env ruby + +# program : textools +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# This script will harbor some handy manipulations on tex +# related files. + +banner = ['TeXTools', 'version 1.2.2', '2002/2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' + +require 'ftools' + +# Remark +# +# The fixtexmftrees feature does not realy belong in textools, but +# since it looks like no measures will be taken to make texlive (and +# tetex) downward compatible with respect to fonts installed by +# users, we provide this fixer. This option also moves script files +# to their new location (only for context) in the TDS. Beware: when +# locating scripts, the --format switch in kpsewhich should now use +# 'texmfscripts' instead of 'other text files' (texmfstart is already +# aware of this). Files will only be moved when --force is given. Let +# me know if more fixes need to be made. + +class Commands + + include CommandBase + + def hidemapnames + report('hiding FontNames in map files') + xidemapnames(true) + end + + def videmapnames + report('unhiding FontNames in map files') + xidemapnames(false) + end + + def removemapnames + + report('removing FontNames from map files') + + if files = findfiles('map') then + report + files.sort.each do |fn| + gn = fn # + '.nonames' + hn = fn + '.original' + begin + if FileTest.file?(fn) && ! FileTest.file?(hn) then + if File.rename(fn,hn) then + if (fh = File.open(hn,'r')) && (gh = File.open(gn,'w')) then + report("processing #{fn}") + while str = fh.gets do + str.sub!(/^([^\%]+?)(\s+)([^\"\<\s]*?)(\s)/) do + $1 + $2 + " "*$3.length + $4 + end + gh.puts(str) + end + fh.close + gh.close + else + report("no permissions to handle #{fn}") + end + else + report("unable to rename #{fn} to #{hn}") + end + else + report("not processing #{fn} due to presence of #{hn}") + end + rescue + report("error in handling #{fn}") + end + end + end + + end + + def restoremapnames + + report('restoring FontNames in map files') + + if files = findfiles('map') then + report + files.sort.each do |fn| + hn = fn + '.original' + begin + if FileTest.file?(hn) then + File.delete(fn) if FileTest.file?(fn) + report("#{fn} restored") if File.rename(hn,fn) + else + report("no original found for #{fn}") + end + rescue + report("error in restoring #{fn}") + end + end + end + + end + + def findfile + + report('locating file in texmf tree') + + # ! not in tree + # ? fuzzy + # . in tree + # > in tree and used + + if filename = @commandline.argument('first') then + if filename && ! filename.empty? then + report + used = kpsefile(filename) || pathfile(filename) + if paths = texmfroots then + found, prefered = false, false + paths.each do |p| + if files = texmffiles(p,filename) then + found = true + files.each do |f| + # unreadable: report("#{if f == used then '>' else '.' end} #{f}") + if f == used then + prefered = true + report("> #{f}") + else + report(". #{f}") + end + end + end + end + if prefered then + report("! #{used}") unless found + else + report("> #{used}") + end + elsif used then + report("? #{used}") + else + report('no file found') + end + else + report('no file specified') + end + else + report('no file specified') + end + + end + + def unzipfiles + + report('g-unzipping files') + + if files = findfiles('gz') then + report + files.each do |f| + begin + system("gunzip -d #{f}") + rescue + report("unable to unzip file #{f}") + else + report("file #{f} is unzipped") + end + end + end + + end + + def fixafmfiles + + report('fixing afm files') + + if files = findfiles('afm') then + report + ok = false + files.each do |filename| + if filename =~ /\.afm$/io then + if f = File.open(filename) then + result = '' + done = false + while str = f.gets do + str.chomp! + str.strip! + if str.empty? then + # skip + elsif (str.length > 200) && (str =~ /^(comment|notice)\s(.*)\s*$/io) then + done = true + tag, words, len = $1, $2.split(' '), 0 + result += tag + while words.size > 0 do + str = words.shift + len += str.length + 1 + result += ' ' + str + if len > (70 - tag.length) then + result += "\n" + result += tag if words.size > 0 + len = 0 + end + end + result += "\n" if len>0 + else + result += str + "\n" + end + end + f.close + if done then + ok = true + begin + if File.rename(filename,filename+'.original') then + if FileTest.file?(filename) then + report("something to fix in #{filename} but error in renaming (3)") + elsif f = File.open(filename,'w') then + f.puts(result) + f.close + report('file', filename, 'has been fixed') + else + report("something to fix in #{filename} but error in opening (4)") + File.rename(filename+'.original',filename) # gamble + end + else + report("something to fix in #{filename} but error in renaming (2)") + end + rescue + report("something to fix in #{filename} but error in renaming (1)") + end + else + report("nothing to fix in #{filename}") + end + else + report("error in opening #{filename}") + end + end + end + report('no files match the pattern') unless ok + end + + end + + def mactodos + + report('fixing mac newlines') + + if files = findfiles('tex') then + report + files.each do |filename| + begin + report("converting file #{filename}") + tmpfilename = filename + '.tmp' + if f = File.open(filename) then + if g = File.open(tmpfilename, 'w') + while str = f.gets do + g.puts(str.gsub(/\r/,"\n")) + end + if f.close && g.close && FileTest.file?(tmpfilename) then + File.delete(filename) + File.rename(tmpfilename,filename) + end + else + report("unable to open temporary file #{tmpfilename}") + end + else + report("unable to open #{filename}") + end + rescue + report("problems with fixing #{filename}") + end + end + end + + end + + def fixtexmftrees + + if paths = @commandline.argument('first') then + paths = [paths] if ! paths.empty? + end + paths = texmfroots if paths.empty? + + if paths then + + moved = 0 + force = @commandline.option('force') + + report + report("checking TDS 2003 => TDS 2004 : map files") + # report + + # move [map,enc] files from /texmf/[dvips,pdftex,dvipdfmx] -> /texmf/fonts/[*] + + ['map','enc'].each do |suffix| + paths.each do |path| + ['dvips','pdftex','dvipdfmx'].each do |program| + report + report("checking #{suffix} files for #{program} on #{path}") + report + moved += movefiles("#{path}/#{program}","#{path}/fonts/#{suffix}/#{program}",suffix) do + # nothing + end + end + end + end + + report + report("checking TDS 2003 => TDS 2004 : scripts") + # report + + # move [rb,pl,py] files from /texmf/someplace -> /texmf/scripts/someplace + + ['rb','pl','py'].each do |suffix| + paths.each do |path| + ['context'].each do |program| + report + report("checking #{suffix} files for #{program} on #{path}") + report + moved += movefiles("#{path}/#{program}","#{path}/scripts/#{program}",suffix) do |f| + f.gsub!(/\/(perl|ruby|python)tk\//o) do + "/#{$1}/" + end + end + end + end + end + + begin + if moved>0 then + report + if force then + system('mktexlsr') + report + report("#{moved} files moved") + else + report("#{moved} files will be moved") + end + else + report('no files need to be moved') + end + rescue + report('you need to run mktexlsr') + end + + end + + end + + def replacefile + + report('replace file') + + if newname = @commandline.argument('first') then + if newname && ! newname.empty? then + report + report("replacing #{newname}") + report + oldname = kpsefile(File.basename(newname)) + force = @commandline.option('force') + if oldname && ! oldname.empty? then + oldname = File.expand_path(oldname) + newname = File.expand_path(newname) + report("old: #{oldname}") + report("new: #{newname}") + report + if newname == oldname then + report('unable to replace itself') + elsif force then + begin + File.copy(newname,oldname) + rescue + report('error in replacing the old file') + end + else + report('the old file will be replaced (use --force)') + end + else + report('nothing to replace') + end + else + report('no file specified') + end + else + report('no file specified') + end + + end + + private # general + + def texmfroots + begin + paths = `kpsewhich -expand-path=\$TEXMF`.chomp + rescue + else + return paths.split(/#{File::PATH_SEPARATOR}/) if paths && ! paths.empty? + end + return nil + end + + def texmffiles(root, filename) + begin + files = Dir.glob("#{root}/**/#{filename}") + rescue + else + return files if files && files.length>0 + end + return nil + end + + def pathfile(filename) + used = nil + begin + if ! filename || filename.empty? then + return nil + else + ENV['PATH'].split(File::PATH_SEPARATOR).each do |path| + if FileTest.file?(File.join(path,filename)) then + used = File.join(path,filename) + break + end + end + end + rescue + used = nil + else + used = nil if used && used.empty? + end + return used + end + + def kpsefile(filename) + used = nil + begin + if ! filename || filename.empty? then + return nil + else + used = `kpsewhich #{filename}`.chomp + end + if used && used.empty? then + used = `kpsewhich -progname=context #{filename}`.chomp + end + if used && used.empty? then + used = `kpsewhich -format=texmfscripts #{filename}`.chomp + end + if used && used.empty? then + used = `kpsewhich -progname=context -format=texmfscripts #{filename}`.chomp + end + if used && used.empty? then + used = `kpsewhich -format="other text files" #{filename}`.chomp + end + if used && used.empty? then + used = `kpsewhich -progname=context -format="other text files" #{filename}`.chomp + end + rescue + used = nil + else + used = nil if used && used.empty? + end + return used + end + + def downcasefilenames + + report('downcase filenames') + + force = @commandline.option('force') + + # if @commandline.option('recurse') then + # files = Dir.glob('**/*') + # else + # files = Dir.glob('*') + # end + # if files && files.length>0 then + + if files = findfiles() then + files.each do |oldname| + if FileTest.file?(oldname) then + newname = oldname.downcase + if oldname != newname then + if force then + begin + File.rename(oldname,newname) + rescue + report("#{oldname} == #{oldname}\n") + else + report("#{oldname} => #{newname}\n") + end + else + report("(#{oldname} => #{newname})\n") + end + end + end + end + end + end + + def stripformfeeds + + report('strip formfeeds') + + force = @commandline.option('force') + + if files = findfiles() then + files.each do |filename| + if FileTest.file?(filename) then + begin + data = IO.readlines(filename).join('') + rescue + else + if data.gsub!(/\n*\f\n*/io,"\n\n") then + if force then + if f = open(filename,'w') then + report("#{filename} is stripped\n") + f.puts(data) + f.close + else + report("#{filename} cannot be stripped\n") + end + else + report("#{filename} will be stripped\n") + end + end + end + end + end + end + end + + public + + def showfont + + file = @commandline.argument('first') + + if file.empty? then + report('provide filename') + else + file.sub!(/\.afm$/,'') + begin + report("analyzing afm file #{file}.afm") + file = `kpsewhich #{file}.afm`.chomp + rescue + report('unable to run kpsewhich') + return + end + + names = Array.new + + if FileTest.file?(file) then + File.new(file).each do |line| + if line.match(/^C\s*([\-\d]+)\s*\;.*?\s*N\s*(.+?)\s*\;/o) then + names.push($2) + end + end + ranges = names.size + report("number of glyphs: #{ranges}") + ranges = ranges/256 + 1 + report("number of subsets: #{ranges}") + file = File.basename(file).sub(/\.afm$/,'') + tex = File.open("textools.tex",'w') + map = File.open("textools.map",'w') + tex.puts("\\starttext\n") + tex.puts("\\loadmapfile[textools.map]\n") + for i in 1..ranges do + rfile = "#{file}-range-#{i}" + report("generating enc file #{rfile}.enc") + flushencoding("#{rfile}", (i-1)*256, i*256-1, names) + # catch console output + report("generating tfm file #{rfile}.tfm") + mapline = `afm2tfm #{file}.afm -T #{rfile}.enc #{rfile}.tfm` + # more robust replacement + mapline = "#{rfile} <#{rfile}.enc <#{file}.pfb" + # final entry in map file + mapline = "#{mapline} <#{file}.pfb" + map.puts("#{mapline}\n") + tex.puts("\\showfont[#{rfile}][unknown]\n") + end + tex.puts("\\stoptext\n") + report("generating map file textools.map") + report("generating tex file textools.tex") + map.close + tex.close + else + report("invalid file #{file}") + end + end + + end + + private + + def flushencoding (file, from, to, names) + n = 0 + out = File.open("#{file}.enc",'w') + out.puts("/#{file.gsub(/\-/,'')} [\n") + for i in from..to do + if names[i] then + n += 1 + out.puts("/#{names[i]}\n") + else + out.puts("/.notdef\n") + end + end + out.puts("] def\n") + out.close + return n + end + + private # specific + + def movefiles(from_path,to_path,suffix,&block) + obsolete = 'obsolete' + force = @commandline.option('force') + moved = 0 + if files = texmffiles(from_path, "*.#{suffix}") then + files.each do |filename| + newfilename = filename.sub(/^#{from_path}/, to_path) + yield(newfilename) if block + if FileTest.file?(newfilename) then + begin + File.rename(filename,filename+'.obsolete') if force + rescue + report("#{filename} cannot be made obsolete") if force + else + if force then + report("#{filename} is made obsolete") + else + report("#{filename} will become obsolete") + end + end + else + begin + File.makedirs(File.dirname(newfilename)) if force + rescue + end + begin + File.copy(filename,newfilename) if force + rescue + report("#{filename} cannot be copied to #{newfilename}") + else + begin + File.delete(filename) if force + rescue + report("#{filename} cannot be deleted") if force + else + if force then + report("#{filename} is moved to #{newfilename}") + moved += 1 + else + report("#{filename} will be moved to #{newfilename}") + end + end + end + end + end + else + report('no matches found') + end + return moved + end + + def xidemapnames(hide) + + filter = /^([^\%]+?)(\s+)([^\"\<\s]*?)(\s)/ + banner = '% textools:nn ' + + if files = findfiles('map') then + report + files.sort.each do |fn| + if fn.has_suffix?('map') then + begin + lines = IO.read(fn) + report("processing #{fn}") + if f = File.open(fn,'w') then + skip = false + if hide then + lines.each do |str| + if skip then + skip = false + elsif str =~ /#{banner}/ then + skip = true + elsif str =~ filter then + f.puts(banner+str) + str.sub!(filter) do + $1 + $2 + " "*$3.length + $4 + end + end + f.puts(str) + end + else + lines.each do |str| + if skip then + skip = false + elsif str.sub!(/#{banner}/, '') then + f.puts(str) + skip = true + else + f.puts(str) + end + end + end + f.close + end + rescue + report("error in handling #{fn}") + end + end + end + end + + end + + public + + def updatetree + + nocheck = @commandline.option('nocheck') + merge = @commandline.option('merge') + delete = @commandline.option('delete') + force = @commandline.option('force') + root = @commandline.argument('first').gsub(/\\/,'/') + path = @commandline.argument('second').gsub(/\\/,'/') + + if FileTest.directory?(root) then + report("scanning #{root}") + rootfiles = Dir.glob("#{root}/**/*") + else + report("provide source root") + return + end + if rootfiles.size > 0 then + report("#{rootfiles.size} files") + else + report("no files") + return + end + rootfiles.collect! do |rf| + rf.gsub(/\\/o, '/').sub(/#{root}\//o, '') + end + rootfiles = rootfiles.delete_if do |rf| + FileTest.directory?(File.join(root,rf)) + end + + if FileTest.directory?(path) then + report("scanning #{path}") + pathfiles = Dir.glob("#{path}/**/*") + else + report("provide destination root") + return + end + if pathfiles.size > 0 then + report("#{pathfiles.size} files") + else + report("no files") + return + end + pathfiles.collect! do |pf| + pf.gsub(/\\/o, '/').sub(/#{path}\//o, '') + end + pathfiles = pathfiles.delete_if do |pf| + FileTest.directory?(File.join(path,pf)) + end + + root = File.expand_path(root) + path = File.expand_path(path) + + donepaths = Hash.new + copiedfiles = Hash.new + + # update existing files, assume similar paths + + report("") + pathfiles.each do |f| # destination + p = File.join(path,f) + if rootfiles.include?(f) then + r = File.join(root,f) + if p != r then + if nocheck or File.mtime(p) < File.mtime(r) then + copiedfiles[File.expand_path(p)] = true + report("updating '#{r}' to '#{p}'") + begin + begin File.makedirs(File.dirname(p)) if force ; rescue ; end + File.copy(r,p) if force + rescue + report("updating failed") + end + else + report("not updating '#{r}'") + end + end + end + end + + # merging non existing files + + report("") + rootfiles.each do |f| + donepaths[File.dirname(f)] = true + r = File.join(root,f) + if not pathfiles.include?(f) then + p = File.join(path,f) + if p != r then + if merge then + copiedfiles[File.expand_path(p)] = true + report("merging '#{r}' to '#{p}'") + begin + begin File.makedirs(File.dirname(p)) if force ; rescue ; end + File.copy(r,p) if force + rescue + report("merging failed") + end + else + report("not merging '#{r}'") + end + end + end + end + + # deleting obsolete files + + report("") + donepaths.keys.sort.each do |d| + pathfiles = Dir.glob("#{path}/#{d}/**/*") + pathfiles.each do |p| + r = File.join(root,d,File.basename(p)) + if FileTest.file?(p) and not FileTest.file?(r) and not copiedfiles.key?(File.expand_path(p)) then + if delete then + report("deleting '#{p}'") + begin + File.delete(p) if force + rescue + report("deleting failed") + end + else + report("not deleting '#{p}'") + end + end + end + end + + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registeraction('removemapnames' , '[pattern] [--recurse]') +commandline.registeraction('restoremapnames' , '[pattern] [--recurse]') +commandline.registeraction('hidemapnames' , '[pattern] [--recurse]') +commandline.registeraction('videmapnames' , '[pattern] [--recurse]') +commandline.registeraction('findfile' , 'filename [--recurse]') +commandline.registeraction('unzipfiles' , '[pattern] [--recurse]') +commandline.registeraction('fixafmfiles' , '[pattern] [--recurse]') +commandline.registeraction('mactodos' , '[pattern] [--recurse]') +commandline.registeraction('fixtexmftrees' , '[texmfroot] [--force]') +commandline.registeraction('replacefile' , 'filename [--force]') +commandline.registeraction('updatetree' , 'fromroot toroot [--force --nocheck --merge --delete]') +commandline.registeraction('downcasefilenames', '[--recurse] [--force]') # not yet documented +commandline.registeraction('stripformfeeds' , '[--recurse] [--force]') # not yet documented +commandline.registeraction('showfont' , 'filename') + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registerflag('recurse') +commandline.registerflag('force') +commandline.registerflag('merge') +commandline.registerflag('delete') +commandline.registerflag('nocheck') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'help') diff --git a/Master/texmf-dist/scripts/context/ruby/tmftools.rb b/Master/texmf-dist/scripts/context/ruby/tmftools.rb new file mode 100644 index 00000000000..d125c5cae69 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/tmftools.rb @@ -0,0 +1,133 @@ +#!/usr/bin/env ruby + +# program : tmftools +# copyright : PRAGMA Advanced Document Engineering +# version : 2005 +# author : Hans Hagen +# +# project : ConTeXt +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# The script based alternative is not slower than the kpse one. +# Loading is a bit faster when the log file is used. + +# todo: create database + +# tmftools [some of the kpsewhich switches] + +# tmftools --analyze +# tmftools --analyze > kpsewhat.log +# tmftools --analyze --strict > kpsewhat.log +# tmftools --analyze --delete --force "texmf-local/fonts/.*/somename" + +# the real thing + +banner = ['TMFTools', 'version 1.0.0 (experimental, no help yet)', '2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' +require 'base/kpsefast' + +class Commands + + include CommandBase + + def init_kpse + k = KPSEFAST.new + k.rootpath = @commandline.option('rootpath') + k.treepath = @commandline.option('treepath') + k.progname = @commandline.option('progname') + k.engine = @commandline.option('engine') + k.format = @commandline.option('format') + k.diskcache = @commandline.option('diskcache') + k.renewcache = @commandline.option('renewcache') + k.load_cnf + k.expand_variables + k.load_lsr + return k + end + + def main + if option = @commandline.option('expand-braces') and not option.empty? then + puts init_kpse.expand_braces(option) + elsif option = @commandline.option('expand-path') and not option.empty? then + puts init_kpse.expand_path(option) + elsif option = @commandline.option('expand-var') and not option.empty? then + if option == '*' then + init_kpse.list_expansions() + else + puts init_kpse.expand_var(option) + end + elsif option = @commandline.option('show-path') and not option.empty? then + puts init_kpse.show_path(option) + elsif option = @commandline.option('var-value') and not option.empty? then + if option == '*' then + init_kpse.list_variables() + else + puts init_kpse.expand_var(option) + end + elsif @commandline.arguments.size > 0 then + kpse = init_kpse + @commandline.arguments.each do |option| + puts kpse.find_file(option) + end + else + help + end + end + + def analyze + pattern = @commandline.argument('first') + strict = @commandline.option('strict') + sort = @commandline.option('sort') + delete = @commandline.option('delete') and @commandline.option('force') + init_kpse.analyze_files(pattern, strict, sort, delete) + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +# kpsewhich compatible options + +commandline.registervalue('expand-braces','') +commandline.registervalue('expand-path','') +commandline.registervalue('expand-var','') +commandline.registervalue('show-path','') +commandline.registervalue('var-value','') + +commandline.registervalue('engine','') +commandline.registervalue('progname','') +commandline.registervalue('format','') + +# additional goodies + +commandline.registervalue('rootpath','') +commandline.registervalue('treepath','') +commandline.registervalue('sort','') + +commandline.registerflag('diskcache') +commandline.registerflag('renewcache') +commandline.registerflag('strict') +commandline.registerflag('delete') +commandline.registerflag('force') + +commandline.registeraction('analyze', "[--strict --sort --rootpath --treepath]\n[--delete [--force]] [pattern]") + +# general purpose options + +commandline.registerflag('verbose') +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'main') diff --git a/Master/texmf-dist/scripts/context/ruby/xmltools.rb b/Master/texmf-dist/scripts/context/ruby/xmltools.rb new file mode 100644 index 00000000000..a57f7445020 --- /dev/null +++ b/Master/texmf-dist/scripts/context/ruby/xmltools.rb @@ -0,0 +1,352 @@ +#!/usr/bin/env ruby + +# program : xmltools +# copyright : PRAGMA Advanced Document Engineering +# version : 2002-2005 +# author : Hans Hagen +# +# project : ConTeXt / eXaMpLe +# concept : Hans Hagen +# info : j.hagen@xs4all.nl +# www : www.pragma-ade.com + +# This script will harbor some handy manipulations on tex +# related files. + +banner = ['XMLTools', 'version 1.1.1', '2002/2005', 'PRAGMA ADE/POD'] + +unless defined? ownpath + ownpath = $0.sub(/[\\\/][a-z0-9\-]*?\.rb/i,'') + $: << ownpath +end + +require 'base/switch' +require 'base/logger' + +class String + + def astring(n=10) + gsub(/(\d+)/o) do $1.to_s.rjust(n) end.gsub(/ /o, '0') + end + + def xstring + if self =~ /\'/o then + "\"#{self.gsub(/\"/, '"')}\"" + else + "\'#{self}\'" + end + end + +end + +class Array + + def asort(n=10) + sort {|x,y| x.astring(n) <=> y.astring(n)} + end + +end + +class Commands + + include CommandBase + + def dir + + @xmlns = "xmlns='http://www.pragma-ade.com/rlg/xmldir.rng'" + + pattern = @commandline.option('pattern') + recurse = @commandline.option('recurse') + stripname = @commandline.option('stripname') + longname = @commandline.option('longname') + url = @commandline.option('url') + outputfile = @commandline.option('output') + root = @commandline.option('root') + + def generate(output,files,url,root,longname) + + class << output + def xputs(str,n=0) + puts("#{' '*n}#{str}") + end + end + + dirname = '' + output.xputs("<?xml version='1.0'?>\n\n") + if ! root || root.empty? then + rootatt = @xmlns + else + rootatt = " #{@xmlns} root='#{root}'" + end + rootatt += " timestamp='#{Time.now}'" + if url.empty? then + output.xputs("<files #{rootatt}>\n") + else + output.xputs("<files url='#{url}'#{rootatt}>\n") + end + files.each do |f| + bn, dn = File.basename(f), File.dirname(f) + if dirname != dn then + output.xputs("</directory>\n", 2) if dirname != '' + output.xputs("<directory name='#{dn}'>\n", 2) + dirname = dn + end + if longname && dn != '.' then + output.xputs("<file name='#{dn}/#{bn}'>\n", 4) + else + output.xputs("<file name='#{bn}'>\n", 4) + end + output.xputs("<base>#{bn.sub(/\..*$/,'')}</base>\n", 6) + if File.stat(f).file? then + bt = bn.sub(/^.*\./,'') + if bt != bn then + output.xputs("<type>#{bt}</type>\n", 6) + end + output.xputs("<size>#{File.stat(f).size}</size>\n", 6) + end + output.xputs("<date>#{File.stat(f).mtime.strftime("%Y-%m-%d %H:%M")}</date>\n", 6) + output.xputs("</file>\n", 4) + end + output.xputs("</directory>\n", 2) if dirname != '' + output.xputs("</files>\n") + + end + + if pattern.empty? then + report('provide --pattern=') + return + end + + unless outputfile.empty? then + begin + output = File.open(outputfile,'w') + rescue + report("unable to open #{outputfile}") + return + end + else + report('provide --output') + return + end + + if stripname && pattern.class == String && ! pattern.empty? then + pattern = File.dirname(pattern) + end + + pattern = '*' if pattern.empty? + + unless root.empty? then + unless FileTest.directory?(root) then + report("unknown root #{root}") + return + end + begin + Dir.chdir(root) + rescue + report("unable to change to root #{root}") + return + end + end + + generate(output, globbed(pattern, recurse), url, root, longname) + + output.close if output + + end + + alias ls :dir + + def mmlpages + + file = @commandline.argument('first') + eps = @commandline.option('eps') + jpg = @commandline.option('jpg') + png = @commandline.option('png') + style = @commandline.option('style') + modes = @commandline.option('modes') + + file = file.sub(/\.xml/io, '') + long = "#{file}-mmlpages" + if FileTest.file?(file+'.xml') then + style = "--arg=\"style=#{style}\"" unless style.empty? + modes = "--mode=#{modes}" unless modes.empty? + if system("texmfstart texexec.pl --batch --pdf --once --result=#{long} --use=mmlpag #{style} #{modes} #{file}.xml") then + if eps then + if f = open("#{file}-mmlpages.txt") then + while line = f.gets do + data = Hash.new + if fields = line.split then + fields.each do |fld| + key, value = fld.split('=') + data[key] = value if key && value + end + if data.key?('p') then + page = data['p'] + name = "#{long}-#{page.to_i-1}" + if eps then + report("generating eps file #{name}") + if system("pdftops -eps -f #{page} -l #{page} #{long}.pdf #{name}.eps") then + if data.key?('d') then + if epsfile = IO.read("#{name}.eps") then + epsfile.sub!(/^(\%\%BoundingBox:.*?$)/i) do + newline = $1 + "\n%%Baseline: #{data['d']}\n" + if data.key?('w') && data.key?('h') then + newline += "%%PositionWidth: #{data['w']}\n" + newline += "%%PositionHeight: #{data['h']}\n" + newline += "%%PositionDepth: #{data['d']}" + end + newline + end + if g = File.open("#{name}.eps",'wb') then + g.write(epsfile) + g.close + end + end + end + else + report("error in generating eps from #{name}") + end + end + end + end + end + f.close + else + report("missing data log file #{file}") + end + end + if png then + report("generating png file for #{long}") + system("imagemagick #{long}.pdf #{long}-%d.png") + end + if jpg then + report("generating jpg files for #{long}") + system("imagemagick #{long}.pdf #{long}-%d.jpg") + end + else + report("error in processing file #{file}") + end + system("texmfstart texutil --purge") + else + report("error in processing file #{file}") + end + + end + + def analyze + + file = @commandline.argument('first') + result = @commandline.option('output') + + if FileTest.file?(file) then + if data = IO.read(file) then + report("xml file #{file} loaded") + elements = Hash.new + attributes = Hash.new + entities = Hash.new + data.scan(/<([^>\s\/\!\?]+)([^>]*?)>/o) do + element, attributelist = $1, $2 + if elements.key?(element) then + elements[element] += 1 + else + elements[element] = 1 + end + attributelist.scan(/\s*([^\=]+)\=([\"\'])(.*?)(\2)/) do + key, value = $1, $3 + attributes[element] = Hash.new unless attributes.key?(element) + attributes[element][key] = Hash.new unless attributes[element].key?(key) + if attributes[element][key].key?(value) then + attributes[element][key][value] += 1 + else + attributes[element][key][value] = 1 + end + end + end + data.scan(/\&([^\;]+)\;/o) do + entity = $1 + if entities.key?(entity) then + entities[entity] += 1 + else + entities[entity] = 1 + end + end + result = file.gsub(/\..*?$/, '') + '.xlg' if result.empty? + if f = File.open(result,'w') then + report("saving report in #{result}") + f.puts "<?xml version='1.0'?>\n" + f.puts "<document>\n" + if entities.length>0 then + f.puts " <entities>\n" + entities.keys.asort.each do |entity| + f.puts " <entity name=#{entity.xstring} n=#{entities[entity].to_s.xstring}/>\n" + end + f.puts " </entities>\n" + end + if elements.length>0 then + f.puts " <elements>\n" + elements.keys.sort.each do |element| + if attributes.key?(element) then + f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}>\n" + if attributes.key?(element) then + attributes[element].keys.asort.each do |attribute| + f.puts " <attribute name=#{attribute.xstring}>\n" + attributes[element][attribute].keys.asort.each do |value| + f.puts " <instance value=#{value.xstring} n=#{attributes[element][attribute][value].to_s.xstring}/>\n" + end + f.puts " </attribute>\n" + end + end + f.puts " </element>\n" + else + f.puts " <element name=#{element.xstring} n=#{elements[element].to_s.xstring}/>\n" + end + end + f.puts " </elements>\n" + end + f.puts "</document>\n" + else + report("unable to open file '#{result}'") + end + else + report("unable to load file '#{file}'") + end + else + report("unknown file '#{file}'") + end + end + +end + +logger = Logger.new(banner.shift) +commandline = CommandLine.new + +commandline.registeraction('dir', 'generate directory listing') +commandline.registeraction('mmlpages','generate graphic from mathml') +commandline.registeraction('analyze', 'report entities and elements') + +# commandline.registeraction('dir', 'filename --pattern= --output= [--recurse --stripname --longname --url --root]') +# commandline.registeraction('mmlpages','filename [--eps --jpg --png --style= --mode=]') + +commandline.registeraction('ls') + +commandline.registeraction('help') +commandline.registeraction('version') + +commandline.registerflag('stripname') +commandline.registerflag('longname') +commandline.registerflag('recurse') + +commandline.registervalue('pattern') +commandline.registervalue('url') +commandline.registervalue('output') +commandline.registervalue('root') + +commandline.registerflag('eps') +commandline.registerflag('png') +commandline.registerflag('jpg') +commandline.registervalue('style') +commandline.registervalue('modes') + +commandline.expand + +Commands.new(commandline,logger,banner).send(commandline.action || 'help') |