| PApp documentation | Contained in the PApp distribution. |
PApp::Util - various utility functions that didn't fit anywhere else
use PApp::Util;
This module offers various utility functions that cannot be grouped into other categories easily.
Formats a file supposed to be some "sourcecode" (e.g. perl, papp, xslt etc..) into formatted ascii. It includes line numbering at the front of each line and handles embedded "#line" markers.
Tries to dump the given perl-ref into a nicely-formatted human-readable-format (currently uses either Data::Dumper or Dumpvalue) but tries to be very robust about internal errors, i.e. this functions always tries to output as much usable data as possible without die'ing.
Converts a JSON string into the corresponding perl data structure.
Converts a perl data structure into its JSON representation.
Calculate a SHA1 digest and return it base64-encoded. The result will always be 27 characters long.
Appends all the strings found in $hashref2 to the respective keys in $hashref1 (e.g. $h1->{key} .= $h2->{key} for all keys).
Returns all the elements that are unique inside the array. The elements must be strings, or at least must stringify sensibly.
Returns a very verbose dump of the internals of the given sv. Calls the
sv_peek (sv_dump) core function. If you don't know what I am talking
about then this function is not for you. Or maybe you should try it.
Tries to fetch the document specified by $uri, returning undef
on error. As a special "goody", uri's of the form "data:,body" will
immediately return the body part.
Try to locate the specified document. If the uri is a relative uri (or a
simple unix path) it will use the URIs in @bases and PApp's search path
to locate the file. If bases contain an arrayref than this arrayref should
contain a list of extensions (without a leading dot) to append to the URI
while searching the file.
Locate the document specified by the given uri using find_file, then
fetch and return it's contents using fetch_uri.
Takes text and transforms it to a mime message header suitable for message headers. If the text is US-ASCII it will be returned unchanged, otherwise it will be encoded according to RFC 2047 (it will be split into multiple CRLF-Tab-separated components of no longer than 75 characters, with the first component not be longer than 40 characters).
Same as inet_ntoa/inet_aton, but works on (numerical) IPv4 and IPv6 addresses.
ipv6 not yte implemented
A very primitive form of source filtering can be implemented using
filter_add, filter_read and filter_simple. Better use the
Filter module family, though.
Return a nonce suitable for cryptographically.
Convert the given octet string into a human-readable ascii text, using only a-zA-Z0-9 characters (base62 encoding).
PApp.
Marc Lehmann <schmorp@schmorp.de> http://home.schmorp.de/
| PApp documentation | Contained in the PApp distribution. |
########################################################################## ## All portions of this code are copyright (c) 2003,2004 nethype GmbH ## ########################################################################## ## Using, reading, modifying or copying this code requires a LICENSE ## ## from nethype GmbH, Franz-Werfel-Str. 11, 74078 Heilbronn, ## ## Germany. If you happen to have questions, feel free to contact us at ## ## license@nethype.de. ## ##########################################################################
package PApp::Util; use Carp; use URI; use Socket (); use JSON::XS; use base 'Exporter'; BEGIN { $VERSION = 1.45; @EXPORT = qw(dumpval uniq); @EXPORT_OK = qw( format_source sv_peek sv_dump digest dumpval append_string_hash find_file fetch_uri load_file mime_header nonce alnumbits ); # I was lazy, all the util xs functions are in PApp.xs require XSLoader; XSLoader::load PApp, $VERSION unless defined &PApp::bootstrap; }
sub format_source($) { my $data = shift; my $s = 1; $data =~ s{ ^(?=\#line\ (\d+))? }{ if ($1) { $s = $1; "\n"; } else { sprintf "%03d: ", $s++ } }gemx; $data; }
sub dumpval { eval { local $SIG{__DIE__}; my $d; if (1) { require Data::Dumper; $d = new Data::Dumper([$_[0]], ["*var"]); $d->Terse(1); $d->Indent(2); $d->Quotekeys(0); $d->Useqq(1); #$d->Bless(...); $d->Seen($_[1]) if @_ > 1; $d = $d->Dump(); } else { local *STDOUT; local *PApp::output; tie *STDOUT, PApp::Catch_STDOUT; require Dumpvalue; $d = new Dumpvalue globPrint => 1, compactDump => 0, veryCompact => 0; $d->dumpValue($_[0]); $d = $PApp::output; } $d =~ s/([\x00-\x07\x09\x0b\x0c\x0e-\x1f])/sprintf "\\x%02x", ord($1)/ge; $d; } || "[unable to dump $_[0]: '$@']"; }
*decode_json = \&JSON::XS::decode_json;
*encode_json = \&JSON::XS::encode_json;
sub digest { require Digest::SHA1; goto &Digest::SHA1::sha1_base64; }
sub append_string_hash($$) { my ($h1, $h2) = @_; while (my ($k, $v) = each %$h2) { $h1->{$k} .= $h2->{$k}; } $h1; }
sub uniq { my %seen; grep !$seen{$_}++, @_ }
sub fetch_uri { my ($uri, $head) = @_; if ($uri =~ m%^/|^file:///%i) { # simple file URI $uri = URI->new($uri, "file")->file; return -f $uri if $head; local($/,*FILE); open FILE, "<", $uri or return (); return <FILE>; } elsif ($uri =~ s/^data:,//i) { return 1 if $head; return $uri; } else { require LWP::Simple; return LWP::Simple::head($uri) if $head; return LWP::Simple::get($uri); } }
sub find_file { my $file = shift; my @ext; my %seen; for my $path (@_, PApp::Config::search_path) { if (ref $path eq "ARRAY") { @ext = map ".$_", @$path; } else { for my $ext ("", @ext) { my $uri = URI->new_abs("$file$ext", "$path/"); next if $seen{"$uri"}++; # optimization, probably not worth the effort return $uri if fetch_uri $uri, 1; } } } (); }
sub load_file { my $path = &find_file or return; return fetch_uri $path; }
sub mime_header($) { my ($text) = @_; return $text if $text =~ /^[\x20-\x7e]{0,40}$/ and $text !~ /=\?/; my $enc; my $fraglen = 40; my @frag; while (length $text) { my $len = $fraglen; my $frag; for (;;) { $frag = substr $text, 0, $len; if ($frag =~ /^[\x20-\xff]+$/) { # latin-1 only $enc = "iso-8859-1"; } else { utf8::encode $frag; $enc = "utf-8"; } $frag =~ s/([^\x21-\x3c\x3e\x40-\x7e])/sprintf "=%02X", ord $1/ge; $frag = "=?$enc?q?$frag?="; last if length $frag < $fraglen; $len--; # fragment too long } push @frag, $frag; $fraglen = 75; substr $text, 0, $len, ""; } join "\015\012\011", @frag; }
*ntoa = \&Socket::inet_ntoa; *aton = \&Socket::inet_aton;
sub filter_simple(&) { my $cb = $_[0]; my $buf; sub { unless (defined $buf) { local $_ = ""; 1 while 0 > ($buf = PApp::Util::filter_read $_[0] + 1, $_, 65536); return $buf if $buf < 0; &$cb; $buf = $_; } my $len; if ($_[2]) { $len = $_[2]; } else { $len = index $buf, $/; $len = $len < 0 ? length $buf : $len + length $/; } $_[1] .= substr $buf, 0, $len, ""; return length $_[1]; } } # internal benchmark funs sub bench(;$) { use Time::HiRes 'time'; if (@_) { push @bench, time, $_[0]; } else { @bench = (time, "BEGIN"); } } sub benchlog { my $log; push @bench, time; my $time = shift @bench; while (@bench) { $log .= sprintf "%s - %.3f - ", shift @bench, $bench[0]-$time; $time = shift @bench; } warn sprintf "%.3f: %s\n",(time-$NOW),$log; }
sub nonce($) { my $nonce; if (open my $fh, "</dev/urandom") { sysread $fh, $nonce, $_[0]; } else { $nonce = join "", map +(chr rand 256), 1 .. $_[0] } $nonce }
sub alnumbits($) { my $data = $_[0]; if (eval "use Math::GMP 2.05; 1") { $data = Math::GMP::get_str_gmp ( (Math::GMP::new_from_scalar_with_base (+(unpack "H*", $data), 16)), 62 ); } else { require MIME::Base64; $data = MIME::Base64::encode_base64 ($data, ""); $data =~ s/=//; $data =~ s/x/x0/g; $data =~ s/\//x1/g; $data =~ s/\+/x2/g; } $data } 1;