]> git.ipfire.org Git - thirdparty/bird.git/blob - doc/LinuxDocTools.pm
Doc: BFD update
[thirdparty/bird.git] / doc / LinuxDocTools.pm
1 #! /usr/bin/perl
2 #
3 # LinuxDocTools.pm
4 #
5 # $Id$
6 #
7 # LinuxDoc-Tools driver core. This contains all the basic functionality
8 # we need to control all other components.
9 #
10 # © Copyright 1996, Cees de Groot.
11 # © Copyright 2000, Taketoshi Sano
12 #
13 # THIS VERSION HAS BEEN HACKED FOR BIRD BY MARTIN MARES
14 #
15 package LinuxDocTools;
16
17 require 5.004;
18 use strict;
19
20 =head1 NAME
21
22 LinuxDocTools - SGML conversion utilities for LinuxDoc DTD.
23
24 =head1 SYNOPSIS
25
26 use LinuxDocTools;
27 LinuxDocTools::init;
28 @files = LinuxDocTools::process_options ($0, @ARGV);
29 for $curfile (@files) {
30 LinuxDocTools::process_file ($curfile);
31 }
32
33 =head1 DESCRIPTION
34
35 The LinuxDocTools package encapsulates all the functionality offered by
36 LinuxDoc-Tools. It is used, of course, by LinuxDoc-Tools;
37 but the encapsulation should provide for a simple interface for other users as well.
38
39 =head1 FUNCTIONS
40
41 =over 4
42
43 =cut
44
45 use DirHandle;
46 use File::Basename;
47 use File::Find;
48 use File::Copy;
49 use FileHandle;
50 use IPC::Open2;
51 use Cwd;
52 use LinuxDocTools::Lang;
53 use LinuxDocTools::Utils qw(process_options usage cleanup trap_signals remove_tmpfiles create_temp);
54 use LinuxDocTools::Vars;
55
56 sub BEGIN
57 {
58 #
59 # Make sure we're always looking here. Note that "use lib" adds
60 # on the front of the search path, so we first push dist, then
61 # site, so that site is searched first.
62 #
63 use lib "$main::DataDir/dist";
64 use lib "$main::DataDir/site";
65 }
66
67 =item LinuxDocTools::init
68
69 Takes care of initialization of package-global variables (which are actually
70 defined in L<LinuxDocTools::Vars>). The package-global variables are I<$global>,
71 a reference to a hash containing numerous settings, I<%Formats>, a hash
72 containing all the formats, and I<%FmtList>, a hash containing the currently
73 active formats for help texts.
74
75 Apart from this, C<LinuxDocTools::init> also finds all distributed and site-local
76 formatting backends and C<require>s them.
77
78 =cut
79
80 sub init
81 {
82 trap_signals;
83
84 #
85 # Register the ``global'' pseudoformat. Apart from the global settings,
86 # we also use $global to keep the global variable name space clean;
87 # everything that we need to provide to other modules is stuffed
88 # into $global.
89 #
90 $global = {};
91 $global->{NAME} = "global";
92 $global->{HELP} = "";
93 $global->{OPTIONS} = [
94 { option => "backend", type => "l",
95 'values' => [ "html", "info", "latex",
96 "lyx", "rtf", "txt", "check" ],
97 short => "B" },
98 { option => "papersize", type => "l",
99 'values' => [ "a4", "letter" ], short => "p" },
100 { option => "language", type => "l",
101 'values' => [ @LinuxDocTools::Lang::Languages ], short => "l" },
102 { option => "charset", type => "l",
103 'values' => [ "latin", "ascii", "nippon", "euc-kr" ], short => "c" },
104 { option => "style", type => "s", short => "S" },
105 { option => "tabsize", type => "i", short => "t" },
106 # { option => "verbose", type => "f", short => "v" },
107 { option => "debug", type => "f", short => "d" },
108 { option => "define", type => "s", short => "D" },
109 { option => "include", type => "s", short => "i" },
110 { option => "pass", type => "s", short => "P" }
111 ];
112 $global->{backend} = "linuxdoc";
113 $global->{papersize} = "a4";
114 $global->{language} = "en";
115 $global->{charset} = "ascii";
116 $global->{style} = "";
117 $global->{tabsize} = 8;
118 $global->{verbose} = 0;
119 $global->{define} = "";
120 $global->{debug} = 0;
121 $global->{include} = "";
122 $global->{pass} = "";
123 $global->{InFiles} = [];
124 $Formats{$global->{NAME}} = $global; # All formats we know.
125 $FmtList{$global->{NAME}} = $global; # List of formats for help msgs.
126
127 # automatic language detection: disabled by default
128 # {
129 # my $lang;
130 # foreach $lang (@LinuxDocTools::Lang::Languages)
131 # {
132 # if (($ENV{"LC_ALL"} =~ /^$lang/i) ||
133 # ($ENV{"LC_CTYPE"} =~ /^$lang/i) ||
134 # ($ENV{"LANG"} =~ /^$lang/i)) {
135 # $global->{language} = Any2ISO($lang);
136 # }
137 # }
138 # }
139
140 #
141 # Used when the format is "global" (from sgmlcheck).
142 #
143 $global->{preNSGMLS} = sub {
144 $global->{NsgmlsOpts} .= " -s ";
145 $global->{NsgmlsPrePipe} = "cat $global->{file}";
146 };
147
148 #
149 # Build up the list of formatters.
150 #
151 my $savdir = cwd;
152 my %Locs;
153 chdir "$main::DataDir/dist";
154 my $dir = new DirHandle(".");
155 die "Unable to read directory $main::DataDir/dist: $!" unless defined($dir);
156 foreach my $fmt (grep(/^fmt_.*\.pl$/, $dir->read()))
157 {
158 $Locs{$fmt} = "dist";
159 }
160 $dir->close();
161 chdir "$main::DataDir/site";
162 $dir = new DirHandle(".");
163 die "Unable to read directory $main::DataDir/site: $!" unless defined($dir);
164 foreach my $fmt (grep(/^fmt_.*\.pl$/, $dir->read()))
165 {
166 $Locs{$fmt} = "site";
167 }
168 $dir->close();
169 foreach my $fmt (keys %Locs)
170 {
171 require $fmt;
172 }
173 chdir $savdir;
174 }
175
176 =item LinuxDocTools::process_options ($0, @ARGV)
177
178 This function contains all initialization that is bound to the current
179 invocation of LinuxDocTools. It looks in C<$0> to deduce the backend that
180 should be used (ld2txt activates the I<txt> backend) and parses the
181 options array. It returns an array of filenames it encountered during
182 option processing.
183
184 As a side effect, the environment variables I<SGMLDECL> and
185 I<SGML_CATALOG_FILES> are modified.
186
187 =cut
188
189 sub process_options
190 {
191 my $progname = shift;
192 my @args = @_;
193
194 #
195 # Deduce the format from the caller's file name
196 #
197 my ($format, $dummy1, $dummy2) = fileparse ($progname, "");
198 $global->{myname} = $format;
199 $format =~ s/sgml2*(.*)/$1/;
200
201 #
202 # check the option "--backend / -B"
203 #
204 if ($format eq "linuxdoc") {
205 my @backends = @args;
206 my $arg;
207 while (@backends) {
208 $arg = shift @backends;
209 if ($arg eq "-B") {
210 $arg = shift @backends;
211 $format = $arg;
212 last;
213 }
214 if ( $arg =~ s/--backend=(.*)/$1/ ) {
215 $format = $arg;
216 last;
217 }
218 }
219 }
220
221 $format = "global" if $format eq "check";
222 usage ("") if $format eq "linuxdoc";
223 $format = "latex2e" if $format eq "latex";
224 $FmtList{$format} = $Formats{$format} or
225 usage ("$global->{myname}: unknown format");
226 $global->{format} = $format;
227
228 #
229 # Parse all the options.
230 #
231 my @files = LinuxDocTools::Utils::process_options (@args);
232 $global->{language} = Any2ISO ($global->{language});
233 #
234 # check the number of given files
235 $#files > -1 || usage ("no filenames given");
236
237 #
238 # Setup the SGML environment.
239 # (Note that Debian package rewrite path to catalog of
240 # iso-entities using debian/rules so that it can use
241 # entities from sgml-data pacakge. debian/rules also
242 # removes iso-entites sub directory after doing make install.)
243 #
244 $ENV{SGML_CATALOG_FILES} .= (defined $ENV{SGML_CATALOG_FILES} ? ":" : "") .
245 "$main::prefix/share/sgml/sgml-iso-entities-8879.1986/catalog:" .
246 "$main::prefix/share/sgml/entities/sgml-iso-entities-8879.1986/catalog";
247 $ENV{SGML_CATALOG_FILES} .= ":$main::DataDir/linuxdoc-tools.catalog";
248 $ENV{SGML_CATALOG_FILES} .= ":$main::/etc/sgml.catalog";
249 if (-f "$main::DataDir/dtd/$format.dcl")
250 {
251 $ENV{SGMLDECL} = "$main::DataDir/dtd/$format.dcl";
252 }
253 elsif (-f "$main::DataDir/dtd/$global->{style}.dcl")
254 {
255 $ENV{SGMLDECL} = "$main::DataDir/dtd/$global->{style}.dcl";
256 }
257 elsif (-f "$main::DataDir/dtd/sgml.dcl")
258 {
259 $ENV{SGMLDECL} = "$main::DataDir/dtd/sgml.dcl";
260 }
261
262 #
263 # OK. Give the list of files we distilled from the options
264 # back to the caller.
265 #
266 return @files;
267 }
268
269 =item LinuxDocTools::process_file
270
271 With all the configuration done, this routine will take a single filename
272 and convert it to the currently active backend format. The conversion is
273 done in a number of steps in tight interaction with the currently active
274 backend (see also L<LinuxDocTools::BackEnd>):
275
276 =over
277
278 =item 1. Backend: set NSGMLS options and optionally create a pre-NSGMLS pipe.
279
280 =item 2. Here: Run the preprocessor to handle conditionals.
281
282 =item 3. Here: Run NSGMLS.
283
284 =item 4. Backend: run pre-ASP conversion.
285
286 =item 5. Here: Run SGMLSASP.
287
288 =item 6. Backend: run post-ASP conversion, generating the output.
289
290 =back
291
292 All stages are influenced by command-line settings, currently active format,
293 etcetera. See the code for details.
294
295 =cut
296
297 sub process_file
298 {
299 my $file = shift (@_);
300 my $saved_umask = umask;
301
302 print "Processing file $file\n";
303 umask 0077;
304
305 my ($filename, $filepath, $filesuffix) = fileparse ($file, "\.sgml");
306 my $tmpnam = $filepath . '/' . $filename;
307 $file = $tmpnam . $filesuffix;
308 -f $file || $file =~ /.*.sgml$/ || ($file .= '.sgml');
309 -f $file || ($file = $tmpnam . '.SGML');
310 -f $file || die "Cannot find $file\n";
311 $global->{filename} = $filename;
312 $global->{file} = $file;
313 $global->{filepath} = $filepath;
314
315 my $tmp = new FileHandle "<$file";
316 my $dtd;
317 while ( <$tmp> )
318 {
319 tr/A-Z/a-z/;
320 # check for [<!doctype ... system] type definition
321 if ( /<!doctype\s*(\w*)\s*system/ )
322 {
323 $dtd = $1;
324 last;
325 }
326 # check for <!doctype ... PUBLIC ... DTD ...
327 if ( /<!doctype\s*\w*\s*public\s*.*\/\/dtd\s*(\w*)/mi )
328 {
329 $dtd = $1;
330 last;
331 }
332 # check for <!doctype ...
333 # PUBLIC ... DTD ...
334 # (multi-line version)
335 if ( /<!doctype\s*(\w*)/ )
336 {
337 $dtd = "precheck";
338 next;
339 }
340 if ( /\s*public\s*.*\/\/dtd\s*(\w*)/ && $dtd eq "precheck" )
341 {
342 $dtd = $1;
343 last;
344 }
345 }
346 $tmp->close;
347 if ( $global->{debug} )
348 {
349 print "DTD: " . $dtd . "\n";
350 }
351 $global->{dtd} = $dtd;
352
353 # prepare temporary directory
354 my $tmpdir = $ENV{'TMPDIR'} || '/tmp';
355 $tmpdir = $tmpdir . '/' . 'linuxdoc-dir-' . $$;
356 mkdir ($tmpdir, 0700) ||
357 die " - temporary files can not be created, aborted - \n";
358
359 my $tmpbase = $global->{tmpbase} = $tmpdir . '/sgmltmp.' . $filename;
360 $ENV{"SGML_SEARCH_PATH"} .= ":$filepath";
361
362 #
363 # Set up the preprocessing command. Conditionals have to be
364 # handled here until they can be moved into the DTD, otherwise
365 # a validating SGML parser will choke on them.
366 #
367 # check if output option for latex is pdf or not
368 if ($global->{format} eq "latex2e")
369 {
370 if ($Formats{$global->{format}}{output} eq "pdf")
371 {
372 $global->{define} .= " pdflatex=yes";
373 }
374 }
375 #
376
377 local $ENV{PATH} = "$ENV{PATH}:/usr/lib/linuxdoc-tools";
378 my($precmd) = "|sgmlpre output=$global->{format} $global->{define}";
379
380 #
381 # You can hack $NsgmlsOpts here, etcetera.
382 #
383 $global->{NsgmlsOpts} .= "-D $main::prefix/share/sgml -D $main::DataDir";
384 $global->{NsgmlsOpts} .= "-i$global->{include}" if ($global->{include});
385 $global->{NsgmlsPrePipe} = "NOTHING";
386 if ( defined $Formats{$global->{format}}{preNSGMLS} )
387 {
388 $global->{NsgmlsPrePipe} = &{$Formats{$global->{format}}{preNSGMLS}};
389 }
390
391 #
392 # Run the prepocessor and nsgmls.
393 #
394 my ($ifile, $writensgmls);
395
396 if ($global->{NsgmlsPrePipe} eq "NOTHING")
397 {
398 $ifile = new FileHandle $file;
399 }
400 else
401 {
402 $ifile = new FileHandle "$global->{NsgmlsPrePipe}|";
403 }
404
405 create_temp("$tmpbase.1");
406 $writensgmls = new FileHandle
407 "$precmd|$main::progs->{NSGMLS} $global->{NsgmlsOpts} $ENV{SGMLDECL} >\"$tmpbase.1\"";
408 if ($global->{charset} eq "latin")
409 {
410 while (<$ifile>)
411 {
412 # Outline these commands later on - CdG
413 #change latin1 characters to SGML
414 #by Farzad Farid, adapted by Greg Hankins
415 s/À/\&Agrave;/g;
416 s/Á/\&Aacute;/g;
417 s/Â/\&Acirc;/g;
418 s/Ã/\&Atilde;/g;
419 s/Ä/\&Auml;/g;
420 s/Å/\&Aring;/g;
421 s/Æ/\&AElig;/g;
422 s/Ç/\&Ccedil;/g;
423 s/È/\&Egrave;/g;
424 s/É/\&Eacute;/g;
425 s/Ê/\&Ecirc;/g;
426 s/Ë/\&Euml;/g;
427 s/Ì/\&Igrave;/g;
428 s/Í/\&Iacute;/g;
429 s/Î/\&Icirc;/g;
430 s/Ï/\&Iuml;/g;
431 s/Ñ/\&Ntilde;/g;
432 s/Ò/\&Ograve;/g;
433 s/Ó/\&Oacute;/g;
434 s/Ô/\&Ocirc;/g;
435 s/Õ/\&Otilde;/g;
436 s/Ö/\&Ouml;/g;
437 s/Ø/\&Oslash;/g;
438 s/Ù/\&Ugrave;/g;
439 s/Ú/\&Uacute;/g;
440 s/Û/\&Ucirc;/g;
441 s/Ü/\&Uuml;/g;
442 s/Ý/\&Yacute;/g;
443 s/Þ/\&THORN;/g;
444 s/ß/\&szlig;/g;
445 s/à/\&agrave;/g;
446 s/á/\&aacute;/g;
447 s/â/\&acirc;/g;
448 s/ã/\&atilde;/g;
449 s/ä/\&auml;/g;
450 s/å/\&aring;/g;
451 s/æ/\&aelig;/g;
452 s/ç/\&ccedil;/g;
453 s/è/\&egrave;/g;
454 s/é/\&eacute;/g;
455 s/ê/\&ecirc;/g;
456 s/ë/\&euml;/g;
457 s/ì/\&igrave;/g;
458 s/í/\&iacute;/g;
459 s/î/\&icirc;/g;
460 s/ï/\&iuml;/g;
461 s/µ/\&mu;/g;
462 s/ð/\&eth;/g;
463 s/ñ/\&ntilde;/g;
464 s/ò/\&ograve;/g;
465 s/ó/\&oacute;/g;
466 s/ô/\&ocirc;/g;
467 s/õ/\&otilde;/g;
468 s/ö/\&ouml;/g;
469 s/ø/\&oslash;/g;
470 s/ù/\&ugrave;/g;
471 s/ú/\&uacute;/g;
472 s/û/\&ucirc;/g;
473 s/ü/\&uuml;/g;
474 s/ý/\&yacute;/g;
475 s/þ/\&thorn;/g;
476 s/ÿ/\&yuml;/g;
477 print $writensgmls $_;
478 }
479 }
480 else
481 {
482 while (<$ifile>)
483 {
484 print $writensgmls $_;
485 }
486 }
487 $ifile->close;
488 $writensgmls->close;
489
490 #
491 # Special case: if format is global, we're just checking.
492 #
493 $global->{format} eq "global" && cleanup;
494
495 #
496 # If the output file is empty, something went wrong.
497 #
498 ! -e "$tmpbase.1" and die "can't create file - exiting";
499 -z "$tmpbase.1" and die "SGML parsing error - exiting";
500 if ( $global->{debug} )
501 {
502 print "Nsgmls stage finished.\n";
503 }
504
505 #
506 # If a preASP stage is defined, let the format handle it.
507 #
508 # preASP ($inhandle, $outhandle);
509 #
510 my $inpreasp = new FileHandle "<$tmpbase.1";
511 my $outpreasp = new FileHandle "$tmpbase.2",O_WRONLY|O_CREAT|O_EXCL,0600;
512 if (defined $Formats{$global->{format}}{preASP})
513 {
514 &{$Formats{$global->{format}}{preASP}}($inpreasp, $outpreasp) == 0 or
515 die "error pre-processing $global->{format}.\n";
516 }
517 else
518 {
519 copy ($inpreasp, $outpreasp);
520 }
521 $inpreasp->close;
522 $outpreasp->close;
523 ! -e "$tmpbase.2" and die "can't create file - exiting";
524
525 if ( $global->{debug} )
526 {
527 print "PreASP stage finished.\n";
528 }
529
530 #
531 # Run sgmlsasp, with an optional style if specified.
532 #
533 # Search order:
534 # - datadir/site/<dtd>/<format>
535 # - datadir/dist/<dtd>/<format>
536 # So we need to fetch the doctype from the intermediate.
537 #
538 # Note: this is a very simplistic check - but as far as I know,
539 # it is correct. Am I right?
540 #
541 my $tmp = new FileHandle "<$tmpbase.2";
542 my $dtd;
543 while ( ($dtd = <$tmp>) && ! ( $dtd =~ /^\(/) ) { };
544 $tmp->close;
545 $dtd =~ s/^\(//;
546 $dtd =~ tr/A-Z/a-z/;
547 chop $dtd;
548 $global->{dtd} = $dtd;
549
550 my $style = "";
551 if ($global->{style})
552 {
553 $style = "$main::DataDir/site/$dtd/$global->{format}/$global->{style}mapping";
554 -r $style or
555 $style = "$main::DataDir/dist/$dtd/$global->{format}/$global->{style}mapping";
556 }
557 my $mapping = "$main::DataDir/site/$dtd/$global->{format}/mapping";
558 -r $mapping or $mapping = "$main::DataDir/dist/$dtd/$global->{format}/mapping";
559
560 $global->{charset} = "nippon" if ($global->{language} eq "ja");
561 #
562 # we don't have Korean groff so charset should be latin1.
563 #
564 if ($global->{language} eq "ko")
565 {
566 if ($global->{format} eq "groff")
567 {
568 $global->{charset} = "latin1";
569 }
570 else
571 {
572 $global->{charset} = "euc-kr";
573 }
574 }
575
576 if ($global->{format} eq "groff" or $global->{format} eq "latex2e")
577 {
578 if ($dtd eq "linuxdoctr")
579 {
580 $mapping = "$main::DataDir/dist/$dtd/$global->{format}/tr-mapping";
581 }
582 }
583
584 create_temp("$tmpbase.3");
585 system ("$main::progs->{SGMLSASP} $style $mapping <\"$tmpbase.2\" |
586 expand -$global->{tabsize} >\"$tmpbase.3\"");
587 ! -e "$tmpbase.3" and die "can't create file - exiting";
588
589
590 if ( $global->{debug} )
591 {
592 print "ASP stage finished.\n";
593 }
594
595 #
596 # If a postASP stage is defined, let the format handle it.
597 # It should leave whatever it thinks is right based on $file.
598 #
599 # postASP ($inhandle)
600 #
601 umask $saved_umask;
602 my $inpostasp = new FileHandle "<$tmpbase.3";
603 if (defined $Formats{$global->{format}}{postASP})
604 {
605 &{$Formats{$global->{format}}{postASP}}($inpostasp) == 0 or
606 die "error post-processing $global->{format}.\n";
607 }
608 $inpostasp->close;
609
610 if ( $global->{debug} )
611 {
612 print "postASP stage finished.\n";
613 }
614
615 #
616 # All done, remove the temporaries.
617 #
618 if( !$global->{debug} ) {
619 remove_tmpfiles($tmpbase);
620 }
621 }
622
623 =pod
624
625 =back
626
627 =head1 SEE ALSO
628
629 Documentation for various sub-packages of LinuxDocTools.
630
631 =head1 AUTHOR
632 SGMLTools are written by Cees de Groot, C<E<lt>cg@cdegroot.comE<gt>>,
633 and various SGML-Tools contributors as listed in C<CONTRIBUTORS>.
634 Taketoshi Sano C<E<lt>sano@debian.org<gt>> rename to LinuxDocTools.
635
636 =cut
637 1;