Annotation of loncom/build/piml_parse.pl, revision 1.11

1.1       harris41    1: #!/usr/bin/perl
                      2: 
1.6       harris41    3: # -------------------------------------------------------- Documentation notice
1.10      harris41    4: # Run "perldoc ./piml_parse.pl" in order to best view the software
1.6       harris41    5: # documentation internalized in this program.
                      6: 
                      7: # --------------------------------------------------------- License Information
1.1       harris41    8: # The LearningOnline Network with CAPA
                      9: # piml_parse.pl - Linux Packaging Markup Language parser
                     10: #
1.11    ! albertel   11: # $Id: piml_parse.pl,v 1.10 2002/12/03 22:36:32 harris41 Exp $
1.1       harris41   12: #
1.6       harris41   13: # Written by Scott Harrison, codeharrison@yahoo.com
1.1       harris41   14: #
                     15: # Copyright Michigan State University Board of Trustees
                     16: #
                     17: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                     18: #
                     19: # LON-CAPA is free software; you can redistribute it and/or modify
                     20: # it under the terms of the GNU General Public License as published by
                     21: # the Free Software Foundation; either version 2 of the License, or
                     22: # (at your option) any later version.
                     23: #
                     24: # LON-CAPA is distributed in the hope that it will be useful,
                     25: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     26: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     27: # GNU General Public License for more details.
                     28: #
                     29: # You should have received a copy of the GNU General Public License
                     30: # along with LON-CAPA; if not, write to the Free Software
                     31: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     32: #
                     33: # /home/httpd/html/adm/gpl.txt
                     34: #
                     35: # http://www.lon-capa.org/
                     36: #
                     37: # YEAR=2002
1.6       harris41   38: # 1/28,1/29,1/30,1/31,2/5,4/8 - Scott Harrison
1.1       harris41   39: #
                     40: ###
                     41: 
                     42: ###############################################################################
                     43: ##                                                                           ##
                     44: ## ORGANIZATION OF THIS PERL SCRIPT                                          ##
                     45: ## 1. Notes                                                                  ##
                     46: ## 2. Get command line arguments                                             ##
                     47: ## 3. First pass through (grab distribution-specific information)            ##
                     48: ## 4. Second pass through (parse out what is not necessary)                  ##
                     49: ## 5. Third pass through (translate markup according to specified mode)      ##
                     50: ## 6. Functions (most all just format contents of different markup tags)     ##
                     51: ## 7. POD (plain old documentation, CPAN style)                              ##
                     52: ##                                                                           ##
                     53: ###############################################################################
                     54: 
                     55: # ----------------------------------------------------------------------- Notes
                     56: #
                     57: # I am using a multiple pass-through approach to parsing
                     58: # the piml file.  This saves memory and makes sure the server
                     59: # will never be overloaded.
                     60: #
                     61: # This is meant to parse files meeting the piml document type.
                     62: # See piml.dtd.  PIML=Post Installation Markup Language.
                     63: 
1.10      harris41   64: # To reduce system dependencies, I'm using a lightweight
                     65: # parser.  At some point, I need to get serious with a
                     66: # better xml parsing engine and stylesheet usage.
1.1       harris41   67: use HTML::TokeParser;
                     68: 
1.6       harris41   69: my $usage=(<<END);
1.1       harris41   70: **** ERROR ERROR ERROR ERROR ****
                     71: Usage is for piml file to come in through standard input.
1.9       harris41   72: 1st argument is the category permissions to use (runtime or development)
1.10      harris41   73: 2nd argument is the distribution (default,redhat6,debian2.2,redhat7,etc).
1.9       harris41   74: 3rd argument is to manually specify a targetroot
1.1       harris41   75: 
1.9       harris41   76: Only the 1st argument is mandatory for the program to run.
1.1       harris41   77: 
                     78: Example:
                     79: 
1.10      harris41   80: cat ../../doc/sanitycheck.piml |\\
                     81: perl piml_parse.pl development default /home/sherbert/loncapa
1.1       harris41   82: END
                     83: 
                     84: # ------------------------------------------------- Grab command line arguments
                     85: 
1.10      harris41   86: # If number of arguments is incorrect, then give up and print usage message.
                     87: unless (@ARGV == 3)
                     88:   {
                     89:     @ARGV=();shift(@ARGV);
1.1       harris41   90:     while(<>){} # throw away the input to avoid broken pipes
1.10      harris41   91:     print($usage); # print usage message
1.1       harris41   92:     exit -1; # exit with error status
1.10      harris41   93:   }
1.1       harris41   94: 
                     95: my $categorytype;
1.10      harris41   96: if (@ARGV)
                     97:   {
                     98:     $categorytype = shift(@ARGV);
                     99:   }
1.1       harris41  100: 
                    101: my $dist;
1.10      harris41  102: if (@ARGV)
                    103:   {
                    104:     $dist = shift(@ARGV);
                    105:   }
1.1       harris41  106: 
                    107: my $targetroot;
                    108: my $targetrootarg;
1.10      harris41  109: if (@ARGV)
                    110:   {
                    111:     $targetroot = shift(@ARGV);
                    112:   }
                    113: 
1.1       harris41  114: $targetroot=~s/\/$//;
                    115: $targetrootarg=$targetroot;
                    116: 
                    117: my $logcmd='| tee -a WARNINGS';
                    118: 
                    119: my $invocation;
                    120: # --------------------------------------------------- Record program invocation
1.10      harris41  121: if ($mode eq 'install' or $mode eq 'configinstall' or $mode eq 'build')
                    122:   {
1.1       harris41  123:     $invocation=(<<END);
                    124: # Invocation: STDINPUT | piml_parse.pl
1.2       harris41  125: #             1st argument (category type) is: $categorytype
                    126: #             2nd argument (distribution) is: $dist
                    127: #             3rd argument (targetroot) is: described below
1.1       harris41  128: END
1.10      harris41  129:   }
1.1       harris41  130: 
                    131: # ---------------------------------------------------- Start first pass through
                    132: my @parsecontents = <>;
                    133: my $parsestring = join('',@parsecontents);
1.6       harris41  134: my $outstring='';
1.1       harris41  135: 
                    136: # Need to make a pass through and figure out what defaults are
                    137: # overrided.  Top-down overriding strategy (leaves don't know
                    138: # about distant leaves).
                    139: 
                    140: my @hierarchy;
                    141: $hierarchy[0]=0;
                    142: my $hloc=0;
1.6       harris41  143: my $token='';
1.1       harris41  144: $parser = HTML::TokeParser->new(\$parsestring) or
                    145:     die('can\'t create TokeParser object');
                    146: $parser->xml_mode('1');
                    147: my %hash;
                    148: my $key;
1.10      harris41  149: while ($token = $parser->get_token())
                    150:   {
                    151:     if ($token->[0] eq 'S')
                    152:       {
1.1       harris41  153: 	$hloc++;
                    154: 	$hierarchy[$hloc]++;
                    155: 	$key=$token->[1].join(',',@hierarchy[0..($hloc-1)]);
                    156: 	my $thisdist=' '.$token->[2]{'dist'}.' ';
1.10      harris41  157: 	if ($thisdist eq ' default ')
                    158:           {
1.1       harris41  159: 	    $hash{$key}=1; # there is a default setting for this key
1.10      harris41  160: 	  }
                    161: 	elsif ($dist && $hash{$key}==1 && $thisdist=~/\s$dist\s/)
                    162:           {
1.1       harris41  163: 	    $hash{$key}=2; # disregard default setting for this key if
                    164: 	                   # there is a directly requested distribution match
1.10      harris41  165: 	  }
                    166:       }
                    167:     if ($token->[0] eq 'E')
                    168:       {
1.1       harris41  169: 	$hloc--;
1.10      harris41  170:       }
                    171:   }
1.1       harris41  172: 
                    173: # --------------------------------------------------- Start second pass through
                    174: undef $hloc;
                    175: undef @hierarchy;
                    176: undef $parser;
                    177: $hierarchy[0]=0;
                    178: $parser = HTML::TokeParser->new(\$parsestring) or
                    179:     die('can\'t create TokeParser object');
                    180: $parser->xml_mode('1');
                    181: my $cleanstring;
                    182: while ($token = $parser->get_token()) {
                    183:     if ($token->[0] eq 'S') {
                    184: 	$hloc++;
                    185: 	$hierarchy[$hloc]++;
                    186: 	$key=$token->[1].join(',',@hierarchy[0..($hloc-1)]);
                    187: 	my $thisdist=' '.$token->[2]{'dist'}.' ';
                    188: 	# This conditional clause is set up to ignore two sets
                    189: 	# of invalid conditions before accepting entry into
                    190: 	# the cleanstring.
                    191: 	if ($hash{$key}==2 and
                    192: 	    !($thisdist eq '  ' or $thisdist =~/\s$dist\s/)) {
                    193: 	    if ($token->[4]!~/\/>$/) {
                    194: 		$parser->get_tag('/'.$token->[1]);
                    195: 		$hloc--;
                    196: 	    }
                    197: 	}
                    198: 	elsif ($thisdist ne '  ' and $thisdist!~/\s$dist\s/ and
                    199: 	       !($thisdist eq ' default ' and $hash{$key}!=2)) {
                    200: 	    if ($token->[4]!~/\/>$/) {
                    201: 		$parser->get_tag('/'.$token->[1]);
                    202: 		$hloc--;
                    203: 	    }
                    204: 	}
                    205: 	else {
                    206: 	    $cleanstring.=$token->[4];
                    207: 	}
                    208: 	if ($token->[4]=~/\/>$/) {
1.4       harris41  209: #	    $hloc--;
1.1       harris41  210: 	}
                    211:     }
                    212:     if ($token->[0] eq 'E') {
                    213: 	$cleanstring.=$token->[2];
                    214: 	$hloc--;
                    215:     }
                    216:     if ($token->[0] eq 'T') {
                    217: 	$cleanstring.=$token->[1];
                    218:     }
                    219: }
                    220: $cleanstring=&trim($cleanstring);
                    221: $cleanstring=~s/\>\s*\n\s*\</\>\</g;
                    222: 
                    223: # ---------------------------------------------------- Start final pass through
                    224: 
                    225: # storage variables
                    226: my $piml;
                    227: my $categories;
                    228: my @categorynamelist;
                    229: my $category;
                    230: my $category_att_name;
                    231: my $category_att_type;
                    232: my $chown;
                    233: my $chmod;
                    234: my $abbreviation; # space-free abbreviation; esp. for image names
                    235: my $categoryname;
                    236: my $description;
                    237: my $files;
                    238: my $file;
                    239: my $target;
                    240: my $note;
                    241: my $commands;
                    242: my $command;
                    243: my $dependencies;
                    244: my @links;
                    245: my %categoryhash;
                    246: my $dpathlength;
                    247: my %fab; # file category abbreviation
                    248: my $directory_count;
                    249: my $file_count;
                    250: my $link_count;
                    251: my $fileglob_count;
                    252: my $fileglobnames_count;
                    253: my %categorycount;
                    254: 
1.4       harris41  255: my $mode;
                    256: 
1.1       harris41  257: my @buildall;
                    258: my @buildinfo;
                    259: 
                    260: my @configall;
                    261: 
                    262: # Make new parser with distribution specific input
1.6       harris41  263: undef($parser);
1.1       harris41  264: $parser = HTML::TokeParser->new(\$cleanstring) or
                    265:     die('can\'t create TokeParser object');
                    266: $parser->xml_mode('1');
                    267: 
                    268: # Define handling methods for mode-dependent text rendering
                    269: 
                    270: $parser->{textify}={
                    271:     specialnotices => \&format_specialnotices,
                    272:     specialnotice => \&format_specialnotice,
                    273:     targetroot => \&format_targetroot,
                    274:     categories => \&format_categories,
                    275:     category => \&format_category,
                    276:     abbreviation => \&format_abbreviation,
                    277:     chown => \&format_chown,
                    278:     chmod => \&format_chmod,
                    279:     categoryname => \&format_categoryname,
                    280:     files => \&format_files,
                    281:     file => \&format_file,
                    282:     target => \&format_target,
                    283:     note => \&format_note,
                    284:     build => \&format_build,
                    285:     dependencies => \&format_dependencies,
                    286:     filenames => \&format_filenames,
1.2       harris41  287:     perlscript => \&format_perlscript,
                    288:     TARGET => \&format_TARGET,
1.11    ! albertel  289:     DIST => \&format_DIST,
1.1       harris41  290:     };
                    291: 
                    292: my $text;
                    293: my $token;
1.6       harris41  294: undef($hloc);
                    295: undef(@hierarchy);
1.1       harris41  296: my $hloc;
                    297: my @hierarchy2;
1.10      harris41  298: while ($token = $parser->get_tag('piml'))
                    299:   {
1.1       harris41  300:     &format_piml(@{$token});
                    301:     $text = &trim($parser->get_text('/piml'));
                    302:     $token = $parser->get_tag('/piml');
1.6       harris41  303:     print($piml); 
                    304:     print("\n");
                    305:     print($text);
                    306:     print("\n");
                    307:     print(&end());
1.10      harris41  308:   }
1.6       harris41  309: exit(0);
1.1       harris41  310: 
                    311: # ---------- Functions (most all just format contents of different markup tags)
                    312: 
                    313: # ------------------------ Final output at end of markup parsing and formatting
                    314: sub end {
1.2       harris41  315: 
1.1       harris41  316: }
                    317: 
                    318: # ----------------------- Take in string to parse and the separation expression
                    319: sub extract_array {
                    320:     my ($stringtoparse,$sepexp) = @_;
                    321:     my @a=split(/$sepexp/,$stringtoparse);
                    322:     return \@a;
                    323: }
                    324: 
                    325: # --------------------------------------------------------- Format piml section
                    326: sub format_piml {
                    327:     my (@tokeninfo)=@_;
                    328:     my $date=`date`; chop $date;
1.2       harris41  329:     $piml=<<END;
                    330: #!/usr/bin/perl
1.1       harris41  331: 
1.2       harris41  332: # Generated from a PIML (Post Installation Markup Language) document
1.1       harris41  333: 
                    334: END
                    335: }
1.10      harris41  336: 
1.1       harris41  337: # --------------------------------------------------- Format targetroot section
                    338: sub format_targetroot {
                    339:     my $text=&trim($parser->get_text('/targetroot'));
                    340:     $text=$targetroot if $targetroot;
                    341:     $parser->get_tag('/targetroot');
1.6       harris41  342:     return('# TARGET INSTALL LOCATION is "'.$targetroot."\"\n");
1.2       harris41  343: }
1.10      harris41  344: 
1.2       harris41  345: # -------------------------------------------------- Format perl script section
                    346: sub format_perlscript {
                    347:     my (@tokeninfo)=@_;
1.5       harris41  348:     $mode=$tokeninfo[2]->{'mode'};
1.2       harris41  349:     my $text=$parser->get_text('/perlscript');
                    350:     $parser->get_tag('/perlscript');
1.5       harris41  351:     if ($mode eq 'bg') {
1.6       harris41  352: 	open(OUT,">/tmp/piml$$.pl");
                    353: 	print(OUT $text);
                    354: 	close(OUT);
                    355: 	return(<<END);
1.5       harris41  356: 	# launch background process for $target
                    357: 	system("perl /tmp/piml$$.pl &");
                    358: END
                    359:     }
                    360:     else {
1.6       harris41  361: 	return($text);
1.5       harris41  362:     }
1.1       harris41  363: }
1.10      harris41  364: 
1.2       harris41  365: # --------------------------------------------------------------- Format TARGET
                    366: sub format_TARGET {
                    367:     my (@tokeninfo)=@_;
                    368:     $parser->get_tag('/TARGET');
1.6       harris41  369:     return($target);
1.1       harris41  370: }
1.10      harris41  371: 
1.11    ! albertel  372: # ----------------------------------------------------------------- Format DIST
        !           373: sub format_DIST {
        !           374:     my (@tokeninfo)=@_;
        !           375:     $parser->get_tag('/DIST');
        !           376:     return($dist);
        !           377: }
        !           378: 
1.1       harris41  379: # --------------------------------------------------- Format categories section
                    380: sub format_categories {
                    381:     my $text=&trim($parser->get_text('/categories'));
                    382:     $parser->get_tag('/categories');
1.6       harris41  383:     return('# CATEGORIES'."\n".$text);
1.1       harris41  384: }
1.10      harris41  385: 
1.1       harris41  386: # --------------------------------------------------- Format categories section
                    387: sub format_category {
                    388:     my (@tokeninfo)=@_;
                    389:     $category_att_name=$tokeninfo[2]->{'name'};
                    390:     $category_att_type=$tokeninfo[2]->{'type'};
                    391:     $abbreviation=''; $chmod='';$chown='';
                    392:     $parser->get_text('/category');
                    393:     $parser->get_tag('/category');
                    394:     $fab{$category_att_name}=$abbreviation;
1.2       harris41  395:     if ($category_att_type eq $categorytype) {
                    396: 	my ($user,$group)=split(/\:/,$chown);
                    397: 	$categoryhash{$category_att_name}='-o '.$user.' -g '.$group.
                    398: 	    ' -m '.$chmod;
1.1       harris41  399:     }
1.6       harris41  400:     return('');
1.1       harris41  401: }
1.10      harris41  402: 
1.1       harris41  403: # --------------------------------------------------- Format categories section
                    404: sub format_abbreviation {
                    405:     my @tokeninfo=@_;
                    406:     $abbreviation='';
                    407:     my $text=&trim($parser->get_text('/abbreviation'));
                    408:     if ($text) {
                    409: 	$parser->get_tag('/abbreviation');
                    410: 	$abbreviation=$text;
                    411:     }
1.6       harris41  412:     return('');
1.1       harris41  413: }
1.10      harris41  414: 
1.1       harris41  415: # -------------------------------------------------------- Format chown section
                    416: sub format_chown {
                    417:     my @tokeninfo=@_;
                    418:     $chown='';
                    419:     my $text=&trim($parser->get_text('/chown'));
                    420:     if ($text) {
                    421: 	$parser->get_tag('/chown');
                    422: 	$chown=$text;
                    423:     }
1.6       harris41  424:     return('');
1.1       harris41  425: }
1.10      harris41  426: 
1.1       harris41  427: # -------------------------------------------------------- Format chmod section
                    428: sub format_chmod {
                    429:     my @tokeninfo=@_;
                    430:     $chmod='';
                    431:     my $text=&trim($parser->get_text('/chmod'));
                    432:     if ($text) {
                    433: 	$parser->get_tag('/chmod');
                    434: 	$chmod=$text;
                    435:     }
1.6       harris41  436:     return('');
1.1       harris41  437: }
1.10      harris41  438: 
1.1       harris41  439: # ------------------------------------------------- Format categoryname section
                    440: sub format_categoryname {
                    441:     my @tokeninfo=@_;
                    442:     $categoryname='';
                    443:     my $text=&trim($parser->get_text('/categoryname'));
                    444:     if ($text) {
                    445: 	$parser->get_tag('/categoryname');
                    446: 	$categoryname=$text;
                    447:     }
1.6       harris41  448:     return('');
1.1       harris41  449: }
1.10      harris41  450: 
1.1       harris41  451: # -------------------------------------------------------- Format files section
                    452: sub format_files {
                    453:     my $text=$parser->get_text('/files');
                    454:     $parser->get_tag('/files');
1.6       harris41  455:     return("\n".'# There are '.$file_count.' files this script works on'.
                    456: 	"\n\n".$text);
1.1       harris41  457: }
1.10      harris41  458: 
1.1       harris41  459: # --------------------------------------------------------- Format file section
                    460: sub format_file {
                    461:     my @tokeninfo=@_;
                    462:     $file=''; $source=''; $target=''; $categoryname=''; $description='';
                    463:     $note=''; $build=''; $status=''; $dependencies='';
                    464:     my $text=&trim($parser->get_text('/file'));
                    465:     $file_count++;
                    466:     $categorycount{$categoryname}++;
1.2       harris41  467:     $parser->get_tag('/file');
1.6       harris41  468:     return("# File: $target\n".
                    469: 	"$text\n");
1.1       harris41  470: }
1.10      harris41  471: 
1.1       harris41  472: # ------------------------------------------------------- Format target section
                    473: sub format_target {
                    474:     my @tokeninfo=@_;
                    475:     $target='';
                    476:     my $text=&trim($parser->get_text('/target'));
                    477:     if ($text) {
                    478: 	$parser->get_tag('/target');
1.2       harris41  479: 	$target=$targetrootarg.$text;
1.1       harris41  480:     }
1.6       harris41  481:     return('');
1.1       harris41  482: }
1.10      harris41  483: 
1.1       harris41  484: # --------------------------------------------------------- Format note section
                    485: sub format_note {
                    486:     my @tokeninfo=@_;
                    487:     $note='';
                    488:     my $aref;
                    489:     my $text;
                    490:     while ($aref=$parser->get_token()) {
                    491: 	if ($aref->[0] eq 'E' && $aref->[1] eq 'note') {
                    492: 	    last;
                    493: 	}
                    494: 	elsif ($aref->[0] eq 'S') {
                    495: 	    $text.=$aref->[4];
                    496: 	}
                    497: 	elsif ($aref->[0] eq 'E') {
                    498: 	    $text.=$aref->[2];
                    499: 	}
                    500: 	else {
                    501: 	    $text.=$aref->[1];
                    502: 	}
                    503:     }
                    504:     if ($text) {
                    505: 	$note=$text;
                    506:     }
1.6       harris41  507:     return('');
1.1       harris41  508: }
1.10      harris41  509: 
1.1       harris41  510: # ------------------------------------------------- Format dependencies section
                    511: sub format_dependencies {
                    512:     my @tokeninfo=@_;
                    513:     $dependencies='';
                    514:     my $text=&trim($parser->get_text('/dependencies'));
                    515:     if ($text) {
                    516: 	$parser->get_tag('/dependencies');
                    517: 	$dependencies=join(';',
                    518: 			      (map {s/^\s*//;s/\s$//;$_} split(/\;/,$text)));
                    519:     }
1.6       harris41  520:     return('');
1.1       harris41  521: }
1.10      harris41  522: 
1.1       harris41  523: # ------------------------------------------------ Format specialnotice section
                    524: sub format_specialnotices {
                    525:     $parser->get_tag('/specialnotices');
1.6       harris41  526:     return('');
1.1       harris41  527: }
1.10      harris41  528: 
1.1       harris41  529: # ------------------------------------------------ Format specialnotice section
                    530: sub format_specialnotice {
                    531:     $parser->get_tag('/specialnotice');
1.6       harris41  532:     return('');
1.1       harris41  533: }
1.10      harris41  534: 
1.1       harris41  535: # ------------------------------------- Render less-than and greater-than signs
                    536: sub htmlsafe {
                    537:     my $text=@_[0];
                    538:     $text =~ s/</&lt;/g;
                    539:     $text =~ s/>/&gt;/g;
1.6       harris41  540:     return($text);
1.1       harris41  541: }
1.10      harris41  542: 
1.1       harris41  543: # --------------------------------------- remove starting and ending whitespace
                    544: sub trim {
1.6       harris41  545:     my ($s)=@_; $s=~s/^\s*//; $s=~s/\s*$//; return($s);
                    546: }
1.1       harris41  547: 
                    548: # ----------------------------------- POD (plain old documentation, CPAN style)
                    549: 
1.6       harris41  550: =pod
                    551: 
1.1       harris41  552: =head1 NAME
                    553: 
1.10      harris41  554: piml_parse.pl - This is meant to parse files meeting the piml document type.
                    555: See piml.dtd.  PIML=Post Installation Markup Language.
1.1       harris41  556: 
                    557: =head1 SYNOPSIS
                    558: 
                    559: Usage is for piml file to come in through standard input.
                    560: 
                    561: =over 4
                    562: 
                    563: =item * 
                    564: 
1.2       harris41  565: 1st argument is the category permissions to use (runtime or development)
1.1       harris41  566: 
                    567: =item *
                    568: 
1.2       harris41  569: 2nd argument is the distribution
1.10      harris41  570: (default,redhat6,debian2.2,redhat7,etc).
1.1       harris41  571: 
                    572: =item *
                    573: 
1.2       harris41  574: 3rd argument is to manually specify a targetroot.
1.1       harris41  575: 
                    576: =back
                    577: 
                    578: Only the 1st argument is mandatory for the program to run.
                    579: 
                    580: Example:
                    581: 
                    582: cat ../../doc/loncapafiles.piml |\\
1.10      harris41  583: perl piml_parse.pl development default /home/sherbert/loncapa
1.1       harris41  584: 
                    585: =head1 DESCRIPTION
                    586: 
                    587: I am using a multiple pass-through approach to parsing
                    588: the piml file.  This saves memory and makes sure the server
                    589: will never be overloaded.
                    590: 
                    591: =head1 README
                    592: 
                    593: I am using a multiple pass-through approach to parsing
                    594: the piml file.  This saves memory and makes sure the server
                    595: will never be overloaded.
                    596: 
                    597: =head1 PREREQUISITES
                    598: 
                    599: HTML::TokeParser
                    600: 
                    601: =head1 COREQUISITES
                    602: 
                    603: =head1 OSNAMES
                    604: 
                    605: linux
                    606: 
                    607: =head1 SCRIPT CATEGORIES
                    608: 
                    609: Packaging/Administrative
1.6       harris41  610: 
                    611: =head1 AUTHOR
                    612: 
                    613:  Scott Harrison
1.10      harris41  614:  sharrison@users.sourceforge.net
1.6       harris41  615: 
                    616: Please let me know how/if you are finding this script useful and
                    617: any/all suggestions.  -Scott
1.1       harris41  618: 
                    619: =cut

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>