root/gsdl/trunk/bin/script/import.pl @ 18456

Revision 18456, 20.1 KB (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
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 browser.