package L;
use strict; use warnings;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(slurp belch prod p x touch odds evens escape_tsv unescape_tsv
mtime rmpath min max In
text_to_html html_to_text text_to_textarea_html textarea_html_to_text
random_string str2hex hex2str program usage openfile openstr range_str
needargs op shuffle cp
uniqo
);
our @EXPORT_OK = qw(In And Or Sub);
# sql_connect sql_dbh sql_command sql_query sql_query_hash sql_multi_query sql_multi_query_headers sql_multi_query_hash
use IO::File;
use IO::String;
#use DBI;
#use File::Copy;
use Carp;
use strict;
our $dbh;
sub slurp {
my ($f) = @_;
if (! ref $f) {
$f = IO::File->new($f, "r") ||
die "can't open file `$f' to read: $!";
}
local $/;
return <$f>;
}
sub belch {
my $f = $_[0];
if (! ref $f) {
$f = IO::File->new($f, "w") ||
die "can't open file `$f' to write: $!";
}
print $f $_[1] or
die "can't write to `$f': $!";
}
sub prod {
# used to be called belch_if_changed
if (! -e $_[0] || slurp($_[0]) ne $_[1]) {
belch($_[0], $_[1]);
}
}
sub p {
@_ == 0 and @_ = ($_);
$_="@_"; chomp;
print "$_\n";
}
sub x {
return join " ", map { sprintf("%02x", ord($_)) } split //, $_[0];
}
# warning, this `touch' will also `trunc'!
sub touch {
open my $OUT, ">", $_[0];
close $OUT;
}
sub odds {
my $a = shift;
my @r;
for (my $i=0; $i<@$a; $i+=2) {
push @r, $$a[$i];
}
return \@r;
}
sub evens {
my $a = shift;
my @r;
for (my $i=1; $i<@$a; $i+=2) {
push @r, $$a[$i];
}
return \@r;
}
# this function escapes tabs to \t, newlines to \n, null to \0, \ to \\, and undef to null
sub escape_tsv {
my $v = shift;
return "\0" unless defined $v;
for ($v) {
s/\\/\\\\/g;
s/\t/\\t/g;
s/\n/\\n/g;
s/\0/\\0/g;
}
return $v;
}
sub unescape_tsv {
my $v = shift;
return undef if $v eq "\0";
for ($v) {
s/(?:(?<=[^\\])|^)((?:\\\\)*)\\0/$1\0/g;
s/(?:(?<=[^\\])|^)((?:\\\\)*)\\n/$1\n/g;
s/(?:(?<=[^\\])|^)((?:\\\\)*)\\t/$1\t/g;
s/\\\\/\\/g;
}
return $v;
}
#sub sql_connect {
# $dbh = DBI->connect(@_)
# or die "can't connect to database: " . $DBI::errstr;
# return $dbh;
#}
#
#sub sql_dbh {
# return $dbh;
#}
#
#sub sql_command {
# my $sth = $dbh->prepare(shift)
# or croak("Cannot prepare: " . $dbh->errstr());
# $sth->execute(@_)
# or croak("Cannot execute: " . $sth->errstr());
# $sth->finish();
#}
#
#sub sql_query {
# my $sth = $dbh->prepare(shift)
# or croak("Cannot prepare: " . $dbh->errstr());
# $sth->execute(@_)
# or croak("Cannot execute: " . $sth->errstr());
# my $ary_ref = $sth->fetchrow_arrayref;
# $sth->finish();
# return $ary_ref;
#}
#
#sub sql_query_hash {
# my $sth = $dbh->prepare(shift)
# or croak("Cannot prepare: " . $dbh->errstr());
# $sth->execute(@_)
# or croak("Cannot execute: " . $sth->errstr());
# my $hash_ref = $sth->fetchrow_hashref;
# $sth->finish();
# return $hash_ref;
#}
#
#sub sql_multi_query {
# my $sth = $dbh->prepare(shift)
# or croak("Cannot prepare: " . $dbh->errstr());
# $sth->execute(@_)
# or croak("Cannot execute: " . $sth->errstr());
# my $ary_ref_ary_ref = $sth->fetchall_arrayref();
# $sth->finish();
# return $ary_ref_ary_ref;
#}
#
## TODO rename to sql_multi_query_names ?
#sub sql_multi_query_headers {
# my $sth = $dbh->prepare(shift)
# or croak("Cannot prepare: " . $dbh->errstr());
# $sth->execute(@_)
# or croak("Cannot execute: " . $sth->errstr());
# my $names = $sth->{NAME};
# my $ary_ref_ary_ref = $sth->fetchall_arrayref();
# unshift @$ary_ref_ary_ref, $names;
# $sth->finish();
# return $ary_ref_ary_ref;
#}
#
#sub sql_multi_query_hash {
# my $ary_ref_ary_ref = sql_multi_query_headers(@_);
# my $names = shift @$ary_ref_ary_ref;
# my $n = @$names;
# my @results;
# for my $row (@$ary_ref_ary_ref) {
# my %hash;
# for (my $i; $i<$n; ++$i) {
# $hash{$names->[$i]} = $row->[$i];
# }
# push @results, \%hash;
# }
# return \@results;
#}
sub mtime {
my ($filename) = @_;
return (stat($filename))[9];
}
use File::Basename;
sub rmpath {
my ($dir) = @_;
# this should be in File::Path, does an rmdir -p
rmdir($dir) and
rmpath(dirname($dir));
}
sub min {
my $min = shift;
for (@_) {
$min = $_ if $_ < $min;
}
return $min
}
sub max {
my $max = shift;
for (@_) {
$max = $_ if $_ > $max;
}
return $max
}
sub In {
my ($e, $l) = @_;
$l eq "the universe!" and
return 1;
for (@$l) {
$_ eq $e and
return 1;
}
return 0;
}
sub Or {
if (@_ == 2) {
my ($l1, $l2) = @_;
$l1 eq "the universe!" || $l2 eq "the universe!" and
return "the universe!";
my @ret = @$l1;
for (@$l2) {
In($_, $l1) or
push @ret, $_;
}
return \@ret;
}
if (@_ > 2) { return Or(Or(shift, shift), @_); }
if (@_ == 1) { return [@{$_[0]}] }
if (@_ == 0) { return [] }
}
sub And {
if (@_ == 2) {
my ($l1, $l2) = @_;
$l1 eq "the universe!" and
return $l2;
$l2 eq "the universe!" and
return $l1;
my @ret;
for (@$l1) {
In($_, $l2) and
push @ret, $_;
}
return \@ret;
}
if (@_ > 2) { return And(And(shift, shift), @_); }
if (@_ == 1) { return [@{$_[0]}] }
if (@_ == 0) { return "the universe!" }
}
sub Sub {
my ($l1, $l2) = @_;
$l1 eq "the universe!" and
die "sorry can't sub from the universe yet!";
$l2 eq "the universe!" and
return [];
my @ret;
for (@$l1) {
In($_, $l2) or
push @ret, $_;
}
return \@ret;
}
# NOTE these don't handle all entities,
# should use the right module to help with this
# these seem to have become somewhat specialized for NIML - should move them there?
sub text_to_html {
my ($text) = @_;
for ($text) {
s/&/&/g;
s/</g;
s/>/>/g;
s/"/"/g;
s/\n/
\n/g;
# cope with lynx being bogus w.r.t multiple consecutive
s :
s/\n
/\n
/g;
s/( +)/" " x (length($1)-1)." "/ge;
}
return $text;
}
sub html_to_text {
my ($text) = @_;
for ($text) {
# cope with lynx being bogus w.r.t multiple consecutive
s :
s/\n
/\n
/g;
s/\r\n?/\n/g;
s/\n/ /g;
s/
/\n/g;
s/<//g;
s/"/"/g;
s/&/&/g;
s/ +/ /g;
s/ / /g;
}
return $text;
}
sub text_to_textarea_html {
my ($text) = @_;
for ($text) {
s/&/&/g;
s/</g;
s/>/>/g;
s/"/"/g;
}
$text .= "\n" unless $text =~ /\n\z/;
return $text;
}
sub textarea_html_to_text {
my ($text) = @_;
for ($text) {
s/<//g;
s/"/"/g;
s/&/&/g;
}
if ($text !~ /\n\z/) {
$text .= "\n";
}
return $text;
}
sub random_string {
my ($length) = @_;
my $s = "";
for (my $i=0; $i<$length; ++$i) {
$s .= chr(rand(256));
}
return $s;
}
sub str2hex {
my ($s) = @_;
return unpack("%H*", $s);
}
sub hex2str {
my ($s) = @_;
return pack("%H*", $s);
}
sub program {
my $program = $0;
$program =~ s,^.*/,,;
return $program;
}
sub usage {
my $program = program();
die "usage: $program ".(join "", map "$_\n", @_);
}
sub openfile {
return IO::File->new(@_) || die "can't openfile: @_";
}
sub openstr {
return IO::String->new(@_) || die "can't open string!";
}
#TODO test and use this in any function that takes a "file"
sub op {
my ($fh) = @_;
if (!ref $fh) {
$fh = openfile($fh);
}
return $fh;
}
# this is a localizable function!
sub range_str {
my ($min, $max) = @_;
if ($min == $max) { return $min; }
else { return "$min-$max" }
}
sub needargs {
my ($min, $max, @error) = @_;
$max ||= $min;
if (@ARGV < $min or $ARGV > $max) {
my $range = str_range($min, $max);
@error or @error = "needs ".str_range($min, $max)." arguments";
usage @error;
}
}
sub shuffle {
my $ary = $_[0];
my @out;
while (@$ary) {
my $i = int rand(@$ary);
push @out, splice @$ary, $i, 1;
}
return \@out;
}
#sub cp {
# return copy(@_);
#}
sub uniqo {
my @res;
my %already;
for (@_) {
if (!$already{$_}) {
push @res, $_;
$already{$_} = 1;
}
}
return @res;
}
1