Webperl requires at the moment to statically bundle all needed modules.
The following Proof of Concept shows how to dynamically use non-bundled modules.
The modules need to be pure Perl and have to be present inside the $WEBLIB directory on your server (respecting the same domain policy avoids much trouble)
I just copied the desired libs from my installation and listed allowed modules to %INC_FETCH (to limit unnecessary traffic) .
The following page demonstrate how to use Data::Dump from weblib.
DISCLAIMER: This code is beta and follows the release often paradigm.
Successfully tested with Chrome AND Firefox. FF showed new "Content Security Policy" problems, I didn't have the time to dig into and install the necessary PLACK CORS modules. °
The principle is universal, as soon as webperl can run in a browser and has the capacity to dynamically fetch code, using unbundled (pure) Perl modules becomes trivial.
Cheers Rolf (addicted to the Perl Programming Language :)
Wikisyntax for the MonasteryFootballPerl is like chess, only without the dice
°) works like a charm in FF , forgot to disable "noscript" filtering for localhost! ;)
All begun when I discovered that my shoutcast bookmark was no more opening the usual list of internet strreaming ordered by genre..
I said to myself I know a bit of Perl.. I'll do my own way.. and I started investigating mp3 streams (fulling my screen of binary #*%_ many times ;) and even if I've not reached my original goal I ended with someting useful and cool
The program will listen an mp3 stream and save each song with the correct title, skipping advertisements if possible and doing it's best to produce an entire song in each file
The POD documentation describes what I have understood of the ICY protocol (undocumented).
Suggestions and improvements welcome, have fun!
use strict;
use warnings;
use Net::HTTP;
use URI;
use File::Spec;
use Getopt::Long;
$|++;
my $VERSION = 23;
# SOME DEFAULTS
my $debug = 0;
# --extraparts default value
my $cache_size = 2;
my $url;
my $agent = 'Stream-Cutter-v'.$VERSION;
unless ( GetOptions (
'url=s' => \$url,
'agent=s' => \$agent,
'extraparts|cache=i' => \$cache_size,
'debug=i' => \$debug,
+
)) {die "$0 -url URL [-agent STRING -extraparts N -debug [0-2]
+]"}
unless ( $url ){
print "which URL you want to open?";
$url = <STDIN>;
chomp $url;
}
# OTHER VARIABLES
# chunk number for debug purpose
my $num = 0;
# cache used to have more chunks wrote to a file when new song starts
my @cache;
# used to append to previous file
# how_many parts will be equal to $cache_size when new song begin
my %previous_file = ( name => undef, how_many => 0);
my ( $socket, $icymetaint ) = open_connection( $url );
die "unable to get icy-metaint!" unless defined $icymetaint
and $icymetaint > 0;
read_stream( $socket, $icymetaint );
######################################################################
+#########
sub open_connection {
my $url = shift;
my $uri = URI->new( $url );
my $sock = Net::HTTP->new(
Host => $uri->host,
PeerPort => $uri->port,
) or die $@;
$sock->write_request(
GET => $uri->path,
'User-Agent' => $agent,
# very important: ask for metadata!
'Icy-MetaData' => 1
) or die $@;
my ($http_code, $http_mess, %headers) = $sock->read_response_heade
+rs;
print join ' ', "\nConnecting to:\t",$uri->as_string,
"\nStatus:\t",$http_code,$http_mess,"\n";
# go on if everything is OK 200
if ( $http_code == 200){
# grab useful headers and set them to empty string if undefine
+d
map {$headers{$_} = $headers{$_} // ''} 'Server','icy-name','
+icy-name',
'icy-genre','icy-br';
print join "\n","Server:\t".$headers{'Server'},
"name:\t".$headers{'icy-name'},
"genre:\t".$headers{'icy-genre'},
"byte rate:\t".$headers{'icy-br'}."kb/s\n\n";
if ( $debug ){
print "HEADERS:\n",
(map {qq(\t$_\t=>\t$headers{$_}\n)}
grep{defined $headers{$_}} %headers),"\n\n";
}
return ($sock, $headers{'icy-metaint'});
}
# return undef if not OK 200
else {
print "Errors opening the given site..\n";
return undef;
}
}
######################################################################
+#########
sub read_stream {
my ($socket, $metaint) = @_;
# output filehandle
my $out;
my $new_metadata;
my $file_name;
while( 1 ) {
my $buffer;
# READ the chunk of music
$socket->read($buffer, $metaint);
# CHECK for new metadata
if ( $new_metadata = read_meta($socket)){
# WRITE and get back the NEW filehadle
$out = write_stream( $buffer, $out, $new_metadata );
+
}
else{
# WRITE and get back the OLD filehadle
$out = write_stream( $buffer, $out );
}
}
}
######################################################################
+#########
sub read_meta{
my $socket = shift;
my ( $metalen, $metabyte);
$socket->read($metabyte, 1);
$metalen = unpack("C",$metabyte) * 16;
if( $metalen > 0) {
# We have NEW metadata! JOY
print "[$metalen metadata] " if $debug > 1;
my $metadata;
$socket->read($metadata, $metalen);
$metadata = unpack("A$metalen", $metadata);
print "\nMETADATA: [",$metadata,"]\n" if $debug > 1;
return $metadata;
}
else { return undef; }
}
######################################################################
+#########
sub write_stream{
my ($buf, $out, $new_metadata) = @_;
# count the overall chunk count for debug purpose
$num ++;
# NEW song got from metadata
if ( $new_metadata ){
my $track_name = $1 if $new_metadata =~ /^StreamTitle='([^
+;]*)';/i;
# if StreamTitle is empty probably is an advertisement. Fo
+re example:
# METADATA: [StreamTitle='';StreamUrl='';adw_ad='true';
# durationMilliseconds='20009';adId='12161';insertionType=
+'preroll';
print "\ncurrently playing:\t".
($track_name ? $track_name : '**advertisement**').
+"\n";
if ($out and fileno $out and $cache_size){
print "writing part number [$num] to current file\n" i
+f $debug;
# DOUBLE write of the current buff
print $out $buf ;
}
my $file_name;
($file_name = $track_name) =~ s/\s+/_/g;
$file_name =~ s/\/\\:\*\?\"<>\|//g;
$file_name.='.mp3';
# if StreamTitle is empty probably is an advertisement
$file_name = File::Spec->devnull() unless $track_name;
# set previous filename, but still how_many = 0
$previous_file{name} = $file_name;
# the new file
open $out, '>', $file_name or die "unable to write to $fil
+e_name!";
binmode $out;
if ( $cache_size > 0 ){
# PREPEND cache items to the new opened file
while ( my $cache_item = shift @cache ) {
print "writing cached part to new file: $file_name
+\n" if $debug;
print $out $cache_item;
}
}
# WRITE $buf to a new file
print "writing part number [$num] to new file: $file_name\
+n" if $debug;
print $out $buf;
}
# no new track..
else {
print "$num " if $debug > 1;
# WRITE $buf to the already opened file
if ( $out and fileno $out ){
print $out $buf or die;
}
# check previous_file if needed to be appended
if ( $previous_file{name} and $previous_file{how_many} ){
print "appending part to previous file too\n" if $debug;
open my $oldfh, '>>', $previous_file{name} or
die "unable to open $previous_file{name} in ap
+pend mode!";
binmode $oldfh;
print $oldfh $buf or die "unable to write!";
close $oldfh or die "unable to close filehandle!";
$previous_file{how_many}--;
}
else{
$previous_file{name} = undef;
$previous_file{how_many} = $cache_size ;
}
}
# cache rotates..
if ( $#cache == $cache_size - 1 ){
shift @cache,
}
push @cache, $buf;
# return the current file handle
return $out;
}
__DATA__
=head1 NAME
C<mp3streamcutter.pl>
This program open an mp3 stream and save songs to distinct files. It's
+ intended
to understand the ICY protocol and not intended to save copirighted da
+ta.
=head1 SYNOPSIS
mp3streamcutter.pl -url URL [-agent STRING -extraparts N -debug 0-
+2]
--url URL
is the only necessary argument. Url must be complete of the protoc
+ol
--agent STRING
you can use a custom user-agent to send to server during the conne
+ction.
Agent defaults to Stream-Cutter-v with the version number of the p
+rogram
appended. You can find useful to use the string WinampMPEG/2.9 if
+refused
by some server
--extraparts N
This parameter governs how many extra parts of the stream have to
+be prepended
to a new file (via cache) and appended to the previous file (via
reopening and appending). --extraparts defaults to 2 that is the b
+est I found
to have an entire song to the correct file and not to much junk in
+ it (parts
of other songs). --cache is an alias for --extraparts
--debug 0-2
With -debug 0 only few details of the server and the title of the
+current song
will be displayed.
With -debug 1 also headers received from the server are shown and
+all operations
involving new files creation and extra parts possibly (see --extra
+parts) wrote
to these files
Debug level 2 will display also each metadata received (if it cont
+ains data) and
a progressive number for each chunk of music received
=head1 DESCRIPTION
This program was inspired by a post wrote by qbxk for perlmonks (see r
+eferences).
The core part of the program is just a rewrite of the original code by
+ qbxk
The ICY protocol is not well documented. It's build on top of the HTTP
+ one. This
program can help you to understand it in a better way. Basically music
+ chunks are
intercalated with metadata chunks at the position given by the C<icy-m
+etaint> header
value. At this position you will find a lone byte indicating the lengt
+h of the
following metadata. If this byte is not 0 but N, then the following N
+bytes will be
of metadata. Normally in the metadata you find the C<StreamTitle> cont
+aining the title
of the current song. You can also find the C<StreamUrl> generally empt
+y and other things
like C<adw_ad> related to advertisements, followed by the duration of
+the advertisement
and other characteristics of the advertisement.
So a typical chunk of metadata for a new song in the stream will be li
+ke:
C<StreamTitle='Beethoven - Goldberg Variations';StreamUrl='';>
or sometimes just like:
C<StreamTitle='The Clash - Loose this skin';>
without the C<StreamUrl> part, while an advertisemente will look like:
C<StreamTitle='';StreamUrl='';adw_ad='true';durationMilliseconds='2000
+9';adId='12161';insertionType='preroll';>
The current version of the program will try to skip advertisements che
+cking
for empty C<StreamTitle> and then using C<File::Spec>'s C<devnull()> a
+s filename to save the stream.
In the headers of the HTTP request you had to ask for C<Icy-MetaData>,
+ then the server will answer
with various icy headers, notably C<icy-metaint> that is the dimension
+ of music chunks.
After each chunk there will be a byte containing the lenght of the fol
+lowing metadata.
If this is 0 it means no metadata will follow, but if it is a number a
+ correnspondant
number of bytes have to be read to have the metadata back, typically t
+he title and the author.
The problem is that the title will arrive when the song already starte
+d, so I decided to
add a cache (see C<--extraparts> argument) to append and prepend chunc
+ks to songs.
This way you will have probably unneeded data at start and at the end
+of each file but for
sure the entire song.
Let's say Icy-MetaData is 5 (generally is 16k), you have a situation l
+ike ( '=' it's a chunk):
-unknown song(1)------ -------------- The Clash - Loose This Skin -
+------ ...
| |
| |
STREAM-> = = = [0] = = = = = [3][*][*][*] = = = = = [0] = = = = = [0
+] = = = ...
| | | | | | | | |
+ |
unknown song | new song | | | | ------ The Clash - Loo
+se This Skin
| | | | |
empty metadata | ------------- metadata with new tit
+le
|
length of metadata
(1) about unknown song: probably you never get an unknown song: I su
+spect that ICY protocol
will send icy metadata as first part of a brand new response.
=head1 REFERENCES
See the original post by qbxk at L<perlmonks|https://www.perlmonks.org
+/index.pl?node_id=534645>
L<a post about ICY protocol|https://stackoverflow.com/questions/491106
+2/pulling-track-info-from-an-audio-stream-using-php/4914538#4914538>
L<The ICY protocol explained|http://www.smackfu.com/stuff/programming/
+shoutcast.html>
L<A very datailed tutorial|https://thecodeartist.blogspot.com/2013/02/
+shoutcast-internet-radio-protocol.html>
L<a not complete but useful description of ICY|https://www.radiotoolbo
+x.com/community/forums/viewtopic.php?t=74>
L<a technical article about streaming networks|https://people.kth.se/~
+johanmon/dse/casty.pdf>
=head1 AUTHOR
This program is by Discipulus as found in perlmonks.org with the fund
+amental
inspiration of the above mentioned qbxk
This program is licensed under the same terms of the Perl languange.
For a very long time, I've been using a Nokia 6303 classic phone. I was very satisfied with that phone. the built-in camera made good photos, I could use the music player to listen to music through headphones, and the user interface was usable.
You may recall that back in 2016, I even wrote a perl script to decode the contact lists after that phone backs it up into a zip file (with .NBF extension) containing indivdual files for each contact. That script exports phonebook entries into a semicolon-separated file with lines like this:
That's a simple entry. Some lines list additional data, such as multiple phone numbers and possibly text notes in the same entry separated by a semicolon. I never figured out how to make a backup file that the phone could import though. In fact I once had to restore all the backups by hand.
In 2017-11, I bought a Nokia 216 as a spare phone, because I figured that if I lose my phone or it becomes non-functional, I'd like to have a spare phone at hand immediately. That one only has a much worse camera, but that didn't matter anymore, because I had a pretty good compact camera now. I charged the phone and verified that it worked, then put it in a drawer.
In 2018-08, I lost the Nokia 6303. I cleaned it with too much water, which in itself wouldn't have been a problem, but I then put the battery back in the phone before it dried properly. The phone turned on, but went off after a few seconds, and I couldn't revive it after drying. I'd like to add that this was the second time the phone got wet, it has survived falling into the toilet once before.
So I mourned for the old phone, but was happy that I had the foresight to have bought a replacement earlier. For a few days.
I actually also had the foresight to have most of the important phone numbers copied to the SIM card, so I could transfer those phone numbers to the new phone by copying from that, and entered a few more important ones from the dump of the backup, so I had like fifty important phone numbers in the phone. You may ask why I don't just transfer all phone numbers through SIM cards then, since SIM cards are pretty cheap, and I have several old spare ones in my drawer. The problem is that the contact list stored on SIM cards has some big limitations: names can't be longer than about 15 bytes (some characters take more than one byte, I don't know the exact rule), the card can only store 250 contacts (I already had more than that back then), and the card can't store additional information such as notes.
Anyway, I at least got a phone that I could use for a temporary basis, and transfering the whole contact list was something that could wait a few days. But since I actually tried to use the phone for other tasks, and it turned out to be a disaster.
It only took a few days to find out how terrible the user interface of the Nokia 216 was. How I raged!
Read more... Rant on how terrible the Nokia 216 is (3 kB)
There are only three good things I can say about the Nokia 216: it generally reacts quickly enough to keypresses, it accepts two SIM cards, and it's possible to import a contact list prepared on a computer.
Here's how I imported the contact list. After finding out the limitations of the phone, First, I edited the semicolon-separated backup file to shorten the names and otherwise clean up the list. While I was there, I fixed all the names to have the correct letters, because some of the entries actually had names inherited from one of the two even older phones, which didn't have a full character set, so they had characters like "ŕ" instead of "á" and "ö" instead of "ő" I never bothered to fix that on the 6303, even though that one already supported all letters of Hungarian, and I entered all the new names with the correct characters. Then I used a messy perl script to verify that the list of contacts looks fine, and convert them to the format that the 216 accepts, which I could reverse engineer from an exported contact list in a few tries. Here's the code, with a few details omitted.
Two phone numbers in the same entry are handled by writing it as two entries, but extra notes are discarded.
You must copy that file to the SD card (either with an SD card reader or through a USB cable) as "/Backup/backup.dat", then restore the backup in the settings. Note that this will erase the existing contacts in the memory of the phone (but not the SIM card).
Read more... More about how the Nokia 216 sucks (413 Bytes)
Anyway, I eventually set off to try to buy a better phone. That's not easy. It's hard to find reviews of cheap feature phones, or find copies that I can try without having to buy them.
Copy the file from the Nokia 216 to the computer. Here I used the default filename "backup.dat" (I actually save the backups in more sensible names including a date so I can distinguish backups, but this is just an example). Get the backup rewritten with this one-liner:
perl -we 'while (<>) { if (/^\r\n\z/) {} elsif ($c) { $c=0; /^(.*);;;\
+r\n\z/ or die "nc $_"; $f=";CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:"
+; print "N$f;$;$1;;;\r\nFN$f$1\r\n"; } elsif (/^N;/) { /^N;ENCODING=Q
+UOTED-PRINTABLE;CHARSET=UTF-8:;=\r\n\z/ or die; $c=1; } elsif (/^TEL;
+/) { /^TEL;[A-Z]+;(CELL:[+0-9#*p]+)\r\n/ or die qq(tel $_); print "TE
+L;CELL:$1\r\n"; } elsif (/^(BEGIN:|VERSION:|END:)/) { print } else {
+die "parse $_" } }' backup.dat > backup.vcf
Copy the output file ("backup.vcf") onto the CAT B30, and this time you can use any filename and any directory, as long as the extension is ".vcf". Delete the existing phone numbers from the contacts list, because the CAT B30 doesn't do this automatically, it merges the contact list. Then import the contacts from the contacts menu by selecting that file on the SD card.
(This script doesn't handle assigning the custom ringtones to family members directly, I'll set those directly in the phone.)
I wrote a new module for converting markdown files to HTML for use with Dancer2. No perl code is necessary to use! Just make some entries in your config.yml file and you are done. I think it'll be very useful. For example, you can now just throw your copy into a repo with markdown files and clone it to your local hard drive and you are done ever having to mess with HTML.
It has two basic modes of operation right now: convert a single markdown file to a single HTML document or convert all markdown files in a directory to an HTML document. It can can also automatically generate a table of contents for the file that links to the headers in the content.
I've used it to generate a pretty nifty looking tutorial that's super easy to navigate with a table of contents that's actually useful. It's all on one page so no annoying clicking around.
Some time ago, i saw those fake "emergency stop" buttons on Amazon that play a funny audio sample when you press them. But they were too expensive and - much more important - you can't change the sound on them. So i implemented my own.
First i hooked up a real (non-latching) emergency button to a Raspberry Pi GPIO pin and a set of rather ancient and crappy Desktop speakers to the analog out of the Pi. I also prepared some audio files in raw format: Decoding MP3 costs performance, so it is not as instantaneous as playing a raw file, also the Pi runs some other performance critical stuff and using raw files instead of wav/mp3 fixed some timing issues.
A short button press plays one of the 3 "bullshit detected" samples. Holding the button for about 1 second before releasing plays the 30 second jeopardy "thinking" music.
Note: This is actually the "dumb" version of the script. My local implementation also triggers some LED display for the bullshit alerts and runs an analog meter (via an Arduino) from 100% to 0% while the jeopardy music ticks down the seconds.
Calculates the highest score possible from the letters given, taking into account any bonuses on the squares to be covered.
(Rudimentary tool that does not handle combinations with the words already on the board).
My local newspaper has a Scrabble-based game that involves simply finding the highest scoring word from seven letters and bonus tile positions provided. Note: quite often the highest scoring word according to the newspaper is not found in my words list :-(
Specify double- and triple-word bonuses with -dw and -tw, and double- and triple-letter bonuses with -dl=N and -tl=N where N is the letter position.
Examples:
$ perl scrabble.pl eoaprzn
Found 13,699 1-7 letter strings in eoaprzn.
Found 57 words in eoaprzn.
zap : 14
$ perl scrabble.pl eoaprzn -dw
Found 13,699 1-7 letter strings in eoaprzn.
Found 57 words in eoaprzn.
zap : 28
$ perl scrabble.pl eoaprzn -tl=3
Found 13,699 1-7 letter strings in eoaprzn.
Found 57 words in eoaprzn.
raze : 33
use strict; use warnings; use feature 'say';
use Path::Tiny;
use Algorithm::Permute;
use Number::Format 'format_number';
use List::Util 'uniq';
use Getopt::Long;
my @dl;
my @tl;
my $dw;
my $tw;
my $debug;
GetOptions(
'dl=i' => \@dl,
'tl=i' => \@tl,
'dw' => \$dw,
'tw' => \$tw,
'v' => \$debug,
);
my $input = shift or die 'Died: No input!';
my $length = length $input;
my @input_chars = split '', $input;
my $words_file = '/usr/share/dict/words';
my %words = map { $_ => 1 } path( $words_file )->lines({chomp =>
+ 1});
my %worth = (
a => 1, b => 3, c => 3, d => 2, e => 1,
f => 4, g => 2, h => 4, i => 1, j => 8,
k => 5, l => 1, m => 3, n => 1, o => 1,
p => 3, q => 10, r => 1, s => 1, t => 1,
u => 1, v => 2, w => 2, x => 8, y => 4, z => 10,
);
my @partials;
for (1 .. $length) {
my $P = Algorithm::Permute->new( \@input_chars, $_ );
while (my @res = $P->next) {
push @partials, join '', @res;
}
}
@partials = uniq @partials;
say sprintf 'Found %s 1-%s letter strings in %s.',
format_number(scalar @partials), $length, $input;
my %found = map { $_ => calc_score($_) } grep { $words{$_} } @partials
+ ;
say sprintf 'Found %s words in %s.', format_number(scalar keys %found)
+, $input;
for ( sort { $found{$b} <=> $found{$a} } keys %found ) {
say "$_ : $found{$_}";
last if not $debug;
}
###############
sub calc_score {
my $word = shift;
my $val;
$val += $worth{$_} for split '', $word;
$val += 50 if length $word == 7;
return $val + calc_bonus($word, $val);
}
sub calc_bonus {
my ($word, $val) = @_;
my @chars = split '', $word;
my $bonus = 0;
for (@dl) {
$bonus += $worth{ $chars[$_ - 1] } if $chars[$_ - 1];
}
for (@tl) {
$bonus += 2 * $worth{ $chars[$_ - 1] } if $chars[$_ - 1];
}
$bonus += $val if $dw;
$bonus += 2 * $val if $tw;
return $bonus;
}
__END__
update newer version, with more features, is on my github
use strict;
use warnings;
use URI::Escape;
use Getopt::Long;
use JSON::MaybeXS qw(encode_json);
my (@infiles, @outfiles, $script, $lineofcode, $browse, $help);
unless ( GetOptions (
"script=s" => \$script,
"line|oneliner|code|c=s" => \$lineofcode,
"inputfiles=s" => \@infiles,
"outputfiles|o=s" => \@outfiles,
"browse" => \$browse,
"help" => \$help
))
{
print "GetOpt::Long returned errors (see a
+bove), available options:\n\n".help();
exit;
}
if ($help){ print help(); exit 0;}
my $json = {};
if ($lineofcode){
$$json{cmdline} = "perl $lineofcode";
}
elsif ($script){
open my $fh, '<', $script or die "unable to read $script!";
while (<$fh>){
$$json{script} .= $_ ;
}
$$json{script_fn} = $script;
$$json{cmdline} = "perl $script";
}
else{
die "Please feed at least one script using -script or a line of pe
+rl code via -code\n\n".help();
}
if ( $infiles[0] ){
$$json{inputs}=[];
}
foreach my $in (@infiles){
open my $fh, '<', $in or die "unable to read $in!";
my $file = { fn => $in};
while (<$fh>){
$$file{text}.=$_;
}
push @{$$json{inputs}},$file;
}
if ( $outfiles[0]){
$$json{outputs} = \@outfiles ;
}
my $url = 'https://webperl.zero-g.net/democode/perleditor.html#'.(uri_
+escape( encode_json( $json ) ));
if ($browse){
if ($^O =~/mswin32/i) {exec "start $url"}
else{ exec "xdg-open $url"}
}
else{
print $url;
}
####
sub help{
return <<EOH;
$0 USAGE:
--script file|--code line [--inputfile file [--inputfile file] --o
+utputfile file [--outputfile file] --browse]
$0 -script script.pl
$0 -script script.pl [ -inputfile file1.txt -inputfile file2.txt
+-outputfile file3.txt -browse]
$0 -code "-e 'print qq(Hello WebPerl!)'"
$0 -code "-e 'print qq(Hello WebPerl!)'" [ -i infile1.txt -i infil
+e2.txt -o outfile3.txt -browse]
--script -s accept a perl program filename as only argument.
Both --script and --code make no sense: just specify one.
--code -c is intended to be used to pass a oneliner. The execu
+table name, aka perl, will be
prepended automatically. Any perl switch must be explicitly passed
+ also -e
For example:
webperlizator.pl -code "-le 'print qq(Hello WebPerl!)'"
webperlizator.pl -code "-lne 'print \"found a b\" if /b/' file1.tx
+t" -i file1.txt -b
Pay attention on quotes suitable for you OS.
--inputfiles -i is for input files; more than one can be feed
--outputfiles -o is for output file and more than one can be passe
+d in
--browse -b open the default browser, hopefully, pointing to the W
+ebPerl right page
--help -h prints this help
EOH
}
This isn't really all that cool, but I'm posting it just in case someone might be interested. I'm trying to get back to my QRP transmitter/receiver project and had just wanted to convince myself that Pi attenuators properly reduced to the expected input and output impedance. Rather than do a couple by hand, I went full nerd and wrote code to do it instead.
Essentially, the code lets you create a network of resistors (via the build_impedance() function) and pi_pads (via build_pad()) and attach them together via the named ports. Once you've got the network built, remove all named nodes you don't care about and then tell it to generate the simplified network.
I introduce a Game AI statistics package on GitHub next week, here is some code for it, it was written in Perl6. It uses evolvable distribution populations for the math formulas :
class Population
{
has @.population;
method BUILD() {
.population = <>;
}
method add($x) {
push(.population, $x);
}
}
use Covariance;
role ThisCovariance { method cov($xpop,$ypop) {
return Covariance().Covariance($xpop,$ypop);
}
}
class Correlation does ThisCovariance {
method BUILD() {
}
method correlation($xpop,$ypop) { ### These are distribution a
+rgs
my $varx = $xpop.Variance(), $vary = $ypop.Variance();
my $cov = .cov($xpop, $ypop);
return $cov / (sqrt($varx) * sqrt($vary));
}
}
So I've been doing quite a bit of web development recently, and several of my HTML files use resources from CDNs, like jQuery or normalize.css.
While I'm developing, I refresh pages quite often, and also usually use my browser's development tools to disable caching.
This means that I hit the CDNs quite often, and aside from the speed and bandwidth usage, one of the CDNs actually started rate limiting me... oops.
In other projects, I'd usually just pull those resources onto my local server, keep them there, and be done with it.
But the stuff I'm currently working on is for distribution, so I'd like to keep the CDN URLs in the HTML, and not have to rewrite them by hand.
Enter git filters: Documented in the Git Book Chapter 8.2 and in
gitattributes, they provide a way to pipe source code files through an external program
on checkout, as well as on diff, checkin, and so on. This allows you to have one version of a file checked into the repository, but to
use a filter to make changes to the files that actually end up in the working copy on your machine. These changes are also reversed by the filter
and not checked back into the repository on checkin, and don't show up in any commands like git diff, git status, etc.
So in this case, the files I want to have in the repository will have lines that look something like this:
So, your project is going fine, your codebase is groing fast. But now your have the problem
that some of your processes have to communicate with each other. Maybe, some temperature
sensor needs to report its sensor value every few seconds to the central heating system.
Maybe the central heating system needs to know if the windows are open and close them
before heating the house. Another process wants to count how many times the door has been
opened and log the sum once a minute...
Net::Clacks to the rescue!
The Net::Clacks modules implement a client/server based interprocess messaging. Without going
too much into the internals of the protocol, a client can either send notifications ("event
xy has just happened") or values ("the reading for sensor xy is now 42"). Other clients may (or may not)
choose to listen to those broadcasts.
Net::Clacks also implements Memcached-like value storage and retrieval. So instead of broadcasting,
a value can be stored, read out, incremented, decremented and deleted.
A note on security: Currently, the system only implements a shared-secret type thing (all clients in a clacks network use the same
username/password). This will get changed in the future. I'm planning to make it so that you can override the authentication checks with
your own function and return which permissions the client has. But that is not yet implemented.
Let's do a simple example project: Server, chatclient, chatbot and a clock process to trigger some actions at the start of every minute.
Read more... Click to see the rest of the rather long post (15 kB)
"For me, programming in Perl is like my cooking. The result may not always taste nice, but it's quick, painless and it get's food on the table."
I've been working quite a lot with ZPL compatible label printers (Zebra, TSC) the last few years. When working with complex forms or large graphics, printing can be quite slow, because the printer has to - basically - calculate everything pixel-by-pixel for every single label. Not to mention the fact that the standard ZPL image format is not... well "nice" to work with with standard open source tools.
ZPL has a way to store a rendered label, which it can reuse later quite a bit faster (in most cases). Basically, you load in the saved, pre-rendered label and then add you dynamic content.
Here is the Perl script to convert a PNG file to ZPL, including the "save" command. E.g. this generates the ZPL file to pre-render the image and save it to the printers flash memory.
#!/usr/bin/env perl
use strict;
use warnings;
use GD;
use Data::Dumper;
my $xoffs = 20;
my $yoffs = 20;
my $minwhite = 200;
my $verbose = 0;
my $image = GD::Image->new('examplelogo.png');
my $w = $image->width();
my $h = $image->height();
print "Image size: $w x $h\n";
open(my $ofh, '>', 'savelogo.zpl') or die($!);
# Start of form;
print $ofh "^XA\n";
for(my $x = 0; $x < $w; $x++) {
for(my $y = 0; $y < $h; $y++) {
my $index = $image->getPixel($x,$y);
my ($r,$g,$b) = $image->rgb($index);
if($r < $minwhite) {
print "#" if $verbose;
print $ofh '^FO' , ($x*1) + $xoffs, ',' , ($y*1) + $yoffs,
+ '^GB1,1,1,B,0^FS', "\n";
} else {
print " " if $verbose;
}
}
print "\n" if $verbose;
}
# Save graphic to flash mem
print $ofh "^ISR:LOGO.GRF,Y\n";
# End of form
print $ofh "^XZ\n";
close $ofh;
$xoffs and $yoffs change the upper left starting point where the image is drawn and $minwhite sets the threshold of what is considered a white vs. black pixel. This generates ZPL code like this (abbreviated), setting one pixel black per line:
Of course, this can trivially be used for dynamic content. But i leave it to the reader to figure out how to load label.zpl, replace XXCAPTIONXX with an increasing number, saving it to tmp.zpl and calling lpr...
You also can get the whole example (including a badly made example png) with Mercurial SCM:
In addition to Perl and PDL, one of my favourite topics is cryptography, specifically cryptanalysis.
One 'common' cryptanalytical attack, for which modern ciphers are designed against is differential cryptanalysis. Some older ciphers are vulnerable to this attack and various tutorials exist to teach differential cryptanalysis. One of these is by Jon King against the FEAL cipher and is located here.
One aspect of the differential cryptanalysis attack is to enumerate all potential differentials against the non-linear round function. The below code performs this analysis against the FEAL-4 cipher's round sub-function 'G'. It successfully identifies the two fixed input differentials.
Enjoy!
#!/usr/bin/env perl
use 5.020;
use warnings;
use autodie;
use PDL;
use PDL::NiceSlice;
# This code attempts to find all differential characteristics in the
# FEAL-4 cipher round subfunction 'G'.
#
# Reference: http://theamazingking.com/crypto-feal.php
#
#
# 'G' function is addition of a, b and x, then bitwise rotate left
# by 2 bits
# a, b, x and the final value are all 8 bits.
# For our purposes, x can be ignored, as it's constant 0 or 1
#
# a
# |
# x -> [+] <- b
# |
# [<<<]
# |
# OUT
#
# Perform addition
my $G = sequence( byte, 256 ) + sequence( byte, 256 )->transpose;
# Bitwise rotation
$G = ( $G << 2 ) | ( $G >> 6 );
# At this point, $G contains all possible inputs for a and b, and
# the associated output value
#
# Now we wish to find all differentials throughout this function
#
# To do this, we need to find differentials between each possible
# inputs to 'a', and 'b' and observe the differential in the result
#
# There are two known differentials for this function. A differential
# value of 0 and 0x80 (128) for 'a' will always return a constant
# differential output (0 and 2) respectively.
# Calculate the differential table
my $diffs = $G ^ $G ( (0) );
# Find the minimum and maximum value for each differential
my ( $min, $max ) = minmaxover($diffs);
# Print index of differentials where minimum and maximum value are
# equal. As the index is also in the input value, this returns the
# actual differential:
print "Contant differentials for input differentials of: ",
which( $min == $max ), "\n";