home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
FAQ
/
cgi-bin
/
discus4_00
/
source
/
fcn-logs.pl
< prev
next >
Wrap
Text File
|
2009-11-06
|
21KB
|
714 lines
# FILE: fcn-logs.pl
# DESCRIPTION: Manipulate ###-log.txt and ###-search.txt
#-------------------------------------------------------------------------------
# DISCUS COPYRIGHT NOTICE
#
# Discus is copyright (c) 2002 by DiscusWare, LLC, all rights reserved.
# The use of Discus is governed by the Discus License Agreement which is
# available from the Discus WWW site at:
# http://www.discusware.com/discus/license
#
# Pursuant to the Discus License Agreement, this copyright notice may not be
# removed or altered in any way.
#-------------------------------------------------------------------------------
use strict;
use vars qw($GLOBAL_OPTIONS $DCONF $PARAMS);
###
### Which mini-logs should be kept?
###
my @mini_logs = (1, 7);
sub get_mini_logs {
return @mini_logs;
}
###
### create_blank_mini_logs
###
### Create blank mini logs
###
sub create_blank_mini_logs {
my ($topic) = @_;
dreq("adm-tpc");
foreach my $m (@mini_logs) {
topic_error_touch("$DCONF->{admin_dir}/msg_index/$topic-log-$m.txt");
}
}
###
### minilog_choose_log
###
### Chooses which minilog to use when performing a search
###
sub minilog_choose_log {
my ($cutoff, $timecache) = @_;
return undef if $cutoff == 0;
$timecache = time if ! defined $timecache;
my $days = int(($timecache - $cutoff)/86400);
$days += 1 if ($timecache - $cutoff) % 86400 != 0;
foreach my $ml (sort { $a <=> $b } @mini_logs) {
return $ml if $ml >= $days;
}
return undef;
}
###
### minilog_maintenance
###
### Removes outdated entries from recent posting log file, or creates new
### minilogs if one or more is missing (usually called when operations unrelated
### to logs are running, so it sequentially updates the appropriate logs)
###
sub minilog_maintenance {
return undef if $GLOBAL_OPTIONS->{database};
my ($threshold, $topic) = @_;
if ($topic == 0) {
my @dir = ();
my $d = {};
opendir(DIR, "$DCONF->{admin_dir}/msg_index");
while (my $dir = readdir(DIR)) {
if ($dir =~ m|^(\d+)-log\.txt$|) {
$d->{$1} += scalar(@mini_logs);
} elsif ($dir =~ m|^(\d+)-log-(\d+)\.txt$|) {
$d->{$1} -= 1;
}
next if $dir !~ m|^(\d+)-log-(\d+)\.txt$|;
next if $2 != $threshold && $threshold;
my @s = stat "$DCONF->{admin_dir}/msg_index/$dir";
push @dir, { file => $dir, topic => $1, mtime => $s[9], thres => $2 };
}
closedir(DIR);
foreach my $k (keys(%{ $d })) {
next if $d->{$k} == 0;
my $rv = create_minilog($k);
return $rv if $rv == 1;
}
my $one = (sort { $a->{mtime} <=> $b->{mtime} } @dir)[0];
$topic = $one->{topic};
$threshold = $one->{thres};
}
return undef if $topic == 0;
return undef if ! -e "$DCONF->{admin_dir}/msg_index/$topic-log-$threshold.txt";
my $cutoff = time - (24*60*60*$threshold);
my $fsize = 0;
if (open(FILE, "< $DCONF->{admin_dir}/msg_index/$topic-log-$threshold.txt")) {
if (open(FILE2, "> $DCONF->{admin_dir}/msg_index/$topic-log-$threshold.NEW")) {
while (<FILE>) {
my $h = log_line_to_hash($_);
if ($h->{time} >= $cutoff) {
print FILE2 $_;
$fsize += length($_);
}
}
close (FILE2);
close (FILE);
} else {
log_error("fcn-logs.pl", "minilog_remove_outdated", "Could not open new minilog for writing [$topic]: $!");
close (FILE);
return undef;
}
} else {
log_error("fcn-logs.pl", "minilog_remove_outdated", "Could not open old minilog for reading [$topic]: $!");
return undef;
}
rename_file("$DCONF->{admin_dir}/msg_index/$topic-log-$threshold.NEW", $fsize, "$DCONF->{admin_dir}/msg_index/$topic-log-$threshold.txt");
}
###
### create_minilog
###
### Creates a "minilog" - portion of a posting log of messages posted after a given
### time, so searching goes much faster.
###
sub create_minilog {
my ($topic) = @_;
return undef if ! -e "$DCONF->{admin_dir}/msg_index/$topic-log.txt";
my @ml = sort { $a <=> $b } @mini_logs;
my $max = pop @ml;
my $time = time;
my @d = ();
if (open(FILE, "< $DCONF->{admin_dir}/msg_index/$topic-log.txt")) {
my $t = $ml[$#ml];
open (OUT, "> $DCONF->{admin_dir}/msg_index/$topic-log-$max.txt");
while (<FILE>) {
my $h = log_line_to_hash($_);
next if $h->{'time'} < $time-(24*60*60*$max);
print OUT $_;
next if $h->{'time'} < $time-(24*60*60*$t);
push @d, $h;
}
close (OUT);
close (FILE);
chmod (oct($DCONF->{perms0666}), "$DCONF->{admin_dir}/msg_index/$topic-log-$max.txt");
}
while (my $x = pop @ml) {
my @k = ();
my $t = $ml[$#ml];
open (OUT, "> $DCONF->{admin_dir}/msg_index/$topic-log-$x.txt");
foreach my $d (@d) {
print OUT log_hash_to_line($d);
next if $d->{'time'} < $time-(24*60*60*$t);
push @k, $d;
}
close (OUT);
chmod (oct($DCONF->{perms0666}), "$DCONF->{admin_dir}/msg_index/$topic-log-$x.txt");
@d = @k;
}
return 1;
}
###
### search_line_to_hash
###
### Converts a line to a hash from ###-search.txt. Note that
### search_line_to_hash(search_hash_to_line($ref)) = $ref
###
sub search_line_to_hash {
my ($line) = @_;
chomp $line;
my $d = {};
my @s = ('postindex', 'time', 'text');
my @l = split(/\s+/, $line, 3);
while (my $p = shift(@s)) {
$d->{$p} = shift @l;
}
return $d;
}
###
### search_hash_to_line
###
### Converts a hash to a line for writing into ###-search.txt. Note that
### search_hash_to_line(search_line_to_hash($str)) = $str
###
sub search_hash_to_line {
my ($hash) = @_;
my $l = join(" ", $hash->{postindex}, $hash->{'time'}, $hash->{text});
$l .= "\n";
return $l;
}
###
### log_line_to_hash
###
### Converts a line to a hash from ###-log.txt. Note that
### log_line_to_hash(log_hash_to_line($ref)) = $ref
###
sub log_line_to_hash {
my ($line) = @_;
chomp $line;
my $d = {};
my @s = ('postindex', 'username', 'time', 'where', 'remote_addr', 'remote_host', 'firstchars', 'poststr');
my @l = split(/;/, $line);
while (my $p = shift(@s)) {
$d->{$p} = shift @l;
}
($d->{topic}, $d->{page}) = split(/\//, $d->{where});
return $d;
}
###
### log_hash_to_line
###
### Converts a hash to a line for writing into ###-log.txt. Note that
### log_hash_to_line(log_line_to_hash($str)) = $str
###
sub log_hash_to_line {
my ($hash) = @_;
$hash->{'time'} = ( $hash->{'time'} > 0 ? $hash->{'time'} : time );
$hash->{'remote_addr'} = ( defined $hash->{'remote_addr'} ? $hash->{'remote_addr'} : $ENV{'REMOTE_ADDR'});
$hash->{'remote_host'} = ( defined $hash->{'remote_host'} ? $hash->{'remote_host'} : $ENV{'REMOTE_HOST'});
$hash->{'where'} = ( defined $hash->{'where'} ? $hash->{'where'} : "$hash->{topic}/$hash->{page}");
my $l = join(";", $hash->{postindex}, $hash->{username}, $hash->{'time'}, $hash->{where}, $hash->{remote_addr}, $hash->{remote_host}, $hash->{firstchars}, $hash->{poststr});
$l .= "\n";
return $l;
}
###
### log_read_file
###
### Reads from the log file. Some searching is built in too.
### bottom_cutoff Bottom cutoff time
### top_cutoff Top cutoff time
### poster_match Require matching a poster's name
### postindex Return a single record identified by 'postindex'
###
sub log_read_file {
my ($topics, $param, $searches) = @_;
my $tpc = {};
if (ref $topics ne "HASH") {
foreach (split(/,/, $topics)) {
$tpc->{$_} = 1;
}
} else {
$tpc = $topics;
}
my %pi = {};
if (defined $searches->{postindex}) {
if (ref $searches->{postindex} eq "ARRAY") {
foreach my $j (@{ $searches->{postindex} }) { $pi{$j} = 1; }
} elsif (ref $searches->{postindex} eq "HASH") {
foreach my $j (keys(%{ $searches->{postindex} })) { $pi{$j} = 1; }
} else {
foreach my $j (split(/,/, $searches->{postindex})) { $pi{$j} = 1; }
}
} else {
$pi{'*'} = 1;
}
if ($GLOBAL_OPTIONS->{database} && $DCONF->{pro}) {
dreq("sql-logs-PRO");
return sql_logs_read_log_file($param, $searches, $tpc);
} else {
my $ml_ext = ".txt";
if (ref $searches eq 'HASH' && $searches->{bottom_cutoff} > 0) {
my $ml = minilog_choose_log($searches->{bottom_cutoff});
$ml_ext = "-$ml.txt" if defined $ml;
}
my @r = ();
my $req_re = [];
my $neg_re = "";
my $opt_re = "";
if ($searches->{poster_match} ne "") {
my ($req, $neg, $opt) = search_create_query_conditions($searches->{query_info}, undef, 1);
$req_re = $req;
$neg_re = $neg;
$opt_re = $opt;
}
foreach my $t (keys(%{ $tpc })) {
my $file = join("/", $DCONF->{admin_dir}, "msg_index", "$t-log$ml_ext");
$PARAMS->{files_read}++;
$PARAMS->{file_access}->{$file}->{read} += 1;
$PARAMS->{file_access}->{$file}->{write} += 0;
performance_string("< log_read_file $file") if $GLOBAL_OPTIONS->{performance_monitoring};
open (LOG, "< $file");
W: while (<LOG>) {
my $h = log_line_to_hash($_);
next W if defined $searches->{id} && $h->{username} ne $searches->{id};
if ($searches->{postindex} && $pi{$h->{postindex}}) {
push @r, $h;
delete $pi{$h->{postindex}};
if (scalar(keys(%pi)) == 0) {
close (LOG);
return $h if $searches->{postindex} =~ /^\d+/;
return \@r;
}
}
next W if defined $searches->{postindex};
my $poststr = unescape($h->{poststr});
next W if (defined $searches->{bottom_cutoff} && $h->{'time'} < $searches->{bottom_cutoff});
next W if (defined $searches->{top_cutoff} && $h->{'time'} > $searches->{top_cutoff});
next W if ($neg_re ne "" && $poststr =~ /$neg_re/);
if (ref $req_re eq "ARRAY" && scalar(@{ $req_re })) {
foreach my $re (@{ $req_re }) {
next W if $poststr !~ /$re/;
}
}
next W if ($opt_re ne "" && $poststr !~ /$opt_re/);
$h->{topic} = $t;
push @r, $h;
if ($param->{no_cutoff} == 0 && $GLOBAL_OPTIONS->{tree_search_absolute_cutoff_point} > 0 && scalar(@r) > $GLOBAL_OPTIONS->{tree_search_absolute_cutoff_point}) {
close(LOG);
return \@r;
}
}
close (LOG);
}
return \@r;
}
}
###
### search_create_query_conditions
###
### Creates a full search query
###
sub search_create_query_conditions {
my ($o, $flag, $no_stop) = @_;
dreq("search");
my @r = search_prepare_query($o, undef, $no_stop);
my @req = @{ $r[0] };
my @neg = @{ $r[1] };
my @opt = @{ $r[2] };
return (\@req, \@neg, \@opt) if $flag == 303;
my $opt_re = join("|", @opt);
my $neg_re = join("|", @neg);
$opt_re = join("", "(", $opt_re, ")") if $opt_re ne "";
$neg_re = join("", "(", $neg_re, ")") if $neg_re ne "";
$opt_re = join("", "(?i)", $opt_re) if (! $o->{search_opt}->{sensitive} && $opt_re);
$neg_re = join("", "(?i)", $neg_re) if (! $o->{search_opt}->{sensitive} && $neg_re);
return (\@req, $neg_re, $opt_re);
}
###
### log_search_update_entry
###
### Updates a log or search entry within the same topic
###
sub log_search_update_entry {
my ($topic, $type, $updates) = @_;
if ($DCONF->{pro} && $GLOBAL_OPTIONS->{database}) {
dreq("sql-logs-PRO");
return sql_log_search_update_entry($topic, $type, $updates);
}
my %miniseen = {};
my $timecache = time;
my @file = ("$DCONF->{admin_dir}/msg_index/$topic-$type.txt");
while (my $filename = shift @file) {
&lock("log_search_update_entry", $filename);
open (FILE1, "< $filename");
open (FILE2, "> $filename.new");
my $bytectr = 0;
my $ctr = 1;
my %seen = {};
foreach my $u (@{ $updates }) {
$seen{$u->{postindex}} = $ctr; $ctr++;
}
F: while (<FILE1>) {
my $hash = $type eq "search" ? search_line_to_hash($_) : log_line_to_hash($_);
if (! $seen{ $hash->{postindex} }) {
print FILE2 $_;
$bytectr += length($_);
next F;
} else {
my $x = $updates->[ $seen{ $hash->{postindex} } -1 ];
foreach my $k (keys(%{ $x })) {
$hash->{$k} = $x->{$k};
}
my $line = $type eq "search" ? search_hash_to_line($hash) : log_hash_to_line($hash);
print FILE2 $line;
delete $seen{ $hash->{postindex} };
$bytectr += length($line);
if ($type eq "log") {
M: foreach my $m (@mini_logs) {
next M if $miniseen{$m};
if ($timecache - 24*60*60*$m <= $hash->{'time'}) {
push @file, "$DCONF->{admin_dir}/msg_index/$topic-$type-$m.txt";
$miniseen{$m} = 1;
}
}
}
if (scalar(keys(%seen)) == 0) {
while (<FILE1>) {
$bytectr += length($_);
print FILE2;
}
last F;
}
}
}
close (FILE1);
close (FILE2);
rename_file("$filename.new", $bytectr, "$filename");
unlock("log_search_update_entry", $filename);
}
}
###
### search_push_entry
###
### Adds one or more entries to a ###-search.txt file (or database)
###
sub search_push_entry {
my ($topic, $data, $params) = @_;
my @d = ref $data eq "ARRAY" ? @{ $data } : ( $data );
if ($GLOBAL_OPTIONS->{database} && $DCONF->{pro}) {
dreq("sql-logs-PRO", "dbint");
return sql_logs_search_push_entry(\@d, $topic, $params);
} else {
my @q = ();
foreach my $hash (@d) {
push (@q, search_hash_to_line($hash));
}
appendfile("$DCONF->{admin_dir}/msg_index/$topic-search.txt", \@q, "search_push_entry", undef);
}
}
###
### log_push_entry
###
### Adds one or more entries to a ###-log.txt file (or database)
###
sub log_push_entry {
my ($topic, $data, $params) = @_;
my @d = ref $data eq "ARRAY" ? @{ $data } : ( $data );
if ($GLOBAL_OPTIONS->{database} && $DCONF->{pro}) {
dreq("sql-logs-PRO", "dbint");
return sql_logs_log_push_entry(\@d, $topic, $params);
} else {
my @q = ();
foreach my $hash (@d) {
push (@q, log_hash_to_line($hash));
}
appendfile("$DCONF->{admin_dir}/msg_index/$topic-log.txt", \@q, "log_push_entry", undef);
append_minilogs(\@q, $topic);
}
}
###
### append_minilogs
###
### Writes out new information in posting mini-logs
###
sub append_minilogs {
my ($data, $topic) = @_;
my $flag = 0;
foreach my $mini_log (@mini_logs) {
return undef if ! -e "$DCONF->{admin_dir}/msg_index/$topic-log-$mini_log.txt";
}
foreach my $mini_log (@mini_logs) {
appendfile("$DCONF->{admin_dir}/msg_index/$topic-log-$mini_log.txt", $data, "append_minilogs", undef);
}
}
###
### search_stop_word_file
###
### Returns the list of stop words from the stop words file
### (or from the cache if this file has already been opened)
###
sub search_stop_word_file {
return $PARAMS->{'stop_words'} if defined $PARAMS->{'stop_words'};
my $sw = readfile("$DCONF->{admin_dir}/stopwords.conf", "search_stop_word_file", { no_lock => 1, no_unlock => 1, zero_ok => 1 } );
foreach my $x (@{$sw}) {
next if $x =~ m|^\s*#|;
next if $x !~ m|\S|;
$x =~ s/\s//g;
$x = case_lower($x);
$PARAMS->{'stop_words'}->{$x} = 1;
}
return $PARAMS->{'stop_words'};
}
###
### search_stop
###
### Removes words in "stopwords.conf" from posts for writing into search index files
###
sub search_stop {
my ($input, $flag) = @_;
my $y = remove_html($input, 1);
return $y if $GLOBAL_OPTIONS->{'search_stopwords'} eq "0";
my $l = search_stop_word_file();
my @q = ();
foreach my $k (keys(%{ $l })) { push @q, quotemeta($k); }
my $x = join("|", @q);
$y =~ s/(^|\b)($x)(\b|$)//gio;
$y =~ s/\+\+\+ (\w+) \+\+\+ (\d+) \+\+\+ (.*?) \+\+\+/\[$3\]/g;
return trim($y);
}
###
### delete_entry
###
### Removes things from the search index and the posting logs
###
sub delete_entry {
my ($arg) = @_;
if ($GLOBAL_OPTIONS->{database} && $DCONF->{pro}) {
dreq("sql-logs-PRO");
return sql_logs_delete_entry($arg);
} else {
my @f = ();
my $tcache = time;
foreach my $m (@mini_logs) {
next if $arg->{time} > ($tcache - $m*24*60*60);
push @f, "$DCONF->{admin_dir}/msg_index/$arg->{topic}-log-$m.txt";
}
push @f, "$DCONF->{admin_dir}/msg_index/$arg->{topic}-log.txt";
foreach my $file (@f) {
lock($file) if ! $arg->{files_already_locked};
my $fs = 0;
if (open (FILE, "< $file")) {
if (open (FILE2, "> $file.new")) {
while (<FILE>) {
my ($msgnum) = split(/;/, $_, 2);
if (! defined $arg->{messages}->{$msgnum}) {
print FILE2 $_; $fs += length($_);
}
}
close (FILE2);
}
close (FILE);
rename_file("$file.new", $fs, "$file");
}
unlock($file) if ! $arg->{files_already_locked};
}
my $fs = 0;
lock("$DCONF->{admin_dir}/msg_index/$arg->{topic}-search.txt") if ! $arg->{files_already_locked};
if (open (FILE, "< $DCONF->{admin_dir}/msg_index/$arg->{topic}-search.txt")) {
if (open (FILE2, "> $DCONF->{admin_dir}/msg_index/$arg->{topic}-search.new")) {
while (<FILE>) {
my ($msgnum) = split(/\s+/, $_, 2);
if (! defined $arg->{messages}->{$msgnum}) {
print FILE2 $_; $fs += length($_);
}
}
close (FILE2);
}
close (FILE);
rename_file("$DCONF->{admin_dir}/msg_index/$arg->{topic}-search.new", $fs, "$DCONF->{admin_dir}/msg_index/$arg->{topic}-search.txt");
}
unlock("$DCONF->{admin_dir}/msg_index/$arg->{topic}-search.txt") if ! $arg->{files_already_locked};
}
}
###
### update_entry_post_topic
###
### Another function to move posts from one topic to another
###
sub update_entry_post_topic {
my ($moved, $old, $new) = @_;
my @m = @{$moved};
foreach my $m (@m) {
$m->{old_topic} = $old;
$m->{new_topic} = $new;
$m->{postindex} = $m->{number};
}
update_entry_post_location(\@m);
}
###
### update_entry_post_location
###
### Allows moving of messages from one page to another, whether or not in
### the same topic.
###
sub update_entry_post_location {
my ($info) = @_;
return undef if ref $info ne "ARRAY";
if ($GLOBAL_OPTIONS->{database} && $DCONF->{pro}) {
dreq("sql-logs-PRO");
return sql_logs_update_entry_post_location($info);
}
my @posts = @{ $info };
my $timecache = time;
my $cuts = {};
my $posthash = {};
foreach my $post (@posts) {
my $x = minilog_choose_log($post->{'time'}, $timecache);
$cuts->{$x} = 1 if defined $x;
$posthash->{$post->{postindex}} = $post;
}
my $min_mini = (sort { $a <=> $b } keys(%{ $cuts }))[0];
foreach my $i (@mini_logs) {
$cuts->{$i} = 1 if $i > $min_mini;
}
my $p = $posts[0];
my @source_files = ("$DCONF->{admin_dir}/msg_index/$p->{old_topic}-log.txt");
foreach my $minilog (keys(%{ $cuts })) {
push @source_files, "$DCONF->{admin_dir}/msg_index/$p->{old_topic}-log-$minilog.txt";
}
push @source_files, "$DCONF->{admin_dir}/msg_index/$p->{old_topic}-search.txt";
my @dest_files = ("$DCONF->{admin_dir}/msg_index/$p->{new_topic}-log.txt");
foreach my $minilog (keys(%{ $cuts })) {
push @dest_files, "$DCONF->{admin_dir}/msg_index/$p->{new_topic}-log-$minilog.txt";
}
push @dest_files, "$DCONF->{admin_dir}/msg_index/$p->{new_topic}-search.txt";
foreach my $SRCFILE (@source_files) {
&lock("update_entry_post_location", $SRCFILE);
my $DSTFILE = shift @dest_files;
open (SRCFILE, "< $SRCFILE");
open (DSTFILE, "> $DSTFILE.NEW");
my $exp_len = 0;
if ($SRCFILE =~ m|\-search\.txt$|) {
if ($DSTFILE eq $SRCFILE) {
while (my $line = <SRCFILE>) {
my $h = search_line_to_hash($line);
if ($posthash->{$h->{postindex}}->{new_page}) {
$h->{page} = $posthash->{$h->{postindex}}->{new_page};
}
my $ln = search_hash_to_line($h);
print DSTFILE $ln;
$exp_len += length($ln);
}
close (DSTFILE);
close (SRCFILE);
rename_file("$DSTFILE.NEW", $exp_len, "$DSTFILE");
} else {
my @newhash = ();
while (my $line = <SRCFILE>) {
my $h = search_line_to_hash($line);
if ($posthash->{$h->{postindex}}) {
$h->{page} = $posthash->{$h->{postindex}}->{new_page};
$h->{topic} = $posthash->{$h->{postindex}}->{new_topic};
my $ln = search_hash_to_line($h);
push @newhash, $ln;
} else {
print DSTFILE $line;
$exp_len += length($line);
}
}
close (DSTFILE);
close (SRCFILE);
&lock("update_entry_post_location", $DSTFILE);
open (DSTFILE, ">> $DSTFILE");
print DSTFILE @newhash;
close (DSTFILE);
unlock("update_entry_post_location", $DSTFILE);
rename_file("$DSTFILE.NEW", $exp_len, "$SRCFILE");
}
} else {
if ($DSTFILE eq $SRCFILE) {
while (my $line = <SRCFILE>) {
my $h = log_line_to_hash($line);
$h->{where} = join("/", $posthash->{$h->{postindex}}->{new_topic}, $posthash->{$h->{postindex}}->{new_page}) if $posthash->{$h->{postindex}}->{new_page};
my $ln = log_hash_to_line($h);
print DSTFILE $ln;
$exp_len += length($ln);
}
close (DSTFILE);
close (SRCFILE);
rename_file("$DSTFILE.NEW", $exp_len, "$DSTFILE");
} else {
my @newhash = ();
while (my $line = <SRCFILE>) {
my $h = log_line_to_hash($line);
if ($posthash->{$h->{postindex}}) {
$h->{where} = join("/", $posthash->{$h->{postindex}}->{new_topic}, $posthash->{$h->{postindex}}->{new_page});
my $ln = log_hash_to_line($h);
push @newhash, $line;
} else {
print DSTFILE $line;
$exp_len += length($line);
}
}
close (DSTFILE);
close (SRCFILE);
&lock("update_entry_post_location", $DSTFILE);
open (DSTFILE, ">> $DSTFILE");
print DSTFILE @newhash;
close (DSTFILE);
unlock("update_entry_post_location", $DSTFILE);
rename_file("$DSTFILE.NEW", $exp_len, "$SRCFILE");
}
}
unlock("update_entry_post_location", $SRCFILE);
}
}
1;