source: git/doc/doc2tex.pl @ b8ba2a

spielwiese
Last change on this file since b8ba2a was caf1e0, checked in by Hans Schönemann <hannes@…>, 17 years ago
*hannes: fixed manual.tex git-svn-id: file:///usr/local/Singular/svn/trunk@10261 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100755
File size: 17.9 KB
Line 
1#!/usr/local/bin/perl
2# $Id: doc2tex.pl,v 1.29 2007-07-25 16:10:45 Singular Exp $
3###################################################################
4#  Computer Algebra System SINGULAR
5#
6#  doc2tex: utility to generate the Singular manual
7#
8####
9# @c example [error] [no_comp] [unix_only]
10#    -> the text till the next @c example is feed into Singular,
11#       the text is then substituted by
12#       @c computed example $ex_prefix $doc_file:$line
13#       <text from the input>
14#       @expansion{} <corresponding output>
15#       ....
16#       @c end computed example $ex_prefix $doc_file:$line
17#       reuse computed examples if ($reuse && -r $ex_prefix.inc)
18#       cut absolute directory names from the loaded messages
19#       substituted @,{ and } by @@, @{ resp. @}
20#       wrap around output lines  longer than $ex_length = 73;
21#       Processing is aborted if error occures in Singular run,
22#       unless 'error' is specified
23#       if no_comp is given, then computation is not run
24#       if unix_only is given, then computation is only run
25#                              under unix
26#       
27#
28####
29# @c include file
30#    -> copy content of file into output file protecting texi special chars
31#
32####
33# @c ref
34# ....
35# @c ref
36#    -> scans intermediate lines for @ref{..} strings
37#    Creates menu of (sorted) refs for ifinfo
38#    Creates comma-separated (sorted) refs for iftex, prepended with
39#    the text before first @ref.
40#
41####
42# @c lib libname.lib[:proc] [no_ex, no_fun, Fun, (\w*)section]
43#   --> replaced by @include $texfile where
44#        $texfile = $subdir/libname_lib[_noFun,_noEx].tex
45#   --> if $make, calls "make $texfile"
46#   --> Error, if $tex_file does not exist
47#   --> if [:proc] is given, then includes only of respective
48#       proc body
49#   --> Fun overwrites global no_fun
50#   --> if (\w*)section is given, replaces @subsubsection by @$1section
51#       and pastes in content of tex file directly
52#
53#
54###################################################################
55
56use Config;
57$Win32 = 1 if ($Config{osname} =~ /win/i);
58
59#
60# default settings of command-line arguments
61#
62$Singular = "../Singular/Singular"; # should be overwritten
63$Singular_opts = " -teqr12345678 --no-rc";
64$clean = 0;
65$verbose = 1;
66$reuse = 1;
67$no_ex = 0;
68%exclude_ex = ();
69$no_fun = 0;
70$doc_subdir = "./d2t_singular";
71$ex_subdir = "./examples";
72@include_dirs = (".", "../Singular/LIB");
73$make = 0;
74$make_opts = " --no-print-directory";
75
76
77#
78# misc. defaults
79#
80$ex_length = 73;
81$ERROR = "\n**** Error:";
82$WARNING = "\n** Warning:";
83
84#
85# flush stdout and stderr after every write
86#
87select(STDERR);
88$| = 1;
89select(STDOUT);
90$| = 1;
91
92#
93# get file to convert
94#
95$doc_file = pop(@ARGV);
96if ($doc_file =~  /(.+)\..*$/)
97{
98  die "*** Error: Need .doc file as input\n" . &Usage 
99    unless ($doc_file =~ /(.+)\.doc$/);
100}
101else
102{
103  if ($doc_file =~ /^-h(elp)?$/) { print &Usage; exit;}
104  $doc_file .= ".doc";
105}
106
107#
108# parse command line options
109#
110$args = join(" ", @ARGV);
111while (@ARGV && $ARGV[0] =~ /^-/) 
112{
113  $_ = shift(@ARGV);
114  if (/^-S(ingular)?$/)  { $Singular = shift(@ARGV); next;}
115  if (/^-o(utput)?$/)    { $tex_file = shift(@ARGV); next;}
116  if (/^-no_r(euse)?$/)  { $reuse = 0; next;}
117  if (/^-c(lean)?$/)     { $clean = 1; next;}
118  if (/^-no_e(x)?$/)     { $no_ex = 1; next;}
119  if (/^-exclude$/)      { $exclude_ex{shift(@ARGV)} = 1;next;}
120  if (/^-no_fu(n)?$/)    { $no_fun = 1;next;}
121  if (/^-m(ake)?$/)      { $make =  shift(@ARGV);next;}
122  if (/^-d(ocdir)?$/)    { $doc_subdir = shift(@ARGV); next;}
123  if (/^-e(xdir)?$/)     { $ex_subdir = shift(@ARGV); next;}
124  if (/^-I$/)            { unshift(@include_dirs, shift(@ARGV)); next;}
125  if (/^-v(erbose)?$/)   { $verbose = shift(@ARGV); next;}
126  if (/^-h(elp)?$/)      { print &Usage; exit;}
127  Error("Unknown Option $_\n" .&Usage);
128}
129$verbose = ($verbose < 0 ? 0 : $verbose);
130$make_opts .= " -s" unless $verbose > 1;
131
132#
133# construct filenames
134#
135($doc_dir = $doc_file) =~ s/(.+)\.doc$/$1/;
136if ($doc_dir =~ /(.*)\//)
137{
138  $doc = $';
139  $doc_dir = $1;
140}
141else
142{
143  $doc = $doc_dir;
144  $doc_dir = ".";
145}
146unless ($tex_file)
147{
148  $tex_file = ($no_ex ? "$doc_dir/${doc}_noEx.tex" : "$doc_dir/$doc.tex");
149}
150
151#
152# open files
153#
154open(DOC, "<$doc_file") 
155  || Error("can't open $doc_file for reading: $!\n" . &Usage);
156open(TEX, ">$tex_file") || Error("can't open $tex_file for writing: $!\n");
157print "(d2t $doc_file==>$tex_file" if ($verbose);
158if (-d $doc_subdir)
159{
160  print "<docdir:$doc_subdir>"  if ($verbose > 1);
161}
162else
163{
164  mkdir($doc_subdir, oct(755)) 
165    || Error("can't create directory $doc_subdir: $!\n");
166  print "(docdir:$doc_subdir)"  if ($verbose > 1);
167}
168if (-d $ex_subdir)
169{
170  print "<exdir:$ex_subdir>"  if ($verbose > 1);
171}
172else
173{
174  mkdir($ex_subdir, oct(755)) 
175    || Error("can't create directory $ex_subdir: $!\n");
176  print "(exdir:$ex_subdir)"  if ($verbose > 1);
177}
178
179#######################################################################
180#
181# go !
182#
183while (<DOC>)
184{
185  $line++;
186  if (/^\@c\s*example/)     {&HandleExample; next;}
187  if (/^\@c\s*include\s+/)  {&HandleInclude; next;}
188  if (/^\@c\s*ref\s*$/)     {&HandleRef; next;}
189  if (/^\@c\s*lib\s+/)      {&HandleLib; next;}
190  if (/^\@setfilename/)     {print TEX "\@setfilename $doc.hlp\n"; next;}
191
192  if (/^\@\w*section\s*/ || /^\@\w*chapter\s*/ || /^\@\w*heading\s*/)
193  {
194    $section = $';
195    $section =~ s/^\s*(.*)\s*$/$1/;
196    if ($section =~ /\w/)
197    {
198      $section =~ s/\s/_/g
199    }
200    else
201    {
202      $section = "unknown";
203    }
204  }
205
206  # handle math
207  $in_info = 1 if /^\@ifinfo/;
208  $in_info = 0 if /^\@end\s*ifinfo/;
209  s[\@math\{(.*?)\}][&HandleMath($1)]eg unless $in_info;
210
211  print TEX $_;
212
213  if (! $printed_header && /^\@c/) 
214  {
215    $printed_header = 1;
216    print TEX <<EOT;
217\@comment This file was generated by doc2tex.pl from $doc_file
218\@comment DO NOT EDIT DIRECTLY, BUT EDIT $doc_file INSTEAD
219EOT
220  }
221
222  if (/^\@bye$/)            {last;}
223}
224
225#
226# wrap up
227#
228close(TEX);
229print "==>$tex_file)\n" if ($verbose);
230
231sub HandleMath
232{
233  my $what = shift;
234  return <<EOT;
235
236\@ifinfo
237\@math{$what}
238\@end ifinfo
239\@tex
240\$$what\$
241\@end tex
242EOT
243}
244
245######################################################################
246# @c example [error] [no_comp] [unix_only]
247#    -> the text till the next @c example is fed into Singular,
248#       the text is then substituted by
249#       @c computed example $ex_prefix $doc_file:$line
250#       <text from the input>
251#       @expansion{} <corresponding output>
252#       ....
253#       @c end computed example $ex_prefix $doc_file:$line
254#       reuse computed examples if ($reuse && -r $ex_prefix.inc)
255#       cut absolute directory names from the loaded messages
256#       substituted @,{ and } by @@, @{ resp. @}
257#       wrap around output lines  longer than $ex_length = 73;
258#       Processing is aborted if error occures in Singular run,
259#       unless 'error' is specified
260#       If [no_comp] is given, actual computation is not run
261#       if [unix_only] is given, then computation is only run
262#                                under unix
263
264sub HandleExample
265{
266  my($inc_file, $ex_file, $lline, $thisexample, $error_ok, $cache, $no_comp,
267     $unix_only, $tag);
268 
269  $lline = $line;
270  $section = 'unknown' unless $section;
271  $ex_prefix = $section;
272  $ex_prefix .= "_$examples{$section}" if $examples{$section};
273  $examples{$section}++;
274
275  if (/tag:(\w+)/)
276  {
277    $tag = $1;
278  }
279  else
280  {
281    $tag = 'NOTAG';
282  }
283 
284  if ($no_ex or $exclude_ex{$tag})
285  {
286    print "{$ex_prefix}" if ($verbose);
287    print TEX "\@c skipped computation of example $ex_prefix $doc_file:$lline \n";
288   
289  }
290  else
291  {
292    $inc_file = "$ex_subdir/$ex_prefix.inc";
293    $ex_file = "$ex_subdir/$ex_prefix.sing";
294    if (-r $inc_file && -r $ex_file && -s $inc_file && -s $ex_file)
295    {
296      $cache = 1;
297      open(SING, "<$ex_file") || ($cache = 0);
298    }
299  }
300
301  $thisexample = '';
302  $error_ok = 1 if /error/;
303  $no_comp = 1 if /no_comp/;
304  $unix_only = 1 if /unix_only/ && $Win32;
305 
306  # print content in example file till next @c example
307  print TEX "// only supported on Unix platforms\n"
308    if $unix_only;
309 
310  while (<DOC>)
311  {
312    $line++;
313    last if (/^\@c\s*example\s*$/);
314#    s/^\s*//; # remove preceeding white spaces
315    if ($no_ex || $exclude_ex{$tag} || $no_comp || $unix_only)
316    {
317      &protect_texi;
318      print TEX $_;
319    }
320    else
321    {
322      $thisexample .= $_;
323      if ($cache && $_ && $_ ne <SING>)
324      {
325        $cache = 0;
326        close(SING);
327      }
328    }
329  }
330  close(SING) if $cache;
331  Error("no matching '\@c example' found for $doc_file:$lline\n")
332    unless (/^\@c\s*example\s*$/);
333
334  # done, if no examples
335  return if ($no_ex || $exclude_ex{$tag} || $no_comp || $unix_only);
336
337  # check whether it can be reused
338  if ($reuse && $cache)
339  {
340    my $ok = 1;
341    print "<$ex_prefix>" if ($verbose);
342    print TEX "\@c reused example $ex_prefix $doc_file:$lline \n";
343    open(INC, "<$inc_file") || Error("can't open $inc_file for reading: $!\n");
344    while (<INC>)
345    {
346      if (/error occurred/ && $ok && !error_ok)
347      {
348        Warn("Result file $inc_file contains errors. Force re-computation by removing $inc_file");
349        $ok = 0;
350      }
351      print TEX $_;
352    }
353    close(INC);
354  }
355  else
356  {
357    print "($ex_prefix" if ($verbose == 1);
358    my ($res_file);
359    $res_file = "$ex_subdir/$ex_prefix.res";
360
361    print TEX "\@c computed example $ex_prefix $doc_file:$lline \n";
362
363    # run singular
364    open(EX, ">$ex_file") || Error("can't open $ex_file for writing: $!\n");
365    print EX "$thisexample";
366    close(EX);
367
368    unless ($Singular_OK)
369    {
370      if (system("echo '\$' | $Singular $Singular_opts > $res_file"))
371      {
372        $Singular .= '.exe' if ($Win32 && $Singular !~ /\.exe$/);
373        Error("CanŽt run '$Singular $Singular_opts': $@")
374          if (system("echo '\$' | $Singular $Singular_opts > $res_file"));
375      }
376      $Singular_OK = 1
377    }
378
379    &System("echo '\$' | $Singular $Singular_opts $ex_file > $res_file");
380    print ")" if ($verbose == 1);
381
382    open(RES, "<$res_file") || Error("can't open $res_file for reading: $!\n");
383    open(INC, ">$inc_file") || Error("can't open $inc_file for writing: $!\n");
384
385    # get result, manipulate it and put it into inc file
386    while (<RES>)
387    {
388      last if (/^STDIN\s*([0-9]+)..\$/);
389      # check for error
390      Error("while running example $ex_prefix from $doc_file:$lline.\nCall: '$Singular $Singular_opts $ex_file > $res_file'\n")
391        if (/error occurred/ && ! $error_ok);
392      # remove stuff from echo
393      if (/^$ex_file\s*([0-9]+)../)
394      {
395        $_ = $';
396        &protect_texi;
397      }
398      else
399      {
400        local($to_do, $done);
401        # remove absolute path names from laoded messages
402        s/^(\/\/ \*\* loaded )(.*)\/(.+).lib(.*)/$1$3.lib$4/;
403        # shorten error occurred in messages
404        s/\? error occurred in [^ ]* line/\? error occurred in line/;
405        # break after $ex_length characters
406        $to_do = $_;
407        while (length($to_do) > $ex_length && $to_do =~ /\w/ && 
408               substr($to_do, $ex_length) !~ /^\s*$/)
409        {
410         
411          $done .= substr($to_do, 0, $ex_length)."\\\n   ";
412          $to_do = substr($to_do, $ex_length);
413        }
414        $_ = $done.$to_do if($done);
415        &protect_texi;
416        $_ = "\@expansion{} ".$_;
417      }
418      print INC $_;
419      print TEX $_;
420    }
421    close(RES);
422    close(INC);
423    unlink $ex_file, $res_file, $inc_file if ($clean);
424  }
425  print TEX "\@c end example $ex_prefix $doc_file:$lline\n";
426}
427 
428######################################################################
429# @c include file
430#    -> copy content of file into output file protecting texi special chars
431sub HandleInclude
432{
433  s/^\@c\s*include\s+([^\s]+)\s/$1/;
434  s/\s*$//;
435  unless (&Open(*INC, "<$_"))
436  {
437    warn "$WARNING HandleInclude: can't open $_ for reading\n";
438    print TEX "\@c include file $_ not found\n";
439    return;
440  }
441  print "<$_>" if ($verbose);
442  print TEX "\@c begin included file $_ from $doc_file:$line\n";
443  while (<INC>)
444  {
445    &protect_texi;
446    print TEX $_;
447  }
448  print TEX "\@c end included file from $doc_file:$line\n";
449  close (INC);
450}
451
452######################################################################
453# @c ref
454# ....
455# @c ref
456#    -> scans intermediate lines for @ref{..} strings
457#    Creates menu of (sorted) refs for ifinfo
458#    Creates comma-separated (sorted) refs for iftex, prepended with
459#    the text before first @ref.
460
461sub HandleRef
462{
463  local(%refs, @refs, $header, $lline, $lref);
464 
465  print TEX "\@c inserted refs from $doc_file:$line\n";
466 
467  # scan lines use %ref to remove duplicates
468  $lline = $line;
469  while (<DOC>)
470  {
471    $line++;
472    last if (/^\@c\s*ref\s*$/);
473   
474    while (/\@ref{(.*?)}[;\.]/)
475    {
476      $refs{$1} = 1;
477      $_ = $';
478      unless ($header)
479      {
480        $header = $`;
481        $header = " " unless ($header);
482      }
483    }
484    $header = $_ unless ($header)
485  }
486  chomp $header;
487  die "$ERRROR no matching \@c ref found for $doc_file:$lline\n" 
488    unless (/^\@c\s*ref\s*$/);
489  # sort refs
490  @refs = sort(keys(%refs));
491  # put them out
492  print TEX "\@ifinfo\n";
493  print TEX "\@menu\n";
494  if ($header && $header ne " ")
495  {
496    print TEX "$header\n";
497  }
498  else
499  {
500    print TEX "See also:\n";
501  }
502  foreach $ref (@refs) {print TEX "* ".$ref."::\n";}
503  print TEX "\@end menu\n\@end ifinfo\n\@iftex\n";
504
505  if ($header ne " ")
506  {
507    print TEX "$header\n" unless ($header eq " ");
508  }
509  else
510  {
511    print TEX "\@strong{See also:}\n";
512  }
513  $lref = pop(@refs);
514  foreach $ref (@refs) {print TEX "\@ref{".$ref."};\n";}
515  print TEX "\@ref{".$lref."}.\n" if ($lref); 
516  print TEX "\@end iftex\n\@c end inserted refs from $doc_file:$lline\n";
517}
518
519###################################################################
520#
521# @c lib libname.lib[:proc] [no_ex, no_fun, Fun, (\w*)section]
522#   --> replaced by @include $texfile where
523#        $texfile = $subdir/libname_lib[_noFun,_noEx].tex
524#   --> if $make, calls "make $texfile"
525#   --> Error, if $tex_file does not exist
526#   --> if [:proc] is given, then includes only of respective
527#       proc body
528#   --> Fun overwrites global no_fun
529#   --> if (\w*)section is given, replaces @subsubsection by @$1section
530#       and pastes in content of tex file directly
531
532sub HandleLib
533{
534  my($lib, $proc, $n_fun, $n_ex, $section, $tex_file);
535
536  if (/^\@c\s*lib\s+([^\.]+)\.lib(.*)/)
537  {
538    $lib = $1;
539    $_ = $2;
540  }
541  else
542  {
543    warn "$WARNING need .lib file to process '$_'\n";
544    print TEX $_;
545    return;
546  }
547
548  $proc = $1 if (/^:(.*?) /);
549  $n_fun = 1 if ($no_fun || /no_fun/);
550  $n_fun = 0 if (/Fun/);
551  $n_ex = 1 if ($no_ex || /no_ex/ || (/unix_only/ && $Win32));
552  $section = $1 if /(\w*)section/;
553 
554  # contruct tex file name
555  $tex_file = "$doc_subdir/$lib"."_lib";
556  if ($n_fun)
557  {
558    $tex_file .= "_noFun";
559  }
560  elsif ($n_ex)
561  {
562    $tex_file .= "_noEx";
563  }
564  $tex_file .= ".tex";
565
566  if ($make)
567  {
568    print "<lib $lib " if ($verbose);
569    System("$make $make_opts VERBOSE=$verbose $tex_file"); 
570  }
571 
572  # make sure file exists
573  if (-r $tex_file)
574  {
575    if ($verbose)
576    {
577      print "<lib $lib " unless $make;
578      print "$proc>";
579    }
580  }
581  else
582  {
583    Error("Can't read $tex_file\n") unless -r $tex_file;
584  }
585
586  # see whether we have to paste something in
587  if ($proc || $section)
588  {
589    open(LTEX, "<$tex_file") 
590      || Error("Can't open $tex_file for reading: $!\n");
591
592    print TEX "\@c start include of docu for $lib.lib:$proc\n";
593    print TEX "\@c replaced \@subsubsection by \@$section\n" if ($section);
594    if ($proc)
595    {
596      my $found = 0;
597      while (<LTEX>)
598      {
599        $found = 1 if /c ---content $proc---/;
600        if ($found)
601        {
602          s/subsubsection/${section}section/ if $section;
603          print TEX $_; 
604        }
605        last if $found && /c ---end content $proc---/;
606      }
607      if ($found)
608      {
609        Error("no end content found for lib proc docu for $lib.lib:$proc $doc_file:$line in $tex_file\n")
610          unless (/c ---end content $proc---/);
611        print TEX "\@c generated lib proc docu for $lib.lib:$proc $doc_file:$line \n";
612      }
613      else
614      {
615        Error("did not find lib proc docu for $lib.lib:$proc $doc_file:$line in $tex_file\n");
616      }
617    }
618    else
619    {
620      while (<LTEX>)
621      {
622        s/subsubsection/${section}section/;
623        print TEX $_;
624      }
625    }
626    print TEX "\@c end include of docu for $lib.lib:$proc\n";
627    close(LTEX);
628  }
629  else
630  {
631    print TEX "\@c include of docu for $lib.lib\n";
632    print TEX "\@include $tex_file\n";
633  }
634}
635
636####################################################################
637# Auxillary routines
638#
639
640# protect texi special characters
641sub protect_texi
642{
643  s/\@/\@\@/g;
644  s/{/\@{/g;
645  s/}/\@}/g;
646}       
647
648# open w.r.t. include_dirs
649sub Open
650{
651  local(*FH, $file) = @_;
652  local($mode);
653  $file =~ s/^(.{1})(.*)/$2/;
654  $mode = $1;
655
656  foreach $dir (@include_dirs)
657  {
658    return $dir if(open(FH, $mode.$dir."/".$file));
659  }
660}
661   
662# system call with echo on verbose > 1 and die on fail
663sub System
664{
665  local($call) = @_;
666  print " d2t system:\n$call\n" if ($verbose > 1);
667  Error("non-zero exit status of system call: '$call': $!\n")
668    if (system($call));
669}
670
671sub Error
672{
673  print "$ERROR $_[0]";
674  close(TEX);
675  unlink $tex_file if $tex_file && -e $tex_file;
676  exit(1);
677}
678
679sub Warn
680{
681  print "$WARNING $_[0]\n";
682}
683#
684# leave this here --otherwise fontification in my emacs gets screwd up
685#
686sub Usage
687{
688  return <<EOT;
689This is doc2tex: a utility to generate Singular texinfo from doc file
690To convert a doc file to texinfo: $0 [options] input_file.doc
691where options can be (abbreviated to shortest possible prefix):
692  -Singular prog: use 'prog' as Singular program to generate ex output
693                          (default: '../Singular/Singular')
694  -output file  : use 'file' as output file
695                          (default: input_file.tex)
696  -clean        : delete intermediate files
697  -make  cmd    : use cmd as make command to generate tex files for libraries
698  -no_reuse     : don't reuse intermediate files
699  -no_ex        : skip computation of examples
700  -no_fun       : don't include help for library functions
701  -docdir  dir  : put intermediate doc/tex files into 'dir'
702                          (default: './d2t_singular')
703  -exdir   dir  : put example files into 'dir'
704                          (default: './examples')
705  -I dir        : look also into 'dir' for include  and lib files
706                          (default: ".", "../Singular/LIB")
707  -verbose  val : Set verbosity to 'val' (0=quiet, 1=prot, >1=all)
708  -help         : print help and exit
709EOT
710}
711
Note: See TracBrowser for help on using the repository browser.