<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">#! /usr/bin/perl 
#
#  SGMLTools.pm
#
#  $Id: SGMLTools.pm,v 1.40 1998/10/13 11:54:59 cg Exp $
#
#  SGML-Tools driver core. This contains all the basic functionality
#  we need to control all other components.
#
#  Đ Copyright 1996, Cees de Groot.
#
package SGMLTools;

require 5.004;
use strict;

=head1 NAME

SGMLTools - SGML conversion utilities

=head1 SYNOPSIS

  use SGMLTools;
  SGMLTools::init;
  @files = SGMLTools::process_options ($0, @ARGV);
  for $curfile (@files) {
    SGMLTools::process_file ($curfile);
  }

=head1 DESCRIPTION

The SGMLTools package encapsulates all the functionality offered by
SGML-Tools. It is used, of course, by SGML-Tools; but the encapsulation
should provide for a simple interface for other users as well. 

=head1 FUNCTIONS

=over 4

=cut

use DirHandle;
use File::Basename;
use File::Find;
use File::Copy;
use FileHandle;
use IPC::Open2;
use Cwd;
use SGMLTools::Lang;
use SGMLTools::Utils qw(process_options usage cleanup trap_signals remove_tmpfiles);
use SGMLTools::Vars;

sub BEGIN
{
  #
  #  Make sure we're always looking here. Note that "use lib" adds 
  #  on the front of the search path, so we first push dist, then
  #  site, so that site is searched first.
  #
  use lib "$main::LibDir/dist";
  use lib "$main::LibDir/site";
}

=item SGMLTools::init

Takes care of initialization of package-global variables (which are actually
defined in L&lt;SGMLTools::Vars&gt;). The package-global variables are I&lt;$global&gt;,
a reference to a hash containing numerous settings, I&lt;%Formats&gt;, a hash
containing all the formats, and I&lt;%FmtList&gt;, a hash containing the currently
active formats for help texts. 

Apart from this, C&lt;SGMLTools::init&gt; also finds all distributed and site-local
formatting backends and C&lt;require&gt;s them.

=cut

sub init
{
  trap_signals;

  #
  #  Register the ``global'' pseudoformat. Apart from the global settings,
  #  we also use $global to keep the global variable name space clean; 
  #  everything that we need to provide to other modules is stuffed
  #  into $global.
  #
  $global = {};
  $global-&gt;{NAME} = "global";
  $global-&gt;{HELP} = "";
  $global-&gt;{OPTIONS} = [
    { option =&gt; "papersize", type =&gt; "l",
      'values' =&gt; [ "a4", "letter" ], short =&gt; "p" },
    { option =&gt; "language",  type =&gt; "l",
      'values' =&gt; [ @SGMLTools::Lang::Languages ], short =&gt; "l" },
    { option =&gt; "charset",   type =&gt; "l",
      'values' =&gt; [ "latin", "ascii" ], short =&gt; "c" },
    { option =&gt; "style",     type =&gt; "s", short =&gt; "S" },
    { option =&gt; "tabsize",   type =&gt; "i", short =&gt; "t" },
    { option =&gt; "verbose",   type =&gt; "f", short =&gt; "v" },
    { option =&gt; "debug",     type =&gt; "f", short =&gt; "d" },
    { option =&gt; "define",    type =&gt; "s", short =&gt; "D" },
    { option =&gt; "include",   type =&gt; "s", short =&gt; "i" },
    { option =&gt; "pass",      type =&gt; "s", short =&gt; "P" }
  ];
  $global-&gt;{papersize} = "letter";
  $global-&gt;{language}  = "en";
  $global-&gt;{charset}   = "ascii";
  $global-&gt;{style}     = "";
  $global-&gt;{tabsize}   = 8;
  $global-&gt;{verbose}   = 0;
  $global-&gt;{define}    = "";
  $global-&gt;{debug}     = 0;
  $global-&gt;{include}   = "";
  $global-&gt;{pass}      = "";
  $global-&gt;{InFiles}   = [];
  $Formats{$global-&gt;{NAME}} = $global;	# All formats we know.
  $FmtList{$global-&gt;{NAME}} = $global;  # List of formats for help msgs.

  #
  #  Used when the format is "global" (from sgmlcheck).
  #
  $global-&gt;{preNSGMLS} = sub { $global-&gt;{NsgmlsOpts} .= " -s " };

  #
  #  Build up the list of formatters.
  #
  my $savdir = cwd;
  my %Locs;
  chdir "$main::LibDir/dist";
  my $dir = new DirHandle(".");
  die "Unable to read directory $main::LibDir/dist: $!" unless defined($dir);
  foreach my $fmt (grep(/^fmt_.*\.pl$/, $dir-&gt;read()))
  {
    $Locs{$fmt} = "dist";
  }
  $dir-&gt;close();
  chdir "$main::LibDir/site";
  $dir = new DirHandle(".");
  die "Unable to read directory $main::LibDir/site: $!" unless defined($dir);
  foreach my $fmt (grep(/^fmt_.*\.pl$/, $dir-&gt;read()))
  {
    $Locs{$fmt} = "site";
  }
  $dir-&gt;close();
  foreach my $fmt (keys %Locs)
  {
    require $fmt;
  }
  chdir $savdir;
}

=item SGMLTools::process_options ($0, @ARGV)

This function contains all initialization that is bound to the current
invocation of SGMLTools. It looks in C&lt;$0&gt; to deduce the backend that
should be used (sgml2txt activates the I&lt;txt&gt; backend) and parses the
options array. It returns an array of filenames it encountered during
option processing.

As a side effect, the environment variables I&lt;SGMLDECL&gt; and 
I&lt;SGML_CATALOG_FILES&gt; are modified.

=cut

sub process_options
{
  my $progname = shift;
  my @args = @_;

  #
  #  Deduce the format from the caller's file name
  #
  my ($format, $dummy1, $dummy2) = fileparse ($progname, "");
  $global-&gt;{myname} = $format;
  $format =~ s/sgml2(.*)/$1/;
  $format = "global" if $format eq "sgmlcheck";
  $format = "latex2e" if $format eq "latex";
  $FmtList{$format} = $Formats{$format} or 
     usage ("$global-&gt;{myname}: unknown format");
  $global-&gt;{format} = $format;

  #
  #  Parse all the options.
  #
  my @files = SGMLTools::Utils::process_options (@args);
  $#files &gt; -1 || usage ("no filenames given");
  $global-&gt;{language} = Any2ISO ($global-&gt;{language});

  #
  #  Setup the SGML environment.
  #
  $ENV{SGML_CATALOG_FILES} .= (defined $ENV{SGML_CATALOG_FILES} ? ":" : "") .
     "$main::prefix/lib/sgml/iso-entities-8879.1986/iso-entities.cat";
  $ENV{SGML_CATALOG_FILES} .= ":$main::LibDir/dtd/catalog";
  if (-f "$main::LibDir/dtd/$format.dcl")
    {
      $ENV{SGMLDECL} = "$main::LibDir/dtd/$format.dcl";
    }
  elsif (-f "$main::LibDir/dtd/$global-&gt;{style}.dcl")
    {
      $ENV{SGMLDECL} = "$main::LibDir/dtd/$global-&gt;{style}.dcl";
    }
  elsif (-f "$main::LibDir/dtd/sgml.dcl")
    {
      $ENV{SGMLDECL} = "$main::LibDir/dtd/sgml.dcl";
    }

  #
  #  OK. Give the list of files we distilled from the options
  #  back to the caller.
  #
  return @files;
}

# Pre-create temp files safely
sub create_temp {
	my $fh = new FileHandle(shift,O_CREAT|O_EXCL|O_WRONLY,0600);
	$fh or die "$0: failed to create temporary file: $!";
	$fh-&gt;close;
}

=item SGMLTools::process_file

With all the configuration done, this routine will take a single filename
and convert it to the currently active backend format. The conversion is
done in a number of steps in tight interaction with the currently active
backend (see also L&lt;SGMLTools::BackEnd&gt;):

=over

=item 1. Backend: set NSGMLS options and optionally create a pre-NSGMLS pipe.

=item 2. Here: Run the preprocessor to handle conditionals.

=item 3. Here: Run NSGMLS.

=item 4. Backend: run pre-ASP conversion.

=item 5. Here: Run SGMLSASP.

=item 6. Backend: run post-ASP conversion, generating the output.

=back

All stages are influenced by command-line settings, currently active format,
etcetera. See the code for details.

=cut

sub process_file
{
  my $file = shift (@_);

  print "Processing file $file\n";
  my ($filename, $filepath, $filesuffix) = fileparse ($file, "\.sgml");
  my $tmpnam = $filepath . '/' . $filename;
  $file = $tmpnam . $filesuffix;
  -f $file || $file =~ /.*.sgml$/ || ($file .= '.sgml');
  -f $file || ($file = $tmpnam . '.SGML');
  -f $file || die "Cannot find $file\n";
  $global-&gt;{filename} = $filename;
  $global-&gt;{file} = $file;
  $global-&gt;{filepath} = $filepath;

  # prepare temporary directory
  my $tmpdir = $ENV{'TMPDIR'} || '/tmp';
  $tmpdir = $tmpdir . '/' . 'sgml-tools-dir-' . $$;
  mkdir ($tmpdir, 0700) || return -1;

  my $tmpbase = $global-&gt;{tmpbase} = $tmpdir . '/sgmltmp.' . $filename . $$;
  $ENV{"SGML_SEARCH_PATH"} .= ":$filepath";

  #
  # Set up the preprocessing command.  Conditionals have to be
  # handled here until they can be moved into the DTD, otherwise
  # a validating SGML parser will choke on them.
  #
  my($precmd) = "|sgmlpre output=$global-&gt;{format} $global-&gt;{define}";

  #
  #  You can hack $NsgmlsOpts here, etcetera.
  #
  $global-&gt;{NsgmlsOpts} .= "-i$global-&gt;{include}" if ($global-&gt;{include});
  $global-&gt;{NsgmlsPrePipe} = "NOTHING";
  &amp;{$Formats{$global-&gt;{format}}{preNSGMLS}} if 
     defined $Formats{$global-&gt;{format}}{preNSGMLS};

  #
  #  Run the prepocessor and nsgmls.
  #
  my ($ifile, $writensgmls);
  if ($global-&gt;{NsgmlsPrePipe} eq "NOTHING")
    {
      $ifile = new FileHandle $file;
    }
  else
    {
      $ifile = new FileHandle "$global-&gt;{NsgmlsPrePipe}|";
    }

  create_temp("$tmpbase.1");
  $writensgmls = new FileHandle
      "$precmd|$main::progs-&gt;{NSGMLS} $global-&gt;{NsgmlsOpts} $ENV{SGMLDECL} &gt;\"$tmpbase.1\"";
  if ($global-&gt;{charset} eq "latin")
    {
      while (&lt;$ifile&gt;) 
        {
	  # Outline these commands later on - CdG
	  #change latin1 characters to SGML
	  #by Farzad Farid, adapted by Greg Hankins
	  s/Ā/\&amp;Agrave;/g;
	  s/Á/\&amp;Aacute;/g;
	  s/Â/\&amp;Acirc;/g;
	  s/Ã/\&amp;Atilde;/g;
	  s/Ä/\&amp;Auml;/g;
	  s/Å/\&amp;Aring;/g;
	  s/Æ/\&amp;AElig;/g;
	  s/Į/\&amp;Ccedil;/g;
	  s/Č/\&amp;Egrave;/g;
	  s/É/\&amp;Eacute;/g;
	  s/Ę/\&amp;Ecirc;/g;
	  s/Ë/\&amp;Euml;/g;
	  s/Ė/\&amp;Igrave;/g;
	  s/Í/\&amp;Iacute;/g;
	  s/Î/\&amp;Icirc;/g;
	  s/Ï/\&amp;Iuml;/g;
	  s/Ņ/\&amp;Ntilde;/g;
	  s/Ō/\&amp;Ograve;/g;
	  s/Ó/\&amp;Oacute;/g;
	  s/Ô/\&amp;Ocirc;/g;
	  s/Õ/\&amp;Otilde;/g;
	  s/Ö/\&amp;Ouml;/g;
	  s/Ø/\&amp;Oslash;/g;
	  s/Ų/\&amp;Ugrave;/g;
	  s/Ú/\&amp;Uacute;/g;
	  s/Û/\&amp;Ucirc;/g;
	  s/Ü/\&amp;Uuml;/g;
	  s/Ý/\&amp;Yacute;/g;
	  s/Þ/\&amp;THORN;/g;
	  s/ß/\&amp;szlig;/g;
	  s/ā/\&amp;agrave;/g;
	  s/á/\&amp;aacute;/g;
	  s/â/\&amp;acirc;/g;
	  s/ã/\&amp;atilde;/g;
	  s/ä/\&amp;auml;/g;
	  s/å/\&amp;aring;/g;
	  s/æ/\&amp;aelig;/g;
	  s/į/\&amp;ccedil;/g;
	  s/č/\&amp;egrave;/g;
	  s/é/\&amp;eacute;/g;
	  s/ę/\&amp;ecirc;/g;
	  s/ë/\&amp;euml;/g;
	  s/ė/\&amp;igrave;/g;
	  s/í/\&amp;iacute;/g;
	  s/î/\&amp;icirc;/g;
	  s/ï/\&amp;iuml;/g;
	  s/ĩ/\&amp;mu;/g;
	  s/ð/\&amp;eth;/g;
	  s/ņ/\&amp;ntilde;/g;
	  s/ō/\&amp;ograve;/g;
	  s/ó/\&amp;oacute;/g;
	  s/ô/\&amp;ocirc;/g;
	  s/õ/\&amp;otilde;/g;
	  s/ö/\&amp;ouml;/g;
	  s/ø/\&amp;oslash;/g;
	  s/ų/\&amp;ugrave;/g;
	  s/ú/\&amp;uacute;/g;
	  s/û/\&amp;ucirc;/g;
	  s/ü/\&amp;uuml;/g;
	  s/ý/\&amp;yacute;/g;
	  s/þ/\&amp;thorn;/g;
	  s/ĸ/\&amp;yuml;/g;
          print $writensgmls $_;
	}
    }
  else
    {
      while (&lt;$ifile&gt;)
        {
          print $writensgmls $_;
	}
    }
  $ifile-&gt;close;
  $writensgmls-&gt;close;
        
  #
  #  Special case: if format is global, we're just checking.
  #
  $global-&gt;{format} eq "global" &amp;&amp; cleanup;

  #
  #  If the output file is empty, something went wrong.
  #
  -z "$tmpbase.1" and die "SGML parsing error - exiting";

  #
  #  If a preASP stage is defined, let the format handle it.
  #  
  #  preASP ($inhandle, $outhandle);
  #
  my $inpreasp = new FileHandle "&lt;$tmpbase.1";
  my $outpreasp = new FileHandle "$tmpbase.2",O_WRONLY|O_CREAT|O_EXCL,0600;
  if (defined $Formats{$global-&gt;{format}}{preASP})
    {
      &amp;{$Formats{$global-&gt;{format}}{preASP}}($inpreasp, $outpreasp) == 0 or
       die "error pre-processing $global-&gt;{format}.\n";
    }  
  else
    {
      copy ($inpreasp, $outpreasp);
    }
  $inpreasp-&gt;close;
  $outpreasp-&gt;close;

  #
  #  Run sgmlsasp, with an optional style if specified.
  #
  #  Search order:
  #  - libdir/site/&lt;dtd&gt;/&lt;format&gt;
  #  - libdir/dist/&lt;dtd&gt;/&lt;format&gt;
  #  So we need to fetch the doctype from the intermediate.
  #
  #  Note: this is a very simplistic check - but as far as I know,
  #  it is correct. Am I right?
  #
  my $tmp = new FileHandle "&lt;$tmpbase.2";
  my $dtd = &lt;$tmp&gt;;
  $tmp-&gt;close;
  $dtd =~ s/^\(//;
  $dtd =~ tr/A-Z/a-z/;
  chop $dtd;
  $global-&gt;{dtd} = $dtd;

  my $style = "";
  if ($global-&gt;{style})
    {
      $style = "$main::LibDir/site/$dtd/$global-&gt;{format}/$global-&gt;{style}mapping";
      -r $style or
         $style = "$main::LibDir/dist/$dtd/$global-&gt;{format}/$global-&gt;{style}mapping";
    }
  my $mapping = "$main::LibDir/site/$dtd/$global-&gt;{format}/mapping";
  -r $mapping or $mapping = "$main::LibDir/dist/$dtd/$global-&gt;{format}/mapping";

  create_temp("$tmpbase.3");
  system ("$main::progs-&gt;{SGMLSASP} $style $mapping &lt;\"$tmpbase.2\" |
      expand -$global-&gt;{tabsize} &gt;\"$tmpbase.3\"");

  #
  #  If a postASP stage is defined, let the format handle it.
  #  It should leave whatever it thinks is right based on $file.
  #
  #  postASP ($inhandle)
  #
  my $inpostasp = new FileHandle "&lt;$tmpbase.3";
  if (defined $Formats{$global-&gt;{format}}{postASP})
    {
      &amp;{$Formats{$global-&gt;{format}}{postASP}}($inpostasp) == 0 or
	die "error post-processing $global-&gt;{format}.\n";
    }
  $inpostasp-&gt;close;

  #
  #  All done, remove the temporaries.
  #
  if( !$global-&gt;{debug} ) {
      remove_tmpfiles($tmpbase);
  }
}

=pod

=back

=head1 SEE ALSO

Documentation for various sub-packages of SGMLTools.

=head1 AUTHOR

Cees de Groot, C&lt;E&lt;lt&gt;cg@pobox.comE&lt;gt&gt;&gt;, and various SGML-Tools contributors as
listed in C&lt;CONTRIBUTORS&gt;.

=cut
1;
</pre></body></html>