source: git/doc/doc2tex.pl @ e94158

spielwiese
Last change on this file since e94158 was 9177e3, checked in by Hans Schönemann <hannes@…>, 19 years ago
*hannes: Mariano changes (tag:MP), --prefix etc. git-svn-id: file:///usr/local/Singular/svn/trunk@8415 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100755
File size: 17.7 KB
Line 
1#!/usr/local/bin/perl
2# $Id: doc2tex.pl,v 1.28 2005-07-04 14:50:16 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, (\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#   --> 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 =~ /\w/)
196    {
197      $section =~ s/\s/_/g
198    }
199    else
200    {
201      $section = "unknown";
202    }
203  }
204
205  # handle math
206  $in_info = 1 if /^\@ifinfo/;
207  $in_info = 0 if /^\@end\s*ifinfo/;
208  s[\@math\{(.*?)\}][&HandleMath($1)]eg unless $in_info;
209
210  print TEX $_;
211
212  if (! $printed_header && /^\@c/) 
213  {
214    $printed_header = 1;
215    print TEX <<EOT;
216\@comment This file was generated by doc2tex.pl from $doc_file
217\@comment DO NOT EDIT DIRECTLY, BUT EDIT $doc_file INSTEAD
218EOT
219  }
220
221  if (/^\@bye$/)            {last;}
222}
223
224#
225# wrap up
226#
227close(TEX);
228print "==>$tex_file)\n" if ($verbose);
229
230sub HandleMath
231{
232  my $what = shift;
233  return <<EOT;
234
235\@ifinfo
236\@math{$what}
237\@end ifinfo
238\@tex
239\$$what\$
240\@end tex
241EOT
242}
243
244######################################################################
245# @c example [error] [no_comp] [unix_only]
246#    -> the text till the next @c example is fed into Singular,
247#       the text is then substituted by
248#       @c computed example $ex_prefix $doc_file:$line
249#       <text from the input>
250#       @expansion{} <corresponding output>
251#       ....
252#       @c end computed example $ex_prefix $doc_file:$line
253#       reuse computed examples if ($reuse && -r $ex_prefix.inc)
254#       cut absolute directory names from the loaded messages
255#       substituted @,{ and } by @@, @{ resp. @}
256#       wrap around output lines  longer than $ex_length = 73;
257#       Processing is aborted if error occures in Singular run,
258#       unless 'error' is specified
259#       If [no_comp] is given, actual computation is not run
260#       if [unix_only] is given, then computation is only run
261#                                under unix
262
263sub HandleExample
264{
265  my($inc_file, $ex_file, $lline, $thisexample, $error_ok, $cache, $no_comp,
266     $unix_only, $tag);
267 
268  $lline = $line;
269  $section = 'unknown' unless $section;
270  $ex_prefix = $section;
271  $ex_prefix .= "_$examples{$section}" if $examples{$section};
272  $examples{$section}++;
273
274  if (/tag:(\w+)/)
275  {
276    $tag = $1;
277  }
278  else
279  {
280    $tag = 'NOTAG';
281  }
282 
283  if ($no_ex or $exclude_ex{$tag})
284  {
285    print "{$ex_prefix}" if ($verbose);
286    print TEX "\@c skipped computation of example $ex_prefix $doc_file:$lline \n";
287   
288  }
289  else
290  {
291    $inc_file = "$ex_subdir/$ex_prefix.inc";
292    $ex_file = "$ex_subdir/$ex_prefix.sing";
293    if (-r $inc_file && -r $ex_file && -s $inc_file && -s $ex_file)
294    {
295      $cache = 1;
296      open(SING, "<$ex_file") || ($cache = 0);
297    }
298  }
299
300  $thisexample = '';
301  $error_ok = 1 if /error/;
302  $no_comp = 1 if /no_comp/;
303  $unix_only = 1 if /unix_only/ && $Win32;
304 
305  # print content in example file till next @c example
306  print TEX "// only supported on Unix platforms\n"
307    if $unix_only;
308 
309  while (<DOC>)
310  {
311    $line++;
312    last if (/^\@c\s*example\s*$/);
313#    s/^\s*//; # remove preceeding white spaces
314    if ($no_ex || $exclude_ex{$tag} || $no_comp || $unix_only)
315    {
316      &protect_texi;
317      print TEX $_;
318    }
319    else
320    {
321      $thisexample .= $_;
322      if ($cache && $_ && $_ ne <SING>)
323      {
324        $cache = 0;
325        close(SING);
326      }
327    }
328  }
329  close(SING) if $cache;
330  Error("no matching '\@c example' found for $doc_file:$lline\n")
331    unless (/^\@c\s*example\s*$/);
332
333  # done, if no examples
334  return if ($no_ex || $exclude_ex{$tag} || $no_comp || $unix_only);
335
336  # check whether it can be reused
337  if ($reuse && $cache)
338  {
339    my $ok = 1;
340    print "<$ex_prefix>" if ($verbose);
341    print TEX "\@c reused example $ex_prefix $doc_file:$lline \n";
342    open(INC, "<$inc_file") || Error("can't open $inc_file for reading: $!\n");
343    while (<INC>)
344    {
345      if (/error occurred/ && $ok && !error_ok)
346      {
347        Warn("Result file $inc_file contains errors. Force re-computation by removing $inc_file");
348        $ok = 0;
349      }
350      print TEX $_;
351    }
352    close(INC);
353  }
354  else
355  {
356    print "($ex_prefix" if ($verbose == 1);
357    my ($res_file);
358    $res_file = "$ex_subdir/$ex_prefix.res";
359
360    print TEX "\@c computed example $ex_prefix $doc_file:$lline \n";
361
362    # run singular
363    open(EX, ">$ex_file") || Error("can't open $ex_file for writing: $!\n");
364    print EX "$thisexample";
365    close(EX);
366
367    unless ($Singular_OK)
368    {
369      if (system("echo '\$' | $Singular $Singular_opts > $res_file"))
370      {
371        $Singular .= '.exe' if ($Win32 && $Singular !~ /\.exe$/);
372        Error("CanŽt run '$Singular $Singular_opts': $@")
373          if (system("echo '\$' | $Singular $Singular_opts > $res_file"));
374      }
375      $Singular_OK = 1
376    }
377
378    &System("echo '\$' | $Singular $Singular_opts $ex_file > $res_file");
379    print ")" if ($verbose == 1);
380
381    open(RES, "<$res_file") || Error("can't open $res_file for reading: $!\n");
382    open(INC, ">$inc_file") || Error("can't open $inc_file for writing: $!\n");
383
384    # get result, manipulate it and put it into inc file
385    while (<RES>)
386    {
387      last if (/^STDIN\s*([0-9]+)..\$/);
388      # check for error
389      Error("while running example $ex_prefix from $doc_file:$lline.\nCall: '$Singular $Singular_opts $ex_file > $res_file'\n")
390        if (/error occurred/ && ! $error_ok);
391      # remove stuff from echo
392      if (/^$ex_file\s*([0-9]+)../)
393      {
394        $_ = $';
395        &protect_texi;
396      }
397      else
398      {
399        local($to_do, $done);
400        # remove absolute path names from laoded messages
401        s/^(\/\/ \*\* loaded )(.*)\/(.+).lib(.*)/$1$3.lib$4/;
402        # shorten error occurred in messages
403        s/\? error occurred in [^ ]* line/\? error occurred in line/;
404        # break after $ex_length characters
405        $to_do = $_;
406        while (length($to_do) > $ex_length && $to_do =~ /\w/ && 
407               substr($to_do, $ex_length) !~ /^\s*$/)
408        {
409         
410          $done .= substr($to_do, 0, $ex_length)."\\\n   ";
411          $to_do = substr($to_do, $ex_length);
412        }
413        $_ = $done.$to_do if($done);
414        &protect_texi;
415        $_ = "\@expansion{} ".$_;
416      }
417      print INC $_;
418      print TEX $_;
419    }
420    close(RES);
421    close(INC);
422    unlink $ex_file, $res_file, $inc_file if ($clean);
423  }
424  print TEX "\@c end example $ex_prefix $doc_file:$lline\n";
425}
426 
427######################################################################
428# @c include file
429#    -> copy content of file into output file protecting texi special chars
430sub HandleInclude
431{
432  s/^\@c\s*include\s+([^\s]+)\s/$1/;
433  s/\s*$//;
434  unless (&Open(*INC, "<$_"))
435  {
436    warn "$WARNING HandleInclude: can't open $_ for reading\n";
437    print TEX "\@c include file $_ not found\n";
438    return;
439  }
440  print "<$_>" if ($verbose);
441  print TEX "\@c begin included file $_ from $doc_file:$line\n";
442  while (<INC>)
443  {
444    &protect_texi;
445    print TEX $_;
446  }
447  print TEX "\@c end included file from $doc_file:$line\n";
448  close (INC);
449}
450
451######################################################################
452# @c ref
453# ....
454# @c ref
455#    -> scans intermediate lines for @ref{..} strings
456#    Creates menu of (sorted) refs for ifinfo
457#    Creates comma-separated (sorted) refs for iftex, prepended with
458#    the text before first @ref.
459
460sub HandleRef
461{
462  local(%refs, @refs, $header, $lline, $lref);
463 
464  print TEX "\@c inserted refs from $doc_file:$line\n";
465 
466  # scan lines use %ref to remove duplicates
467  $lline = $line;
468  while (<DOC>)
469  {
470    $line++;
471    last if (/^\@c\s*ref\s*$/);
472   
473    while (/\@ref{(.*?)}[;\.]/)
474    {
475      $refs{$1} = 1;
476      $_ = $';
477      unless ($header)
478      {
479        $header = $`;
480        $header = " " unless ($header);
481      }
482    }
483    $header = $_ unless ($header)
484  }
485  chomp $header;
486  die "$ERRROR no matching \@c ref found for $doc_file:$lline\n" 
487    unless (/^\@c\s*ref\s*$/);
488  # sort refs
489  @refs = sort(keys(%refs));
490  # put them out
491  print TEX "\@ifinfo\n";
492  print TEX "\@menu\n";
493  if ($header && $header ne " ")
494  {
495    print TEX "$header\n";
496  }
497  else
498  {
499    print TEX "See also:\n";
500  }
501  foreach $ref (@refs) {print TEX "* ".$ref."::\n";}
502  print TEX "\@end menu\n\@end ifinfo\n\@iftex\n";
503
504  if ($header ne " ")
505  {
506    print TEX "$header\n" unless ($header eq " ");
507  }
508  else
509  {
510    print TEX "\@strong{See also:}\n";
511  }
512  $lref = pop(@refs);
513  foreach $ref (@refs) {print TEX "\@ref{".$ref."};\n";}
514  print TEX "\@ref{".$lref."}.\n" if ($lref); 
515  print TEX "\@end iftex\n\@c end inserted refs from $doc_file:$lline\n";
516}
517
518###################################################################
519#
520# @c lib libname.lib[:proc] [no_ex, no_fun, (\w*)section]
521#   --> replaced by @include $texfile where
522#        $texfile = $subdir/libname_lib[_noFun,_noEx].tex
523#   --> if $make, calls "make $texfile"
524#   --> Error, if $tex_file does not exist
525#   --> if [:proc] is given, then includes only of respective
526#       proc body
527#   --> if (\w*)section is given, replaces @subsubsection by @$1section
528#       and pastes in content of tex file directly
529
530sub HandleLib
531{
532  my($lib, $proc, $n_fun, $n_ex, $section, $tex_file);
533
534  if (/^\@c\s*lib\s+([^\.]+)\.lib(.*)/)
535  {
536    $lib = $1;
537    $_ = $2;
538  }
539  else
540  {
541    warn "$WARNING need .lib file to process '$_'\n";
542    print TEX $_;
543    return;
544  }
545
546  $proc = $1 if (/^:(.*?) /);
547  $n_fun = 1 if ($no_fun || /no_fun/);
548  $n_ex = 1 if ($no_ex || /no_ex/ || (/unix_only/ && $Win32));
549  $section = $1 if /(\w*)section/;
550 
551  # contruct tex file name
552  $tex_file = "$doc_subdir/$lib"."_lib";
553  if ($n_fun)
554  {
555    $tex_file .= "_noFun";
556  }
557  elsif ($n_ex)
558  {
559    $tex_file .= "_noEx";
560  }
561  $tex_file .= ".tex";
562
563  if ($make)
564  {
565    print "<lib $lib " if ($verbose);
566    System("$make $make_opts VERBOSE=$verbose $tex_file"); 
567  }
568 
569  # make sure file exists
570  if (-r $tex_file)
571  {
572    if ($verbose)
573    {
574      print "<lib $lib " unless $make;
575      print "$proc>";
576    }
577  }
578  else
579  {
580    Error("Can't read $tex_file\n") unless -r $tex_file;
581  }
582
583  # see whether we have to paste something in
584  if ($proc || $section)
585  {
586    open(LTEX, "<$tex_file") 
587      || Error("Can't open $tex_file for reading: $!\n");
588
589    print TEX "\@c start include of docu for $lib.lib:$proc\n";
590    print TEX "\@c replaced \@subsubsection by \@$section\n" if ($section);
591    if ($proc)
592    {
593      my $found = 0;
594      while (<LTEX>)
595      {
596        $found = 1 if /c ---content $proc---/;
597        if ($found)
598        {
599          s/subsubsection/${section}section/ if $section;
600          print TEX $_; 
601        }
602        last if $found && /c ---end content $proc---/;
603      }
604      if ($found)
605      {
606        Error("no end content found for lib proc docu for $lib.lib:$proc $doc_file:$line \n")
607          unless (/c ---end content $proc---/);
608        print TEX "\@c generated lib proc docu for $lib.lib:$proc $doc_file:$line \n";
609      }
610      else
611      {
612        Error("did not find lib proc docu for $lib.lib:$proc $doc_file:$line \n");
613      }
614    }
615    else
616    {
617      while (<LTEX>)
618      {
619        s/subsubsection/${section}section/;
620        print TEX $_;
621      }
622    }
623    print TEX "\@c end include of docu for $lib.lib:$proc\n";
624    close(LTEX);
625  }
626  else
627  {
628    print TEX "\@c include of docu for $lib.lib\n";
629    print TEX "\@include $tex_file\n";
630  }
631}
632
633####################################################################
634# Auxillary routines
635#
636
637# protect texi special characters
638sub protect_texi
639{
640  s/\@/\@\@/g;
641  s/{/\@{/g;
642  s/}/\@}/g;
643}       
644
645# open w.r.t. include_dirs
646sub Open
647{
648  local(*FH, $file) = @_;
649  local($mode);
650  $file =~ s/^(.{1})(.*)/$2/;
651  $mode = $1;
652
653  foreach $dir (@include_dirs)
654  {
655    return $dir if(open(FH, $mode.$dir."/".$file));
656  }
657}
658   
659# system call with echo on verbose > 1 and die on fail
660sub System
661{
662  local($call) = @_;
663  print " d2t system:\n$call\n" if ($verbose > 1);
664  Error("non-zero exit status of system call: '$call': $!\n")
665    if (system($call));
666}
667
668sub Error
669{
670  print "$ERROR $_[0]";
671  close(TEX);
672  unlink $tex_file if $tex_file && -e $tex_file;
673  exit(1);
674}
675
676sub Warn
677{
678  print "$WARNING $_[0]\n";
679}
680#
681# leave this here --otherwise fontification in my emacs gets screwd up
682#
683sub Usage
684{
685  return <<EOT;
686This is doc2tex: a utility to generate Singular texinfo from doc file
687To convert a doc file to texinfo: $0 [options] input_file.doc
688where options can be (abbreviated to shortest possible prefix):
689  -Singular prog: use 'prog' as Singular program to generate ex output
690                          (default: '../Singular/Singular')
691  -output file  : use 'file' as output file
692                          (default: input_file.tex)
693  -clean        : delete intermediate files
694  -make  cmd    : use cmd as make command to generate tex files for libraries
695  -no_reuse     : don't reuse intermediate files
696  -no_ex        : skip computation of examples
697  -no_fun       : don't include help for library functions
698  -docdir  dir  : put intermediate doc/tex files into 'dir'
699                          (default: './d2t_singular')
700  -exdir   dir  : put example files into 'dir'
701                          (default: './examples')
702  -I dir        : look also into 'dir' for include  and lib files
703                          (default: ".", "../Singular/LIB")
704  -verbose  val : Set verbosity to 'val' (0=quiet, 1=prot, >1=all)
705  -help         : print help and exit
706EOT
707}
708
Note: See TracBrowser for help on using the repository browser.