source: gsdl/trunk/bin/script/import.pl@ 18456

Last change on this file since 18456 was 18456, checked in by davidb, 12 years ago

Additions to support the deleting of documents from the index. Only works for indexers that support incremental building, e.g. lucene

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 20.1 KB
Line 
1#!/usr/bin/perl -w
2
3###########################################################################
4#
5# import.pl --
6# A component of the Greenstone digital library software
7# from the New Zealand Digital Library Project at the
8# University of Waikato, New Zealand.
9#
10# Copyright (C) 1999 New Zealand Digital Library Project
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25#
26###########################################################################
27
28
29# This program will import a number of files into a particular collection
30
31package import;
32
33BEGIN {
34 die "GSDLHOME not set\n" unless defined $ENV{'GSDLHOME'};
35 die "GSDLOS not set\n" unless defined $ENV{'GSDLOS'};
36 unshift (@INC, "$ENV{'GSDLHOME'}/perllib");
37 unshift (@INC, "$ENV{'GSDLHOME'}/perllib/cpan");
38 unshift (@INC, "$ENV{'GSDLHOME'}/perllib/plugins");
39 unshift (@INC, "$ENV{'GSDLHOME'}/perllib/plugouts");
40 unshift (@INC, "$ENV{'GSDLHOME'}/perllib/classify");
41
42 if (defined $ENV{'GSDLEXTS'}) {
43 my @extensions = split(/:/,$ENV{'GSDLEXTS'});
44 foreach my $e (@extensions) {
45 my $ext_prefix = "$ENV{'GSDLHOME'}/ext/$e";
46
47 unshift (@INC, "$ext_prefix/perllib");
48 unshift (@INC, "$ext_prefix/perllib/cpan");
49 unshift (@INC, "$ext_prefix/perllib/plugins");
50 unshift (@INC, "$ext_prefix/perllib/plugouts");
51 unshift (@INC, "$ext_prefix/perllib/classify");
52 }
53 }
54}
55
56use strict;
57no strict 'refs'; # allow filehandles to be variables and vice versa
58no strict 'subs'; # allow barewords (eg STDERR) as function arguments
59
60use arcinfo;
61use colcfg;
62use plugin;
63use plugout;
64use manifest;
65use inexport;
66use util;
67use scriptutil;
68use FileHandle;
69use gsprintf 'gsprintf';
70use printusage;
71use parse2;
72
73
74
75my $oidtype_list =
76 [ { 'name' => "hash",
77 'desc' => "{import.OIDtype.hash}" },
78 { 'name' => "assigned",
79 'desc' => "{import.OIDtype.assigned}" },
80 { 'name' => "incremental",
81 'desc' => "{import.OIDtype.incremental}" },
82 { 'name' => "dirname",
83 'desc' => "{import.OIDtype.dirname}" } ];
84
85
86# used to control output file format
87my $saveas_list =
88 [ { 'name' => "GreenstoneXML",
89 'desc' => "{export.saveas.GreenstoneXML}"},
90 { 'name' => "GreenstoneMETS",
91 'desc' => "{export.saveas.GreenstoneMETS}"},
92 ];
93
94
95# Possible attributes for each argument
96# name: The name of the argument
97# desc: A description (or more likely a reference to a description) for this argument
98# type: The type of control used to represent the argument. Options include: string, int, flag, regexp, metadata, language, enum etc
99# reqd: Is this argument required?
100# hiddengli: Is this argument hidden in GLI?
101# modegli: The lowest detail mode this argument is visible at in GLI
102
103my $saveas_argument
104 = { 'name' => "saveas",
105 'desc' => "{import.saveas}",
106 'type' => "enum",
107 'list' => $saveas_list,
108 'deft' => "GreenstoneXML",
109 'reqd' => "no",
110 'modegli' => "3" };
111
112
113my $arguments =
114 [
115 $saveas_argument,
116 { 'name' => "archivedir",
117 'desc' => "{import.archivedir}",
118 'type' => "string",
119 'reqd' => "no",
120 'hiddengli' => "yes" },
121 { 'name' => "importdir",
122 'desc' => "{import.importdir}",
123 'type' => "string",
124 'reqd' => "no",
125 'hiddengli' => "yes" },
126 { 'name' => "collectdir",
127 'desc' => "{import.collectdir}",
128 'type' => "string",
129 # parsearg left "" as default
130 #'deft' => &util::filename_cat ($ENV{'GSDLHOME'}, "collect"),
131 'deft' => "",
132 'reqd' => "no",
133 'hiddengli' => "yes" },
134 { 'name' => "site",
135 'desc' => "{import.site}",
136 'type' => "string",
137 'deft' => "",
138 'reqd' => "no",
139 'hiddengli' => "yes" },
140 { 'name' => "manifest",
141 'desc' => "{import.manifest}",
142 'type' => "string",
143 'deft' => "",
144 'reqd' => "no",
145 'hiddengli' => "yes" },
146 { 'name' => "debug",
147 'desc' => "{import.debug}",
148 'type' => "flag",
149 'reqd' => "no",
150 'hiddengli' => "yes" },
151 { 'name' => "faillog",
152 'desc' => "{import.faillog}",
153 'type' => "string",
154 # parsearg left "" as default
155 #'deft' => &util::filename_cat("<collectdir>", "colname", "etc", "fail.log"),
156 'deft' => "",
157 'reqd' => "no",
158 'modegli' => "4" },
159 { 'name' => "incremental",
160 'desc' => "{import.incremental}",
161 'type' => "flag",
162 'hiddengli' => "yes" },
163 { 'name' => "keepold",
164 'desc' => "{import.keepold}",
165 'type' => "flag",
166 'reqd' => "no",
167 'hiddengli' => "yes" },
168 { 'name' => "removeold",
169 'desc' => "{import.removeold}",
170 'type' => "flag",
171 'reqd' => "no",
172 'hiddengli' => "yes" },
173 { 'name' => "language",
174 'desc' => "{scripts.language}",
175 'type' => "string",
176 'reqd' => "no",
177 'hiddengli' => "yes" },
178 { 'name' => "maxdocs",
179 'desc' => "{import.maxdocs}",
180 'type' => "int",
181 'reqd' => "no",
182 # parsearg left "" as default
183 #'deft' => "-1",
184 'range' => "1,",
185 'modegli' => "1" },
186 # don't set the default to hash - want to allow this to come from
187 # entry in collect.cfg but want to override it here
188 { 'name' => "OIDtype",
189 'desc' => "{import.OIDtype}",
190 'type' => "enum",
191 'list' => $oidtype_list,
192 # parsearg left "" as default
193 #'deft' => "hash",
194 'reqd' => "no",
195 'modegli' => "2" },
196 { 'name' => "OIDmetadata",
197 'desc' => "{import.OIDmetadata}",
198 'type' => "metadata",
199 'deft' => "dc.Identifier",
200 'reqd' => "no",
201 'modegli' => "2" },
202 { 'name' => "out",
203 'desc' => "{import.out}",
204 'type' => "string",
205 'deft' => "STDERR",
206 'reqd' => "no",
207 'hiddengli' => "yes" },
208 { 'name' => "sortmeta",
209 'desc' => "{import.sortmeta}",
210 'type' => "metadata",
211 'reqd' => "no",
212 'modegli' => "3" },
213 { 'name' => "reversesort",
214 'desc' => "{import.reversesort}",
215 'type' => "flag",
216 'reqd' => "no",
217 'modegli' => "3" },
218 { 'name' => "removeprefix",
219 'desc' => "{BasClas.removeprefix}",
220 'type' => "regexp",
221 'deft' => "",
222 'reqd' => "no",
223 'modegli' => "3" },
224 { 'name' => "removesuffix",
225 'desc' => "{BasClas.removesuffix}",
226 'type' => "regexp",
227 'deft' => "",
228 'reqd' => "no",
229 'modegli' => "3" },
230 { 'name' => "groupsize",
231 'desc' => "{import.groupsize}",
232 'type' => "int",
233 'deft' => "1",
234 'reqd' => "no",
235 'modegli' => "3" },
236 { 'name' => "gzip",
237 'desc' => "{import.gzip}",
238 'type' => "flag",
239 'reqd' => "no",
240 'modegli' => "4" },
241 { 'name' => "statsfile",
242 'desc' => "{import.statsfile}",
243 'type' => "string",
244 'deft' => "STDERR",
245 'reqd' => "no",
246 'hiddengli' => "yes" },
247 { 'name' => "verbosity",
248 'desc' => "{import.verbosity}",
249 'type' => "int",
250 'range' => "0,",
251 # parsearg left "" as default
252 #'deft' => "2",
253 'reqd' => "no",
254 'modegli' => "4" },
255 { 'name' => "gli",
256 'desc' => "{scripts.gli}",
257 'type' => "flag",
258 'reqd' => "no",
259 'hiddengli' => "yes" },
260 { 'name' => "xml",
261 'desc' => "{scripts.xml}",
262 'type' => "flag",
263 'reqd' => "no",
264 'hiddengli' => "yes" }];
265
266my $options = { 'name' => "import.pl",
267 'desc' => "{import.desc}",
268 'args' => $arguments };
269
270
271&main();
272
273sub main {
274 # params
275 my ($language, $verbosity, $debug,
276 $collectdir, $importdir, $archivedir, $site, $manifest,
277 $incremental, $keepold, $removeold,
278 $saveas,
279 $OIDtype, $OIDmetadata,
280 $maxdocs, $statsfile,
281 $out, $faillog, $gli,
282 $gzip, $groupsize,
283 $sortmeta, $reversesort, $removeprefix, $removesuffix
284 );
285
286 my $xml = 0;
287
288 # other vars
289 my ($configfilename, $collection, $collectcfg,
290 $arcinfo_doc_filename, $arcinfo_src_filename, $archive_info,
291 $gs_mode,
292 $processor, $pluginfo);
293
294 my $service = "import";
295
296 my $hashParsingResult = {};
297 # general options available to all plugins
298 my $intArgLeftinAfterParsing = parse2::parse(\@ARGV,$arguments,$hashParsingResult,"allow_extra_options");
299 # Parse returns -1 if something has gone wrong
300 if ($intArgLeftinAfterParsing == -1)
301 {
302 &PrintUsage::print_txt_usage($options, "{import.params}");
303 die "\n";
304 }
305
306 foreach my $strVariable (keys %$hashParsingResult)
307 {
308 eval "\$$strVariable = \$hashParsingResult->{\"\$strVariable\"}";
309 }
310
311 # If $language has been specified, load the appropriate resource bundle
312 # (Otherwise, the default resource bundle will be loaded automatically)
313 if ($language && $language =~ /\S/) {
314 &gsprintf::load_language_specific_resource_bundle($language);
315 }
316
317 if ($xml) {
318 &PrintUsage::print_xml_usage($options);
319 print "\n";
320 return;
321 }
322
323 if ($gli) { # the gli wants strings to be in UTF-8
324 &gsprintf::output_strings_in_UTF8;
325 }
326
327 # now check that we had exactly one leftover arg, which should be
328 # the collection name. We don't want to do this earlier, cos
329 # -xml arg doesn't need a collection name
330 # Or if the user specified -h, then we output the usage also
331 if ($intArgLeftinAfterParsing != 1 || (@ARGV && $ARGV[0] =~ /^\-+h/))
332 {
333 &PrintUsage::print_txt_usage($options, "{import.params}");
334 die "\n";
335 }
336
337 my $close_out = 0;
338 if ($out !~ /^(STDERR|STDOUT)$/i) {
339 open (OUT, ">$out") ||
340 (&gsprintf(STDERR, "{common.cannot_open_output_file}: $!\n", $out) && die);
341 $out = 'import::OUT';
342 $close_out = 1;
343 }
344 $out->autoflush(1);
345
346 # get and check the collection name
347 if (($collection = &colcfg::use_collection($site, @ARGV, $collectdir)) eq "") {
348 &PrintUsage::print_txt_usage($options, "{import.params}");
349 die "\n";
350 }
351
352 # add collection's perllib dir into include path in
353 # case we have collection specific modules
354 unshift (@INC, "$ENV{'GSDLCOLLECTDIR'}/perllib");
355
356 # check that we can open the faillog
357 if ($faillog eq "") {
358 $faillog = &util::filename_cat($ENV{'GSDLCOLLECTDIR'}, "etc", "fail.log");
359 }
360 open (FAILLOG, ">$faillog") ||
361 (&gsprintf(STDERR, "{import.cannot_open_fail_log}\n", $faillog) && die);
362
363
364 my $faillogname = $faillog;
365 $faillog = 'import::FAILLOG';
366 $faillog->autoflush(1);
367
368 # Read in the collection configuration file.
369 ($configfilename, $gs_mode) = &colcfg::get_collect_cfg_name($out);
370
371 if ($gs_mode eq "gs2") {
372 $collectcfg = &colcfg::read_collect_cfg ($configfilename);
373 } elsif ($gs_mode eq "gs3") {
374 $collectcfg = &colcfg::read_collection_cfg_xml ($configfilename);
375 }
376
377 if (defined $collectcfg->{'importdir'} && $importdir eq "") {
378 $importdir = $collectcfg->{'importdir'};
379 }
380 if (defined $collectcfg->{'archivedir'} && $archivedir eq "") {
381 $archivedir = $collectcfg->{'archivedir'};
382 }
383 # fill in the default import and archives directories if none
384 # were supplied, turn all \ into / and remove trailing /
385 $importdir = &util::filename_cat ($ENV{'GSDLCOLLECTDIR'}, "import") if $importdir eq "";
386 $importdir =~ s/[\\\/]+/\//g;
387 $importdir =~ s/\/$//;
388 if (!-e $importdir) {
389 &gsprintf($out, "{import.no_import_dir}\n\n", $importdir);
390 die "\n";
391 }
392
393 $archivedir = &util::filename_cat ($ENV{'GSDLCOLLECTDIR'}, "archives") if $archivedir eq "";
394 $archivedir =~ s/[\\\/]+/\//g;
395 $archivedir =~ s/\/$//;
396
397 my $plugins = [];
398 if (defined $collectcfg->{'plugin'}) {
399 $plugins = $collectcfg->{'plugin'};
400 }
401 #some global options for the plugins
402 my @global_opts = ();
403
404 if ($verbosity !~ /\d+/) {
405 if (defined $collectcfg->{'verbosity'} && $collectcfg->{'verbosity'} =~ /\d+/) {
406 $verbosity = $collectcfg->{'verbosity'};
407 } else {
408 $verbosity = 2; # the default
409 }
410 }
411 if (defined $collectcfg->{'manifest'} && $manifest eq "") {
412 $manifest = $collectcfg->{'manifest'};
413 }
414
415 if (defined $collectcfg->{'gzip'} && !$gzip) {
416 if ($collectcfg->{'gzip'} =~ /^true$/i) {
417 $gzip = 1;
418 }
419 }
420
421 if ($maxdocs !~ /\-?\d+/) {
422 if (defined $collectcfg->{'maxdocs'} && $collectcfg->{'maxdocs'} =~ /\-?\d+/) {
423 $maxdocs = $collectcfg->{'maxdocs'};
424 } else {
425 $maxdocs = -1; # the default
426 }
427 }
428 if ($groupsize == 1) {
429 if (defined $collectcfg->{'groupsize'} && $collectcfg->{'groupsize'} =~ /\d+/) {
430 $groupsize = $collectcfg->{'groupsize'};
431 }
432 }
433
434 if (!defined $OIDtype || ($OIDtype !~ /^(hash|incremental|assigned|dirname)$/ )) {
435 if (defined $collectcfg->{'OIDtype'} && $collectcfg->{'OIDtype'} =~ /^(hash|incremental|assigned|dirname)$/) {
436 $OIDtype = $collectcfg->{'OIDtype'};
437 } else {
438 $OIDtype = "hash"; # the default
439 }
440 }
441
442 if (defined $collectcfg->{'sortmeta'} && (!defined $sortmeta || $sortmeta eq "")) {
443 $sortmeta = $collectcfg->{'sortmeta'};
444 }
445 # sortmeta cannot be used with group size
446 $sortmeta = undef unless defined $sortmeta && $sortmeta =~ /\S/;
447 if (defined $sortmeta && $groupsize > 1) {
448 &gsprintf($out, "{import.cannot_sort}\n\n");
449 $sortmeta = undef;
450 }
451
452 if (defined $sortmeta) {
453 if (defined $collectcfg->{'reversesort'} && $collectcfg->{'reversesort'} =~ /^true$/i) {
454 $reversesort = 1;
455 }
456 } else {
457 # reversesort only valid with sortmeta
458 $reversesort = 0;
459 }
460 if (defined $collectcfg->{'removeprefix'} && $removeprefix eq "") {
461 $removeprefix = $collectcfg->{'removeprefix'};
462 }
463
464 if (defined $collectcfg->{'removesuffix'} && $removesuffix eq "") {
465 $removesuffix = $collectcfg->{'removesuffix'};
466 }
467 if (defined $collectcfg->{'debug'} && $collectcfg->{'debug'} =~ /^true$/i) {
468 $debug = 1;
469 }
470 if (defined $collectcfg->{'gli'} && $collectcfg->{'gli'} =~ /^true$/i) {
471 $gli = 1;
472 }
473 $gli = 0 unless defined $gli;
474
475 # check keepold and removeold
476 ($removeold, $keepold, $incremental) = &scriptutil::check_removeold_and_keepold($removeold, $keepold, $incremental, "archives", $collectcfg);
477
478
479 print STDERR "<Import>\n" if $gli;
480
481 my $manifest_lookup = new manifest();
482 if ($manifest ne "") {
483 my $manifest_filename = $manifest;
484
485 if ($manifest_filename !~ m/^[\\\/]/) {
486 $manifest_filename = &util::filename_cat ($ENV{'GSDLCOLLECTDIR'}, $manifest_filename);
487 }
488
489 $manifest =~ s/[\\\/]+/\//g;
490 $manifest =~ s/\/$//;
491
492 $manifest_lookup->parse($manifest_filename);
493 }
494
495
496 # load all the plugins
497 $pluginfo = &plugin::load_plugins ($plugins, $verbosity, $out, $faillog, \@global_opts, $incremental);
498 if (scalar(@$pluginfo) == 0) {
499 &gsprintf($out, "{import.no_plugins_loaded}\n");
500 die "\n";
501 }
502
503 # remove the old contents of the archives directory (and tmp directory) if needed
504 if ($removeold) {
505 if (-e $archivedir) {
506 &gsprintf($out, "{import.removing_archives}\n");
507 &util::rm_r ($archivedir);
508 }
509 my $tmpdir = &util::filename_cat ($ENV{'GSDLCOLLECTDIR'}, "tmp");
510 $tmpdir =~ s/[\\\/]+/\//g;
511 $tmpdir =~ s/\/$//;
512 if (-e $tmpdir) {
513 &gsprintf($out, "{import.removing_tmpdir}\n");
514 &util::rm_r ($tmpdir);
515 }
516 }
517 # create the archives dir if needed
518 &util::mk_all_dir($archivedir);
519
520 # read the archive information file
521## $arcinfo_doc_filename = &util::filename_cat ($archivedir, "archives.inf");
522
523 my $db_ext = &util::is_little_endian() ? ".ldb" : ".bdb";
524 my $doc_db = "archiveinf-doc$db_ext";
525 my $src_db = "archiveinf-src$db_ext";
526 $arcinfo_doc_filename = &util::filename_cat ($archivedir, $doc_db);
527 $arcinfo_src_filename = &util::filename_cat ($archivedir, $src_db);
528
529 $archive_info = new arcinfo ();
530 $archive_info->load_info ($arcinfo_doc_filename);
531 if ($reversesort) {
532 $archive_info->reverse_sort();
533 }
534
535 if ($manifest eq "") {
536 # Load in list of files in import folder from last import (if present)
537 $archive_info->load_prev_import_filelist ($arcinfo_src_filename);
538 }
539
540 ####Use Plugout####
541 my ($plugout);
542 if (defined $collectcfg->{'plugout'} && $collectcfg->{'plugout'} =~ /^(GreenstoneXML|GreenstoneMETS)Plugout/) {
543 $plugout = $collectcfg->{'plugout'};
544 }
545 else{
546 if ($saveas !~ /^(GreenstoneXML|GreenstoneMETS)$/) {
547 push @$plugout,"GreenstoneXMLPlugout";
548 }
549 else{
550 push @$plugout,$saveas."Plugout";
551 }
552 }
553
554 push @$plugout,("-output_info",$archive_info) if (defined $archive_info);
555 push @$plugout,("-verbosity",$verbosity) if (defined $verbosity);
556 push @$plugout,("-gzip_output") if ($gzip);
557 push @$plugout,("-group_size",$groupsize) if (defined $groupsize);
558 push @$plugout,("-output_handle",$out) if (defined $out);
559 push @$plugout,("-debug") if ($debug);
560
561 $processor = &plugout::load_plugout($plugout);
562 $processor->setoutputdir ($archivedir);
563 $processor->set_sortmeta ($sortmeta, $removeprefix, $removesuffix) if defined $sortmeta;
564 $processor->set_OIDtype ($OIDtype, $OIDmetadata);
565
566 &plugin::begin($pluginfo, $importdir, $processor, $maxdocs, $gli);
567
568 if ($manifest eq "") {
569 # process the import directory
570 my $block_hash = {};
571 my $metadata = {};
572 # gobal blocking pass may set up some metadata
573 &plugin::file_block_read($pluginfo, $importdir, "", $block_hash, $metadata, $gli);
574 # Can now work out which files were new, already existed, and have
575 # been deleted
576
577 &inexport::new_vs_old_import_diff($archive_info,$block_hash,$importdir);
578
579 my @deleted_files = sort keys %{$block_hash->{'deleted_files'}};
580 if (scalar(@deleted_files>0)) {
581 print STDERR "Delete files:\n ";
582 print STDERR join("\n ",@deleted_files), "\n";
583 }
584
585 my @new_files = sort keys %{$block_hash->{'new_files'}};
586 if (scalar(@new_files>0)) {
587 print STDERR "New files:\n ";
588 print STDERR join("\n ",@new_files), "\n";
589 }
590
591 &inexport::mark_docs_for_deletion($archive_info,\@deleted_files,$archivedir,
592 $verbosity);
593
594 &plugin::read ($pluginfo, $importdir, "", $block_hash, $metadata, $processor, $maxdocs, 0, $gli);
595 }
596 else
597 {
598 # process any files marked for importing
599 foreach my $file (keys %{$manifest_lookup->{'index'}}) {
600 &plugin::read ($pluginfo, $importdir, $file, {}, {}, $processor, $maxdocs, 0, $gli);
601 }
602
603 my @deleted_files = keys %{$manifest_lookup->{'delete'}};
604
605 &inexport::mark_docs_for_deletion($archive_info,\@deleted_files,$archivedir);
606 }
607
608 &plugin::end($pluginfo, $processor);
609
610 &plugin::deinit($pluginfo, $processor);
611
612 # write out the archive information file
613 $processor->close_file_output() if $groupsize > 1;
614 $processor->close_group_output() if $processor->is_group();
615
616# The following 'if' statement is in the export.pl version of the script,
617# The reason for the 'if' statement is now given in export.pl
618# Unclear at this point if the same should be done here
619## if (($saveas =~ m/^.*METS$/) || ($saveas eq "MARC")) {
620 # Not all export types need this (e.g. DSpace)
621
622 # should we still do this in debug mode??
623
624 # for backwards compatability with archvies.inf file
625 if ($arcinfo_doc_filename =~ m/\.inf$/) {
626 $archive_info->save_info($arcinfo_doc_filename);
627 }
628
629## }
630
631 # write out import stats
632 my $close_stats = 0;
633 if ($statsfile !~ /^(STDERR|STDOUT)$/i) {
634 if (open (STATS, ">$statsfile")) {
635 $statsfile = 'import::STATS';
636 $close_stats = 1;
637 } else {
638 &gsprintf($out, "{import.cannot_open_stats_file}", $statsfile);
639 &gsprintf($out, "{import.stats_backup}\n");
640 $statsfile = 'STDERR';
641 }
642 }
643
644 &gsprintf($out, "\n");
645 &gsprintf($out, "*********************************************\n");
646 &gsprintf($out, "{import.complete}\n");
647 &gsprintf($out, "*********************************************\n");
648
649 &plugin::write_stats($pluginfo, $statsfile, $faillogname, $gli);
650 if ($close_stats) {
651 close STATS;
652 }
653
654 close OUT if $close_out;
655 close FAILLOG;
656}
Note: See TracBrowser for help on using the repository browser.