File:  [LON-CAPA] / loncom / build / Attic / parse.pl
Revision 1.41: download - view: text, annotated - select for diffs
Wed May 30 17:42:28 2001 UTC (23 years, 1 month ago) by harris41
Branches: MAIN
CVS tags: stable_2002_spring, stable_2001_fall, HEAD, Before_rewrite
hiding long test expressions from make output

    1: #!/usr/bin/perl
    2: 
    3: # Scott Harrison
    4: # November 2000
    5: # 12/5/2000,12/7,12/8,12/9,12/10,12/11,12/12,12/13,12/14,12/21 Scott Harrison
    6: # 1/8/2001,1/10/2001,1/13/2001,1/16/2001,1/18/2001,1/23/2001 Scott Harrison
    7: # 2/26/2001,2/27/2001,3/5/2001,4/10/2001,4/11,2001,4/13,2001 Scott Harrison
    8: # 4/17/2001 Scott Harrison
    9: 
   10: # Read in loncapa tags and metagroup tags.  Output various
   11: # useful files for viewing, compiling, monitoring, updating,
   12: # and installing.
   13: 
   14: # This code works well.  HOWEVER, I was in a rush when I
   15: # wrote it.  Future plans are that the parsing should be
   16: # much more event-state based and the input should be
   17: # more XML-ish. -Scott
   18: 
   19: # ---------------------------------------------- Read in command line arguments
   20: my ($file,$mode)=@ARGV;
   21: 
   22: # ---------------------------------------------------- Read in master data file
   23: open IN,"<$file";
   24: my @lines=<IN>;
   25: close IN;
   26: my $info1=join('',@lines);
   27: my $info2=$info1; # value to allow for meta data group retrieval
   28: 
   29: # ------------------------------------------------------- Make default settings
   30: my $distribution="redhat6.2";
   31: my $date=`date +'%B %e, %Y'`; chop $date;
   32: my $buildhost=`hostname`; chop $buildhost;
   33: # file category mappings
   34: my %fcm=(
   35: 	 'conf' => 'configurable',
   36: 	 'graphic file' => 'graphicfile',
   37: 	 'handler' => 'handler',
   38: 	 'interface file' => 'interfacefile',
   39: 	 'symbolic link' => 'link',
   40: 	 'root script' => 'rootscript',
   41: 	 'script' => 'script',
   42: 	 'setuid script' => 'setuid',
   43: 	 'static conf' => 'static',
   44: 	 'system file' => 'systemfile',
   45: 	 );
   46: 
   47: # ---------------------------------------------------- Parse the marked up data
   48: my %info; # big data storage object
   49: while ($info1=~/\<loncapa\s+(.*?)\>/isg) {
   50:     my $keystring=$1;
   51:     # In the parsing of LON-CAPA tags, remove boundary white-space,
   52:     # and handle quotation commands.
   53:     my %hash=map {my ($key,$value)=split(/\=(?!")|\=(?=\s*"[^"]*"[^"]*$)/);
   54:                                    $value=~s/^"//;
   55:  				   $value=~s/"$//;
   56:                                    (uc($key),$value);}
   57:              split(/\s+(?=\w+\s*\=)/,$keystring);
   58:     # Handle the different types of commands
   59:     if (uc($hash{'TYPE'}) eq "OWNERSHIP") {
   60:         $info{$hash{'TYPE'}}{$hash{'CATEGORY'}}{'CHMOD'}=$hash{'CHMOD'};
   61:         $info{$hash{'TYPE'}}{$hash{'CATEGORY'}}{'CHOWN'}=$hash{'CHOWN'};
   62:     }
   63:     elsif (uc($hash{'TYPE'}) eq "DEVOWNERSHIP") {
   64:         $info{$hash{'TYPE'}}{$hash{'CATEGORY'}}{'CHMOD'}=$hash{'CHMOD'};
   65:         $info{$hash{'TYPE'}}{$hash{'CATEGORY'}}{'CHOWN'}=$hash{'CHOWN'};
   66:     }
   67:     elsif (uc($hash{'TYPE'}) eq "RPM") {
   68:         $hash{'VALUE'}=~s/\\n/\n/g;
   69:         $info{$hash{'TYPE'}}{$hash{'NAME'}}=$hash{'VALUE'};
   70:     }
   71:     elsif (uc($hash{'TYPE'}) eq "DIRECTORY") {
   72:         $info{$hash{'TYPE'}}{$hash{'DIST'}}{$hash{'TARGET'}}{'CATEGORY'}=
   73:                                                        $hash{'CATEGORY'};
   74:         $info{$hash{'TYPE'}}{$hash{'DIST'}}{$hash{'TARGET'}}{'DESCRIPTION'}=
   75:                                $hash{'DESCRIPTION'} if $hash{'DESCRIPTION'};
   76:     }
   77:     elsif (uc($hash{'TYPE'}) eq "LOCATION") {
   78:         $info{$hash{'TYPE'}}{$hash{'DIST'}}{$hash{'TARGET'}}{'CATEGORY'}=                               $hash{'CATEGORY'};
   79:         $info{$hash{'TYPE'}}{$hash{'DIST'}}{$hash{'TARGET'}}{'LINKTO'}=                               $hash{'LINKTO'};
   80:         $info{$hash{'TYPE'}}{$hash{'DIST'}}{$hash{'TARGET'}}{'SOURCE'}=                                               $hash{'SOURCE'};
   81:         # get surrounding metagroup information
   82:         my $ckeystring=$keystring; $ckeystring=~s/(SOURCE\=\"[^"]*)\*/$1\\\*/g;
   83:         $ckeystring=~s/(TARGET\=\"[^"]*)\*/$1\\\*/g;
   84:         $info2=~/.*\<(?:metagroup|metasupergroup)\>(.*?)\<loncapa\s+$ckeystring\>(.*?)\<\/(?:metagroup|metasupergroup)\>/is;
   85: 	my $data=$1.$2;
   86:         my @meta=('description','build','dependencies','files','note');
   87:         foreach my $m (@meta) {
   88: 	    if ($data=~/\<($m)\>(.*?)\<\/$m\>/sgi) {
   89: 		my ($key,$value)=($1,$2);
   90: 		$info{$hash{'TYPE'}}{$hash{'DIST'}}{$hash{'TARGET'}}{uc($key)}=
   91: 		                                                    $value;
   92: 	    }
   93:         }
   94:     }
   95:     else {
   96:         warn("WARNING: this tag text will be ignored since it cannot be understood\n---> $keystring\n");
   97:     }
   98: }
   99: 
  100: my $a;
  101: my @directories;
  102: if ($mode eq "HTML") {
  103:     $a=&begin_description_page;
  104:     print $a;
  105:     $a=&make_rpm_description_block;
  106:     print $a;
  107:     @directories=&determine_directory_structure;
  108:     $a=&make_directory_structure_description_block(\@directories);
  109:     print $a;
  110:     $a=&make_file_type_ownership_and_permissions_description_block;
  111:     print $a;
  112:     $a=&make_directory_and_file_structure_description_block(\@directories);
  113:     print $a;
  114:     $a=&end_description_page;
  115:     print $a;
  116: }
  117: elsif ($mode eq "SPEC") {
  118:     my $out=$info{'RPM'}{'Name'} . '-' . $info{'RPM'}{'Version'} . '.spec';
  119:     open OUT,">$out";
  120:     $a=&make_rpm_spec_block;
  121:     print OUT $a;
  122:     $a=&make_rpm_build_block;
  123:     print OUT $a;
  124:     @directories=&determine_directory_structure;
  125:     $a=&make_directory_structure_spec_block(\@directories);
  126:     print OUT $a;
  127:     $a=&make_directory_and_file_structure_spec_block(\@directories);
  128:     print OUT $a;
  129:     $a=&end_spec_page;
  130:     print OUT $a;
  131:     close OUT;
  132: }
  133: elsif ($mode eq "LCMakefile") {
  134:     @directories=&determine_directory_structure;
  135:     $a=&make_directory_LCMakefile_segment(\@directories);
  136:     print $a;
  137:     $a=&make_files_LCMakefile_segment(\@directories);
  138:     print $a;
  139:     $a=&make_links_LCMakefile_segment(\@directories);
  140:     print $a;
  141: }
  142: elsif ($mode eq "BinaryRoot") {
  143:     mkdir "BinaryRoot",0755;
  144:     open OUT,">Makefile.BinaryRoot";
  145:     @directories=&determine_directory_structure;
  146:     $a=&make_directory_binaryroot_segment(\@directories);
  147:     print OUT $a;
  148:     $a=&make_files_binaryroot_segment(\@directories);
  149:     print OUT $a;
  150:     $a=&make_links_binaryroot_segment(\@directories);
  151:     print OUT $a;
  152:     close OUT;
  153:     print `make -f Makefile.BinaryRoot TARGET='BinaryRoot' SOURCE='../..' directories`;
  154:     print `make -f Makefile.BinaryRoot TARGET='BinaryRoot' SOURCE='../..' files`;
  155:     print `make -f Makefile.BinaryRoot TARGET='BinaryRoot' SOURCE='../..' links`;
  156:     open OUT,">base_file_list.txt";
  157:     $a=&make_file_list(\@directories);
  158:     print OUT $a;
  159:     close OUT;
  160:     open OUT,">setup_file_list.txt";
  161:     print OUT "BinaryRoot/etc/passwd\n";
  162:     close OUT;
  163:     open OUT,">BinaryRoot/etc/passwd";
  164:     print OUT<<END;
  165: root::0:0:root:/root:/bin/bash
  166: bin:!!:1:1:bin:/bin:
  167: daemon:!!:2:2:daemon:/sbin:
  168: adm:!!:3:4:adm:/var/adm:
  169: lp:!!:4:7:lp:/var/spool/lpd:
  170: sync:!!:5:0:sync:/sbin:/bin/sync
  171: shutdown:!!:6:0:shutdown:/sbin:/sbin/shutdown
  172: halt:!!:7:0:halt:/sbin:/sbin/halt
  173: mail:!!:8:12:mail:/var/spool/mail:
  174: news:!!:9:13:news:/var/spool/news:
  175: uucp:!!:10:14:uucp:/var/spool/uucp:
  176: operator:!!:11:0:operator:/root:
  177: games:!!:12:100:games:/usr/games:
  178: gopher:!!:13:30:gopher:/usr/lib/gopher-data:
  179: ftp:!!:14:50:FTP User:/home/ftp:
  180: nobody:!!:99:99:Nobody:/:
  181: www:!!:500:500:www:/home/www:/bin/bash
  182: END
  183: close OUT;
  184:     open OUT,">>setup_file_list.txt";
  185:     print OUT "BinaryRoot/etc/group\n";
  186:     close OUT;
  187:     open OUT,">BinaryRoot/etc/group";
  188:     print OUT<<END;
  189: root::0:root
  190: bin::1:root,bin,daemon
  191: daemon::2:root,bin,daemon
  192: sys::3:root,bin,adm
  193: adm::4:root,adm,daemon
  194: tty::5:
  195: disk::6:root
  196: lp::7:daemon,lp
  197: mem::8:
  198: kmem::9:
  199: wheel::10:root
  200: mail::12:mail
  201: news::13:news
  202: uucp::14:uucp
  203: man::15:
  204: games::20:
  205: gopher::30:
  206: dip::40:
  207: ftp::50:
  208: nobody::99:
  209: users::100:
  210: www::500:
  211: END
  212: close OUT;
  213:     open OUT,">>setup_file_list.txt";
  214:     print OUT "BinaryRoot/etc/hosts.deny\n";
  215:     close OUT;
  216:     open OUT,">BinaryRoot/etc/hosts.deny";
  217:     print OUT<<END;
  218: ALL: ALL
  219: END
  220: close OUT;
  221:     
  222:     `install -o www -g users -m 0700 -d BinaryRoot/home/www`;
  223:     open OUT,">>setup_file_list.txt";
  224:     print OUT "BinaryRoot/home/www\n";
  225:     close OUT;
  226:     `install -d BinaryRoot/etc/pam.d`;
  227:     open OUT,">>setup_file_list.txt";
  228:     print OUT "BinaryRoot/etc/pam.d/passwd\n";
  229:     close OUT;
  230:     open OUT,">BinaryRoot/etc/pam.d/passwd";
  231:     print OUT<<END;
  232: #%PAM-1.0
  233: auth       required     /lib/security/pam_pwdb.so shadow nullok
  234: account    required     /lib/security/pam_pwdb.so
  235: password   required     /lib/security/pam_cracklib.so retry=3
  236: password   required     /lib/security/pam_pwdb.so use_authtok nullok
  237: END
  238: close OUT;
  239:     open OUT,">>setup_file_list.txt";
  240:     print OUT "BinaryRoot/etc/pam.d/login\n";
  241:     close OUT;
  242:     open OUT,">BinaryRoot/etc/pam.d/login";
  243:     print OUT<<END;
  244: #%PAM-1.0
  245: auth       required     /lib/security/pam_securetty.so
  246: auth       required     /lib/security/pam_pwdb.so shadow nullok
  247: auth       required     /lib/security/pam_nologin.so
  248: account    required     /lib/security/pam_pwdb.so
  249: password   required     /lib/security/pam_cracklib.so
  250: password   required     /lib/security/pam_pwdb.so nullok use_authtok
  251: session    required     /lib/security/pam_pwdb.so
  252: session    optional     /lib/security/pam_console.so
  253: END
  254: close OUT;
  255: 
  256: }
  257: elsif ($mode eq "status") {
  258:     $a=&begin_description_page('status');
  259:     print $a;
  260:     $a=&make_rpm_description_block('status');
  261:     print $a;
  262:     @directories=&determine_directory_structure('status');
  263:     $a=&make_directory_structure_description_block(\@directories,'status');
  264:     print $a;
  265:     $a=&make_file_type_ownership_and_permissions_description_block('status');
  266:     print $a;
  267:     $a=&make_directory_and_file_structure_description_block(\@directories,'status');
  268:     print $a;
  269:     $a=&end_description_page('status');
  270:     print $a;
  271: }
  272: elsif ($mode eq "update") {
  273: }
  274: elsif ($mode eq "configinstall") {
  275:     @directories=&determine_directory_structure;
  276:     $a=&make_files_configinstall_segment(\@directories);
  277:     print $a;
  278:     $a=&make_files_configpermissions_segment(\@directories);
  279:     print $a;
  280: }
  281: elsif ($mode eq "install") {
  282:     @directories=&determine_directory_structure;
  283:     $a=&make_directory_install_segment(\@directories);
  284:     print $a;
  285:     $a=&make_files_install_segment(\@directories);
  286:     print $a;
  287:     $a=&make_links_install_segment(\@directories);
  288:     print $a;
  289: }
  290: elsif ($mode eq "build") {
  291:     @directories=&determine_directory_structure;
  292:     $a=&make_files_build_segment(\@directories);
  293:     print $a;
  294: }
  295: 
  296: # ------------------------------------------------------ a list of file targets
  297: sub make_file_list {
  298:     my ($dirs)=@_;
  299:     my $description;
  300:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  301:     foreach my $d (@$dirs) {
  302: 	# set other values
  303: 	$description.=<<END;
  304: BinaryRoot/$d
  305: END
  306: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  307: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  308: 	# find files that are contained in this directory
  309: 	my @files;
  310: 	my @filesfull;
  311: 	foreach my $f (@allfiles) {
  312: 	    if ($f=~/^$d\/([^\/]+)$/) {
  313: 		push @files,$1;
  314: 		push @filesfull,$f;
  315: 	    }
  316: 	}
  317: 	# render starting HTML formatting elements
  318: 	if (@files) {
  319:         }
  320: 	my $pwd=`pwd`; chop $pwd;
  321: 	if (@files) {
  322:             foreach my $i (0..$#files) {
  323: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  324: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  325: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  326: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  327: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  328: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  329: 		my $rot="/".$filesfull[$i];
  330: 		if ($rot=~/\*/) {
  331: 		    $rot=~s/[^\/]+$// if $rot=~/\*/;
  332: 		    my $listing=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'FILES'};
  333: 		    chop $listing;
  334: 		    my @list=split(/\s+/,$listing);
  335: 		    my $rot2;
  336: 		    foreach my $l (@list) {
  337: 			$l=~s/^\s*//; $l=~s/\s*$//;
  338: 			$rot2.="BinaryRoot$rot$l\n" if length($l);
  339: 		    }
  340: 		    chop $rot2;
  341: 		    $rot=$rot2;
  342: 		}
  343: 		else {
  344: 		    $rot="BinaryRoot$rot";
  345: 		}
  346: 		if ($category eq "conf") {
  347: 		    $rot.=" # config";
  348: 		}
  349: 		$description.=<<END;
  350: $rot
  351: END
  352: 	    }
  353: 	}
  354:     }
  355:     $description.=<<END;
  356: 
  357: END
  358:     return $description;
  359: }
  360: 
  361: # --------------------------------- Commands to make BinaryRoot directories
  362: sub make_directory_binaryroot_segment {
  363:     my ($dirs)=@_;
  364:     my $description=<<END;
  365: directories:
  366: END
  367:     foreach my $d (@$dirs) {
  368: 	my $category=$info{'DIRECTORY'}{$distribution}{$d}{'CATEGORY'};
  369: 	my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  370: 	my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  371: 	my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  372: 	my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  373: 	my ($owner,$group)=split(/\:/,$devchown);
  374: 	my $own=$devchown; $own=~s/\:/\,/;
  375: 	$description.=<<END;
  376: \tinstall -o $owner -g $group -m $devchmod -d \$(TARGET)/$d
  377: END
  378:     }
  379:     $description.=<<END;
  380: 
  381: END
  382:     return $description;
  383: }
  384: 
  385: # --------------------------------------- Commands to make BinaryRoot files
  386: sub make_files_binaryroot_segment {
  387:     my ($dirs)=@_;
  388:     my $description=<<END;
  389: files:
  390: END
  391:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  392:     foreach my $d (@$dirs) {
  393: 	# set other values
  394: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  395: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  396: 	# find files that are contained in this directory
  397: 	my @files;
  398: 	my @filesfull;
  399: 	foreach my $f (@allfiles) {
  400: 	    if ($f=~/^$d\/([^\/]+)$/) {
  401: 		push @files,$1;
  402: 		push @filesfull,$f;
  403: 	    }
  404: 	}
  405: 	# render starting HTML formatting elements
  406: 	if (@files) {
  407: 	    $description.=<<END;
  408: \t# $d $dirdescription
  409: END
  410:         }
  411: 	if (@files) {
  412:             foreach my $i (0..$#files) {
  413: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  414: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  415: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  416: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  417: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  418: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  419: 		my $rot=$filesfull[$i];
  420: 		$rot=~s/[^\/]+$/\./ if $rot=~/\*/;
  421: 		my ($owner,$group)=split(/\:/,$devchown);
  422: 		$description.=<<END if $category ne 'symbolic link';
  423: \tinstall -o $owner -g $group -m $devchmod \$(SOURCE)/$source \$(TARGET)/$rot
  424: END
  425: 	    }
  426: 	}
  427:     }
  428:     $description.=<<END;
  429: 
  430: END
  431:     return $description;
  432: }
  433: 
  434: # ------------------------------ Commands to make BinaryRoot symbolic links
  435: sub make_links_binaryroot_segment {
  436:     my ($dirs)=@_;
  437:     my $description=<<END;
  438: links:
  439: END
  440:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  441:     foreach my $d (@$dirs) {
  442: 	# find files that are contained in this directory
  443: 	my @files;
  444: 	my @filesfull;
  445: 	foreach my $f (@allfiles) {
  446: 	    if ($f=~/^$d\/([^\/]+)$/) {
  447: 		push @files,$1;
  448: 		push @filesfull,$f;
  449: 	    }
  450: 	}
  451: 	# render starting HTML formatting elements
  452: 	if (@files) {
  453:             foreach my $i (0..$#files) {
  454: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  455: 		my $linkto=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'LINKTO'};
  456: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  457: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  458: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  459: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  460: 		$description.=<<END if $category eq 'symbolic link';
  461: \tln -s /$linkto \$(TARGET)/$filesfull[$i]
  462: END
  463: 	    }
  464: 	}
  465:     }
  466:     $description.=<<END;
  467: 
  468: END
  469:     return $description;
  470: }
  471: 
  472: # ------ Installation commands for a Makefile used only by a rpm -ba invocation
  473: sub make_directory_LCMakefile_segment {
  474:     my ($dirs)=@_;
  475:     my $description=<<END;
  476: directories:
  477: END
  478:     foreach my $d (@$dirs) {
  479: 	my $category=$info{'DIRECTORY'}{$distribution}{$d}{'CATEGORY'};
  480: 	my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  481: 	my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  482: 	my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  483: 	my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  484: 	my $own=$devchown; $own=~s/\:/\,/;
  485: 	$description.=<<END;
  486: \tinstall -m $devchmod -d \$(SOURCE)/$d \$(ROOT)/$d
  487: END
  488:     }
  489:     $description.=<<END;
  490: 
  491: END
  492:     return $description;
  493: }
  494: 
  495: # ------ Installation commands for a Makefile used only by a rpm -ba invocation
  496: sub make_files_LCMakefile_segment {
  497:     my ($dirs)=@_;
  498:     my $description=<<END;
  499: files:
  500: END
  501:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  502:     foreach my $d (@$dirs) {
  503: 	# set other values
  504: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  505: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  506: 	# find files that are contained in this directory
  507: 	my @files;
  508: 	my @filesfull;
  509: 	foreach my $f (@allfiles) {
  510: 	    if ($f=~/^$d\/([^\/]+)$/) {
  511: 		push @files,$1;
  512: 		push @filesfull,$f;
  513: 	    }
  514: 	}
  515: 	# render starting HTML formatting elements
  516: 	if (@files) {
  517: 	    $description.=<<END;
  518: \t# $d $dirdescription
  519: END
  520:         }
  521: 	if (@files) {
  522:             foreach my $i (0..$#files) {
  523: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  524: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  525: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  526: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  527: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  528: 		my $rot=$filesfull[$i];
  529: 		$rot=~s/[^\/]+$/\./ if $rot=~/\*/;
  530: 		$description.=<<END if $category ne 'symbolic link';
  531: \tinstall -m $devchmod \$(SOURCE)/$filesfull[$i] \$(ROOT)/$rot
  532: END
  533: 	    }
  534: 	}
  535:     }
  536:     $description.=<<END;
  537: 
  538: END
  539:     return $description;
  540: }
  541: 
  542: # ------ Installation commands for a Makefile used only by a rpm -ba invocation
  543: sub make_links_LCMakefile_segment {
  544:     my ($dirs)=@_;
  545:     my $description=<<END;
  546: links:
  547: END
  548:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  549:     foreach my $d (@$dirs) {
  550: 	# find files that are contained in this directory
  551: 	my @files;
  552: 	my @filesfull;
  553: 	foreach my $f (@allfiles) {
  554: 	    if ($f=~/^$d\/([^\/]+)$/) {
  555: 		push @files,$1;
  556: 		push @filesfull,$f;
  557: 	    }
  558: 	}
  559: 	# render starting HTML formatting elements
  560: 	if (@files) {
  561:             foreach my $i (0..$#files) {
  562: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  563: 		my $linkto=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'LINKTO'};
  564: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  565: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  566: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  567: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  568: 		$description.=<<END if $category eq 'symbolic link';
  569: \tln -s /$linkto \$(ROOT)/$filesfull[$i]
  570: END
  571: 	    }
  572: 	}
  573:     }
  574:     $description.=<<END;
  575: 
  576: END
  577:     return $description;
  578: }
  579: 
  580: # --------------------------------- Installation commands to install directories
  581: sub make_directory_install_segment {
  582:     my ($dirs)=@_;
  583:     my $description=<<END;
  584: directories:
  585: END
  586:     foreach my $d (@$dirs) {
  587: 	my $category=$info{'DIRECTORY'}{$distribution}{$d}{'CATEGORY'};
  588: 	my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  589: 	my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  590: 	my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  591: 	my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  592: 	my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  593: 	my ($owner,$group)=split(/\:/,$devchown);
  594: 	my $own=$devchown; $own=~s/\:/\,/;
  595: 	$description.=<<END;
  596: \tinstall -o $owner -g $group -m $devchmod -d \$(TARGET)/$d
  597: END
  598:     }
  599:     $description.=<<END;
  600: 
  601: END
  602:     return $description;
  603: }
  604: 
  605: # ------------------------------------------------------ Commands to build files
  606: sub make_files_build_segment {
  607:     my ($dirs)=@_;
  608:     my $description;
  609:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  610:     my $tab="\t";
  611:     my $sources="# Dynamic Makefile generated by LON-CAPA build process\n\n";
  612:     $sources.="SHELL=\"/bin/sh\"\n\n";
  613:     $sources.="\nall: ";
  614:     foreach my $d (@$dirs) {
  615: 	# set other values
  616: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  617: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  618: 	# find files that are contained in this directory
  619: 	my @files;
  620: 	my @filesfull;
  621: 	foreach my $f (@allfiles) {
  622: 	    if ($f=~/^$d\/([^\/]+)$/) {
  623: 		push @files,$1;
  624: 		push @filesfull,$f;
  625: 	    }
  626: 	}
  627: 	if (@files) {
  628:             foreach my $i (0..$#files) {
  629: 		# if has build information, output appropriate something
  630: 		my $build=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'BUILD'};
  631: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  632: 		$build=~s/^\s+//; $build=~s/\s+$//;
  633: 		if ($build) {
  634: 		    my $dependencies=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'DEPENDENCIES'};
  635: 		    my $source2=$source;
  636: #		    $source2=~s/^[^\/]+\///;
  637:  		    $source2="../../" . $source2;
  638: 		    $sources.="$source2 ";
  639: 		    my $directory=$build;
  640: #		    $directory=~s/^[^\/]+\///;
  641: 		    $directory=~s/([^\/]+)$//;
  642:  		    $directory="../../" . $directory;
  643: 		    my $buildfile=$1;
  644: 		    my $sdir=$source;
  645: #		    $sdir=~s/^[^\/]+\///;
  646: 		    $sdir=~s/([^\/]+)$//;
  647:  		    $sdir="../../" . $sdir;
  648: 		    $dependencies=~s/\s+$//;
  649: 		    my $depstat="";
  650: 		    if ($dependencies=~s/\s+\[ALWAYS_RUN_BUILD_COMMAND\]//) {
  651: 			$depstat=" alwaysrun";
  652: 		    }
  653: 		    $dependencies=~s/\s+/ $sdir/gs;
  654: 		    my @deps=split(/\s+/,$dependencies);
  655: 		    shift @deps;
  656: 		    my $testing=<<END;
  657: 	\@if !(echo "");\\
  658: 	        then echo "**** LON-CAPA WARNING **** Strange shell. Check your path settings.";\\
  659: END
  660: 		    foreach my $d (@deps) {
  661: 			$testing.=<<END;
  662: 	elif !(test -r $d);\\
  663: 		then echo "**** LON-CAPA WARNING **** missing the file: $d";\\
  664: END
  665: 		    }
  666: 		    chop $testing;
  667: 		    $description.=<<END;
  668: $source2: $depstat
  669: $testing
  670:         else \\
  671: 	    ${tab}cd $directory; sh ./$buildfile;\\
  672:         fi
  673: 
  674: END
  675: 		}
  676: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  677: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  678: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  679: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  680: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  681: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  682: 		my $rot=$filesfull[$i];
  683: 		$rot=~s/[^\/]+$/\./ if $rot=~/\*/;
  684: #		$description.=<<END if $category ne 'symbolic link';
  685: #\tinstall -o $owner -g $group -m $devchmod \$(SOURCE)/$source \$(TARGET)/$rot
  686: #END
  687: 	    }
  688: 	}
  689:     }
  690:     $description.=<<END;
  691: alwaysrun:
  692: 
  693: END
  694:     $sources.="\n\n";
  695:     return ($sources . $description);
  696: }
  697: 
  698: # --------------------------------------- Installation commands to install files
  699: sub make_files_install_segment {
  700:     my ($dirs)=@_;
  701:     my $description=<<END;
  702: files:
  703: END
  704:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  705:     foreach my $d (@$dirs) {
  706: 	# set other values
  707: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  708: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  709: 	# find files that are contained in this directory
  710: 	my @files;
  711: 	my @filesfull;
  712: 	foreach my $f (@allfiles) {
  713: 	    if ($f=~/^$d\/([^\/]+)$/) {
  714: 		push @files,$1;
  715: 		push @filesfull,$f;
  716: 	    }
  717: 	}
  718: 	# render starting HTML formatting elements
  719: 	if (@files) {
  720: 	    $description.=<<END;
  721: \t# $d $dirdescription
  722: END
  723:         }
  724: 	if (@files) {
  725:             foreach my $i (0..$#files) {
  726: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  727: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  728: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  729: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  730: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  731: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  732: 		my $rot=$filesfull[$i];
  733: 		$rot=~s/[^\/]+$/\./ if $rot=~/\*/;
  734: 		my ($owner,$group)=split(/\:/,$devchown);
  735: 		if ($category ne 'conf' && $category ne 'symbolic link') {
  736: 		    if ($source!~/\*/) {
  737: 			$description.=<<END;
  738: \t\@test -e \$(SOURCE)/$source \&\& install -o $owner -g $group -m $devchmod \$(SOURCE)/$source \$(TARGET)/$rot || echo "**** LON-CAPA WARNING **** CVS source file does not exist: \$(SOURCE)/$source"
  739: END
  740:                     }
  741: 		    else {
  742: 			$description.=<<END;
  743: \tinstall -o $owner -g $group -m $devchmod \$(SOURCE)/$source \$(TARGET)/$rot
  744: END
  745: 		    }
  746:                 }
  747: 	    }
  748: 	}
  749:     }
  750:     $description.=<<END;
  751: 
  752: END
  753:     return $description;
  754: }
  755: 
  756: # ------ Installation commands to install configuration files (and make backups)
  757: sub make_files_configinstall_segment {
  758:     my ($dirs)=@_;
  759:     my $description=<<END;
  760: configfiles:
  761: END
  762:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  763:     foreach my $d (@$dirs) {
  764: 	# set other values
  765: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  766: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  767: 	# find files that are contained in this directory
  768: 	my @files;
  769: 	my @filesfull;
  770: 	foreach my $f (@allfiles) {
  771: 	    if ($f=~/^$d\/([^\/]+)$/) {
  772: 		push @files,$1;
  773: 		push @filesfull,$f;
  774: 	    }
  775: 	}
  776: 	# render starting HTML formatting elements
  777: 	if (@files) {
  778: 	    $description.=<<END;
  779: \t# $d $dirdescription
  780: END
  781:         }
  782: 	if (@files) {
  783:             foreach my $i (0..$#files) {
  784: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  785: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  786: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  787: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  788: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  789: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  790: 		my $rot=$filesfull[$i];
  791: 		$rot=~s/[^\/]+$/\./ if $rot=~/\*/;
  792: 		my ($owner,$group)=split(/\:/,$devchown);
  793: 		if ($category eq 'conf') {
  794: 		    $description.=<<END;
  795: \tcp -p \$(TARGET)/$rot \$(TARGET)/$rot`date +'.\%Y\%m\%d\%H\%M\%S'` 2>/dev/null; install -o $owner -g $group -m $devchmod \$(SOURCE)/$source \$(TARGET)/$rot
  796: END
  797:                 }
  798: 	    }
  799: 	}
  800:     }
  801:     $description.=<<END;
  802: 
  803: END
  804:     return $description;
  805: }
  806: 
  807: # ------ Commands to enforce configuration file permissions
  808: sub make_files_configpermissions_segment {
  809:     my ($dirs)=@_;
  810:     my $description=<<END;
  811: configpermissions:
  812: END
  813:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  814:     foreach my $d (@$dirs) {
  815: 	# set other values
  816: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  817: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  818: 	# find files that are contained in this directory
  819: 	my @files;
  820: 	my @filesfull;
  821: 	foreach my $f (@allfiles) {
  822: 	    if ($f=~/^$d\/([^\/]+)$/) {
  823: 		push @files,$1;
  824: 		push @filesfull,$f;
  825: 	    }
  826: 	}
  827: 	# render starting HTML formatting elements
  828: 	if (@files) {
  829: 	    $description.=<<END;
  830: \t# $d $dirdescription
  831: END
  832:         }
  833: 	if (@files) {
  834:             foreach my $i (0..$#files) {
  835: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  836: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
  837: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  838: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  839: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  840: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  841: 		my $rot=$filesfull[$i];
  842: 		$rot=~s/[^\/]+$/\./ if $rot=~/\*/;
  843: 		my ($owner,$group)=split(/\:/,$devchown);
  844: 		if ($category eq 'conf') {
  845: 		    $description.=<<END;
  846: \tchmod $devchmod \$(TARGET)/$rot
  847: \tchown $devchown \$(TARGET)/$rot
  848: END
  849:                 }
  850: 	    }
  851: 	}
  852:     }
  853:     $description.=<<END;
  854: 
  855: END
  856:     return $description;
  857: }
  858: 
  859: # ------------------------------ Installation commands to install symbolic links
  860: sub make_links_install_segment {
  861:     my ($dirs)=@_;
  862:     my $description=<<END;
  863: links:
  864: END
  865:     chop $description;
  866:     my $description2;
  867:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  868:     foreach my $d (@$dirs) {
  869: 	# find files that are contained in this directory
  870: 	my @files;
  871: 	my @filesfull;
  872: 	foreach my $f (@allfiles) {
  873: 	    if ($f=~/^$d\/([^\/]+)$/) {
  874: 		push @files,$1;
  875: 		push @filesfull,$f;
  876: 	    }
  877: 	}
  878: 	# render starting HTML formatting elements
  879: 	if (@files) {
  880:             foreach my $i (0..$#files) {
  881: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
  882: 		my $linkto=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'LINKTO'};
  883: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  884: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  885: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  886: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  887: 		if ($category eq 'symbolic link') {
  888: 		    $description.=" \$(TARGET)/$filesfull[$i]";
  889: 		    $description2.=<<END;
  890: \$(TARGET)/$filesfull[$i]:
  891: \tln -s /$linkto \$(TARGET)/$filesfull[$i]
  892: 
  893: END
  894:                 }
  895: 	    }
  896: 	}
  897:     }
  898:     $description.=<<END;
  899: 
  900: END
  901:     $description.=$description2;
  902:     return $description;
  903: }
  904: 
  905: # --------------------------------------------------------- Make RPM .spec block
  906: sub make_rpm_spec_block {
  907:     my $pwd=`pwd`; chop $pwd;
  908:     my $buildroot="$pwd/LON-CAPA-BuildRoot";
  909:     my $source=$info{'RPM'}{'Name'} . "-" . $info{'RPM'}{'Version'} . '.tar.gz';
  910:     my $description=<<END;
  911: Summary: $info{'RPM'}{'Summary'}
  912: Name: $info{'RPM'}{'Name'}
  913: Version: $info{'RPM'}{'Version'}
  914: Release: $info{'RPM'}{'Release'}
  915: Vendor: $info{'RPM'}{'Vendor'} 
  916: BuildRoot: $buildroot
  917: Copyright: $info{'RPM'}{'Copyright'}
  918: Group: $info{'RPM'}{'Group'}
  919: Source: $source
  920: AutoReqProv: $info{'RPM'}{'AutoReqProv'}
  921: \%description
  922: $info{'RPM'}{'description'}
  923: 
  924: END
  925:     return $description;
  926: }
  927: 
  928: # --------------------------------------------------- Make RPM build .spec block
  929: sub make_rpm_build_block {
  930:     my $pwd=`pwd`; chop $pwd;
  931:     my $buildroot="$pwd/LON-CAPA-BuildRoot";
  932:     my $sourceroot="$pwd/LON-CAPA-SourceRoot";
  933:     my $description=<<END;
  934: 
  935: \%prep
  936: \%setup
  937: 
  938: \%build
  939: rm -Rf "$buildroot"
  940: 
  941: \%install
  942: make -f LCMakefile ROOT="\$RPM_BUILD_ROOT" SOURCE="$sourceroot" directories
  943: make -f LCMakefile ROOT="\$RPM_BUILD_ROOT" SOURCE="$sourceroot" files
  944: make -f LCMakefile ROOT="\$RPM_BUILD_ROOT" SOURCE="$sourceroot" links
  945: 
  946: \%pre
  947: $info{'RPM'}{'pre'}
  948: 
  949: \%post
  950: \%postun
  951: 
  952: \%files
  953: # \%doc README COPYING ChangeLog LICENSE
  954: END
  955:     return $description;
  956: }
  957: 
  958: # ------------------------------------- Make directory structure RPM .spec block
  959: sub make_directory_structure_spec_block {
  960:     my ($dirs)=@_;
  961:     foreach my $d (@$dirs) {
  962: 	my $category=$info{'DIRECTORY'}{$distribution}{$d}{'CATEGORY'};
  963: 	my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
  964: 	my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
  965: 	my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
  966: 	my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
  967: 	my $own=$devchown; $own=~s/\:/\,/;
  968: 	$description.=<<END;
  969: \%dir \%attr($devchmod,$own) /$d
  970: END
  971:     }
  972:     return $description;
  973: }
  974: 
  975: # ---------------------------- Make directory and file structure RPM .spec block
  976: sub make_directory_and_file_structure_spec_block {
  977:     my ($dirs)=@_;
  978:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
  979:     foreach my $d (@$dirs) {
  980: 	# set other values
  981: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
  982: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
  983: 	# find files that are contained in this directory
  984: 	my @files;
  985: 	my @filesfull;
  986: 	foreach my $f (@allfiles) {
  987: 	    if ($f=~/^$d\/([^\/]+)$/) {
  988: 		push @files,$1;
  989: 		push @filesfull,$f;
  990: 	    }
  991: 	}
  992: 	# render starting HTML formatting elements
  993: 	if (@files) {
  994: 	    $description.=<<END;
  995: # $d $dirdescription
  996: END
  997:         }
  998: 	if (@files) {
  999:             foreach my $i (0..$#files) {
 1000: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
 1001: 		my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
 1002: 		my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
 1003: 		my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
 1004: 		my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
 1005: 		my $own=$devchown; $own=~s/\:/\,/;
 1006: 		my $config="";
 1007: 		$config="\%config " if $category eq 'conf';
 1008: 		$devchmod='-' if $category eq 'symbolic link';
 1009: 		$description.=<<END;
 1010: $config\%attr($devchmod,$own) /$filesfull[$i]
 1011: END
 1012: 	    }
 1013: 	}
 1014:     }
 1015:     return $description;
 1016: }
 1017: 
 1018: # ----------------------------------------------------------- End RPM .spec page
 1019: sub end_spec_page {
 1020: }
 1021: 
 1022: # ------------------------------------------------------- Begin description page
 1023: sub begin_description_page {
 1024:     my ($mode)=@_;
 1025:     my $description;
 1026:     unless ($mode eq 'status') {
 1027:     $description=<<END;
 1028: <HTML>
 1029: <HEAD>
 1030: <TITLE>LON-CAPA Software Description Page ($distribution, $date)</TITLE>
 1031: </HEAD>
 1032: <BODY>
 1033: <FONT SIZE=+2>LON-CAPA Software Description Page ($distribution, $date)</FONT>
 1034: <BR>Michigan State University
 1035: <BR>Learning Online with CAPA
 1036: <BR>Contact korte\@lon-capa.org
 1037: <UL>
 1038: <LI>About this file
 1039: <LI>Software Package Description
 1040: <LI>Directory Structure
 1041: <LI>File Type Ownership and Permissions
 1042: <LI>File and Directory Structure
 1043: </UL>
 1044: <FONT SIZE=+2>About this file</FONT>
 1045: <P>
 1046: This file is generated dynamically by <TT>parse.pl</TT> as
 1047: part of a development compilation process.  See 
 1048: http://install.lon-capa.org/compile/index.html for more
 1049: information.
 1050: </P>
 1051: END
 1052: }
 1053:     else {
 1054: 	$description=<<END;
 1055: <HTML>
 1056: <HEAD>
 1057: <TITLE>LON-CAPA Software File System Status Page ($distribution, $date)</TITLE>
 1058: </HEAD>
 1059: <BODY>
 1060: <FONT SIZE=+2>LON-CAPA Software File System Status Page ($distribution, $date)</FONT>
 1061: <BR>Michigan State University
 1062: <BR>Learning Online with CAPA
 1063: <BR>Contact korte\@lon-capa.org
 1064: <UL>
 1065: <LI>About this file
 1066: <LI>Software Package Description
 1067: <LI>Directory Structure
 1068: <LI>File Type Ownership and Permissions
 1069: <LI>File and Directory Structure
 1070: </UL>
 1071: <FONT SIZE=+2>About this file</FONT>
 1072: <P>
 1073: This file is generated dynamically by <TT>parse.pl</TT> as
 1074: part of a status checking process.  See http://install.lon-capa.org/
 1075: for more information.
 1076: </P>
 1077: END
 1078:     }
 1079:     return $description;
 1080: 
 1081: }
 1082: 
 1083: # ------------------------------------------------- End description page
 1084: sub end_description_page {
 1085:     my $description=<<END;
 1086: <HR>
 1087: <FONT SIZE=-1>LON-CAPA Software Development Team</FONT>
 1088: </BODY>
 1089: </HTML>
 1090: END
 1091:     return $description;
 1092: }
 1093: 
 1094: # ------------------------------------------------- Make RPM description block
 1095: sub make_rpm_description_block {
 1096:     my ($mode)=@_;
 1097:     my $description;
 1098:     unless ($mode eq 'status') {
 1099:     $description=<<END;
 1100: <FONT SIZE=+2>Rolled in a RedHat 6.2 RPM, $date</FONT>
 1101: <P>
 1102: <TABLE BGCOLOR=#FFFFFF BORDER=0 CELLPADDING=10 CELLSPACING=0>
 1103: <TR><TD>
 1104: <PRE>
 1105: Name        : $info{'RPM'}{'Name'}
 1106: Version     : $info{'RPM'}{'Version'}
 1107: Vendor      : $info{'RPM'}{'Vendor'} 
 1108: Release     : $info{'RPM'}{'Release'}                             
 1109: Build Host  : $buildhost
 1110: Group       : $info{'RPM'}{'Group'}
 1111: License     : $info{'RPM'}{'Copyright'}
 1112: Summary     : $info{'RPM'}{'Summary'}
 1113: Description : 
 1114: $info{'RPM'}{'description'}
 1115: </PRE>
 1116: </TD></TR>
 1117: </TABLE>
 1118: </P>
 1119: END
 1120: }
 1121:     else {
 1122: 	my $exist=`rpm -q LON-CAPA-base 2>/dev/null`;
 1123: 	unless ($exist) {
 1124: 	    $description=<<END;
 1125: <FONT SIZE=+2>No LON-CAPA RPM on the system, (installed ??????)</FONT>
 1126: <P>
 1127: <TABLE BGCOLOR=#FFFFFF BORDER=0 CELLPADDING=10 CELLSPACING=0>
 1128: <TR><TD>
 1129: <FONT SIZE=+3>Error! A LON-CAPA-base RPM
 1130: was never installed on this system!</FONT>
 1131: </TD></TR>
 1132: </TD></TR>
 1133: </TABLE>
 1134: </P>
 1135: END
 1136: 	}
 1137: 	else {
 1138: 	    chop $exist;
 1139: 	    my $rpmname=`rpm -q --queryformat '%{NAME}' LON-CAPA-base`;
 1140: 	    my $rpmversion=`rpm -q --queryformat '%{VERSION}' LON-CAPA-base`;
 1141: 	    my $rpmrelease=`rpm -q --queryformat '%{RELEASE}' LON-CAPA-base`;
 1142: 	    my $idate=`rpm -q --queryformat '%{INSTALLTIME:date}' LON-CAPA-base`;
 1143: 	    my $rpmvendor=`rpm -q --queryformat '%{VENDOR}' LON-CAPA-base`;
 1144: 	    my $rpmbuildhost=`rpm -q --queryformat '%{BUILDHOST}' LON-CAPA-base`;
 1145: 	    my $rpmgroup=`rpm -q --queryformat '%{GROUP}' LON-CAPA-base`;
 1146: 	    my $rpmlicense=`rpm -q --queryformat '%{LICENSE}' LON-CAPA-base`;
 1147: 	    my $rpmsummary=`rpm -q --queryformat '%{SUMMARY}' LON-CAPA-base`;
 1148: 	    my $rpmdescription=`rpm -q --queryformat '%{DESCRIPTION}' LON-CAPA-base`;
 1149: 	    $description=<<END;
 1150: <FONT SIZE=+2>Current RedHat RPM on the system, (installed $idate)</FONT>
 1151: <P>
 1152: <TABLE BGCOLOR=#FFFFFF BORDER=0 CELLPADDING=10 CELLSPACING=0>
 1153: <TR><TD>
 1154: <PRE>
 1155: Name        : $rpmname
 1156: Version     : $rpmversion
 1157: Vendor      : $rpmvendor
 1158: Release     : $rpmrelease
 1159: Build Host  : $rpmbuildhost
 1160: Group       : $rpmgroup
 1161: License     : $rpmlicense
 1162: Summary     : $rpmsummary
 1163: Description : 
 1164: $rpmdescription
 1165: </PRE>
 1166: </TD></TR>
 1167: </TABLE>
 1168: </P>
 1169: END
 1170: }
 1171:     }
 1172:     return $description;
 1173: }
 1174: 
 1175: # ----------------------------------------------- Determine directory structure
 1176: sub determine_directory_structure {
 1177:     my @directories=keys %{$info{'DIRECTORY'}{$distribution}};
 1178:     return (sort @directories);
 1179: }
 1180: 
 1181: 
 1182: # ---------------------------------- Make directory structure description block
 1183: sub make_directory_structure_description_block {
 1184:     my ($dirs,$mode)=@_;
 1185:     my $dirstatus; my $statusheader;
 1186:     my $description=<<END;
 1187: <FONT SIZE=+2>Directory Structure Description, $date</FONT>
 1188: <P>
 1189: The directory structure description below shows only those
 1190: directories which either contain LON-CAPA specific files
 1191: or normally do not exist on a RedHat Linux system (and
 1192: must be generated to allow proper placement of files
 1193: during LON-CAPA run-time operation).
 1194: </P>
 1195: <P>
 1196: <TABLE BORDER=1 CELLPADDING=3 CELLSPACING=0>
 1197: END
 1198:     my $maxcount=0;
 1199:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
 1200:     my %diraccount; # hash to track which directories are accounted for
 1201:     foreach my $file (@allfiles) {
 1202: 	$file=~/^(.*)\/([^\/]+)$/;
 1203: 	$diraccount{$1}=1;
 1204:     }
 1205:     foreach my $d (@$dirs) {
 1206:         my (@matches)=($d=~/\//g);
 1207: 	my $count=scalar(@matches);
 1208: 	$maxcount=$count if $count>$maxcount;
 1209: 	delete $diraccount{$d};
 1210:     }
 1211:     if ($mode eq 'status') {
 1212: 	$statusheader="<TH ALIGN=LEFT BGCOLOR=#FFFFFF>Current Status</TH>";
 1213:     }
 1214:     $description.=<<END;
 1215: <TR>
 1216: $statusheader
 1217: <TH ALIGN=LEFT BGCOLOR=#FFFFFF>Category</TH>
 1218: <TH ALIGN=LEFT BGCOLOR=#FFFFFF>Permissions</TH>
 1219: <TH ALIGN=LEFT BGCOLOR=#FFFFFF><FONT COLOR=#FF0000>Development<BR>Permissions</FONT></TH>
 1220: END
 1221:     $description.="<TH ALIGN=LEFT BGCOLOR=#FFFFFF COLSPAN=".($maxcount+1).">Directory Path</TH>\n";
 1222:     if (keys %diraccount) {
 1223: 	$description.= "<TR><TD ALIGN=LEFT BGCOLOR=#FFFFFF COLSPAN=".($maxcount+4)."><I><PRE>Directories that are unaccounted for: \n";
 1224: 	foreach my $d (keys %diraccount) {
 1225: 	    $description.="$d\n";
 1226: 	}
 1227: 	$description.="</PRE></I></TH></TR>\n";
 1228:     }
 1229:     foreach my $d (@$dirs) {
 1230: 	my $dtable=$d;
 1231: 	$dtable=~s/\//\<\/TD\>\<TD\>/g;
 1232: 	my $category=$info{'DIRECTORY'}{$distribution}{$d}{'CATEGORY'};
 1233: 	my $chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
 1234: 	my $chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
 1235: 	my $devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
 1236: 	my $devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
 1237: 	if ($mode eq 'status') {
 1238: 	    my $ds=`find /$d -type d -prune -printf "\%m\t\%u\t\%g" 2>/dev/null`;
 1239: 	    unless ($ds) {
 1240: 		$dirstatus='<TD BGCOLOR=#FFFFFF><B><U>MISSING</U></B></TD>';
 1241: 	    }
 1242: 	    else {
 1243: 		my @dss=split(/\t/,$ds);
 1244: 		my $dssz=$dss[0];
 1245: 		$dssz="0" . $dss[0] if length($dss[0])<4;
 1246: 		$dss[0]=$dssz;
 1247: 		$ds="$dss[0] $dss[1]:$dss[2]";
 1248: 		if ($ds eq "$chmod $chown" && $ds eq "$devchmod $devchown") {
 1249: 		    $dirstatus='<TD BGCOLOR=#FFFFFF>runtime+development</TD>';
 1250: 		}
 1251: 		elsif ($ds eq "$chmod $chown") {
 1252: 		    $dirstatus='<TD BGCOLOR=#FFFFFF>runtime</TD>';
 1253: 		}
 1254: 		elsif ($ds eq "$devchmod $devchown") {
 1255: 		    $dirstatus='<TD BGCOLOR=#FFFFFF>development</TD>';
 1256: 		}
 1257: 		else {
 1258: 		    $dirstatus="<TD BGCOLOR=#FFFFFF><B><U>ERROR</U></B><BR>$ds</TD>";
 1259: 		}
 1260: 	    }
 1261: 	}
 1262: 	$description.=<<END;
 1263: <TR>
 1264: $dirstatus
 1265: <TD BGCOLOR=#FFFFFF>$category</TD>
 1266: <TD BGCOLOR=#FFFFFF><TT>$chmod $chown</TT></TD>
 1267: <TD BGCOLOR=#FFFFFF><FONT COLOR=#FF0000><TT>$devchmod $devchown</TT></FONT></TD>
 1268: <TD>
 1269: $dtable
 1270: </TD>
 1271: </TR>
 1272: END
 1273:     }
 1274:     $description.=<<END;
 1275: </TABLE>
 1276: </P>
 1277: END
 1278:     return $description;
 1279: }
 1280: 
 1281: # ------------------- Make file type ownership and permissions description block
 1282: sub make_file_type_ownership_and_permissions_description_block {
 1283:     my ($mode)=@_;
 1284:     my $description=<<END;
 1285: <FONT SIZE=+2>File Type Ownership and Permissions Descriptions, $date</FONT>
 1286: <P>
 1287: This table shows what permissions and ownership settings correspond
 1288: to each kind of file type.
 1289: </P>
 1290: <P>
 1291: <TABLE BORDER=1 CELLPADDING=5 WIDTH=60%>
 1292: <TR>
 1293: <TH ALIGN=LEFT BGCOLOR=#FFFFFF>Icon</TH>
 1294: <TH ALIGN=LEFT BGCOLOR=#FFFFFF>Type</TH>
 1295: <TH ALIGN=LEFT BGCOLOR=#FFFFFF>Permissions</TH>
 1296: <TH ALIGN=LEFT BGCOLOR=#FFFFFF>Development Permissions</TH>
 1297: </TR>
 1298: END
 1299:     foreach my $type (keys %{$info{'OWNERSHIP'}}) {
 1300: 	if (defined($fcm{$type})) {
 1301: 	    my $chmod=$info{'OWNERSHIP'}{$type}{'CHMOD'};
 1302: 	    my $chown=$info{'OWNERSHIP'}{$type}{'CHOWN'};
 1303: 	    my $devchmod=$info{'DEVOWNERSHIP'}{$type}{'CHMOD'};
 1304: 	    my $devchown=$info{'DEVOWNERSHIP'}{$type}{'CHOWN'};
 1305: 	    $description.=<<END;
 1306: <TR>
 1307: <TD><IMG SRC="$fcm{$type}.gif" ALT="$type"></TD>
 1308: <TD>$type</TD>
 1309: <TD><TT>$chmod $chown</TT></TD>
 1310: <TD><TT>$devchmod $devchown</TT></TD>
 1311: </TR>
 1312: END
 1313:         }
 1314:     }
 1315:     $description.=<<END;
 1316: </TABLE>
 1317: </P>
 1318: END
 1319: }
 1320: 
 1321: # ------------------------- Make directory and file structure description block
 1322: sub make_directory_and_file_structure_description_block {
 1323:     my ($dirs,$mode)=@_;
 1324:     my $statusheader; my $filestatus;
 1325:     my $description=<<END;
 1326: <FONT SIZE=+2>Directory and File Structure Description, $date</FONT>
 1327: <P>
 1328: The icons on the left column correspond to the file type
 1329: specified in the second column.  The last column "Notes" shows compilation,
 1330: dependency, and configuration information.  The CVS location
 1331: shows the location of the binary source file (if applicable) needed to
 1332: be copied to the target.  If the binary source file is not at
 1333: the specified location, then the text is shown in 
 1334: <FONT COLOR=#FF0000>red</FONT>.
 1335: </P>
 1336: <P>
 1337: <TABLE BORDER=1 CELLPADDING=5 WIDTH=500>
 1338: END
 1339:     my $counter=0;
 1340:     my @colorindex=("#80FF80","#80FFFF","#FFFF80");
 1341:     my @allfiles=keys %{$info{'LOCATION'}{$distribution}};
 1342:     foreach my $d (@$dirs) {
 1343: 	# set color
 1344: 	my $color=$colorindex[$counter%3];
 1345: 	# set other values
 1346: 	my $dirdescription=$info{'DIRECTORY'}{$distribution}{$d}{'DESCRIPTION'};
 1347: 	$dirdescription="(" . $dirdescription . ")" if $dirdescription;
 1348: 	# find subdirectories that are contained in this directory
 1349: 	my @subdirs;
 1350: 	foreach my $d2 (@$dirs) {
 1351: 	    if ($d2=~/^$d\/([^\/]+)$/) {
 1352: 		push @subdirs,$1;
 1353: 	    }
 1354: 	}
 1355: 	# find files that are contained in this directory
 1356: 	my @files;
 1357: 	my @filesfull;
 1358: 	foreach my $f (@allfiles) {
 1359: 	    if ($f=~/^$d\/([^\/]+)$/) {
 1360: 		push @files,$1;
 1361: 		push @filesfull,$f;
 1362: 	    }
 1363: 	}
 1364: 	# render starting HTML formatting elements
 1365: 	if (@subdirs || @files) {
 1366: 	    my $subdirstring="<BR>* Relevant subdirectories: " . join(", ",@subdirs) if @subdirs;
 1367: 	    $description.=<<END;
 1368: <TR><TD BGCOLOR=#000000 COLSPAN=6><FONT COLOR=$color><IMG SRC="directory.gif" ALT="directory">DIRECTORY -- $d $dirdescription
 1369: $subdirstring</FONT></TD></TR>
 1370: END
 1371:         }
 1372: 	else {
 1373: 	    $description.=<<END;
 1374: <TR><TD BGCOLOR=#000000 COLSPAN=6><FONT COLOR=$color><IMG SRC="emptydirectory.gif" ALT="empty directory">EMPTY DIRECTORY - $d $dirdescription</FONT></TD></TR>
 1375: END
 1376:         }
 1377: 	if (@files) {
 1378: 	    if ($mode eq 'status') {
 1379: 		$statusheader=<<END;
 1380: <TH BGCOLOR=$color ALIGN=LEFT>Current Status</TH>
 1381: END
 1382: 	    }
 1383: 	    $description.=<<END;
 1384: <TR>
 1385: $statusheader
 1386: <TH BGCOLOR=$color ALIGN=LEFT COLSPAN=2>Type</TH>
 1387: <TH BGCOLOR=$color ALIGN=LEFT>File Name</TH>
 1388: <TH BGCOLOR=$color ALIGN=LEFT>Function</TH>
 1389: <TH BGCOLOR=$color ALIGN=LEFT>CVS Location</TH>
 1390: <TH BGCOLOR=$color ALIGN=LEFT>Notes</TH>
 1391: </TR>
 1392: END
 1393:             foreach my $i (0..$#files) {
 1394: 		my $category=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'CATEGORY'};
 1395: 		my $fdescription=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'DESCRIPTION'};
 1396: 		my $source=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
 1397: 		my $source2=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'SOURCE'};
 1398: 		my $note=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'NOTE'};
 1399: 		$note.="<BR>" if $note;
 1400: 		my $listing=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'FILES'};
 1401: 		my @E=split(/\s+/,$listing);
 1402: 		$source=~/(.*)\/[^\/]+$/;
 1403: 		my $sd=$1;
 1404: 		my $eflag=0;
 1405: 		foreach my $e (@E) {
 1406: 		    unless (-e "../../$sd/$e") {
 1407: 			$e="<FONT COLOR=#FF0000>$e</FONT>";
 1408: 			$eflag=1;
 1409: 		    }
 1410: 		}
 1411: 		$listing=join("\n",@E);
 1412: 		$listing="<B>listing</B><BR><FONT SIZE=-2>$listing</FONT>" if $listing;
 1413: 		$listing.="<BR>" if $listing;
 1414: 		my $build=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'BUILD'};
 1415: 		$build="<B>build</B><BR>$build" if $build;
 1416: 		$build.="<BR>" if $build;
 1417: 		my $dependencies=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'DEPENDENCIES'};
 1418: 		$dependencies="<B>dependencies</B><BR>$dependencies" if $dependencies;
 1419: 		$dependencies.="<BR>" if $dependencies;
 1420: 		unless (-e "../../$source") {
 1421: 		    $source=~/([^\/]+)$/;
 1422: 		    my $s=$1;
 1423: 		    if ($source!~/\*/) {
 1424: 			$source="<FONT COLOR=#FF0000>$source</FONT>";
 1425: 		    }
 1426: 		    elsif ($eflag) {
 1427: 			$source="<FONT COLOR=#FF0000>$source</FONT>";
 1428: 		    }
 1429: 		}
 1430: 		my $checksum;
 1431: 		my $checksum_source;
 1432: 		my $checksum_target;
 1433: 		if ($mode eq 'status') {
 1434: 		    $filestatus='';
 1435: 		    my $fs;
 1436: 		    my $listing2=$info{'LOCATION'}{$distribution}{$filesfull[$i]}{'FILES'};
 1437: 		    my @E=split(/\s+/,$listing2); shift @E;
 1438: 		    if (@E) {
 1439: 			$fs=`find /$filesfull[$i] -prune -printf "\%m\t\%u\t\%g\n" 2>/dev/null | wc -l`; chop $fs;
 1440: 			if ($fs!=(@E+0)) {
 1441: 			    $ecount=(@E+0);
 1442: 			    $estuff=join(",",@E);
 1443: 			    $filestatus="<TD BGCOLOR=#FFFFFF><B><U>ERROR. SOME FILES ARE MISSING</U></B></TD>";
 1444: 			}
 1445: 			$fs=`find /$filesfull[$i] -prune -printf "\%m\t\%u\t\%g\n" 2>/dev/null | sort | uniq | wc -l`; chop $fs;
 1446: 			if ($fs!=1) {
 1447: 			    $filestatus='<TD BGCOLOR=#FFFFFF><B><U>ERROR. THERE ARE MULTIPLE OWNERSHIPS/PERMISSIONS WHEN ALL THESE FILES SHOULD HAVE THE SAME CONFIGURATION</U></B></TD>';
 1448: 			}
 1449: 			else {
 1450: 			    $fs=`find /$filesfull[$i] -prune -printf "\%m\t\%u\t\%g\n" 2>/dev/null | sort | uniq`; chop $fs;
 1451: 			}
 1452: 		    }
 1453: 		    else {
 1454: 			$fs=`find /$filesfull[$i] -prune -printf "\%m\t\%u\t\%g" 2>/dev/null`;
 1455: 			if (-f "/$filesfull[$i]" && !(-l "/$filesfull[$i]")) {
 1456: 			    $checksum_source=`md5sum ../../$source2 | cut -d ' ' -f1`;
 1457: 			    chop $checksum_source;
 1458: 			    $checksum_target=`md5sum /$filesfull[$i] | cut -d ' ' -f1`;
 1459: 			    chop $checksum_target;
 1460: #			    warn ("CS: $checksum_source, CT: $checksum_target\n");
 1461: 			    unless ($checksum_source eq $checksum_target) {
 1462: 				$checksum="<BR><B><U>CHECKSUM DIFFERENCE</U></B>";
 1463: 			    }
 1464: 			}
 1465: 		    }
 1466: 		    my $fsl=`find /$filesfull[$i] -type l -prune -printf "\%m\t\%u\t\%g" 2>/dev/null`;
 1467: 		    unless ($fs || $filestatus) {
 1468: 			$filestatus='<TD BGCOLOR=#FFFFFF><B><U>MISSING</U></B></TD>';
 1469: 		    }
 1470: 		    elsif (!$filestatus) {
 1471: 
 1472: 			$chmod=$info{'OWNERSHIP'}{$category}{'CHMOD'};
 1473: 			$chown=$info{'OWNERSHIP'}{$category}{'CHOWN'};
 1474: 			$devchmod=$info{'DEVOWNERSHIP'}{$category}{'CHMOD'};
 1475: 			$devchown=$info{'DEVOWNERSHIP'}{$category}{'CHOWN'};
 1476: 
 1477: 			my @fss=split(/\t/,$fs);
 1478: 			my $fssz=$fss[0];
 1479: 			$fssz="0" . $fss[0] if length($fss[0])<4;
 1480: 			$fss[0]=$fssz;
 1481: 			$fs="$fss[0] $fss[1]:$fss[2]";
 1482: 			$s=' ';
 1483: 			if ($fsl) {
 1484: 			    $fs="$fss[1]:$fss[2]";
 1485: 			    $s='';
 1486: 			}
 1487: 			if ($fs eq "$chmod$s$chown" && $fs eq "$devchmod$s$devchown") {
 1488: 			    $filestatus="<TD BGCOLOR=#FFFFFF>runtime+development$checksum</TD>";
 1489: 			}
 1490: 			elsif ($fs eq "$chmod$s$chown") {
 1491: 			    $filestatus="<TD BGCOLOR=#FFFFFF>runtime$checksum</TD>";
 1492: 			}
 1493: 			elsif ($fs eq "$devchmod$s$devchown") {
 1494: 			    $filestatus="<TD BGCOLOR=#FFFFFF>development$checksum</TD>";
 1495: 			}
 1496: 			else {
 1497: 			    $filestatus="<TD BGCOLOR=#FFFFFF><B><U>ERROR</U></B><BR>$fs</TD>";
 1498: 			}
 1499: 		    }
 1500: 		}	    
 1501: 		$description.=<<END;
 1502: <TR>
 1503: $filestatus
 1504: <TD BGCOLOR=#A0A0A0><IMG SRC="$fcm{$category}.gif" ALT="$category"></TD>
 1505: <TD BGCOLOR=$color>$category</TD>
 1506: <TD BGCOLOR=$color>$files[$i]</TD>
 1507: <TD BGCOLOR=$color>$fdescription&nbsp;</TD>
 1508: <TD BGCOLOR=$color>$source</TD>
 1509: <TD BGCOLOR=$color>$note$listing$build$dependencies&nbsp;</TD>
 1510: </TR>
 1511: END
 1512: 	    }
 1513: 	}
 1514: 	$counter++;
 1515:     }
 1516:     $description.=<<END;
 1517: </TABLE>
 1518: </P>
 1519: END
 1520:     return $description;
 1521: }

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