: # *-*-perl-*-* eval 'exec perl -S $0 "$@"' if $running_under_some_shell; # # SGML.sum - SGML summarizer for Harvest # # Usage: SGML.sum [-d file.decl] [-t file.tbl] DOCTYPE [file] # # $Id: SGML.sum,v 2.7 2000/01/21 17:37:32 sxw Exp $ # ############################################################################# # # Harvest Indexer http://harvest.sourceforge.net/ # ----------------------------------------------- # # The Harvest Indexer is a continued development of code developed by # the Harvest Project. Development is carried out by numerous individuals # in the Internet community, and is not officially connected with the # original Harvest Project or its funding sources. # # Please mail lee@arco.de if you are interested in participating # in the development effort. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # ###################################################################### # Copyright (c) 1994, 1995. All rights reserved. # # The Harvest software was developed by the Internet Research Task # Force Research Group on Resource Discovery (IRTF-RD): # # Mic Bowman of Transarc Corporation. # Peter Danzig of the University of Southern California. # Darren R. Hardy of the University of Colorado at Boulder. # Udi Manber of the University of Arizona. # Michael F. Schwartz of the University of Colorado at Boulder. # Duane Wessels of the University of Colorado at Boulder. # # This copyright notice applies to software in the Harvest # ``src/'' directory only. Users should consult the individual # copyright notices in the ``components/'' subdirectories for # copyright information about other software bundled with the # Harvest source code distribution. # # TERMS OF USE # # The Harvest software may be used and re-distributed without # charge, provided that the software origin and research team are # cited in any use of the system. Most commonly this is # accomplished by including a link to the Harvest Home Page # (http://harvest.cs.colorado.edu/) from the query page of any # Broker you deploy, as well as in the query result pages. These # links are generated automatically by the standard Broker # software distribution. # # The Harvest software is provided ``as is'', without express or # implied warranty, and with no support nor obligation to assist # in its use, correction, modification or enhancement. We assume # no liability with respect to the infringement of copyrights, # trade secrets, or any patents, and are not responsible for # consequential damages. Proper use of the Harvest software is # entirely the responsibility of the user. # # DERIVATIVE WORKS # # Users may make derivative works from the Harvest software, subject # to the following constraints: # # - You must include the above copyright notice and these # accompanying paragraphs in all forms of derivative works, # and any documentation and other materials related to such # distribution and use acknowledge that the software was # developed at the above institutions. # # - You must notify IRTF-RD regarding your distribution of # the derivative work. # # - You must clearly notify users that your are distributing # a modified version and not the original Harvest software. # # - Any derivative product is also subject to these copyright # and use restrictions. # # Note that the Harvest software is NOT in the public domain. We # retain copyright, as specified above. # # HISTORY OF FREE SOFTWARE STATUS # # Originally we required sites to license the software in cases # where they were going to build commercial products/services # around Harvest. In June 1995 we changed this policy. We now # allow people to use the core Harvest software (the code found in # the Harvest ``src/'' directory) for free. We made this change # in the interest of encouraging the widest possible deployment of # the technology. The Harvest software is really a reference # implementation of a set of protocols and formats, some of which # we intend to standardize. We encourage commercial # re-implementations of code complying to this set of standards. # # $start_t = time; $syntax_check = 0; # set to 1 if you want sgmls to print error messages $debug = 0; $print_times = 0; # set to 1 if you want SGML.sum to print time info $print_times = 1 if ($debug); # Don't perform "word wrap" on strings larger than this length. The word # wrap function can be very slow for large buffers $MAXWRAPSIZE = 16384; $usage = "usage: $0 DOCTYPE [file]\n"; &Getopts('d:t:'); $doctype = shift || die $usage; die $usage unless ($#ARGV <= $[); $| = 1; $sgmls_cmd = "$ENV{'HARVEST_HOME'}/lib/gatherer/sgmls"; $sgmls_lib = "$ENV{'HARVEST_HOME'}/lib/gatherer/sgmls-lib"; $catalog = "$sgmls_lib/catalog"; $decl = "$sgmls_lib/$doctype/$doctype.decl"; $table = "$sgmls_lib/$doctype/$doctype.sum.tbl"; # default # Set this to get prefixed META NAMEs (and HTTP-EQUIVs). (HS) #$meta_prefix = 'meta-'; $meta_prefix = ''; # Set this to 1 to change all non-alphanumeric characters in SOIF attribute # names to '_'. The only exception is '-' that is not changed. (HS) $fix_attrib_names = 0; # search for the .sum.tbl file; allows a per Gatherer customizations # $LIBPATH = $ENV{'SUMMARIZER_LIBPATH'}; foreach $d (split(':', $LIBPATH)) { if ( -r "$d/$doctype.sum.tbl" ) { $table = "$d/$doctype.sum.tbl"; last; } } # Command-line options override all defaults # $decl = $opt_d if defined $opt_d; $table = $opt_t if defined $opt_t; unless ( -f $decl && -f $table ) { print STDERR "No support for doctype $doctype\n"; print STDERR "Missing: $decl\n" unless ( -f $decl ); print STDERR "Missing: $table\n" unless ( -f $table ); exit (1); } # Load the TAG->ATTR table # open (table) || die "$table: $!\n"; @soifkeys = (); # need a list to preserve the order while () { s/#.*$//; next unless (/\S/); chop; next unless (/^<([^>]+)>\s*(.*)\s*$/); ($tag = $1) =~ tr/a-z/A-Z/; $SOIF{$tag} = $2; print STDERR "table> $tag --> $SOIF{$tag}\n" if ($debug); push (@soifkeys, $tag); } close table; undef ($tag); # Read the SGML data into a tmpfile. Tack on the DOCYTYPE if missing # undef $/; $sgml = <> || die "SGML.sum: No input.\n"; $T = &tempnam; open (T, ">$T") || die "$T: $!\n"; # Ack! this SGML crap junk is ignorant somtimes. Sometimes the authoring # tool writes # # # # and 'sgmls' then looks for 'html.dtd' in the current directory. This # fails in a bad way. So now we check for this type of DOCTYPE spec. # If found, stat the "html.dtd" part and if its not there, change that # whole thing to # # # if ($sgml =~ /^\s*/i) { $xdoctype = $1; $dtdfile = $4; $sgml =~ s/^\s*]+>//i unless ( -f $dtdfile ); undef $xdoctype; undef $dtdfile; } # Sheesh. We need to do the same for: # # # but NOT: # # # or if ($sgml =~ /^\s*/i) { $xdoctype = $1; $dtdfile = $3; $sgml =~ s/^\s*]+>//i unless ( -f $dtdfile ); undef $xdoctype; undef $dtdfile; } elsif ($sgml =~ /^\s*/i) { $xdoctype = $1; $dtdfile = $3; $sgml =~ s/^\s*]+>//i unless ( -f $dtdfile ); undef $xdoctype; undef $dtdfile; } print T "\n" unless $sgml =~ /^\s*) { chop; s/^[^:]*:[^:]*://; ($line_no, $eno, $e, $error_message) = split(':'); print STDERR "SGML error in $ENV{SUMMARIZER_URL} at line $line_no: $error_message\n"; } } } unlink ($Terr) unless ($debug); unlink ($T) unless ($debug); # CLEANUP ATT VALUES # foreach $k (keys %ATT) { $ATT{$k} =~ s/\\n/\n/g; # change backslash-N to newline $ATT{$k} =~ s/\\011/\t/g; # change escaped tabs $ATT{$k} =~ s/\n*\n/\n/g; # remove blank lines } foreach $att (sort keys %ATT) { $val = $ATT{$att}; chop $val if ($val =~ /\n$/); # chop last newline if present next if ($val eq ""); next if ($att =~ /[ \t]/); # don't output atts with whitespace print $att . "{" . length($val) . "}:\t"; print $val . "\n"; } @t = times; $dt = time - $start_t; printf STDERR ("SGML.sum $doctype: %d tags, time: %.2fu %.2fs (%.2fu %.2fs), %02d:%02d\n", $ntags, $t[0], $t[1], $t[2], $t[3], $dt / 60, $dt % 60) if ($print_times); exit 0; #======================================================================== # SUBROUTINES sub parse_sgml { local ($tag, %VARS) = @_; local ($buf); # holds data content for current tag local (%NEWVARS); # holds SGML attrs for next tag local ($pass); local (@atts, $att); local ($k); $r_depth++; print STDERR "parse_sgml: Recursion Level $r_depth\n" if ($debug); foreach $k (keys %VARS) { foreach $att (&select_vars ("$tag:$k", %VARS)) { &add_to_attribute ($VARS{$k}, $att); } } while () { # Hrvoje Stipetic HS - few changes in this block # Optional: convert octal char codes to 8-bit chars # (helps on systems without locale support) s/\\{0}\\(\d\d\d)/sprintf("%c", oct $1)/ge; print STDERR "sgmls> $_" if ($debug); if (/^\((.*)$/) { # SGML start GI $buf .= &parse_sgml ($1, %NEWVARS); $ntags++; undef %NEWVARS; # no undef - bug - HS } last if (/^\)$tag$/); # SGML end GI if (/^A(\S+) (\S+) ?(.*)$/) { # SGML attr # There is no second space if IMPLIED - HS next if ($2 eq 'IMPLIED'); $NEWVARS{$1} = $3; } $buf .= $1 if (/^-(.*)$/); # SGML data } # end tag found, choose SOIF attrs # @atts = &select_content ($tag, %VARS); local ($pass) = $#atts == $[-1 ? 1 : 0; foreach $att (@atts) { print STDERR "parse_sgml: Adding text to attribute '$att'\n" if ($debug); ($pass = 1, next) if ($att eq 'parent'); ($pass = 0, last) if ($att eq 'ignore'); &add_to_attribute ($buf, $att); } print STDERR "parse_sgml: Returning from level $r_depth\n" if ($debug); $r_depth--; return undef unless ($pass); $buf .= "\n"; $buf; } # Selects output SOIF attribute based on SGML tag and variables # Only used to print _CONTENT_ between tags, ie: content # Would not be called on HTML tags such as IMG, META which have no content # sub select_content { local ($tag, %VARS) = @_; local ($t); local (@s); local (@r) = (); local ($s); local ($av); local ($a); local ($v); print STDERR "tag=$tag\n" if ($debug); tagloop: foreach $t (@soifkeys) { print STDERR "select_content: checking SOIF{$t}...\n" if ($debug); @s = split (',', $t); # s[0] is tag, s[1..n] are att=val $s = shift @s; print STDERR "select_content: checking '$tag' eq '$s'\n" if ($debug); next unless ($tag eq $s); foreach $av (@s) { ($a,$v) = split (/=/, $av); print STDERR "A=$a\tV=$v\tVARS{$a}=$VARS{$a}\n" if ($debug); next tagloop unless ($VARS{$a} eq $v); } @r = split (/,/, $SOIF{$t}); last; } print STDERR "select_content: Returning (@r)\n" if ($debug); return @r; } # Selects output SOIF attribute based on SGML tag, variables # Only used to find entries of the form TAG:VARIABLE in the table # sub select_vars { local ($tagvar, %VARS) = @_; local (@atts) = (); local ($att); local ($t); local (@s); local ($s); local ($a); local ($v); local ($av); print STDERR "tagvar=$tagvar\n" if ($debug); # Support for HTML.sum table entry like this one: # soifattr1,soifattr2 # Primary goal is to support Dublin Core. (HS) tagloop: foreach $t (@soifkeys) { @s = split (',', $t); # s[0] is tag:var, s[1..n] are att=val $s = shift @s; if ($s eq $tagvar) { foreach $av (@s) { ($a,$v) = split (/=/, $av); print STDERR "A=$a\tV=$v\tVARS{$a}=$VARS{$a}\n" if ($debug); next tagloop unless ($VARS{$a} eq $v); } foreach $att (split (/,/, $SOIF{$t})) { if ($att =~ /\$/) { # $NAME, $HTTP-EQUIV $att =~ s/\$//; if (defined $VARS{$att}) { $att = $VARS{$att}; $att =~ tr/A-Z/a-z/; # Fix SOIF attribute names $att =~ s/[^\w-]/_/g if ($fix_attrib_names); # Add custom prefix for META # generated SOIF attributes $att = $meta_prefix . $att if ($tagvar =~ /^META/); push (@atts, $att); } } else { push (@atts, $att); } } last; } } @atts; } # Add a string to an attribute # sub add_to_attribute { local ($buf, $a) = @_; return if ($a eq 'ignore' || $a eq ''); print STDERR "ADDING: $buf\n" if ($debug); print STDERR " TO: $a\n" if ($debug); $buf = &wrapline ($buf) if (length($buf) < $MAXWRAPSIZE); $ATT{$a} .= $buf . "\n"; } # Build a temp filename # sub tempnam { srand time; local ($f) = sprintf ("sgml%d%06x", $$, rand (0xFFFFFF)); return "$ENV{'TMPDIR'}/$f" if (defined $ENV{'TMPDIR'} && -d $ENV{'TMPDIR'}); return "/var/tmp/$f" if (-d "/var/tmp"); return "/tmp/$f"; } sub temperrnam { srand time; local ($f) = sprintf ("err%d%06x", $$, rand (0xFFFFFF)); return "$ENV{'TMPDIR'}/$f" if (defined $ENV{'TMPDIR'} && -d $ENV{'TMPDIR'}); return "/var/tmp/$f" if (-d "/var/tmp"); return "/tmp/$f"; } # Parse cmdline options. Taken from /usr/local/lib/perl/getopts.pl # sub Getopts { local($argumentative) = @_; local(@args,$_,$first,$rest); local($errs) = 0; local($[) = 0; local($pos) = 0; @args = split( / */, $argumentative ); while(@ARGV && ($_ = $ARGV[0]) =~ /^-(.)(.*)/) { ($first,$rest) = ($1,$2); $pos = index($argumentative,$first); if($pos >= $[) { if($args[$pos+1] eq ':') { shift(@ARGV); if($rest eq '') { ++$errs unless @ARGV; $rest = shift(@ARGV); } eval "\$opt_$first = \$rest;"; } else { eval "\$opt_$first = 1"; if($rest eq '') { shift(@ARGV); } else { $ARGV[0] = "-$rest"; } } } else { print STDERR "Unknown option: $first\n"; ++$errs; if($rest ne '') { $ARGV[0] = "-$rest"; } else { shift(@ARGV); } } } $errs == 0; } # Do wraparound for long lines. # NOTE, this function can severely munge the spacing of a line. The first # thing it does is split all words on whitespace and then re-joins # the words separated by single spaces. But we think this is # probably OK. SOIF is intendted to be used as indexing data, # and not necessarily displayable for human consumption. # sub wrapline { local ($_) = @_; return $_ if length $_ < 72; $_ = join (" ", split) . "\n"; local ($l) = length $_; local ($i) = 0; local ($j); local ($s1); local ($s2); while (($l - $i) > 72) { $j = rindex ($_, ' ', $i + 72); $j = index ($_, ' ', $i) if ($j <= $i); $j = $l if ($j < $i); $s1 = substr ($_, 0, $j); $s2 = substr ($_, $j+1); $_ = $s1 . "\n" . $s2; $i = $j; } $_; }