;#
;# Copyright (c) 1995-1997
;# Ikuo Nakagawa. All rights reserved.
;#
;# Redistribution and use in source and binary forms, with or without
;# modification, are permitted provided that the following conditions
;# are met:
;#
;# 1. Redistributions of source code must retain the above copyright
;# notice unmodified, this list of conditions, and the following
;# disclaimer.
;# 2. Redistributions in binary form must reproduce the above copyright
;# notice, this list of conditions and the following disclaimer in the
;# documentation and/or other materials provided with the distribution.
;#
;# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
;# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
;# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
;# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
;# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
;# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
;# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
;# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
;# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
;# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
;#
;# $Id: Cool.pm,v 1.12 1999/10/28 10:32:19 ikuo Exp $
;#
package Fan::Cool;
use strict;
use vars qw($VERSION @ISA @EXPORT
$LOG %plog_level @nameofday @nameofmonth @daysofmonth
@accept %escape $black_hole_begin $black_hole_end);
use Carp;
use POSIX qw(errno_h);
use AutoLoader 'AUTOLOAD';
require Exporter;
@ISA = qw(Exporter);
@EXPORT = qw(
append mkdirhier rmdirhier plock encode decode
plog plog_level plog_mask
str4time str4date timezone isleap
dayofweek daysofmonth nameofmonth lookup
);
$VERSION = '0.01';
$LOG = 5; # default is INFO
@plog_level{qw(EMERGE ALERT CRIT ERR WARNING NOTICE INFO DEBUG)} = (0..7);
@nameofday = qw(Sun Mon Tue Wed Thu Fri Sat);
@nameofmonth = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
@daysofmonth = qw(31 28 31 30 31 30 31 31 30 31 30 31);
;# for encoding charactors
@accept = ('*', '-', '.', '0'..'9', '@', 'A'..'Z', '_', 'a'..'z');
@escape{@accept} = @accept;
;# initialize @escape
for (my $i = 0; $i < 256; $i++) {
my $c = pack("C", $i);
defined($escape{$c}) || ($escape{$c} = sprintf("%%%02x", $i));
$escape{' '} = '+';
}
;# for day counter
$black_hole_begin = &days(3, 9, 1752);
$black_hole_end = &days(14, 9, 1752);
;# prototypes are generated automatically by AutoSplit.
;# sub append (@);
;# sub mkdirhier ($$);
;# sub rmdirhier ($);
;# sub plog ($@);
;# sub plog_level ($);
;# sub plog_mask ($;$);
;# sub plock ($;$);
;# sub encode ;
;# sub decode ;
;# sub str4time (;$);
;# sub str4date (;$);
;# sub timezone ();
;# sub isleap (;$);
;# sub dayofyear ($$$);
;# sub days ($$$);
;# sub dayofweek ($$$);
;# sub daysofmonth ($;$);
;# sub nameofmonth ($);
;# sub lookup ($);
;# A special marker for AutoSplit.
1;
__END__
;# append(filename, string, string, ...);
;# Append some data to specified file. This routine
;# uses `plock' to lock output file.
;#
sub append (@) {
local *FILE;
my $file = shift;
my $result =
$file ne '' && ! -d $file &&
@_ &&
plock("$file.LOCK") &&
open(FILE, ">>$file") &&
seek(FILE, 0, 2) &&
grep((print FILE), @_) &&
close(FILE) ? 1 : undef;
unlink("$file.LOCK");
$result;
}
;#
;# make directory hierachy
sub mkdirhier ($$) {
my $dir = shift;
my $mode = shift;
my $d = $dir =~ s%^/+%% ? '' : '.';
my $e;
for $e (split('/', $dir)) {
-d ($d .= '/'.$e) && next;
unlink($d); # ignore result
mkdir($d, $mode) || return undef;
}
1;
}
;# remove directory recursively
sub rmdirhier ($) {
my $dir = shift;
# force to remove even if target is not a directory.
return unlink($dir) if -l $dir || ! -d _;
# try to open the target directory, and...
local *DIR;
opendir(DIR, $dir) || return undef;
# search directory entires in this directory.
my $e;
while (defined($e = readdir(DIR))) {
# rmdirhier can remove non-directory file.
rmdirhier("$dir/$e") if $e ne '.' && $e ne '..';
}
closedir(DIR);
rmdir($dir); # the result value is that of rmdir.
}
;#
;# Usage: plog(level, string, string, ...);
;#
;#
sub plog ($@) {
# determin logging level...
my $level = shift;
if ($level !~ /^\d+$/) {
$level = &plog_level($level);
}
# calculate log mask.
my $mask;
{ # We can't use ${$string} in `use strict'.
no strict 'refs';
$mask = ${(caller)[$[].'::LOG'} || $LOG;
}
# only if specified log level is less or equal to mask.
if ($level <= $mask) {
local $_; # used in grep
grep((print STDERR $_), @_);
}
# always success
1;
}
;# Convert log name to level
;# For example, plog_level('INFO') returns 6.
;#
sub plog_level ($) {
my $lv = uc(shift);
$lv =~ /^\d+$/ ? $lv :
exists($plog_level{$lv}) ? $plog_level{$lv} : $LOG;
}
;# Setting up log masks for generic modules.
;# For example, plog_mask("FTP=7,TCP,Scan", 6) causes
;# $FTP::LOG = 7;
;# $TCP::LOG = 6;
;# $Scan::LOG = 6;
;#
sub plog_mask ($;$) {
my @packages = split(/[,\s]+/, shift);
my $lv = @_ ? plog_level(shift): $LOG; # default is $LOG.
for my $str (@packages) {
my($pack, $level) = $str =~ /=/
? ($`, plog_level($')) : ($str, $lv);
if ($pack ne '') {
no strict 'refs';
${$pack."::LOG"} = $level;
}
}
}
;#
;# Usage: plock($filename, $timeout)
;#
;# An implementation of a lock mechanizem. `plock' uses `link'
;# function to lock a file, but uses no flock/lockf functions.
;# Second argument $timeout means how long seconds you may wait
;# until get lock. The default value of $timeout is 10[sec].
;#
sub plock ($;$) {
my $file = shift;
my $tt = @_ ? shift : 0;
my $res = 0;
local *FILE;
;# generate temporary filename
my $temp = $file;
$temp =~ s,[^/]+$,.LOCK.$$, || return undef;
;# check timeout value
$tt = 10 if $tt < 1;
;# make sure that temporary file does not exist
for (; -e $temp && !unlink($temp) && $tt > 0; $tt--) {
sleep(1);
}
;# create temporary file with process id
$tt > 0 && open(FILE, ">$temp")
&& (print FILE "$$\n")
&& close(FILE)
or unlink($temp), return undef;
;# link it to target file
for (; !($res = link($temp, $file)) && $tt > 0; $tt--) {
my $pid;
if (open(FILE, $file)
&& chomp($pid = <FILE>)
&& close(FILE)
&& $pid =~ /^\d+$/) { # Got pid!
if (kill(0, $pid)) { # Success to kill
sleep(1); # wait a second
} elsif (ESRCH != $!) {
last; # permission denied? or other errors
} else {
unlink($file); sleep(1); # no need to sleep?
}
} else {
unlink($file); sleep(1); # no need to sleep?
}
}
;# unlink temporary file
unlink($temp);
;# result - success if link succeeded
return $res;
}
;#
sub encode ($) {
$_ = shift;
s/./$escape{$&}/g;
$_;
}
;#
sub decode ($) {
$_ = shift;
s/\+/ /go;
s/%(..)/pack("H*", $1)/eg;
$_;
}
;#
;# generate time string, like "12:34:56", for given UTC time.
;# If $time is omitted, `str4time' returns a time string for the
;# current time.
;#
sub str4time (;$) {
sprintf("%02d:%02d:%02d", reverse((localtime(shift || time))[0..2]));
}
;#
;# Usage: &str4data($time);
;#
;# Generate date string, like "1993-03-14", for given UTC time.
;# If $time is omitted, `str4date' returns a data string for the
;# current time.
;#
sub str4date (;$) {
my($d, $m, $y) = (localtime(shift || time))[3..5];
sprintf("%04d-%02d-%02d", $y + 1900, $m + 1, $d);
}
;#
;# Usage: &timezone
;#
;# Find local timezone like "+0900" or "GMT".
;# To calculate timezone, we use UTC clock of `946684800'
;# which means "2000-01-01 00:00:00".
;#
sub timezone () {
local($[); # for zero-based array
my($sec, $min, $hour, $day, $mon, $year) = localtime(946684800);
my $sign = '+';
my $offset = $hour * 60 + $min;
if ($year == 99) {
$sign = '-';
$offset = 24 * 60 - $offset;
}
$offset++ if $offset % 10;
return 'GMT' if !$offset;
return $sign.'2359' if $offset >= 24 * 60;
$sign.sprintf("%02d%02d", $offset / 60, $offset % 60);
}
;#
;# Usage: isleap($year);
;# where $year is optional.
;#
sub isleap (;$) {
my($y) = @_ ? shift : (localtime)[5] + 1900;
$y <= 1752 ? !($y % 4) : !($y % 4) && ($y % 100) || !($y % 400);
}
;#
;#
sub dayofyear ($$$) {
my($day, $month, $year) = @_;
my $i;
$day++ if $month > 2 && &isleap($year);
for ($i = 1; $i < $month; $i++) {
$day += $daysofmonth[$i - 1];
}
$day;
}
;#
;#
sub days ($$$) {
my($day, $month, $year) = @_;
my $days = &dayofyear($day, $month, $year);
my $y = $year - 1;
my $leap_years = int($y / 4)
- ($y > 1700 ? int($y / 100) - 17 : 0)
+ ($y > 1600 ? int(($y - 1600) / 400) : 0);
$days += $y * 365 + $leap_years;
}
;#
sub dayofweek ($$$) {
my $yday = &days(@_);
$yday < $black_hole_begin ? ($yday + 5) % 7 :
$yday > $black_hole_end ? ($yday + 1) % 7 : 6;
}
;#
sub daysofmonth ($;$) {
my $m = shift;
my $y = shift;
my $d = defined($daysofmonth[$m]) ? $daysofmonth[$m] : undef;
$d++ if $y =~ /^\d+$/ && &isleap($y) && $m == 1;
$d;
}
;#
sub nameofmonth ($) {
my $m = shift;
defined($nameofmonth[$m]) ? $nameofmonth[$m] : undef;
}
;#
;# lookup a pathname
;# convert:
;# 1. "abc//xyz" to "abc/xyz"
;# 2. "abc/./xyz" to "abc/xyz"
;# 3. "abc/foo/../xyz" to "abc/syz"
;# NOTES:
;# This routine may be very slow... :-<
;# Would anybody rewrite this code?
;#
sub lookup ($) {
local $_ = shift;
# null string is assumed as the current directory.
$_ = '.' if $_ eq '';
# most of simple case...
return $_ if !/\//;
# try to convert pathname.
$_ .= '/'; # add trailing slash
s|/+|/|go; # foo//bar -> foo/bar
my $absolute = s|^/||; # 1 if $_ is a absolute path
$_ = '/'.$_; # for convinience
1 while s|/\./|/|go; # foo/./bar -> foo/bar
# try main loop.
my $tmp;
do { # xyz/foo/../bar -> xyz/bar
$tmp = $_;
s|([^/]+)/\.\./|$1 eq '..' ? '../../' : ''|geo;
} while ($tmp ne $_) ;
# aftercare
s|^/|| if !$absolute;
s|/+$|| if $_ ne '/';
$_ = '.' if $_ eq '';
$_;
}
;# end of Fan::Cool module
syntax highlighted by Code2HTML, v. 0.9.1