source: git/doc/doc2tex.pl @ 522906

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