Changeset 10468 for trunk/gsdl/perllib/mgppbuilder.pm
- Timestamp:
- 2005-08-10T16:19:17+12:00 (19 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/gsdl/perllib/mgppbuilder.pm
r10158 r10468 26 26 package mgppbuilder; 27 27 28 use basebuilder; 28 29 use classify; 29 30 use cfgread; … … 33 34 use FileHandle; 34 35 35 36 BEGIN { 37 # set autoflush on for STDERR and STDOUT so that mgpp 38 # doesn't get out of sync with plugins 39 STDOUT->autoflush(1); 40 STDERR->autoflush(1); 41 } 42 43 END { 44 STDOUT->autoflush(0); 45 STDERR->autoflush(0); 46 } 47 48 our $maxdocsize = 12000; 36 sub BEGIN { 37 @mgppbuilder::ISA = ('basebuilder'); 38 } 39 40 49 41 50 42 our %level_map = ('document'=>'Doc', … … 107 99 'Para'=>1); 108 100 101 my $maxdocsize = $basebuilder::maxdocsize; 102 109 103 sub new { 110 104 my $class = shift(@_); … … 114 108 $outhandle, $no_text, $failhandle, $gli) = @_; 115 109 116 $outhandle = STDERR unless defined $outhandle; 117 $no_text = 0 unless defined $no_text; 118 119 # create an mgppbuilder object 120 my $self = bless {'collection'=>$collection, 121 'source_dir'=>$source_dir, 122 'build_dir'=>$build_dir, 123 'verbosity'=>$verbosity, 124 'maxdocs'=>$maxdocs, 125 'debug'=>$debug, 126 'keepold'=>$keepold, 127 'remove_empty_classifications'=>$remove_empty_classifications, 128 'outhandle'=>$outhandle, 129 'no_text'=>$no_text, 130 'notbuilt'=>{}, # indexes not built 131 'indexfieldmap'=>\%static_indexfield_map, 132 'gli'=>$gli 133 }, $class; 134 135 $self->{'gli'} = 0 unless defined $self->{'gli'}; 136 137 # read in the collection configuration file 138 my $colcfgname = "$ENV{'GSDLCOLLECTDIR'}/etc/collect.cfg"; 139 if (!-e $colcfgname) { 140 die "mgppbuilder::new - couldn't find collect.cfg for collection $collection\n"; 141 } 142 $self->{'collect_cfg'} = &colcfg::read_collect_cfg ($colcfgname); 143 144 # sort out the indexes 145 #indexes are specified with spaces, but we put them into one index 146 my $indexes = $self->{'collect_cfg'}->{'indexes'}; 147 $self->{'collect_cfg'}->{'indexes'} = []; 148 push (@{$self->{'collect_cfg'}->{'indexes'}}, join(',', @$indexes)); 149 150 151 # sort out subcollection indexes 152 if (defined $self->{'collect_cfg'}->{'indexsubcollections'}) { 153 my $indexes = $self->{'collect_cfg'}->{'indexes'}; 154 $self->{'collect_cfg'}->{'indexes'} = []; 155 foreach my $subcollection (@{$self->{'collect_cfg'}->{'indexsubcollections'}}) { 156 foreach my $index (@$indexes) { 157 push (@{$self->{'collect_cfg'}->{'indexes'}}, "$index:$subcollection"); 158 } 159 } 160 } 161 162 # sort out language subindexes 163 if (defined $self->{'collect_cfg'}->{'languages'}) { 164 my $indexes = $self->{'collect_cfg'}->{'indexes'}; 165 $self->{'collect_cfg'}->{'indexes'} = []; 166 foreach my $language (@{$self->{'collect_cfg'}->{'languages'}}) { 167 foreach my $index (@$indexes) { 168 if (defined ($self->{'collect_cfg'}->{'indexsubcollections'})) { 169 push (@{$self->{'collect_cfg'}->{'indexes'}}, "$index:$language"); 170 } 171 else { # add in an empty subcollection field 172 push (@{$self->{'collect_cfg'}->{'indexes'}}, "$index\:\:$language"); 173 174 } 175 } 176 } 177 } 178 179 # make sure that the same index isn't specified more than once 180 my %tmphash = (); 181 my @tmparray = @{$self->{'collect_cfg'}->{'indexes'}}; 182 $self->{'collect_cfg'}->{'indexes'} = []; 183 foreach my $i (@tmparray) { 184 if (!defined ($tmphash{$i})) { 185 push (@{$self->{'collect_cfg'}->{'indexes'}}, $i); 186 $tmphash{$i} = 1; 187 } 188 } 189 110 my $self = new basebuilder (@_); 111 $self = bless $self, $class; 112 113 $self->{'indexfieldmap'} = \%static_indexfield_map; 190 114 191 115 # get the levels (Section, Paragraph) for indexing and compression … … 212 136 } 213 137 214 print $outhandle "doclevel = ". $self->{'doc_level'}."\n";215 # get the list of plugins for this collection216 217 #build up the extra global options for the plugins218 my @global_opts = ();219 if (defined $self->{'collect_cfg'}->{'separate_cjk'} && $self->{'collect_cfg'}->{'separate_cjk'} =~ /^true$/i) {220 push @global_opts, "-separate_cjk";221 }222 223 my $plugins = [];224 if (defined $self->{'collect_cfg'}->{'plugin'}) {225 $plugins = $self->{'collect_cfg'}->{'plugin'};226 }227 228 # load all the plugins229 $self->{'pluginfo'} = &plugin::load_plugins ($plugins, $verbosity, $outhandle, \@global_opts);230 if (scalar(@{$self->{'pluginfo'}}) == 0) {231 print $outhandle "No plugins were loaded.\n";232 die "\n";233 }234 235 # get the list of classifiers for this collection236 my $classifiers = [];237 if (defined $self->{'collect_cfg'}->{'classify'}) {238 $classifiers = $self->{'collect_cfg'}->{'classify'};239 }240 241 # load all the classifiers242 $self->{'classifiers'} = &classify::load_classifiers ($classifiers, $build_dir, $outhandle);243 244 # load up any dontgdbm fields245 $self->{'dontgdbm'} = {};246 if (defined ($self->{'collect_cfg'}->{'dontgdbm'})) {247 foreach my $dg (@{$self->{'collect_cfg'}->{'dontgdbm'}}) {248 $self->{'dontgdbm'}->{$dg} = 1;249 }250 }251 252 # load up the document processor for building253 # if a buildproc class has been created for this collection, use it254 # otherwise, use the mgpp buildproc255 my ($buildprocdir, $buildproctype);256 if (-e "$ENV{'GSDLCOLLECTDIR'}/perllib/${collection}buildproc.pm") {257 $buildprocdir = "$ENV{'GSDLCOLLECTDIR'}/perllib";258 $buildproctype = "${collection}buildproc";259 } else {260 $buildprocdir = "$ENV{'GSDLHOME'}/perllib";261 $buildproctype = "mgppbuildproc";262 }263 require "$buildprocdir/$buildproctype.pm";264 265 eval("\$self->{'buildproc'} = new $buildproctype(\$collection, " .266 "\$source_dir, \$build_dir, \$keepold, \$verbosity, \$outhandle)");267 die "$@" if $@;268 269 138 $self->{'buildtype'} = "mgpp"; 270 139 … … 272 141 } 273 142 274 sub init { 275 my $self = shift (@_); 276 277 if (!$self->{'debug'} && !$self->{'keepold'}) { 278 # remove any old builds 279 &util::rm_r($self->{'build_dir'}); 280 &util::mk_all_dir($self->{'build_dir'}); 281 282 # make the text directory 283 my $textdir = "$self->{'build_dir'}/text"; 284 &util::mk_all_dir($textdir); 285 } 286 } 287 288 sub set_strip_html { 289 my $self = shift (@_); 290 my ($strip) = @_; 291 292 $self->{'strip_html'} = $strip; 293 $self->{'buildproc'}->set_strip_html($strip); 143 sub generate_index_list { 144 my $self = shift (@_); 145 146 # sort out the indexes 147 #indexes are specified with spaces, but we put them into one index 148 my $indexes = $self->{'collect_cfg'}->{'indexes'}; 149 $self->{'collect_cfg'}->{'indexes'} = []; 150 push (@{$self->{'collect_cfg'}->{'indexes'}}, join(',', @$indexes)); 151 } 152 153 sub default_buildproc { 154 my $self = shift (@_); 155 156 return "mgppbuildproc"; 294 157 } 295 158 … … 423 286 } 424 287 425 sub want_built { 426 my $self = shift (@_); 427 my ($index) = @_; 428 429 if (defined ($self->{'collect_cfg'}->{'dontbuild'})) { 430 foreach my $checkstr (@{$self->{'collect_cfg'}->{'dontbuild'}}) { 431 if ($index =~ /^$checkstr$/) { 432 #push (@{$self->{'notbuilt'}}, $self->{'index_mapping'}->{$index}); 433 $self->{'notbuilt'}->{$index} = 1; 434 return 0; 435 } 436 } 437 } 438 439 return 1; 440 } 441 442 sub build_indexes { 443 my $self = shift (@_); 444 my ($indexname) = @_; 445 my $outhandle = $self->{'outhandle'}; 446 447 my $indexes = []; 448 if (defined $indexname && $indexname =~ /\w/) { 449 push @$indexes, $indexname; 450 } else { 451 $indexes = $self->{'collect_cfg'}->{'indexes'}; 452 } 453 454 # create the mapping between the index descriptions 455 # and their directory names (includes subcolls and langs) 456 $self->{'index_mapping'} = $self->create_index_mapping ($indexes); 457 458 # build each of the indexes 459 foreach my $index (@$indexes) { 460 if ($self->want_built($index)) { 461 print $outhandle "\n*** building index $index in subdirectory " . 462 "$self->{'index_mapping'}->{$index}\n" if ($self->{'verbosity'} >= 1); 463 print STDERR "<Stage name='Index' source='$index'>\n" if $self->{'gli'}; 464 $self->build_index($index); 465 } else { 466 print $outhandle "\n*** ignoring index $index\n" if ($self->{'verbosity'} >= 1); 467 } 468 } 469 288 289 sub build_indexes_extra { 290 my $self = shift(@_); 470 291 #define the final field lists 471 292 $self->make_final_field_list(); 472 473 } 293 } 474 294 475 295 # creates directory names for each of the index descriptions … … 545 365 } 546 366 547 # returns a processed version of a field.548 # if the field has only one component the processed549 # version will contain the first character and next consonant550 # of that componant - otherwise it will contain the first551 # character of the first two components552 sub process_field {553 my $self = shift (@_);554 my ($field) = @_;555 556 return "" unless (defined ($field) && $field =~ /\w/);557 558 my @components = split /,/, $field;559 if (scalar @components >= 2) {560 splice (@components, 2);561 map {s/^(.).*$/$1/;} @components;562 return join("", @components);563 } else {564 my ($a, $b) = $field =~ /^(.).*?([bcdfghjklmnpqrstvwxyz])/i;565 ($a, $b) = $field =~ /^(.)(.)/ unless defined $a && defined $b;566 return "$a$b";567 }568 }569 570 367 sub make_unique { 571 368 my $self = shift (@_); … … 583 380 } 584 381 585 sub get_next_version {586 my $self = shift (@_);587 my ($nameref) = @_;588 my $num=0;589 if ($$nameref =~ /(\d\d)$/) {590 $num = $1; $num ++;591 $$nameref =~ s/\d\d$/$num/;592 } elsif ($$nameref =~ /(\d)$/) {593 $num = $1;594 if ($num == 9) {$$nameref =~ s/\d$/10/;}595 else {$num ++; $$nameref =~ s/\d$/$num/;}596 } else {597 $$nameref =~ s/.$/0/;598 }599 }600 382 601 383 sub build_index { … … 812 594 } 813 595 814 sub make_infodatabase { 815 my $self = shift (@_); 816 my $outhandle = $self->{'outhandle'}; 817 818 819 my $textdir = &util::filename_cat($self->{'build_dir'}, "text"); 820 my $assocdir = &util::filename_cat($self->{'build_dir'}, "assoc"); 821 &util::mk_all_dir ($textdir); 822 &util::mk_all_dir ($assocdir); 823 824 # get db name 825 my $dbext = ".bdb"; 826 $dbext = ".ldb" if &util::is_little_endian(); 827 my $fulldbname = &util::filename_cat ($textdir, "$self->{'collection'}$dbext"); 828 $fulldbname =~ s/\//\\/g if ($ENV{'GSDLOS'} =~ /^windows$/i); 829 830 my $exedir = "$ENV{'GSDLHOME'}/bin/$ENV{'GSDLOS'}"; 831 my $exe = &util::get_os_exe (); 832 my $txt2db_exe = &util::filename_cat($exedir, "txt2db$exe"); 833 834 # define the indexed field mapping if not already done so (ie if infodb called separately from build_index) 835 if (!defined $self->{'build_cfg'}) { 836 $self->read_final_field_list(); 837 } 838 print $outhandle "\n*** creating the info database and processing associated files\n" 839 if ($self->{'verbosity'} >= 1); 840 print STDERR "<Stage name='CreateInfoData'>\n" if $self->{'gli'}; 841 842 # init all the classifiers 843 &classify::init_classifiers ($self->{'classifiers'}); 844 845 # set up the document processor 846 my ($handle); 847 if ($self->{'debug'}) { 848 $handle = STDOUT; 849 } else { 850 if (!-e "$txt2db_exe" || !open (PIPEOUT, "| txt2db$exe \"$fulldbname\"")) { 851 print STDERR "<FatalError name='NoRunText2DB'/>\n</Stage>\n" if $self->{'gli'}; 852 die "mgppbuilder::make_infodatabase - couldn't run $txt2db_exe\n"; 853 } 854 $handle = mgppbuilder::PIPEOUT; 855 } 856 857 $self->{'buildproc'}->set_output_handle ($handle); 858 $self->{'buildproc'}->set_mode ('infodb'); 859 $self->{'buildproc'}->set_assocdir ($assocdir); 860 $self->{'buildproc'}->set_dontgdbm ($self->{'dontgdbm'}); 861 $self->{'buildproc'}->set_classifiers ($self->{'classifiers'}); 862 $self->{'buildproc'}->set_indexing_text (0); 863 $self->{'buildproc'}->set_store_text(1); 864 #$self->{'buildproc'}->set_indexfieldmap ($self->{'indexfieldmap'}); 865 866 # make_infodatabase does not support incremental build 867 # => full reset needed 868 $self->{'buildproc'}->zero_reset(); 596 597 sub output_collection_meta { 598 my $self = shift(@_); 599 my ($handle) = @_; 869 600 870 601 # do the collection info … … 873 604 # first do the collection meta stuff - everything without a dot 874 605 my $collmetadefined = 0; 606 my $metadata_entry; 875 607 if (defined $self->{'collect_cfg'}->{'collectionmeta'}) { 876 608 $collmetadefined = 1; 877 609 foreach my $cmeta (keys (%{$self->{'collect_cfg'}->{'collectionmeta'}})) { 878 610 next if ($cmeta =~ /^\./); # for now, ignore ones with dots 879 my ($metadata_entry)= $self->create_language_db_map($cmeta, $cmeta);611 $metadata_entry = $self->create_language_db_map($cmeta, $cmeta); 880 612 #write the entry to the file 881 613 print $handle $metadata_entry; … … 896 628 $collmeta = ".$longfield"; 897 629 if ($collmetadefined && defined $self->{'collect_cfg'}->{'collectionmeta'}->{$collmeta}) { 898 my$metadata_entry = $self->create_language_db_map($collmeta, $shortfield);630 $metadata_entry = $self->create_language_db_map($collmeta, $shortfield); 899 631 $field_entry .= $metadata_entry; 900 632 } else { #use the metadata names, or the text macros for allfields and textonly … … 917 649 my $levelid = $level_map{$level}; # find the actual value we used in the index 918 650 if ($collmetadefined && defined $self->{'collect_cfg'}->{'collectionmeta'}->{$collmeta}) { 919 my$metadata_entry = $self->create_language_db_map($collmeta, $levelid);651 $metadata_entry = $self->create_language_db_map($collmeta, $levelid); 920 652 $level_entry .= $metadata_entry; 921 653 } else { … … 955 687 # end the collection entry 956 688 print $handle "\n" . ('-' x 70) . "\n"; 957 958 &plugin::read ($self->{'pluginfo'}, $self->{'source_dir'}, 959 "", {}, $self->{'buildproc'}, $self->{'maxdocs'}, 0, $self->{'gli'}); 960 961 # output classification information 962 &classify::output_classify_info ($self->{'classifiers'}, $handle, 963 $self->{'remove_empty_classifications'}, 964 $self->{'gli'}); 965 966 #output doclist 967 my @doclist = $self->{'buildproc'}->get_doc_list(); 968 my $docs = join (";",@doclist); 969 print $handle "[browselist]\n"; 970 print $handle "<hastxt>0\n"; 971 print $handle "<childtype>VList\n"; 972 print $handle "<numleafdocs>" . ($#doclist+1) . "\n"; 973 print $handle "<thistype>Invisible\n"; 974 print $handle "<contains>$docs"; 975 print $handle "\n" . ('-' x 70) . "\n"; 976 close ($handle) if !$self->{'debug'}; 977 978 print STDERR "</Stage>\n" if $self->{'gli'}; 979 } 980 689 690 691 } 981 692 sub create_language_db_map { 982 693 my $self = shift (@_); … … 1020 731 1021 732 } 1022 sub collect_specific {1023 my $self = shift (@_);1024 }1025 733 1026 734 # at the end of building, we have an indexfieldmap with all the mappings, … … 1047 755 # those again. 1048 756 1049 foreach my $field (@{$self->{'collect_cfg'}->{'indexes'}}) { 757 my $field; 758 foreach $field (@{$self->{'collect_cfg'}->{'indexes'}}) { 1050 759 # remove subcoll stuff 1051 760 my $parts = $field; … … 1061 770 1062 771 #add all fields bit 1063 foreach my$field (@specifiedfieldorder) {772 foreach $field (@specifiedfieldorder) { 1064 773 if ($field eq "metadata") { 1065 774 foreach my $newfield (keys %{$self->{'buildproc'}->{'indexfields'}}) { … … 1114 823 1115 824 my $buildcfg = &colcfg::read_build_cfg( $buildconfigfile); 1116 825 my $field; 1117 826 if (defined $buildcfg->{'indexfields'}) { 1118 foreach my$field (@{$buildcfg->{'indexfields'}}) {827 foreach $field (@{$buildcfg->{'indexfields'}}) { 1119 828 push (@indexfields, "$field"); 1120 829 } … … 1122 831 1123 832 if (defined $buildcfg->{'indexfieldmap'}) { 1124 foreach my$field (@{$buildcfg->{'indexfieldmap'}}) {833 foreach $field (@{$buildcfg->{'indexfieldmap'}}) { 1125 834 push (@indexfieldmap, "$field"); 1126 835 my ($f, $v) = $field =~ /^(.*)\-\>(.*)$/; … … 1133 842 } 1134 843 1135 sub make_auxiliary_files { 844 845 sub write_cfg_file { 846 my $self = shift(@_); 847 my ($build_cfg) = @_; 848 849 # write out the build information 850 &cfgread::write_cfg_file("$self->{'build_dir'}/build.cfg", $build_cfg, 851 '^(builddate|buildtype|numdocs|numsections|numbytes|textlevel|indexstem|maxnumeric)$', 852 '^(indexmap|subcollectionmap|languagemap|indexfieldmap|notbuilt|indexfields|indexlevels|levelmap)$'); 853 854 } 855 856 sub build_cfg_extra { 1136 857 my $self = shift (@_); 1137 my ($index); 1138 1139 my $build_cfg = {}; 1140 # this already includes indexfieldmap and indexfields 1141 if (defined $self->{'build_cfg'}) { 1142 $build_cfg = $self->{'build_cfg'}; 1143 } 1144 1145 my $outhandle = $self->{'outhandle'}; 1146 print $outhandle "\n*** creating auxiliary files \n" if ($self->{'verbosity'} >= 1); 1147 print STDERR "<Stage name='CreatingAuxilary'>\n" if $self->{'gli'}; 1148 1149 # get the text directory 1150 &util::mk_all_dir ($self->{'build_dir'}); 1151 1152 # store the build date 1153 $build_cfg->{'builddate'} = time; 1154 $build_cfg->{'buildtype'} = $self->{'buildtype'}; 1155 $build_cfg->{'indexstem'} = $self->{'collection'}; 858 my ($build_cfg) = @_; 859 860 $build_cfg->{'numsections'} = $self->{'buildproc'}->get_num_sections(); 861 1156 862 # store the level info 1157 863 my @indexlevels = (); … … 1169 875 $build_cfg->{'textlevel'} = $level_map{'document'}; 1170 876 } 1171 # store the number of documents and number of bytes 1172 $build_cfg->{'numdocs'} = $self->{'buildproc'}->get_num_docs(); 1173 $build_cfg->{'numsections'} = $self->{'buildproc'}->get_num_sections(); 1174 $build_cfg->{'numbytes'} = $self->{'buildproc'}->get_num_bytes(); 1175 1176 # store the mapping between the index names and the directory names 1177 my @indexmap = (); 1178 foreach my $index (@{$self->{'index_mapping'}->{'indexmaporder'}}) { 1179 if (not defined ($self->{'notbuilt'}->{$index})) { 1180 push (@indexmap, "$index\-\>$self->{'index_mapping'}->{'indexmap'}->{$index}"); 1181 } 1182 } 1183 $build_cfg->{'indexmap'} = \@indexmap; 1184 1185 my @subcollectionmap = (); 1186 foreach my $subcollection (@{$self->{'index_mapping'}->{'subcollectionmaporder'}}) { 1187 push (@subcollectionmap, "$subcollection\-\>" . 1188 $self->{'index_mapping'}->{'subcollectionmap'}->{$subcollection}); 1189 } 1190 $build_cfg->{'subcollectionmap'} = \@subcollectionmap if scalar (@subcollectionmap); 1191 1192 my @languagemap = (); 1193 foreach my $language (@{$self->{'index_mapping'}->{'languagemaporder'}}) { 1194 push (@languagemap, "$language\-\>" . 1195 $self->{'index_mapping'}->{'languagemap'}->{$language}); 1196 } 1197 $build_cfg->{'languagemap'} = \@languagemap if scalar (@languagemap); 1198 1199 my @notbuilt = (); 1200 foreach my $nb (keys %{$self->{'notbuilt'}}) { 1201 push (@notbuilt, $nb); 1202 } 1203 $build_cfg->{'notbuilt'} = \@notbuilt if scalar (@notbuilt); 1204 1205 # write out the build information 1206 &cfgread::write_cfg_file("$self->{'build_dir'}/build.cfg", $build_cfg, 1207 '^(builddate|buildtype|numdocs|numsections|numbytes|textlevel|indexstem)$', 1208 '^(indexmap|subcollectionmap|languagemap|indexfieldmap|notbuilt|indexfields|indexlevels|levelmap)$'); 1209 1210 print STDERR "</Stage>\n" if $self->{'gli'}; 1211 } 1212 1213 sub deinit { 1214 my $self = shift (@_); 1215 1216 &plugin::deinit($self->{'pluginfo'},$self->{'buildproc'}); 1217 } 1218 1219 sub print_stats { 1220 my $self = shift (@_); 1221 1222 my $outhandle = $self->{'outhandle'}; 1223 my $indexing_text = $self->{'buildproc'}->get_indexing_text(); 1224 my $index = $self->{'buildproc'}->get_index(); 1225 my $num_bytes = $self->{'buildproc'}->get_num_bytes(); 1226 my $num_processed_bytes = $self->{'buildproc'}->get_num_processed_bytes(); 1227 1228 if ($indexing_text) { 1229 print $outhandle "Stats (Creating index $index)\n"; 1230 } else { 1231 print $outhandle "Stats (Compressing text from $index)\n"; 1232 } 1233 print $outhandle "Total bytes in collection: $num_bytes\n"; 1234 print $outhandle "Total bytes in $index: $num_processed_bytes\n"; 1235 1236 if ($num_processed_bytes < 50 && ($indexing_text || !$self->{'no_text'})) { 1237 1238 if ($self->{'keepold'}) { 1239 if ($num_processed_bytes == 0) { 1240 if ($indexing_text) { 1241 print $outhandle "No additional text was added to $index\n"; 1242 } elsif (!$self->{'no_text'}) { 1243 print $outhandle "No additional text was compressed\n"; 1244 } 1245 } 1246 } 1247 else { 1248 print $outhandle "***************\n"; 1249 if ($indexing_text) { 1250 print $outhandle "WARNING: There is very little or no text to process for $index\n"; 1251 } elsif (!$self->{'no_text'}) { 1252 print $outhandle "WARNING: There is very little or no text to compress\n"; 1253 } 1254 print $outhandle " Was this your intention?\n"; 1255 print $outhandle "***************\n"; 1256 } 1257 1258 } 1259 877 1260 878 } 1261 879
Note:
See TracChangeset
for help on using the changeset viewer.