<?xml version="1.0" encoding="UTF-8"?>



<rss version="2.0" xmlns:blogChannel="http://backend.userland.com/blogChannelModule">

    <channel>
        <title>cufp</title>
        <link>http://prlmnks.org/list/</link>
        <description>RSS feeds from perlmonks.org</description>
        <language>en</language>
        <ttl>5</ttl>

        

<item>
    <title>review a show (teamster_jr)</title>
    <link>http://prlmnks.org/html/580880.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/580880.html</guid>

    <description>
        It&#39;s been a very long time since i posted - my new job allows me significantly less obfu time :).&lt;br&gt;Here&#39;s a quick something:&lt;p&gt;My friends and I are off to see a kung fu demonstration by a bunch of shaolin monks on sunday.  One of my friends can&#39;t make it and said:&lt;p&gt;&quot;I can&#39;t do this weekend, so I expect a 1000-word review first thing on Monday.&quot;So I knocked this up:&lt;pre class=&quot;block_code&quot;&gt;@d = map { rand &gt; .9 ? &quot;kick&quot; : rand &gt; .8 ? &quot;jump&quot; : &quot;punch&quot; } 0 .. 1000;for (    &quot;&lt;b&gt;leap through burning hoop&lt;/b&gt;&quot;,    &quot;&lt;b&gt;lie on bed of spikes&lt;/b&gt;&quot;,    &quot;&lt;b&gt;smash block over head&lt;/b&gt;&quot;  ){    @a = split / /;    splice( @d, rand($#d), 1 + $#a, @a );}open(FH, &quot;&gt;kung_fu_review.html&quot;);print FH &quot;&lt;html&gt;&lt;body&gt;@d&lt;/body&gt;&lt;/html&gt;&quot;;close(FH);&lt;/pre&gt;an instant (not entirely serious :)) 1000 word review (in the file kung_fu_review.html) of what i&#39;m sure will be an amazing spectacle (if they keep to my script).&lt;br&gt;Alex&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; it seems my review was spot on :)
    </description>
</item>

        

<item>
    <title>Sports Conference Rankings, Colley Matrix Style (Zaxo)</title>
    <link>http://prlmnks.org/html/580484.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/580484.html</guid>

    <description>
        &lt;p&gt;It&#39;s (US) football season now, and arguments over Strength of Schedule and the iniquity of zebras are heard across the land.&lt;/p&gt;&lt;p&gt;This is [http://www.colleyrankings.com|Matthew Colley&#39;s] elegant method of calculating a probability-like ranking from the results of contests. I won&#39;t go into the mathematical details or properties of the method - there is a paper at Colley&#39;s site which gives that.&lt;/p&gt;&lt;p&gt;Colley uses this method to rank all Division-1A teams as part of the &quot;computer&quot; segment of the all-important BCS ratings. It is distinguished by its simplicity and lack of mystery tweaks.&lt;/p&gt;&lt;p&gt;The magnificent [cpan://PDL] module is ideal for carrying out these calculations. Here, I&#39;ve applied them to intra-conference games only, to get a current conference ranking. The data is hard-coded in this simple version, with enough information in comments to let you replace it with your own favorite conference&#39;s results. It is more flexible and convenient to get the data by a database query or by web scraping.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl    # -*-EPerl-*-use PDL;my @becteams = (    &#39;Pittsburgh&#39;,    &#39;Louisville&#39;,    &#39;Rutgers&#39;,    &#39;West Virginia&#39;,    &#39;South Florida&#39;,    &#39;Connecticut&#39;,    &#39;Syracuse&#39;,    &#39;Cincinnati&#39;);# $C is the Colley Matrix. It depends only on the schedule# of games already played. Rows and columns are indexed in# the same order, by teams. The diagonal elements are the# number of games played plus two. Off-diagonals are zero # for no game yet played for the indexed teams, or minus one# for a game played. It contains nothing about the result of# the games. It&#39;s obviously a symmetric matrix.my $C = pdl([   # UP UL RU WV SF CT SU UC    [ 5, 0,-1, 0, 0, 0,-1,-1], # Pittsburgh    [ 0, 4, 0, 0, 0, 0,-1,-1], # Louisville    [-1, 0, 4, 0,-1, 0, 0, 0], # Rutgers    [ 0, 0, 0, 4, 0,-1,-1, 0], # West Virginia    [ 0, 0,-1, 0, 5,-1, 0,-1], # South Florida    [ 0, 0, 0,-1,-1, 4, 0, 0], # Connecticut    [-1,-1, 0,-1, 0, 0, 5, 0], # Syracuse    [-1,-1, 0, 0,-1, 0, 0, 5]  # Cincinnati]);# $wl is a column vector containing win and loss information.# For each team in the same order as $C is indexed, the value# is numerically 1 + (wins - losses)/2.my $wl = pdl([[ 3/2],[ 2 ],[ 2 ],[ 2 ],[ 1/2],[ 0 ],[-1/2],[ 1/2]]);#              Pitt   UL    Rut   WVU    USF  UConn   SU    Cincymy $c = $C-&gt;inv;my $r = $c x $wl;my %rating;@rating{@becteams} = list $r;{    my $ct = 1;    for (sort {$rating{$b}&lt;=&gt;$rating{$a}} keys %rating) {        my $out = pack &#39;A4 A20 A6&#39;, $ct++, $_, sprintf &#39;%5.4f&#39;, $rating{$_};        print $out, $/;    }}__END__1   Rutgers             0.74432   Louisville          0.67793   West Virginia       0.63394   Pittsburgh          0.59125   Cincinnati          0.43106   South Florida       0.38617   Syracuse            0.28068   Connecticut         0.2550&lt;/pre&gt;&lt;p&gt;Congratulations to Rutgers, their higher rating for the same record as Louisville and West Virginia comes from having beaten tougher teams so far. In the Big East everybody plays everybody, so that advantage will level off by the end of the season. A conference which is too large to allow all-pairs play admits more interesting use of this method.&lt;/p&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-82147&quot;&gt;&lt;p&gt;After Compline,&lt;br/&gt;Zaxo&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>What are YOUR friends listening to? (ailivac)</title>
    <link>http://prlmnks.org/html/580481.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/580481.html</guid>

    <description>
        &lt;p&gt;Last month I decided to jump on the last.fm (aka audioscrobbler) bandwagon. For those not in the know, this is a service where your music player program (using a special plugin) sends real-time updates about what music you&#39;re playing to a website, where they are entered into a database awaiting all sorts of statistical analysis you never thought possible (or at least not worth anybody&#39;s time). These range from simple things like a list of tracks you listened to recently, to weekly and monthly charts of your favorite artists and locating &quot;neighbors&quot; from among the other users who like the same things you do. And, as you would expect from a social-type website, you can establish friendships with other users.&lt;/p&gt;&lt;p&gt;Most of this information is available through web pages and also XML/RSS web services ready to be consumed by news aggregators or any other programs that people (like me) think up. One of the simplest web services lets you download an RSS feed with the last 10 tracks that any user played. Great, I can use Liferea to see what songs I&#39;ve just listened to. This is of course sort of useless since by the time Quod Libet sends the song name to last.fm &lt;em&gt;and&lt;/em&gt; Liferea pulls a fresh copy of the RSS, I&#39;m probably onto another song already. More interesting is the fact that I can monitor one of my friend&#39;s listining habits in near real-time. But still, a single user isn&#39;t very interesting...&lt;/p&gt;&lt;p&gt;Another web service allows you to download an XML list of a user&#39;s friends. Combining this information with the feeds for individual users&#39; recent tracks would actually provide some interesting information, but unfortunately they don&#39;t have a web service for this. HTTP requests, XML parsing, data sorting... Perl to the rescue! So I wrote the following script (it doesn&#39;t actually use the aforementioned RSS feeds, but an XML service that I figured would be easier to parse) to find out who my friends are, find out what they&#39;ve been listening to, and write the results in text, RSS, or Data::Dumper format.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;use warnings;use XML::Parser;use Getopt::Lucid qw/:all/;use LWP::Simple;use Data::Dumper;use Heap::Simple;use POSIX qw/strftime/;my @friends;sub fparse_start {    my $expat = shift;    my $elt = shift;    if ($elt eq &#39;user&#39;)    {        my %attr = @_;        push @friends, $attr{username};    }}my @tracks;my $insertpoint = -1;sub tparse_start {    my $expat = shift;    my $elt = shift;    my %attr = @_;    if ($elt eq &#39;track&#39;)    {        push @tracks, [];    } elsif ($elt eq &#39;artist&#39;)    {        $insertpoint = 0;    } elsif ($elt eq &#39;name&#39;)    {        $insertpoint = 1;    } elsif ($elt eq &#39;url&#39;)    {        $insertpoint = 2;    } elsif ($elt eq &#39;date&#39;)    {        $tracks[$#tracks]-&gt;[3] = $attr{uts};        $insertpoint = 4;    }}sub tparse_char {    my $expat = shift;    my $string = shift;    if ($insertpoint &gt; -1)    {        $tracks[$#tracks]-&gt;[$insertpoint] = $string;        $insertpoint = -1;    }}my @opts = (            Param(&#39;user|u&#39;)-&gt;required,            Param(&#39;mode|m&#39;)-&gt;default(&#39;text&#39;)           );my $opt = Getopt::Lucid-&gt;getopt( \@opts );my $user = $opt-&gt;get_user();my $mode = $opt-&gt;get_mode();my $xp = new XML::Parser(Handlers =&gt; {Start =&gt; \&amp;fparse_start});my $content = get(&quot;http://ws.audioscrobbler.com/1.0/user/${user}/friends.xml&quot;);if ($content){    $xp-&gt;parse($content);    my $alltracks = new Heap::Simple(elements =&gt; [Array =&gt; 4],                                     order =&gt; &#39;&gt;&#39;);    foreach my $fr(@friends)    {        my $content = get(&quot;http://ws.audioscrobbler.com/1.0/user/${fr}/recenttracks.xml&quot;);        if ($content)        {            my $xp = new XML::Parser(Handlers =&gt; {                                                  Start =&gt; \&amp;tparse_start,                                                  Char =&gt; \&amp;tparse_char                                                 });            $xp-&gt;parse($content);            foreach my $t(@tracks)            {                $alltracks-&gt;insert([ $fr, @$t ]);            }            @tracks = ();        } else        {            print &quot;error getting tracks for $fr\n&quot;;        }    }    if ($mode eq &#39;dump&#39;)    {        for (1..$alltracks-&gt;count())        {            my $tr = $alltracks-&gt;extract_top();            print Dumper(\$tr);        }    } elsif ($mode eq &#39;text&#39;)    {        for (1..$alltracks-&gt;count())        {            my $tr = $alltracks-&gt;extract_top();            print &quot;User: $tr-&gt;[0]\n&quot;;            print &quot;Track: $tr-&gt;[1] - $tr-&gt;[2]\nDate: $tr-&gt;[5]\n&quot;;        }    } elsif ($mode eq &#39;rss&#39;)    {        print &quot;&lt;?xml version=\&quot;1.0\&quot; encoding=\&quot;utf-8\&quot;?&gt;\n&quot;;        print &quot;&lt;rss version=\&quot;2.0\&quot;&gt;\n&quot;;        print &quot;&lt;channel&gt;\n&quot;;        print &quot;&lt;language&gt;en&lt;/language&gt;\n&quot;;        print &quot;&lt;pubDate&gt;&quot;, scalar localtime, &quot;&lt;/pubDate&gt;\n&quot;;        print &quot;&lt;ttl&gt;10&lt;/ttl&gt;\n&quot;;        print &quot;&lt;title&gt;${user}&#39;s Friends&#39; Recent Tracks&lt;/title&gt;\n&quot;;        for (1..$alltracks-&gt;count())        {            print &quot;&lt;item&gt;\n&quot;;            my $tr = $alltracks-&gt;extract_top();            my $title = &quot;$tr-&gt;[0] - $tr-&gt;[1] - $tr-&gt;[2]&quot;;            my $date = strftime &#39;%a, %d %b %Y %H:%M:%S %z&#39;, localtime $tr-&gt;[4];            print &quot;&lt;title&gt;$title&lt;/title&gt;\n&quot;;            print &quot;&lt;link&gt;$tr-&gt;[3]&lt;/link&gt;\n&quot;;            print &quot;&lt;pubDate&gt;$date&lt;/pubDate&gt;\n&quot;;            print &quot;&lt;/item&gt;\n&quot;;        }        print &quot;&lt;/channel&gt;\n&quot;;        print &quot;&lt;/rss&gt;\n&quot;;    } else    {        print &quot;Undefined output mode $mode\n&quot;;    }} else{    die &quot;Badness!&quot;;}&lt;/pre&gt;
    </description>
</item>

        

<item>
    <title>Natural Doc - Create fancy documentation for your Perl code. (Ace128)</title>
    <link>http://prlmnks.org/html/578742.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/578742.html</guid>

    <description>
        Hey!&lt;br /&gt;&lt;br /&gt;I just had to post about this little tool since it&#39;s so cool and I was looking for such for ages, and if it wasnt for pure chance, I wouldnt have found it and I would have tried to do my own version wasting time. So, to avoid others do the same &quot;misstake&quot;, I post about it here.&lt;br /&gt;&lt;br /&gt;Besides, its also MADE in Perl! How cool is that? :)&lt;br /&gt;&lt;br /&gt;What am I talking about? Well, [http://www.naturaldocs.org]&lt;br /&gt;&lt;br /&gt;So, I can just do:&lt;pre class=&quot;block_code&quot;&gt;# Function: myFunc## Arguments:#  1. - This object.#  2. - Path to file.sub myFunc {  my ($self, $path) = @_;}&lt;/pre&gt;Runing the tool on this you get some really fancy documentation. (Check out [http://www.naturaldocs.org/documenting.html])&lt;br /&gt;&lt;br /&gt;Well, thanks for your time and hope you like it.&lt;br /&gt;&lt;br /&gt;/ Ace
    </description>
</item>

        

<item>
    <title>Efficiently selecting a random, weighted element (jimt)</title>
    <link>http://prlmnks.org/html/577433.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/577433.html</guid>

    <description>
        &lt;p&gt;This problem was originally presented to me by a co-worker as such,&lt;/p&gt;&lt;p&gt;&lt;i&gt;I have a set of 100 files, and I want to randomly choose 5 of them. However, I want to weight the selection of each file based upon the number of words in the file. More words == greater chance it will be randomly selected. The files could contain between 100 - 3,000 words. What&#39;s a good way to do this?&lt;/i&gt;&lt;/p&gt;&lt;p&gt;First of all, the approach I&#39;m going to detail here is specific to this example, but can easily be adapted to any random selection of weighted values. It should scale very easily to very large data sets (number of files, in this case), with very large weighting information (number of words, in this case). This post is mostly pseudo-code and explanation, not a functional piece of code. This solution has probably been created by other people before.&lt;/p&gt;&lt;p&gt;Okay, for sake of example, we&#39;re going to start off with 5 files.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;my @files = qw(file_a.txt file_b.txt file_c.txt file_d.txt file_e.txt);&lt;/pre&gt;&lt;p&gt;And we want to randomly choose 2 of the files. But, we want to weight our selections based off of the number of words in the file. More words in a file == more likely the file will be chosen.&lt;/p&gt;&lt;p&gt;Your first thought might be to build an array of all of the words in all of the files, then pick a random index, and determine which file the word is in. Note - you would need to pre-cache which word at which index is associate with which file. For example, the word &quot;the&quot; could appear at file_a.txt or file_b.txt. So you can&#39;t just randomly choose index 3, see &quot;the&quot; there, and know which file it&#39;s in. You have to know that index 3 =&gt; the =&gt; file_a.txt.&lt;/p&gt;&lt;p&gt;This is the first optimization. The words don&#39;t matter, only which file they&#39;re in. So instead of storing the word at each point, you can just store the file name. At this point, you&#39;ll need to be able to count the number of words in each file to get your weighting information. This is left as an exercise to the reader - use your favorite word counting widget. For example&#39;s sake, we&#39;ll say you end up with this structure:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;my %words_in_files = (&#39;file_a.txt&#39;=&gt; 10,&#39;file_b.txt&#39;=&gt; 1,&#39;file_c.txt&#39;=&gt; 3,&#39;file_d.txt&#39;=&gt; 5,&#39;file_e.txt&#39;=&gt; 10);&lt;/pre&gt;&lt;p&gt;Now you can build up an array where the first 10 elements are &quot;file_a.txt&quot;, the next one is &quot;file_b.txt&quot; and so on. For simplicity&#39;s sake, we&#39;ll display each file as its trailing letter (&quot;file_a.txt&quot; becomes &quot;a&quot;). This way, we can see our data:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;aaaaaaaaaabcccdddddeeeeeeeeee&lt;/pre&gt;&lt;p&gt;This approach is fully functional, but doesn&#39;t scale well. In our original problem, we had 100 files, with up to 3,000 words each. This is potentially a 300,000 element array, and it&#39;s just gonna get bigger if you add more files or words within the files. Don&#39;t get me wrong - perl can do it, but there&#39;s a better way.&lt;/p&gt;&lt;p&gt;The key is to realize that most of the information in that array is redundant. We&#39;re storing &quot;a&quot; 10 times. Do we really need to? Instead, we&#39;ll build a different data structure. In this structure, we&#39;ll store the file name, and the index at which the file begins. Externally, we&#39;ll also store a count of the total number of words. We end up with a structure along these lines:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;my @indexes_of_files = (# terminology: index 0 == &quot;file offset, index 1 == &quot;file name&quot;[ qw( 0  file_a.txt ) ],[ qw( 10 file_b.txt ) ],[ qw( 11 file_c.txt ) ],[ qw( 14 file_d.txt ) ],[ qw( 19 file_e.txt ) ],);my $total_number_of_words = 29;&lt;/pre&gt;&lt;p&gt;Feel free to use hashrefs instead of arrayrefs, they may be easier to work with. I used arrayrefs here for simplicity of code display in the example. Note that the order of the files in this data structure is arbitrary. Whatever order the files are assigned in this array is irrelevant, so long as their file offsets change as appropriate.&lt;/p&gt;&lt;p&gt;We now have a much more compact data structure. Our algorithm is easy - generate a random integer between 0 and &lt;i&gt;$total_number_of_words - 1&lt;/i&gt; (0..28, in this case). Let&#39;s say that we generated &quot;15&quot;.&lt;/p&gt;&lt;p&gt;Next, you need to search through the @indexes_of_files array to find the greatest file offset that&#39;s less than our generated number. Since the file information is in sorted order, a binary search can zip through the data in no time. Implementing the binary search (or whatever) algorithm is another exercise left to the reader.&lt;/p&gt;&lt;p&gt;However you find your data, you&#39;ll discover that you&#39;re looking at array element 4, which has file offset 14, corresponding to &quot;file_d.txt&quot;. Note that if you count off 15 ticks into the &quot;aaaa...b...cc...&quot; array drawn out above, you&#39;ll also reach a &quot;d&quot;, corresponding to file_d.txt.&lt;/p&gt;&lt;p&gt;You have now successfully chosen your first file, so you need to set up for subsequent ones. This is a 3 step op. One is easy, one is expensive, and one is tedious. First, the easy step.&lt;/p&gt;&lt;p&gt;Subtract from $total_number_of_words the length of the file you just chosen. In this case, file_d.txt has a length of 5 words, so $total_number_of_words becomes 24. You can re-calculate this length now using the the index of the element you were at and the index of the next element, you can cache it into the data structure, you can look up in the hash created earlier. Dealer&#39;s choice. But you need the length.&lt;/p&gt;&lt;p&gt;The &quot;expensive&quot; operation is simply to splice out the element at index &amp;#91;4&amp;#93;.&lt;/p&gt;&lt;p&gt;Finally, for all elements &gt;= the one you&#39;ve removed (&amp;#91;4&amp;#93;), subtract from their file offset the length of the file just removed (5, in this case). You&#39;ll end up with this data structure when you&#39;re done:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;my @indexes_of_files = (# terminology: index 0 == &quot;file offset, index 1 == &quot;file name&quot;[ qw( 0  file_a.txt ) ],[ qw( 10 file_b.txt ) ],[ qw( 11 file_c.txt ) ],# THIS FILE WAS REMOVED [ qw( 14 file_d.txt ) ],[ qw( 14 file_e.txt ) ], #this file offset was 19, is now 14.);my $total_number_of_words = 24;#previously 29&lt;/pre&gt;&lt;p&gt;And blam-o, you&#39;re set up to choose your next file, it&#39;s as if file_d.txt never existed, and you can repeat ad infinitum until you&#39;ve selected enough files out.&lt;/p&gt;&lt;h4&gt;Considerations&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;With tremendously long lists of data (for this example, say thousands or millions of files), you need to do a splice on a large array, and then run through all higher elements to do a subtraction. You&#39;re just iterating over an array and doing a subtraction on an integer, but it&#39;s still O(n). There may be fancier ways to do this w/o splicing or changing offsets, but I was unable to come up with an elegant one. They all seemed fragile and complicated relative to just decrementing the indexes. YMMV.&lt;/li&gt;&lt;li&gt;This can be applied to any set of data that you need to randomly select a value from based on a weighting value.&lt;/li&gt;&lt;li&gt;For smaller data sets, it may be simpler to just use the &quot;aaa...b...cc...&quot; approach of an array that lists all the file names. But it doesn&#39;t scale as well.&lt;/li&gt;&lt;li&gt;There may be something that does this efficiently already on CPAN.&lt;/li&gt;&lt;/ul&gt;
    </description>
</item>

        

<item>
    <title>A CGI whiteboard in Perl (OfficeLinebacker)</title>
    <link>http://prlmnks.org/html/576688.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/576688.html</guid>

    <description>
        Greetings, esteemed monks!&lt;p&gt;I just want to share with the community a project of which I am particularly proud, not so much because of its complexity (though I did have to learn a little DBI, SQLite, and Postgres to do it), but because of the success with which it has met since I wrote it.  It was originally conceived as an electronic whiteboard for communication in an emergency situation, where people could put up notes for to inform others in the organization of their status.  Anyway my Division Director (boss&#39; boss&#39; boss&#39; boss) liked it so much it&#39;s now a whiteboard for everyday use.  I&#39;ve also gotten two feature requests, one of which I plan to try to implement today.  I will post the program in a follow-up to this post so I don&#39;t have to do readmore tags or take up lots of vertical space.&lt;p&gt;_________________________________________________________________________________&lt;p&gt;I like computer programming because it&#39;s like Legos for the mind.&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>New family addition (tbone1)</title>
    <link>http://prlmnks.org/html/575992.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/575992.html</guid>

    <description>
        My wife (better known as She Who Must Be Obeyed) and I decided to expand our family, as &lt;A HREF=&quot;http://www.flickr.com/photos/ejly/sets/72157594309413872/&quot;&gt;can be seen here&lt;/A&gt;. We had trouble picking a name for her, though. I thought of &quot;Cato June&quot; because 1) we&#39;re Colts fans, 2) he lays some wicked tackles, and 3) when we were at the humane society, she put a from-behind tackle on our older son that had to be seen to be believed. (His nose was thoroughly licked in the process.) After trying about a dozen names, none of which really worked, we settled on Perl. Well, of course. The best part is, it was my wife&#39;s idea (ah, geek love!) and we can always say that we wanted a name that none of our friends or family (or their pets) have. Which, if you haven&#39;t done it, is not as easy as it sounds.&lt;P&gt;Anyway, she is an absolute sweetie and has won our hearts completely, and I cannot think of a better use of perl than naming you family dog.&lt;P&gt;&lt;!-- Node text goes above. Div tags should contain sig only --&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-187037&quot;&gt;&lt;P&gt;--&lt;BR&gt;tbone1, YAPS (Yet Another Perl Schlub)&lt;BR&gt;&lt;I&gt;And remember, if he succeeds, so what.&lt;BR&gt; - Chick McGee&lt;BR&gt;&lt;/I&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>XML Navigator (ruoso)</title>
    <link>http://prlmnks.org/html/575194.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/575194.html</guid>

    <description>
        &lt;P&gt;Well, editing XMLs in the text itself is a boring task. Mentally visualizing it is quite hard sometimes. So i wanted to write a node-centric XML Viewer/Editor which would allow me to make a better visualization of semi-structured XMLs.&lt;/P&gt;&lt;P&gt;I decided to craft something in Gtk2 and ended with the following code...&lt;/P&gt;&lt;P&gt;This is the start for what the editor will looks like, and the good news is that you can use the navigator as a widget in any application, as it&#39;s a Perl/Glib Object. It works nice for complex XMLs, but I think it would suffer with big XMLs as, for some reason I can&#39;t explain, I choosed to use XML::DOM.&lt;p&gt;&lt;p&gt;Well, the first snippet is the module that implements the widget XMLNavigator. The second snippet is a program that uses it.&lt;/P&gt;&lt;P&gt;The way you can use it is: 1) click on a node to put it into the center. 2) for children of the center node, you can use the mouse scroll to navigate through them. Have fun.&lt;/p&gt;&lt;P&gt;The Module:&lt;/P&gt;&lt;pre class=&quot;block_code&quot;&gt;package XMLNavigator;use strict;use warnings;use Gtk2;use XML::DOM;use POSIX qw(ceil);use constant DEFAULT_COLLAPSED_BOX_WIDTH =&gt; 75;use constant DEFAULT_COLLAPSED_BOX_HEIGHT =&gt; 45;use constant DEFAULT_FULL_BOX_HEIGHT =&gt; 155;use constant DEFAULT_FULL_BOX_WIDTH =&gt; 160;use constant DEFAULT_INSETS =&gt; 40;use constant RETICENCIAS_WIDTH =&gt; 15;use constant RETICENCIAS_HEIGHT =&gt; 5;use Glib::Object::Subclass  &#39;Gtk2::DrawingArea&#39;,  signals =&gt;  {   model_changed =&gt;   {    method =&gt; &#39;do_model_changed&#39;,    flags =&gt; [qw/run-first/],    return_type =&gt; undef,    param_types =&gt; []   }  },  properties =&gt;  [   Glib::ParamSpec-&gt;scalar   (&#39;domdocument&#39;,    &#39;domdocument&#39;,    &#39;Object containing the DOM Document&#39;,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;collapsed_box_width&#39;,    &#39;collapsed_box_width&#39;,    &#39;Width of the collapsed box&#39;,    0,    500,    DEFAULT_COLLAPSED_BOX_WIDTH,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;collapsed_box_height&#39;,    &#39;collapsed_box_height&#39;,    &#39;Height of the collapsed box&#39;,    0,    500,    DEFAULT_COLLAPSED_BOX_HEIGHT,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;full_box_height&#39;,    &#39;full_box_height&#39;,    &#39;Height of the full box&#39;,    0,    500,    DEFAULT_FULL_BOX_HEIGHT,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;full_box_width&#39;,    &#39;full_box_width&#39;,    &#39;Width of the full box&#39;,    0,    500,    DEFAULT_FULL_BOX_WIDTH,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;insets&#39;,    &#39;insets&#39;,    &#39;Insets between boxes&#39;,    0,    500,    DEFAULT_FULL_BOX_WIDTH,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;max_hori_distance&#39;,    &#39;max_hori_distance&#39;,    &#39;Maximum Horizontal Distance&#39;,    0,    500,    3,    [qw/readable writable/]),   Glib::ParamSpec-&gt;int   (&#39;max_vert_distance&#39;,    &#39;max_vert_distance&#39;,    &#39;Maximum Vertical Distance&#39;,    0,    500,    3,    [qw/readable writable/])  ];sub INIT_INSTANCE {        my $self = shift;        $self-&gt;{domdocument} = undef;        $self-&gt;{matrix} = {};        $self-&gt;{collapsed_box_height} = DEFAULT_COLLAPSED_BOX_HEIGHT;        $self-&gt;{collapsed_box_width} = DEFAULT_COLLAPSED_BOX_WIDTH;        $self-&gt;{full_box_width} = DEFAULT_FULL_BOX_WIDTH;        $self-&gt;{full_box_height} = DEFAULT_FULL_BOX_HEIGHT;        $self-&gt;{max_vert_distance} = 3;        $self-&gt;{max_hori_distance} = 3;        $self-&gt;{insets} = DEFAULT_INSETS;        $self-&gt;signal_connect(button_press_event =&gt; \&amp;button_press_event);        $self-&gt;signal_connect(expose_event =&gt; \&amp;expose_event);        $self-&gt;signal_connect(configure_event =&gt; \&amp;configure_event);        $self-&gt;signal_connect(size_request =&gt; \&amp;do_size_request);        $self-&gt;signal_connect(scroll_event =&gt; \&amp;scroll_event);        $self-&gt;set_events          ([qw(exposure-mask               leave-notify-mask               button-press-mask               button-release-mask               scroll-mask)]);}sub GET_PROPERTY {        my ($self,$pspec) = @_;        if ($pspec-&gt;get_name eq &#39;collapsed_box_height&#39;) {                return $self-&gt;{collapsed_box_height};        } elsif ($pspec-&gt;get_name eq &#39;collapsed_box_width&#39;) {                return $self-&gt;{collapsed_box_width};        } elsif ($pspec-&gt;get_name eq &#39;full_box_height&#39;) {                return $self-&gt;{full_box_height};        } elsif ($pspec-&gt;get_name eq &#39;full_box_width&#39;) {                return $self-&gt;{full_box_width};        } elsif ($pspec-&gt;get_name eq &#39;insets&#39;) {                return $self-&gt;{insets};        } elsif ($pspec-&gt;get_name eq &#39;domdocument&#39;) {                return $self-&gt;{domdocument};        } elsif ($pspec-&gt;get_name eq &#39;matrix&#39;) {                return $self-&gt;{matrix};        } elsif ($pspec-&gt;get_name eq &#39;max_hori_distance&#39;) {                return $self-&gt;{max_hori_distance};        } elsif ($pspec-&gt;get_name eq &#39;max_vert_distance&#39;) {                return $self-&gt;{max_vert_distance};        }}sub SET_PROPERTY {        my ($self,$pspec,$newval) = @_;        if ($pspec-&gt;get_name eq &#39;collapsed_box_height&#39;) {                return $self-&gt;{collapsed_box_height} = $newval;        } elsif ($pspec-&gt;get_name eq &#39;collapsed_box_width&#39;) {                return $self-&gt;{collapsed_box_width} = $newval;        } elsif ($pspec-&gt;get_name eq &#39;full_box_height&#39;) {                return $self-&gt;{full_box_height} = $newval;        } elsif ($pspec-&gt;get_name eq &#39;full_box_width&#39;) {                return $self-&gt;{full_box_width} = $newval;        } elsif ($pspec-&gt;get_name eq &#39;insets&#39;) {                return $self-&gt;{insets} = $newval;        } elsif ($pspec-&gt;get_name eq &#39;domdocument&#39;) {                $self-&gt;{domdocument} = $newval;                $self-&gt;{matrix} = {};                $self-&gt;plan_matrix();                $self-&gt;queue_draw();        } elsif ($pspec-&gt;get_name eq &#39;matrix&#39;) {                $self-&gt;{matrix} = $newval;                $self-&gt;plan_matrix();                $self-&gt;queue_draw();        } elsif ($pspec-&gt;get_name eq &#39;max_hori_distance&#39;) {                return $self-&gt;{max_hori_distance} = $newval;        } elsif ($pspec-&gt;get_name eq &#39;max_vert_distance&#39;) {                return $self-&gt;{max_vert_distance} = $newval;        }}sub do_model_changed {}sub plan_matrix {        my $self = shift;        my $max_hori_distance = $self-&gt;{max_hori_distance};        my $max_vert_distance = $self-&gt;{max_vert_distance};        my $xmlthink_center = $self-&gt;{matrix}{0}{0};        my %center_on_row = ();        foreach my $key (keys %{$self-&gt;{matrix}}) {                $center_on_row{$key} = $self-&gt;{matrix}{$key}{0};        }        $self-&gt;{matrix} = {};        if (not defined $self-&gt;{domdocument}) {                return;        }        my $xmlthink_root = $self-&gt;{domdocument}-&gt;getDocumentElement();        if (not defined $xmlthink_root) {                return;        }        if (not defined $xmlthink_center) {                $xmlthink_center = $xmlthink_root;        }        #print &quot;0,0 = $xmlthink_center\n&quot;;        $self-&gt;{matrix}{0}{0} = $xmlthink_center;      HORIZONTAL_BW:        for my $hori_distance (1..$max_hori_distance+1) {                $hori_distance *= -1;                my $elem = $self-&gt;{matrix}{$hori_distance + 1}{0};                my $other = $elem-&gt;getParentNode();                if ($other &amp;&amp; $other != $self-&gt;{domdocument}) {                        #print &quot;$hori_distance,0 = $other\n&quot;;                        $self-&gt;{matrix}{$hori_distance}{0} = $other;                } else {                        last HORIZONTAL_BW;                }                for my $sign_v (-1,1) {                      VERTICAL_BW:                        for my $vert_distance (1..$max_vert_distance) {                                $vert_distance *= $sign_v;                                my $other = $self-&gt;{matrix}{$hori_distance + 1}{$vert_distance - $sign_v};                                my $brot = $sign_v&lt;0?$other-&gt;getPreviousSibling():$other-&gt;getNextSibling();                                while (defined $brot &amp;&amp; $brot-&gt;getNodeType() != XML::DOM::ELEMENT_NODE) {                                        $brot = $sign_v&lt;0?$brot-&gt;getPreviousSibling():$brot-&gt;getNextSibling();                                }                                if ($brot) {                                        #print &quot;&quot;.($hori_distance+1).&quot;,$vert_distance = $brot\n&quot;;                                        $self-&gt;{matrix}{$hori_distance + 1}{$vert_distance} = $brot;                                        my @list = grep { $_-&gt;getNodeType() == XML::DOM::ELEMENT_NODE } $brot-&gt;getChildNodes();                                        if ($#list &gt;= 0) {                                                my $la = $hori_distance+1.0001;                                                $la =~ s/,/./g;                                                #print &quot;$la,$vert_distance = undef\n&quot;;                                                $self-&gt;{matrix}{$la}{$vert_distance} = undef                                        }                                } else {                                        last VERTICAL_BW;                                }                                if ($hori_distance == -1) {                                        if (abs($vert_distance) == $max_vert_distance -1) {                                                my $obrot = $sign_v&lt;0?$other-&gt;getPreviousSibling():$other-&gt;getNextSibling();                                                if ($obrot) {                                                        #print &quot;&quot;.($hori_distance+1).&quot;,&quot;.($vert_distance + $sign_v).&quot; = undef\n&quot;;                                                        $self-&gt;{matrix}{$hori_distance + 1}{$vert_distance + $sign_v} = undef;                                                }                                                last VERTICAL_BW;                                        }                                } else {                                        if (abs($vert_distance) == $max_vert_distance) {                                                my $obrot = $sign_v&lt;0?$other-&gt;getPreviousSibling():$other-&gt;getNextSibling();                                                if ($obrot) {                                                        #print &quot;&quot;.($hori_distance+1).&quot;,&quot;.($vert_distance + $sign_v).&quot; = undef\n&quot;;                                                        $self-&gt;{matrix}{$hori_distance + 1}{$vert_distance + $sign_v} = undef;                                                }                                        }                                }                        }                }                if (abs($hori_distance) == $max_hori_distance + 1) {                        my $obrot = $other-&gt;getParentNode();                        #print &quot;$hori_distance,0 = undef\n&quot;;                        $self-&gt;{matrix}{$hori_distance}{0} = undef;                }        }      HORIZONTAL_FW:        for my $hori_distance (1..$max_hori_distance) {                my $elem = $self-&gt;{matrix}{$hori_distance - 1}{0};                last HORIZONTAL_FW unless defined $elem;                my @list = grep { $_-&gt;getNodeType() == XML::DOM::ELEMENT_NODE } $elem-&gt;getChildNodes();                last HORIZONTAL_FW unless $#list &gt;= 0;                if (not $center_on_row{$hori_distance}) {                        $center_on_row{$hori_distance} = $list[ceil($#list/2)];                }                my $obj = $center_on_row{$hori_distance};                #print &quot;$hori_distance,0 = $obj\n&quot;;                $self-&gt;{matrix}{$hori_distance}{0} = $obj;                for my $sign_v (-1,1) {                      VERTICAL_FW:                        for my $vert_distance (1..$max_vert_distance) {                                $vert_distance *= $sign_v;                                my $other = $self-&gt;{matrix}{$hori_distance}{$vert_distance - $sign_v};                                my $brot = $sign_v&lt;0?$other-&gt;getPreviousSibling():$other-&gt;getNextSibling();                                while (defined $brot &amp;&amp; $brot-&gt;getNodeType() != XML::DOM::ELEMENT_NODE) {                                        $brot = $sign_v&lt;0?$brot-&gt;getPreviousSibling():$brot-&gt;getNextSibling();                                }                                if ($brot) {                                        #print &quot;&quot;.($hori_distance).&quot;,$vert_distance = $brot\n&quot;;                                        $self-&gt;{matrix}{$hori_distance}{$vert_distance} = $brot;                                        if (grep { $_-&gt;getNodeType() == XML::DOM::ELEMENT_NODE } $brot-&gt;getChildNodes()) {                                                my $la = $hori_distance+0.0001;                                                $la =~ s/,/./g;                                                #print &quot;&quot;.($la).&quot;,$vert_distance = undef\n&quot;;                                                $self-&gt;{matrix}{$la}{$vert_distance} = undef                                        }                                } else {                                        last VERTICAL_FW;                                }                                if (abs($vert_distance) == $max_vert_distance) {                                        my $obrot = $sign_v&lt;0?$other-&gt;getPreviousSibling():$other-&gt;getNextSibling();                                        if ($obrot) {                                                #print &quot;&quot;.($hori_distance).&quot;,&quot;.($vert_distance+$sign_v).&quot; = $brot\n&quot;;                                                $self-&gt;{matrix}{$hori_distance}{$vert_distance + $sign_v} = undef;                                        }                                }                        }                }                if (abs($hori_distance) == $max_hori_distance) {                        my @list = grep { $_-&gt;getNodeType() == XML::DOM::ELEMENT_NODE } $elem-&gt;getChildNodes();                        if ($#list &gt;= 0) {                                #print &quot;&quot;.($hori_distance+1).&quot;,0 = undef\n&quot;;                                $self-&gt;{matrix}{$hori_distance+1}{0} = undef;                        }                }        }        $self-&gt;drawit_all();}sub do_size_request {        my ($self, $requisition) = @_;        $requisition-&gt;width ($self-&gt;{full_box_width});        $requisition-&gt;height ($self-&gt;{full_box_height});}sub configure_event {        my $self = shift;        my $event = shift;        $self-&gt;{pixmap} = Gtk2::Gdk::Pixmap-&gt;new          ($self-&gt;window,           $self-&gt;allocation-&gt;width,           $self-&gt;allocation-&gt;height,           -1); # same depth as window        $self-&gt;{max_vert_distance} = int((($self-&gt;allocation-&gt;height/2)-($self-&gt;{full_box_height}/2)-($self-&gt;{insets}/2))/($self-&gt;{collapsed_box_height}+$self-&gt;{insets})) || 1;        $self-&gt;{max_hori_distance} = int((($self-&gt;allocation-&gt;width/2)-($self-&gt;{full_box_width}/2)-($self-&gt;{insets}/2))/($self-&gt;{collapsed_box_width}+$self-&gt;{insets})) || 1;        $self-&gt;plan_matrix();}sub expose_event {        my $self = shift;        my $event = shift;        $self-&gt;window-&gt;draw_drawable          ($self-&gt;style-&gt;fg_gc($self-&gt;state),           $self-&gt;{pixmap},           $event-&gt;area-&gt;x, $event-&gt;area-&gt;y,           $event-&gt;area-&gt;x, $event-&gt;area-&gt;y,           $event-&gt;area-&gt;width, $event-&gt;area-&gt;height);}sub drawit_all {        my $self = shift;        return unless $self-&gt;{pixmap};        $self-&gt;drawit_in(0,0,$self-&gt;allocation-&gt;width(),$self-&gt;allocation-&gt;height());}sub drawit_in {        my $self = shift;        my ($x,$y,$w,$h) = @_;        my $gc = $self-&gt;style-&gt;fg_gc($self-&gt;state);        $gc-&gt;set_clip_rectangle(Gtk2::Gdk::Rectangle-&gt;new($x, $y, $w, $h));        $gc-&gt;set_rgb_fg_color(Gtk2::Gdk::Color-&gt;new(255*257,255*257,255*257));        $self-&gt;{pixmap}-&gt;draw_rectangle($gc,1,0,0,$w,$h);        $gc-&gt;set_rgb_fg_color(Gtk2::Gdk::Color-&gt;new(0,0,0));        $self-&gt;draw_data($self-&gt;{pixmap},$gc);        $gc-&gt;set_clip_rectangle(undef);        $self-&gt;queue_draw();}sub draw_data {        my $self = shift;        my $area = shift;        my $gc = shift;        my $max_hori_distance = $self-&gt;{max_hori_distance};        my $max_vert_distance = $self-&gt;{max_vert_distance};        my $xmlthink_doc = $self-&gt;{domdocument};        return unless $xmlthink_doc;        my $xmlthink_root = $xmlthink_doc-&gt;getDocumentElement();        my $xmlthink_center = $self-&gt;{matrix}{0}{0};        if (not defined $xmlthink_center) {                $self-&gt;plan_matrix();                $xmlthink_center = $self-&gt;{matrix}{0}{0};                return unless $xmlthink_center;        }        my $canvas_width = $self-&gt;allocation-&gt;width;        my $canvas_height = $self-&gt;allocation-&gt;height;        my $center_x = int($canvas_width/2);        my $center_y = int($canvas_height/2);        my $fullb_w = $self-&gt;{full_box_width};        my $fullb_h = $self-&gt;{full_box_height};        my $collb_w = $self-&gt;{collapsed_box_width};        my $collb_h = $self-&gt;{collapsed_box_height};        my $insets  = $self-&gt;{insets};        $self-&gt;draw_center_element($area,$gc);        if ($xmlthink_root != $xmlthink_center) {                $self-&gt;draw_line_to_parent($area,$gc,$center_x - int($insets/2) - int($fullb_w/2) + 1,$center_y - int($insets/2));        }        for my $hori (sort {$a &lt;=&gt; $b} keys %{$self-&gt;{matrix}}) {                for my $vert (sort {$a &lt;=&gt; $b} keys %{$self-&gt;{matrix}{$hori}}) {                        next if ($hori == 0 &amp;&amp; $vert == 0);                        my $obj = $self-&gt;{matrix}{$hori}{$vert};                        my $abs_distance_x = (abs($hori) * ($collb_w + $insets));                        my $abs_distance_y = (abs($vert) * ($collb_h + $insets));                        $abs_distance_x += int(($fullb_w - $collb_w)/2);                        if (abs($hori)&lt;0.1) {                                # Center Column !!                                $abs_distance_y += int(($fullb_h - $collb_h)/2);                        }                        my $distance_x = $abs_distance_x * ($hori&gt;=1?1:-1);                        my $distance_y = $abs_distance_y * ($vert==0?0:$vert&gt;0?1:-1);                        my $box_center_x = $center_x + $distance_x;                        my $box_center_y = $center_y + $distance_y;                        my $x = $box_center_x - (int($collb_w/2)+int($insets/2));                        my $y = $box_center_y - (int($collb_h/2)+int($insets/2));                        if (exists $self-&gt;{matrix}{$hori - 1} &amp;&amp; int($hori)==$hori) {                                $self-&gt;draw_line_to_parent($area,$gc,$x,int($y+$collb_h/2));                        }                        if ($obj) {                                $self-&gt;draw_collapsed_element($area,$gc,$obj,$x,$y);                        } else {                                if (int($hori)!=$hori) {                                        # side reticencia                                        $x = $box_center_x + (int($collb_w/2)-int($insets/2));                                        $y = $box_center_y - (int($collb_h/2)+int($insets/2));                                        $self-&gt;draw_reticencia($area,$gc,$x+2,$y);                                } else {                                        # element reticencia                                        $x = $box_center_x - (int($collb_w / 2)+int($insets/2));                                        $y = $box_center_y - (int(RETICENCIAS_HEIGHT / 2)+int($insets/2));                                        $self-&gt;draw_reticencia($area,$gc,$x,$y);                                }                        }                }        }        $self-&gt;queue_draw();}sub draw_center_element {        my $draw = shift;        my $self = $draw;        my $area = shift;        my $gc = shift;        my $canvas_width = $draw-&gt;allocation-&gt;width;        my $canvas_height = $draw-&gt;allocation-&gt;height;        my $pangoc = $draw-&gt;get_pango_context();        my $fontdesc = Gtk2::Pango::FontDescription-&gt;from_string(&quot;Sans 10&quot;);        my $xmlthink_center = $draw-&gt;{matrix}{0}{0};        my $center_x = int($canvas_width/2);        my $center_y = int($canvas_height/2);        my $fullb_w = $self-&gt;{full_box_width};        my $fullb_h = $self-&gt;{full_box_height};        my $insets = $self-&gt;{insets};        my $x = $center_x - int($fullb_w/2) - int($insets/2);        my $y = $center_y - int($fullb_h/2) - int($insets/2);        # BIG RECTANGLE        $area-&gt;draw_rectangle($gc,0,$x,$y,$fullb_w,$fullb_h);        # TAG        my $tag_rect = Gtk2::Gdk::Rectangle-&gt;new($x+2,$y+2,$fullb_w-4,20);        $area-&gt;draw_rectangle($gc,0,$tag_rect-&gt;x,$tag_rect-&gt;y,$tag_rect-&gt;width,$tag_rect-&gt;height);        $draw-&gt;draw_text($gc,$xmlthink_center-&gt;getTagName(),$tag_rect-&gt;x+2,$tag_rect-&gt;y+2,$tag_rect-&gt;width-4,$tag_rect-&gt;height-4);        # ATTRIBUTES        my $attr_rect = Gtk2::Gdk::Rectangle-&gt;new($x+2,$y+24,$fullb_w-4,int(($fullb_h-32)/2));        $area-&gt;draw_rectangle($gc,0,$attr_rect-&gt;x,$attr_rect-&gt;y,$attr_rect-&gt;width,$attr_rect-&gt;height);        $draw-&gt;draw_text($gc,$draw-&gt;make_text_from_attributes($xmlthink_center,&quot;\n&quot;),$attr_rect-&gt;x+2,$attr_rect-&gt;y+2,$attr_rect-&gt;width-4,$attr_rect-&gt;height-4);        # CDATA        my $cdata_rect = Gtk2::Gdk::Rectangle-&gt;new($x+2,$y+24+int(($fullb_h-32)/2)+2,$fullb_w-4,int(($fullb_h-23)/2));        $area-&gt;draw_rectangle($gc,0,$cdata_rect-&gt;x,$cdata_rect-&gt;y,$cdata_rect-&gt;width,$cdata_rect-&gt;height);        $draw-&gt;draw_text($gc,$draw-&gt;make_text_from_cdata($xmlthink_center),$cdata_rect-&gt;x+2,$cdata_rect-&gt;y+2,$cdata_rect-&gt;width-4,$cdata_rect-&gt;height-4);}sub make_text_from_attributes {        my $draw = shift;        my $element = shift;        my $sep = shift;        my $str = &#39;&#39;;        my $nmap = $element-&gt;getAttributes();        for my $i (0..($nmap-&gt;getLength() - 1)) {                my $node = $nmap-&gt;item($i);                $str .= $node-&gt;getNodeName().&quot;=&quot;.$node-&gt;getNodeValue().$sep;        }        chop($str);        return $str;}sub make_text_from_cdata {        my $draw = shift;        my $element = shift;        my $str = &#39;&#39;;        for my $child ($element-&gt;getChildNodes()) {                if ($child-&gt;getNodeType() != XML::DOM::ELEMENT_NODE &amp;&amp;                    $child-&gt;getNodeType() != XML::DOM::ATTRIBUTE_NODE) {                        my $o = $child-&gt;getNodeValue();                        $o =~ s/^\s*//;                        $o =~ s/\s*$//;                        $str .= $o.&quot;\n&quot; if $o;                }        }        return $str;}sub draw_reticencia {        my $draw = shift;        my $area = shift;        my $gc = shift;        my $x = shift;        my $y = shift;        $area-&gt;draw_arc($gc,0,$x,$y,(RETICENCIAS_WIDTH / 3),RETICENCIAS_HEIGHT,90*64,180*64);        $area-&gt;draw_arc($gc,0,$x +(2*RETICENCIAS_WIDTH / 3) - 1,$y,(RETICENCIAS_WIDTH / 3),RETICENCIAS_HEIGHT,270*64,180*64);        $area-&gt;draw_line($gc,$x+(2*RETICENCIAS_WIDTH/6),$y+(RETICENCIAS_HEIGHT/2),$x+(4*RETICENCIAS_WIDTH/6),$y+(RETICENCIAS_HEIGHT/2));        $area-&gt;draw_line($gc,$x+(RETICENCIAS_WIDTH/2),$y,$x+(RETICENCIAS_WIDTH/2),$y+(RETICENCIAS_HEIGHT));}sub draw_collapsed_element {        my $draw = shift;        my $area = shift;        my $gc = shift;        my $element = shift;        my $x = shift;        my $y = shift;        my $collb_w = $draw-&gt;{collapsed_box_width};        my $collb_h = $draw-&gt;{collapsed_box_height};        my $tag = $element-&gt;getTagName();        my $attr = $draw-&gt;make_text_from_attributes($element,&quot;,&quot;);        my $cdata = $draw-&gt;make_text_from_cdata($element);        $draw-&gt;{pixmap}-&gt;draw_rectangle($gc,0,$x,$y,$collb_w,$collb_h);        $draw-&gt;draw_text($gc,$tag,$x+2,$y,$collb_w-4,int($collb_h/3));        $draw-&gt;draw_text($gc,$attr,$x+2,$y+int($collb_h/3),$collb_w-4,int($collb_h/3));        $draw-&gt;draw_text($gc,$cdata,$x+2,$y+2*int($collb_h/3),$collb_w-4,int($collb_h/3));}sub draw_text {        my $draw = shift;        my $gc = shift;        my $text = shift;        my $x = shift;        my $y = shift;        my $w = shift;        my $h = shift;        my $pangoc = $draw-&gt;get_pango_context();        my $fontdesc = Gtk2::Pango::FontDescription-&gt;from_string(&quot;Sans 10&quot;);        my $rect = Gtk2::Gdk::Rectangle-&gt;new($x,$y,$w,$h);        my $layout = Gtk2::Pango::Layout-&gt;new($pangoc);        my $clipped = Gtk2::Gdk::GC-&gt;new($draw-&gt;{pixmap});        $clipped-&gt;set_clip_rectangle($rect);        $layout-&gt;set_font_description($fontdesc);        $layout-&gt;set_text($text);        $draw-&gt;{pixmap}-&gt;draw_layout($clipped,$x,$y,$layout);}sub draw_line_to_parent {        my $draw = shift;        my $self = $draw;        my $area = shift;        my $gc = shift;        my $x = shift;        my $y = shift;        my $canvas_width = $draw-&gt;allocation-&gt;width;        my $canvas_height = $draw-&gt;allocation-&gt;height;        $area-&gt;draw_line($gc,$x,$y,$x-int($draw-&gt;{insets}/2),$y);        $area-&gt;draw_line($gc,$x-int($draw-&gt;{insets}/2),$y,$x-int($draw-&gt;{insets}/2),int($canvas_height/2)-int($draw-&gt;{insets}/2));        $area-&gt;draw_line($gc,$x-int($draw-&gt;{insets}/2),int($canvas_height/2)-int($draw-&gt;{insets}/2),$x-$self-&gt;{insets},int($canvas_height/2)-int($draw-&gt;{insets}/2));}sub scroll_event {        my $self = shift;        my $event = shift;        my $x = $event-&gt;x;        my $y = $event-&gt;y;        my $dir = $event-&gt;direction;        my ($col,$row) = $self-&gt;x_y_to_col_row($x,$y);        if ($col &gt; 0) {                my $other = $dir eq &#39;up&#39;?-1:1;                if (exists $self-&gt;{matrix}{$col}{$other} &amp;&amp; $self-&gt;{matrix}{$col}{$other}) {                        $self-&gt;{matrix}{$col}{0} = $self-&gt;{matrix}{$col}{$other};                        while (exists $self-&gt;{matrix}{$col + 1}) {                                delete $self-&gt;{matrix}{$col + 1};                                $col++;                        }                        $self-&gt;plan_matrix();                }        }}sub element_on_col_row {        my $self = shift;        my ($col,$row) = @_;        return $self-&gt;{matrix}{$col}{$row};}sub x_y_to_col_row {        my $self = shift;        my $x = shift;        my $y = shift;        my ($col,$row);        my $canvas_width = $self-&gt;allocation-&gt;width;        my $canvas_height = $self-&gt;allocation-&gt;height;        my $center_x = int($canvas_width/2);        my $center_y = int($canvas_height/2);        my $fullb_w = $self-&gt;{full_box_width};        my $fullb_h = $self-&gt;{full_box_height};        my $collb_w = $self-&gt;{collapsed_box_width};        my $collb_h = $self-&gt;{collapsed_box_height};        my $insets  = $self-&gt;{insets};        if ($x &gt; ($center_x + int($fullb_w/2))) {                my $rel_x = $x - ($center_x + int($fullb_w/2));                $col = int($rel_x/($collb_w+$insets)) + 1;                my $rel_y = $y - $center_y;                $row = ceil($rel_y/($collb_h+$insets))+0;        } elsif ($x &lt; ($center_x - int($fullb_w/2))) {                my $rel_x = $x - ($center_x - int($fullb_w/2));                $col = int($rel_x/($collb_w+$insets)) - 1;                my $rel_y = $y - $center_y;                $row = ceil($rel_y/($collb_h+$insets));        } else {                $col = 0;                if ($y &gt; ($center_y + int($fullb_h/2))) {                        my $rel_y = $y - ($center_y + int($fullb_h/2));                        $row = int($rel_y/($collb_h+$insets)) + 1;                } elsif ($y &lt; ($center_y - int($fullb_h/2))) {                        my $rel_y = $y - ($center_y - int($fullb_h/2) - int($insets/2));                        $row = int($rel_y/($collb_h+$insets)) - 1;                }        }        return ($col,$row);}sub button_press_event {        my $draw = shift;        my $event = shift;        my $x = $event-&gt;x;        my $y = $event-&gt;y;        my $button = $event-&gt;button;        my ($col,$row) = $draw-&gt;x_y_to_col_row($x,$y);        $draw-&gt;click_col_row($button,$col,$row);}sub click_col_row {        my $self = shift;        my $button = shift;        my $col = shift || 0;        my $row = shift || 0;        if (exists $self-&gt;{matrix}{$col}{$row} and $self-&gt;{matrix}{$col}{$row}) {                my $obj = $self-&gt;{matrix}{$col}{$row};                if ($button == 1) {                        $self-&gt;{matrix} = {};                        $self-&gt;{matrix}{0}{0} = $obj;                        $self-&gt;plan_matrix();                }        }}1;&lt;/pre&gt;&lt;P&gt;Trial Program:&lt;/P&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;use warnings;use XML::DOM;use Gtk2 qw(-init);use XMLNavigator;my $mainwindow = Gtk2::Window-&gt;new(&quot;toplevel&quot;);my $hbox = Gtk2::HBox-&gt;new();my $parser = XML::DOM::Parser-&gt;new();my $nav = XMLNavigator-&gt;new(domdocument =&gt; $parser-&gt;parsefile($ARGV[0]));$hbox-&gt;add($nav);$mainwindow-&gt;add($hbox);$mainwindow-&gt;show_all();Gtk2-&gt;main();&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-463883&quot;&gt;daniel&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Manipulating a Window (HarmB)</title>
    <link>http://prlmnks.org/html/575093.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/575093.html</guid>

    <description>
        There are so many modules around, but is there a module to read-back information of a particular window? And to press buttons in that window, as well as to fill-in fields in that window? The window not being a HTML page, or other online-service, but a regular application window, e.g WORD, or Notepad or Calculator(for Microsoft Windows).I cannot imagine there is not such a module, but apparantly I cannot find it. Who can help?
    </description>
</item>

        

<item>
    <title>Manipulating a Window (HarmB)</title>
    <link>http://prlmnks.org/html/575089.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/575089.html</guid>

    <description>
        There are so many modules around, but is there a module to read-back information of a particular window? And to press buttons in that window, as well as to fill-in fields in that window? The window not being a HTML page, or other online-service, but a regular application window, e.g WORD, or Notepad or Calculator(for Microsoft Windows).I cannot imagine there is not such a module, but apparantly I cannot find it. Who can help?
    </description>
</item>

        

<item>
    <title>ssh chain (shmem)</title>
    <link>http://prlmnks.org/html/574891.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/574891.html</guid>

    <description>
        My work involves logging into remote machines via ssh, possibly over multiple hops to do random tasks.&lt;p&gt;Tired of typing&lt;pre class=&quot;block_code&quot;&gt;ssh -p 3334 shmem@gateway -L1025:host.example.com:22 &lt;/pre&gt;&lt;p&gt;and for the next hop, in another shell,&lt;pre class=&quot;block_code&quot;&gt;ssh -p 1025 admin@localhost -L1026:192.168.123.2:975 -R993:localhost:1993&lt;/pre&gt;&lt;p&gt;then in yet another shell&lt;pre class=&quot;block_code&quot;&gt;ssh -p 1026 wrxd15@localhost -L1027:192.168.254.2:22&lt;/pre&gt;&lt;p&gt;and so on, I cranked out the following, which lets me say&lt;pre class=&quot;block_code&quot;&gt;ssc shmem@gateway:3334 admin@host.example.com wrxd15@192.168.123.2:975,-R993:localhost:1993 root@192.168.254.2&lt;/pre&gt;&lt;p&gt;which does the chaining. I only have to type in the hops in order, and presto - I have a shell open at the endpoint. Aliasing a chain is much easier this way. Secure Shells are invoked with &lt;tt class=&quot;inline_code&quot;&gt;-f -N&lt;/tt&gt; except the last. &lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w# $Id: ssc,v 0.0 2006/09/26 09:17:18 shmem Exp $use strict;use Getopt::Std;my %o;getopts(&#39;p:&#39;,\%o);$o{&#39;p&#39;} ||= 1025; # default port range startdie &quot;ssh chain - usage: ssc [-p] user\@host1[:sshport][,arg,arg,...] \\\n&quot;   .&quot;                            user\@host1[:sshport][,arg,arg,...] \\\n&quot;   .&quot;                            ...\n&quot;   .&quot;where -p is of the forwarded ports range&#39;s start (default: $o{p})\n&quot;   unless @ARGV;# sshost chain to port 22while (@ARGV) {    my ($host,@args) = split/,/,shift;    my ($port,$nexthop,$silent,$nextport);    $port = $nextport = $nexthop = $silent = &#39;&#39;;    $host =~ s/:(\d+)// &amp;&amp; ($port=&quot;-p $1&quot;);    if(@ARGV) {        my ($c,@args) = split/,/,$ARGV[0];        my ($u,$host)    = split /\@/,$c;        $host =~ s/:(\d+)// &amp;&amp; ($nextport=$1);        $nexthop = &quot;-L$o{p}:$host:&quot;. ($nextport ? $nextport : 22);        $ARGV[0] = &quot;$u\@localhost:$o{p}&quot;;        $ARGV[0] .= &#39;,&#39;. join(&#39;,&#39;,@args);        $o{&#39;p&#39;}++;        $silent = &#39;-f -N&#39;;    }    if(@ARGV) {        print  &quot;ssh $silent $port $host $nexthop @args\n&quot;;        system &quot;ssh $silent $port $host $nexthop @args&quot;;    } else {        print &quot;ssh $silent $port $host $nexthop @args\n&quot;;        exec  &quot;ssh $silent $port $host $nexthop @args&quot;;    }}&lt;/pre&gt;&lt;p&gt;Comments, critique and improvements welcome.&lt;p&gt;--shmem&lt;!-- Node text goes above. Div tags should contain sig only --&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-510280&quot;&gt;&lt;small&gt;&lt;pre&gt;_($_=&quot; &quot;x(1&lt;&lt;5).&quot;?\n&quot;.q&amp;middot;/)Oo.  G&amp;deg;\        /                              /\_&amp;macr;/(q    /----------------------------  \__(m.====&amp;middot;.(_(&quot;always off the crowd&quot;)).&quot;&amp;middot;&quot;);sub _{s./.($e=&quot;&#39;Itrs `mnsgdq Gdbj O`qkdq&quot;)=~y/&quot;-y/#-z/;$e.e &amp;&amp; print}&lt;/pre&gt;&lt;/small&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Parsing attributes in one line using map (ruoso)</title>
    <link>http://prlmnks.org/html/574710.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/574710.html</guid>

    <description>
        &lt;P&gt;After writing this code, I realized many people may have just not realized this is possible, so I think posting it here would make people think more about it.&lt;/p&gt;&lt;P&gt;I was writing a simple script wich would receive a attr=value,attr2=value2 string and automatically I wrote:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;my %attrs = map { split /=/ } split /,/, $attr;&lt;/pre&gt;&lt;p&gt;Well, the trick is that map doesn&#39;t have to return the same number of elements of the list it receives. If you returns a list inside a map, the resulting list will have more elements than the input list.&lt;/p&gt;&lt;p&gt;This sounds very intuitive to me, but maybe it&#39;s not for others...&lt;/p&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-463883&quot;&gt;daniel&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Get your Daily Tao Te Ching fix (radiantmatrix)</title>
    <link>http://prlmnks.org/html/574265.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/574265.html</guid>

    <description>
        &lt;p&gt;I&#39;m a big fan of the Tao te Ching, and after finding a free version (called [http://acc6.its.brooklyn.cuny.edu/~phalsall/texts/taote-v4.html|GNL]), I decided to write a tool that would give me a new chapter each day I ran it, in order, and start over when I reached the end.&lt;p&gt;I personally use this in a cron job to change my /etc/motd message.&lt;p&gt;When run for the first time with either the --daily or --chapter parameters, it will retrieve a copy of the GNL from the web, parse it into its chapters (saving the license to &#39;license.txt&#39;), and store a YAML cache of that work.  If you&#39;re running from behind a proxy, you can use some other proxy-enabled tool to save the GNL from the link in the first paragraph to a file named &#39;taote-v4.html&#39; -- when put in the exec directory, the app will prefer that local copy.&lt;p&gt;--today causes the program to print the next chapter of the TTC if it&#39;s not the same day as the last time it was run.&lt;p&gt;--chapter lets you get a specific chapter (see usage for details)&lt;p&gt;To clean up and start fresh, use the --reset parameter.&lt;p&gt;Run without parameters or with &#39;--usage&#39;, &#39;--help&#39;, &#39;-h&#39;, or &#39;-?&#39; for a usage message.&lt;p&gt;Depends on CPAN modules [dist://YAML-Syck] (fast libsyck) &lt;ins&gt;or [dist://YAML] (slower, pure-perl)&lt;/ins&gt;, and [dist://HTML-TokeParser-Simple] (for parsing the HTML version of GNL).  &lt;ins&gt;The last dependency is only needed for parsing the HTML document -- it is not needed once tao.yaml exists.&lt;/ins&gt;&lt;readmore title=&#39;dailytao.pl&#39;&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/local/bin/perluse strict;use warnings;BEGIN {  # Try to load YAML::Syck (faster, preferred), fall back to YAML otherwise  # thanks to PerlMonks&#39; shmem for pointing out potential YAML::Syck trouble  eval {     require YAML::Syck;    YAML::Syck-&gt;import( qw[LoadFile DumpFile] );  };  if ($@) {    # fall back to YAML    require YAML;    YAML-&gt;import( qw[LoadFile DumpFile] );  }}use Getopt::Long;# how do we want to operate?usage() unless @ARGV;GetOptions(  &#39;help|usage|?|h&#39; =&gt; \&amp;usage,  &#39;reset&#39;          =&gt; sub { for (&lt;*.yaml&gt;) { unlink while -f } },  &#39;today&#39;          =&gt; \&amp;daily_tao,  &#39;chapter&#39;        =&gt; \&amp;my_tao,  &#39;license&#39;        =&gt; sub { print join(&#39;&#39;,&lt;DATA&gt;); exit; },);#========================================================================# load_tao - loads or regenerates cache of tao chapters, returns as listsub load_tao {  if ( -f &#39;tao.yaml&#39;) {    # load chapters from disk cache, if we have them    return @{ LoadFile(&#39;tao.yaml&#39;) };  }  else {    # otherwise, get them from the web and build the cache    return @{ get_web_tao() };  }}#========================================================================# daily_tao - shows a new dao chapter if the date has advanced by at#             least one day since the last run.  Uses &#39;day.yaml&#39; for#             cache.sub daily_tao {  my @chapter = load_tao();  my $day_index = 0;  my $time = time;  my $now = $time;    # load last run info, if any.  if ( -f &#39;day.yaml&#39; ) {     ($day_index, $time) = @{ LoadFile(&#39;day.yaml&#39;) }  }    my @now  = localtime($now);  my @last = localtime($time);  if ( $last[7] != $now[7] || $last[5] != $now[5]) {    # it&#39;s not the same day as last we ran    $day_index++;    if ($day_index &gt; $#chapter) { $day_index = 0 } # roll over    $time = $now; # we will record the *current* time  }    printf &quot;%s: %s\n%s&quot;, @{ $chapter[$day_index] };  DumpFile(&#39;day.yaml&#39;, [ $day_index, $time ]);}#========================================================================# my_tao - shows the chapter specified on the command line ($ARGV[0])#          with no parameter, shows the first chapter#          with a numeric parameter, shows chap. for that record number#          with a paramter ending in &#39;.&#39;, shows record with that chap. id#          Dies if there&#39;s an invalid chap. id or spec.sub my_tao {  my @chapter = load_tao();  my $chapter = (@ARGV ? shift @ARGV : 0);    if ($chapter =~ /\./) {    # this is a chapter spec    my $chindex = 0;    for (0..$#chapter) {      $chindex = $_;      last if &quot;$chapter[$_][0].&quot; eq $chapter;    }        die &quot;$chapter is not a valid chapter spec\n&quot;      if &quot;$chapter[$chindex][0].&quot; ne $chapter;        $chapter = $chindex;  }  elsif ($chapter =~ /\D/) {    # doesn&#39;t look like a spec, but isn&#39;t just a number - invalid    die &quot;&#39;$chapter&#39; is neither a valid spec nor a valid numeric id\n&quot;;  }  die &quot;The last record is no.$#chapter, but you asked for no.$chapter\n&quot;    if $chapter &gt; $#chapter;  printf &quot;%s: %s\n%s&quot;, @{$chapter[$chapter]};}#========================================================================# get_web_tao - retrieves the &quot;GNL&quot; tao te ching from the web and #               creates the disk cache of the chapters in &#39;tao.yaml&#39;#               if a local file &#39;taote-v4.html&#39; exists, will use that#               instead of fetching from the websub get_web_tao {  require HTML::TokeParser::Simple;    my $p = HTML::TokeParser::Simple-&gt;new( -f &#39;taote-v4.html&#39;     ?(file =&gt; &#39;taote-v4.html&#39;)    :(url =&gt; &#39;http://acc6.its.brooklyn.cuny.edu/~phalsall/texts/taote-v4.html&#39;)  );    # skip notices and such, but record them to license.txt  my $LIC;  while (my $token = $p-&gt;get_token) {    last if $token-&gt;is_end_tag(&#39;h2&#39;);    if ($token-&gt;is_start_tag(&#39;pre&#39;)) {      # start of license text and copyright notice      open $LIC, &#39;&gt;license.txt&#39; or die (&quot;Can&#39;t write license.txt: $!&quot;);    }    elsif ($token-&gt;is_end_tag(&#39;pre&#39;)) {      # end of license text and copyright notice      close $LIC; undef $LIC;    }    elsif ($token-&gt;is_text &amp;&amp; defined $LIC) {      # body text for license and copyright      print $LIC $token-&gt;as_is;    }  }    # add chapters to data struct in form [id, title, text]  my @chapter;  while (my $token = $p-&gt;get_token)  {    next unless $token-&gt;is_start_tag(&#39;h3&#39;); #title start    (my $title = $p-&gt;peek(1)) =~ s{^(.+?)\.\s}{}g;    my $num = $1;    my $text = &#39;&#39;;        #finish end of title    while ($token = $p-&gt;get_token) { last if $token-&gt;is_end_tag(&#39;h3&#39;) }        #grab text *as* text    while ($token = $p-&gt;get_token) {      last if $p-&gt;peek =~ /&lt;h3&gt;/i;      next if $token-&gt;is_tag;      $text.=$token-&gt;as_is;    }        push @chapter, [ $num, $title, $text ];  }    DumpFile(&#39;tao.yaml&#39;, \@chapter);  return \@chapter;}#========================================================================# usage - prints a nice usage message and exits the appsub usage {  print &lt;&lt;USAGE_DOC;  Daily Tao te Ching chapter printer.  Uses the GNL version of the Tao Te Ching (TTC) from: http://acc6.its.brooklyn.cuny.edu/~phalsall/texts/taote-v4.htmlSee the file license.txt after first run for the GNL license.$0 [--today] [--chapter id|spec] [--reset] $0 --license$0 [--help] [--usage] [-h] [-?]      --chapter  Display a specific chapter from the TTC, either by record id              (0-81) or by chapter number followed by a period. For example,              to see TTC chapter 64b, use --chapter 64b. (including the              period).  Displays the last chapter if an invalid chapter              number is specified.     --today    If the date has advanced at least one day since the last time              we got a new chapter, display the next chapter from the TTC.     --reset    Delete all the disk caches: this will cause the next run (or              this run, if followed by one of the other options) to start               at the beginning of the TTC and fetch a new copy from the web               (or from the local file taote-v4.html)     --license  Display the license for this application (license for the GNL              text is saved to license.txt once it is retrieved)     --usage    Display this message: aliases are --help, -h, and -?USAGE_DOC  exit; }__DATA__Copyright (c) 2006 RadiantMatrix (http://radiantmatrix.org)Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the &quot;Software&quot;), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.((See license.txt once the GNL has been retrieved for the GNL license terms))&lt;/pre&gt;&lt;p&gt;&lt;small&gt;&lt;b&gt;Updates:&lt;/b&gt;&lt;ul type=&#39;square&#39;&gt;&lt;li&gt;2004-09-22 : [shmem] noted that [cpan://YAML::Syck] might be problematic for some.  Updated code to prefer YAML::Syck, but fall back on [dist://YAML] if Syck is not installed.&lt;/li&gt;&lt;/ul&gt;&lt;/small&gt;&lt;/p&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-375088&quot;&gt;&lt;small&gt;&lt;small&gt;&lt;font color=&#39;#000000&#39;&gt;&amp;lt;&lt;/font&gt;&lt;font color=&#39;#1a1a1a&#39;&gt;&amp;ndash;&lt;/font&gt;&lt;font color=&#39;#343434&#39;&gt;r&lt;/font&gt;&lt;font color=&#39;#4e4e4e&#39;&gt;a&lt;/font&gt;&lt;font color=&#39;#686868&#39;&gt;d&lt;/font&gt;&lt;font color=&#39;#828282&#39;&gt;i&lt;/font&gt;&lt;font color=&#39;#9c9c9c&#39;&gt;a&lt;/font&gt;&lt;font color=&#39;#b6b6b6&#39;&gt;n&lt;/font&gt;&lt;font color=&#39;#d0d0d0&#39;&gt;t&lt;/font&gt;&lt;font color=&#39;#eaeaea&#39;&gt;.&lt;/font&gt;&lt;font color=&#39;#d0d0d0&#39;&gt;m&lt;/font&gt;&lt;font color=&#39;#b6b6b6&#39;&gt;a&lt;/font&gt;&lt;font color=&#39;#9c9c9c&#39;&gt;t&lt;/font&gt;&lt;font color=&#39;#828282&#39;&gt;r&lt;/font&gt;&lt;font color=&#39;#686868&#39;&gt;i&lt;/font&gt;&lt;font color=&#39;#4e4e4e&#39;&gt;x&lt;/font&gt;&lt;font color=&#39;#343434&#39;&gt;&amp;ndash;&lt;/font&gt;&lt;font color=&#39;#1a1a1a&#39;&gt;&amp;gt;&lt;/font&gt;&lt;/small&gt;&lt;!--&amp;lt;-&lt;/small&gt;&lt;b&gt;radiant&lt;/b&gt;.&lt;b&gt;matrix&lt;/b&gt;&lt;small&gt;-&amp;gt;&lt;/small--&gt;&lt;br&gt;&lt;a href=&#39;http://radiantmatrix.org/&#39;&gt;A collection of thoughts and links from the minds of geeks&lt;/a&gt;&lt;br&gt;&lt;em&gt;The Code that can be seen is not the true Code&lt;/em&gt;&lt;br&gt;&lt;em&gt;I haven&#39;t found a problem yet that can&#39;t be solved by a well-placed [http://en.wikipedia.org/wiki/Trebuchet|trebuchet]&lt;/em&gt;&lt;/small&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>A little introducation to piratical Perl me hearties (GrandFather)</title>
    <link>http://prlmnks.org/html/573629.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/573629.html</guid>

    <description>
        &lt;pre class=&quot;block_code&quot;&gt;use Acme::Lingua::Pirate::Perl;use warnings; Arrrr!use strict; Yo ho ho!Shiver me timbers!Ahoy (&quot;Avast there me maties&quot;);Splice the mainbrace!sub Ahoy {    the doubloons = plunder the booty;    yell the doubloons;    cast off}&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-461912&quot;&gt;&lt;hr&gt;DWIM is Perl&#39;s answer to G&amp;ouml;del&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Mirror a local directory to an FTP server (davis)</title>
    <link>http://prlmnks.org/html/573531.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/573531.html</guid>

    <description>
        &lt;div&gt;This is disgusting, but it&#39;s a just a quick-n-dirty hack to upload a directory structure to a remote FTP server where the FTP server would only allow one &quot;put&quot; command per login session. Yes, really. Obviously there was no ssh access otherwise I would have used rsync/scp -r. Oh well.&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl use warnings;use strict;use Net::FTP;use File::Basename;my $startdir = &quot;somewhere&quot;;my $ftphost  = &quot;somehost&quot;;my $ftpuser  = &quot;someuser&quot;;my $ftppass  = &quot;somepass&quot;;upload_dir($startdir);sub upload_dir {    my $dir = shift;    foreach my $entry (&lt;$dir/*&gt;) {            upload_dir($entry) if(-d $entry);            my $ftp =  Net::FTP-&gt;new($ftphost)                or die &quot;Can&#39;t connect to $ftphost: $@&quot;;            $ftp-&gt;login($ftpuser, $ftppass)                or die &quot;Can&#39;t login &quot;, $ftp-&gt;message;            my $dirname = dirname $entry;            $ftp-&gt;mkdir($dirname, 1);            $ftp-&gt;cwd($dirname);            $ftp-&gt;put($entry);            $ftp-&gt;quit;    }}&lt;/pre&gt;&lt;/div&gt;&lt;!-- Node text goes above. Div tags should contain sig only --&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-80839&quot;&gt;&lt;br&gt;davis&lt;br&gt;&lt;div class=&quot;pmsig-80839&quot;&gt;&lt;font size=&quot;-3&quot;&gt;Kids, you tried your hardest, and you failed miserably. The lesson is: Never try.&lt;/font&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Webcam Auto-Screensaver (ailivac)</title>
    <link>http://prlmnks.org/html/573441.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/573441.html</guid>

    <description>
        This core of this program takes an set of three images, two references and one test, and classifies the test image as more similar to reference A or reference B. It then uses a loop to poll for new images (from an HTTP server) in real time and run commands when the images changes between the two states. A fun application of this is to monitor a webcam and turn the screensaver on and off depending on whether or not you&#39;re sitting in front of your computer.First, a module to summarize an image. This samples pixels at regular intervals and stores the values in an array or arrays:&lt;pre class=&quot;block_code&quot;&gt;# ImgSum.pmpackage ImgSum;use strict;use warnings;use Image::Magick;sub summary {    my ($imgf, $res) = @_;    my $img = new Image::Magick;    $img-&gt;Read($imgf);    my ($w, $h) = $img-&gt;Get(qw/rows columns/);    my @sum;    my $i = 0;    for (my $y = 0; $y &lt; $h; $y += $res)    {        for (my $x = 0; $x &lt; $w; $x += $res)        {            my $px = $img-&gt;Get(&quot;pixel[$x,$y]&quot;);            my $rgb = [split/,/, $px];            $sum[$i++] = $rgb;        }    }    return \@sum;}1;&lt;/pre&gt;Next, a script to summarize an image file and store it for later use:&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl# sum.pluse strict;use warnings;use ImgSum;use Data::Dumper;use Storable;my ($img, $res, $out) = @ARGV;# $res is the sampling resolutionmy $sum = ImgSum::summary($img, $res);store($sum, $out);&lt;/pre&gt;The next module takes two image summaries and calculates the average difference between them. It&#39;s an incredibly simple algorithm that has numerous shortcomings, but it works in simple situations.&lt;pre class=&quot;block_code&quot;&gt;# SumDif.pmpackage SumDiff;use strict;use warnings;use Data::Dumper;sub mean {    my $tt = 0;    my $s = 0;    while (defined($_ = shift))    {        $tt += $_;        $s++;    }    return $tt/$s;}sub diffmap {    my ($s1, $s2) = @_;    my @diff;    foreach my $i(0..$#$s1)    {        my $c1 = $s1-&gt;[$i];        my $c2 = $s2-&gt;[$i];        my @d = map { abs($c2-&gt;[$_] - $c1-&gt;[$_]) } (0..$#$c1);        $diff[$i] = mean(@d);    }    return \@diff;}sub diffavg {    return mean(@{diffmap(@_)});}1;&lt;/pre&gt;Finally, watch.pl downloads images and compares them with two references and runs a command whenever the current image changes from being more like one reference to being more like the other. You can use a program like spook to serve images captured from a webcam.&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl# watch.pluse strict;use warnings;use ImgSum;use LWP::Simple;use SumDiff;use Storable;my @refs = map {retrieve($_)} (&#39;sum-r0.dat&#39;, &#39;sum-r1.dat&#39;);sub min {    my $mni = 0;    for my $i(0..$#_)    {        if ($_[$i] &lt; $_[$mni])        {            $mni = $i        }    }    return $mni;}sub getclass {    my $test = shift;    my @diffs;    for my $i(0..$#refs)    {        my $diff = SumDiff::diffavg($refs[$i], $test);        $diffs[$i] = $diff;        print &quot;Difference to $i: $diff\n&quot;;    }    return min(@diffs);}my $lc = 1;while (sleep 2){    my $stat = getstore(&#39;http://localhost:45005/webcam.jpg&#39;, &#39;current.jpg&#39;);    print &quot;HTTP status: $stat\n&quot;;    my $sum = ImgSum::summary(&#39;current.jpg&#39;, 4);    my $cl = getclass($sum);    print &quot;Likely class: $cl\n&quot;;    if ($lc != $cl)    {        system(&#39;xscreensaver-command &#39; .               ($cl?&#39;-deactivate&#39;:&#39;-activate&#39;));        $lc = $cl;    }}&lt;/pre&gt;To use it:&lt;pre class=&quot;block_code&quot;&gt;( take a picture of the empty room, save it as ref0.jpg )$ ./sum.pl ref0.jpg 4 sum-r0.dat( take a picture of yourself, call it ref1.jpg )$ ./sum.pl ref1.jpg 4 sum-r1.dat$ ./watch.pl&lt;/pre&gt;Now walk in front of and away from the camera slowly and your screensaver will turn on and off.
    </description>
</item>

        

<item>
    <title>List based language (gmol)</title>
    <link>http://prlmnks.org/html/572427.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/572427.html</guid>

    <description>
        Some time ago, I came to the monastery gates and received some much needed help in writing a list based mini language.Ikegami was very kind in helping me out (references [id://548582] and [id://564482]), Ive made a few simple changes that make this grammar behave the way I need.  Basically you can do assignment and evaluation.  Evaluation interpolates and autoflattens lists and variables using dot products (default composition and *) cross products (**) duplication (%) and replication (%%), and spits out interpolations that are new line delimited.Here is an example input:&lt;pre class=&quot;block_code&quot;&gt;hellohello{1,2}{a,b}{1,2}{a,b}**{1,2}temp-&gt;somestringsomeotherstring-&gt;{a,b}$temp$()$someotherstring${a,b}%{1,2}{a,b}%%{1,2}&lt;/pre&gt;and the output:&lt;pre class=&quot;block_code&quot;&gt;hellohello1hello2a1b2a1a2b1b2asomestring()bsomestring()ababaabb&lt;/pre&gt;Some of the design choices that were made relate to the application that I am doing using the SMILES language.  FWIW I can define sets of reactions (and combinatorial libraries) nicely by specifying them in this language.  Here is an example of a set of reactions&lt;pre class=&quot;block_code&quot;&gt;amines-&gt;{CN(C),{C,c}N(H)}thiols-&gt;{{C,c}S}alcohols-&gt;{cO}sulfonamides-&gt;{{C,c}S(=O)(=O)N(H)}nSynthons-&gt;{$amines$,$thiols$,$alcohols$,$sulfonamides$}lGroups-&gt;{Cl,Br,I}eSynthons-&gt;{C(=O){C,c}}reactants-&gt;{ $nSynthons$(H).}**{$lGroups$**$eSynthons$ }products-&gt;{ $nSynthons$**{$eSynthons$%$lGroups$} }$reactants$&gt;&gt;$products$&lt;/pre&gt;and the output:&lt;pre class=&quot;block_code&quot;&gt;CN(C)(H).ClC(=O)C&gt;&gt;CN(C)C(=O)CCN(C)(H).ClC(=O)c&gt;&gt;CN(C)C(=O)cCN(C)(H).BrC(=O)C&gt;&gt;CN(C)C(=O)CCN(C)(H).BrC(=O)c&gt;&gt;CN(C)C(=O)cCN(C)(H).IC(=O)C&gt;&gt;CN(C)C(=O)CCN(C)(H).IC(=O)c&gt;&gt;CN(C)C(=O)cCN(H)(H).ClC(=O)C&gt;&gt;CN(H)C(=O)CCN(H)(H).ClC(=O)c&gt;&gt;CN(H)C(=O)cCN(H)(H).BrC(=O)C&gt;&gt;CN(H)C(=O)CCN(H)(H).BrC(=O)c&gt;&gt;CN(H)C(=O)cCN(H)(H).IC(=O)C&gt;&gt;CN(H)C(=O)CCN(H)(H).IC(=O)c&gt;&gt;CN(H)C(=O)ccN(H)(H).ClC(=O)C&gt;&gt;cN(H)C(=O)CcN(H)(H).ClC(=O)c&gt;&gt;cN(H)C(=O)ccN(H)(H).BrC(=O)C&gt;&gt;cN(H)C(=O)CcN(H)(H).BrC(=O)c&gt;&gt;cN(H)C(=O)ccN(H)(H).IC(=O)C&gt;&gt;cN(H)C(=O)CcN(H)(H).IC(=O)c&gt;&gt;cN(H)C(=O)cCS(H).ClC(=O)C&gt;&gt;CSC(=O)CCS(H).ClC(=O)c&gt;&gt;CSC(=O)cCS(H).BrC(=O)C&gt;&gt;CSC(=O)CCS(H).BrC(=O)c&gt;&gt;CSC(=O)cCS(H).IC(=O)C&gt;&gt;CSC(=O)CCS(H).IC(=O)c&gt;&gt;CSC(=O)ccS(H).ClC(=O)C&gt;&gt;cSC(=O)CcS(H).ClC(=O)c&gt;&gt;cSC(=O)ccS(H).BrC(=O)C&gt;&gt;cSC(=O)CcS(H).BrC(=O)c&gt;&gt;cSC(=O)ccS(H).IC(=O)C&gt;&gt;cSC(=O)CcS(H).IC(=O)c&gt;&gt;cSC(=O)ccO(H).ClC(=O)C&gt;&gt;cOC(=O)CcO(H).ClC(=O)c&gt;&gt;cOC(=O)ccO(H).BrC(=O)C&gt;&gt;cOC(=O)CcO(H).BrC(=O)c&gt;&gt;cOC(=O)ccO(H).IC(=O)C&gt;&gt;cOC(=O)CcO(H).IC(=O)c&gt;&gt;cOC(=O)cCS(=O)(=O)N(H)(H).ClC(=O)C&gt;&gt;CS(=O)(=O)N(H)C(=O)CCS(=O)(=O)N(H)(H).ClC(=O)c&gt;&gt;CS(=O)(=O)N(H)C(=O)cCS(=O)(=O)N(H)(H).BrC(=O)C&gt;&gt;CS(=O)(=O)N(H)C(=O)CCS(=O)(=O)N(H)(H).BrC(=O)c&gt;&gt;CS(=O)(=O)N(H)C(=O)cCS(=O)(=O)N(H)(H).IC(=O)C&gt;&gt;CS(=O)(=O)N(H)C(=O)CCS(=O)(=O)N(H)(H).IC(=O)c&gt;&gt;CS(=O)(=O)N(H)C(=O)ccS(=O)(=O)N(H)(H).ClC(=O)C&gt;&gt;cS(=O)(=O)N(H)C(=O)CcS(=O)(=O)N(H)(H).ClC(=O)c&gt;&gt;cS(=O)(=O)N(H)C(=O)ccS(=O)(=O)N(H)(H).BrC(=O)C&gt;&gt;cS(=O)(=O)N(H)C(=O)CcS(=O)(=O)N(H)(H).BrC(=O)c&gt;&gt;cS(=O)(=O)N(H)C(=O)ccS(=O)(=O)N(H)(H).IC(=O)C&gt;&gt;cS(=O)(=O)N(H)C(=O)CcS(=O)(=O)N(H)(H).IC(=O)c&gt;&gt;cS(=O)(=O)N(H)C(=O)c&lt;/pre&gt;This is the recdescent meat:&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl# make_grammar.pl: Creates Grammar.pmuse strict;use warnings;use Parse::RecDescent ();my $grammar = &lt;&lt;&#39;__END_OF_GRAMMAR__&#39;;   {      use strict;      use warnings;      use List::Util qw( sum );    my %variables=();      sub replicate {   my ($l, $r) = @_;      my $lt = $l-&gt;[0];   my $rt = $r-&gt;[0];   die(&quot;Type error\n&quot;) if $lt ne &#39;list&#39;;   die(&quot;Type error\n&quot;) if $rt ne &#39;list&#39;;   my $ld = $l-&gt;[1];   my $rd = $r-&gt;[1];      my @replicated;   my $value;      foreach (@{$ld})   {    $value=$_;     foreach(0..$#$rd){push @replicated, $value;}   }   return [list =&gt; \@replicated];  }  sub duplicate {   my ($l, $r) = @_;      my $lt = $l-&gt;[0];   my $rt = $r-&gt;[0];   die(&quot;Type error\n&quot;) if $lt ne &#39;list&#39;;   die(&quot;Type error\n&quot;) if $rt ne &#39;list&#39;;   my $ld = $l-&gt;[1];   my $rd = $r-&gt;[1];      my @duplicated;         foreach(0..$#$rd){push @duplicated, @{$ld};}       return [list =&gt; \@duplicated];  }     sub cross_prod {         my ($l, $r) = @_; my ($i, $j)=(&#39;&#39;,&#39;&#39;);         my $lt = $l-&gt;[0];         my $rt = $r-&gt;[0];         die(&quot;Type error\n&quot;) if $lt ne &#39;list&#39;;         die(&quot;Type error\n&quot;) if $rt ne &#39;list&#39;;         my $ld = $l-&gt;[1];         my $rd = $r-&gt;[1]; return [ list =&gt; [map { $i=$_; map { $j=$_;  $ld-&gt;[$i].$rd-&gt;[$j]} 0..$#$rd} 0..$#$ld]];      }      sub dot_prod {         my ($l, $r) = @_;  my $l_is_string = $l-&gt;[0] eq &#39;string&#39;; my $r_is_string = $r-&gt;[0] eq &#39;string&#39;; my $l_is_list   = $l-&gt;[0] eq &#39;list&#39;; my $r_is_list   = $r-&gt;[0] eq &#39;list&#39;; my ($ld, $rd) = ($l-&gt;[1], $r-&gt;[1]);          if ($l_is_string &amp;&amp; $r_is_string) { return [ string =&gt; $ld.$rd ];         }         if ($l_is_list &amp;&amp; $r_is_list) {die(&quot;Size error\n&quot;) if @$ld != @$rd;            return [ list =&gt; [ map { $ld-&gt;[$_].$rd-&gt;[$_] } 0..$#$rd ] ];         }         if ($l_is_string &amp;&amp; $r_is_list) {    return [ list =&gt; [ map { $ld.$_ } @$rd ] ]; }  if ($l_is_list &amp;&amp; $r_is_string) {    return [ list =&gt; [ map { $_.$rd } @$ld ] ]; }      }   }      parse:    assignment EOF   | expr EOF {  if($item[1]-&gt;[0] eq &#39;list&#39;)  {   map {print $_.&quot;\n&quot;;} @{$item[1]-&gt;[1]};  }    if($item[1]-&gt;[0] eq &#39;string&#39;)  {print $item[1]-&gt;[1].&quot;\n&quot;;}    $return = $item[1]; }      assignment: /\w+/ &#39;-&gt;&#39; &lt;commit&gt; expr {$variables{$item[1]}=$item[4];}    expr   : term expr_[ $item[1] ]   expr_  : &#39;%%&#39;   &lt;commit&gt; term expr_[ replicate($arg[0], $item[3]) ]|&#39;%&#39;   &lt;commit&gt; term expr_[ duplicate($arg[0], $item[3]) ]|&#39;**&#39;  &lt;commit&gt; term expr_[ cross_prod($arg[0], $item[3]) ]            |&#39;*&#39;   &lt;commit&gt; term expr_[ dot_prod   ($arg[0], $item[3]) ]            | term  &lt;commit&gt;     expr_[ dot_prod   ($arg[0], $item[1]) ]            | { $arg[0] }   term   : &#39;{&#39; &lt;commit&gt; mbody &#39;}&#39;                {  my @to_flatten = @{$item[3]};  my @flattened=();  my ($i, $j)=(&#39;&#39;,&#39;&#39;);    foreach (@to_flatten)  {   $i=$_;   if($i-&gt;[0] eq &#39;list&#39;)   {    push @flattened, @{$_-&gt;[1]};next;   }   push @flattened, $i-&gt;[1];  }    $return = [list =&gt; \@flattened]; }  | VARIABLE | STRING     mbody  : &lt;leftop: expr &#39;,&#39; expr&gt;   # Tokens   VARIABLE:  &#39;$&#39; &lt;commit&gt; /\w+/ &#39;$&#39; { $return = $variables{$item[3]}; }   STRING : /[^\{\}\,\*\$\%]+/ { $return = [ string =&gt; $item[1]]; }   EOF    : /\Z/__END_OF_GRAMMAR__Parse::RecDescent-&gt;Precompile($grammar, &#39;Grammar&#39;)   or die(&quot;Bad grammar\n&quot;);&lt;/pre&gt;and what you want to run it&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl# test.pluse strict;use warnings;use Grammar      qw( );my $parser = Grammar-&gt;new();while(&lt;&gt;){ chomp; my $rv = eval { $parser-&gt;parse($_) }; my $e = $@;  if ($e) {      $rv = &quot;$_ = $e&quot;;      $rv =~ s/\n\z//;   } elsif (!defined($rv)) {      $rv = &quot;$_ = Bad Expression&quot;;   } }exit;&lt;/pre&gt;Any comments/advice appreciated.  Thanks again Ikegami.
    </description>
</item>

        

<item>
    <title>Easily parallelize program execution (bennymack)</title>
    <link>http://prlmnks.org/html/569260.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/569260.html</guid>

    <description>
        &lt;p&gt;This is a script that uses POE to run a series of tasks in parallel. It&#39;s &quot;easy&quot; because the code to run the tasks is simply defined as subroutines and executed. One in the parent, one in the children. Behold!&lt;/p&gt;&lt;readmore title=&#39;PoeSubs.pm&#39;&gt;&lt;pre class=&quot;block_code&quot;&gt;package PoeSubs;use strict;use warnings; use Data::Dumper;use POE qw(Wheel::Run Filter::Reference);# sub run_tasks#{{{sub run_tasks {    my( %ARGS )            = @_;    my( $debug )           = $ARGS{debug};    my( $handle_task_sub ) = delete $ARGS{handle_task_sub};    my( $start_tasks )     = get_start_tasks( %ARGS );    no warnings &#39;redefine&#39;;    local *POE::Kernel::_warn = sub {} if not $debug;       POE::Session-&gt;create (        inline_states =&gt; {            _start      =&gt; $start_tasks,            next_task   =&gt; $start_tasks,            task_result =&gt; sub { $handle_task_sub-&gt;( $_[ARG0] ); },            task_done   =&gt; \&amp;PoeSubs::handle_task_done,            task_debug  =&gt; \&amp;PoeSubs::handle_task_stderr,        }    );        # Run until there are no more tasks.    POE::Kernel-&gt;run();    my( $reduce_sub ) = delete $ARGS{reduce_sub};    if( &#39;CODE&#39; eq ref $reduce_sub ) {        &amp;$reduce_sub;    }}#}}}# sub get_start_tasks#{{{sub get_start_tasks {    my( %ARGS ) = @_;    my( $debug )                = $ARGS{debug};    my( @tasks )                = @{ $ARGS{tasks} };    my( $max_concurrent_tasks ) = $ARGS{max_concurrent_tasks} || 1;    my( $run_task_sub )         = $ARGS{run_task_sub};    return sub {        my $heap = $_[HEAP];        while ( keys( %{ $heap-&gt;{task} } ) &lt; $max_concurrent_tasks ) {            my $next_task = shift @tasks;            last unless defined $next_task;                        print &quot;Starting task for $next_task...\n&quot; if $debug;                        my $task = POE::Wheel::Run-&gt;new (#               Program =&gt; sub { $run_task_sub-&gt;( $next_task ) },                Program =&gt; sub { get_program_sub( $run_task_sub )-&gt;( $next_task ) },                StdoutFilter =&gt; POE::Filter::Reference-&gt;new(),                StdoutEvent  =&gt; &quot;task_result&quot;,                StderrEvent  =&gt; &quot;task_debug&quot;,                CloseEvent   =&gt; &quot;task_done&quot;,            );                        $heap-&gt;{task}-&gt;{ $task-&gt;ID } = $task;        }    };}#}}}# sub get_program_sub#{{{sub get_program_sub {    my( $run_task_sub ) = @_;    return sub {        my( $task )   = @_;# warn &#39;$task = &#39;, $task;        my( $filter ) = POE::Filter::Reference-&gt;new();        my( $task_output ) = $run_task_sub-&gt;( $task );# warn Dumper( $task_output );        my $output = $filter-&gt;put( [ $task_output ] );        print @$output;    };}#}}}# sub handle_task_stderr#{{{# Catch and display information from the child&#39;s STDERR.  This was# useful for debugging since the child&#39;s warnings and errors were not# being displayed otherwise.sub handle_task_stderr {    my $stderr = $_[ARG0];#   print &quot;Debug: $stderr\n&quot;;    warn &quot;handle_task_stderr: $stderr\n&quot;;}#}}}# sub handle_task_done#{{{# The task is done.  Delete the child wheel, and try to start a new# task to take its place.sub handle_task_done {    my ( $kernel, $heap, $task_id ) = @_[ KERNEL, HEAP, ARG0 ];    delete $heap-&gt;{task}-&gt;{$task_id};    $kernel-&gt;yield(&quot;next_task&quot;);}#}}}1;__END__&lt;/pre&gt;And the test script.&lt;readmore title=&quot;Poe_Test.pl&quot;&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;use warnings;use Data::Dumper;use PoeSubs;use constant MAX_CONCURRENT_TASKS =&gt; 100;# use constant TASKS                =&gt; qw( 4 10 3 7 );my( $run_task_sub, $handle_task_sub, $reduce_sub ) = get_subs();# for( 1..20 ) {    PoeSubs::run_tasks(#       debug                =&gt; 1,        tasks                =&gt; [ 1..100 ],        max_concurrent_tasks =&gt; MAX_CONCURRENT_TASKS,        run_task_sub         =&gt; $run_task_sub,        handle_task_sub      =&gt; $handle_task_sub,        reduce_sub           =&gt; $reduce_sub,    );# }exit 0;sub get_subs {# Gets the task as the first argument# Should return a reference of some type    my( $run_task_sub ) = sub {        my( $task )   = @_;        my( $sleep ) = int( rand 4 ) + 1;        sleep( $sleep );        return { task =&gt; $task, sleep =&gt; $sleep, message =&gt; sprintf( &#39;[Task %s: slept %s seconds]&#39;, $task, $sleep ) };    };# Gets the reference returned by run_task as the first argument    my( $totals ) = 0;    my( $handle_task_sub ) = sub {        my( $result ) = @_;        $totals += $result-&gt;{sleep};        printf( &quot;{Task: %s, Got message: %s}\n&quot;, $result-&gt;{task}, $result-&gt;{message} );    };# Prints out the totals    my( $reduce_sub ) = sub {        print &#39;$totals = &#39;, $totals, &quot;\n&quot;;    };    return( $run_task_sub, $handle_task_sub, $reduce_sub );}   &lt;/pre&gt;&lt;p&gt;I&#39;m just looking to get some feedback. Is it idiotic? Is it cool? Is it a CPAN module already?&lt;/p&gt;&lt;p&gt;I tend to like the way that all you need to do is define a few subs and let er rip. I&#39;m a big fan of easy interfaces.&lt;/p&gt;&lt;p&gt;Thank you, that is all.&lt;/p&gt;
    </description>
</item>

        

<item>
    <title>Wootoff monitor (zigdon)</title>
    <link>http://prlmnks.org/html/568159.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/568159.html</guid>

    <description>
        Silly little script to keep track of the currently running Wootoff ([http://www.woot.com]):&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl #===============================================================================##         FILE:  wootoff.pl##        USAGE:  ./wootoff.pl ##  DESCRIPTION:  Monitor woot.com and alert when there are changes##      OPTIONS:  ---# REQUIREMENTS:  ---#         BUGS:  ---#        NOTES:  ---#       AUTHOR:  Zigdon#      VERSION:  1.0#      CREATED:  08/17/2006 09:54:43 AM EDT#     REVISION:  $Id: wootoff.pl 70 2006-08-17 22:30:27Z dan $#===============================================================================use strict;use LWP::Simple;use HTML::TreeBuilder;use Date::Manip;my $URL = &quot;http://www.woot.com&quot;;my $oldtitle;my ($woottime, $eta);$|++;my $idle = 0;while (1) {  my ($title, $price, $percent) = &amp;update;  if ($title ne $oldtitle) {      my $now = scalar localtime;      exec(&quot;xmessage &#39;$now - NEW WOOT!\n$title\n$price&#39; 2&gt;/dev/null&quot;) unless fork;      print &quot;\n&quot;, UnixDate(ParseDate(&quot;now&quot;), &#39;%H:%M:%S&#39;), &quot; - NEW WOOT!\n&quot;;      $woottime = time;      $oldtitle = $title;  }  $eta = ParseDateDelta(int((time - $woottime) / (100 - $percent) * 100)) if $percent &lt; 99;  my $etatext;  if ($eta) {      my $delta = DateCalc(&quot;now&quot;, &quot;epoch $woottime&quot;);      $eta = DateCalc($delta, $eta);      $etatext = Delta_Format($eta, 0, &#39;%mhm, %svs&#39;);      $eta = DateCalc(&quot;now&quot;, $eta);      $eta = UnixDate($eta, &#39;%H:%M:%S&#39;);  } else {      $eta = &quot;N/A&quot;;  }  print UnixDate(ParseDate(&quot;now&quot;), &#39;%H:%M:%S&#39;), &quot; - $title - $price ($percent\% eta: $etatext $eta)\r&quot;;  if ($idle++ &gt; 600) {      print &quot;\n&quot;;      $idle = 0;  }  sleep 30;}sub update {    my $html;    until ($html = get $URL) {         warn &quot;Can&#39;t retrieve $URL!&quot;;         sleep 5;    }    my $tree = HTML::TreeBuilder-&gt;new_from_content($html) or die &quot;Can&#39;t parse html!&quot;;    my $title = $tree-&gt;look_down(_tag =&gt; &quot;h3&quot;, id =&gt; &quot;ctl00_ContentPlaceHolder_TitleHeader&quot;)-&gt;as_text;    my $price = $tree-&gt;look_down(_tag =&gt; &quot;span&quot;, id =&gt; &quot;PriceSpan&quot;)-&gt;as_text;    my $percent = $tree-&gt;look_down(_tag =&gt; &quot;div&quot;, class =&gt; &quot;bar&quot;)-&gt;attr_get_i(&quot;style&quot;);    ($percent) = ($percent =~ /(\d+)/);    return ($title, $price, $percent);}&lt;/pre&gt;&lt;!-- Node text goes above. Div tags should contain sig only --&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-193854&quot;&gt;&lt;P&gt;-- [zigdon]&lt;/P&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>[FreeBSD] Look out your (network) peephole. (starbolin)</title>
    <link>http://prlmnks.org/html/568077.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/568077.html</guid>

    <description>
        &lt;p&gt;Looks out your network stack to see what&#39;s working and what&#39;s not.&lt;p&gt;I wrote this when my cat unplugged the router for the Nth time.  (He likes to chew the little rubber boots on the premium patch cords.)  I use this script so I don&#39;t have to type the usual mantra: ifconfig, netstat -rn, ping localhost, ping default, dig nameserver, etc.... I also have found it very usefull when The Commander yells over &quot;Hon, why isn&#39;t Pogo working?&quot;  I just run this one script and yell back &quot;It&#39;s not on our end.&quot;&lt;p&gt;I tried to make it robust and have inteligent error messages but here is more work to be done. Still, it&#39;s very useable nevertheless.  I haven&#39;t spent much time on it lately  so I thought I&#39;d post it before it became completely dust covered.The output looks like this:&lt;pre class=&quot;block_code&quot;&gt;1 interfaces down:        plip03 interfaces up:        rl0        fxp0        lo0Checking localnet:        127.0.0.1      working.Checking &quot;localhost&quot;                     working.Checking interface rl0:   10.0.0.1       working.Checking interface fxp0:  192.168.1.11   working.2 external interfaces found.Checking Default route:   192.168.1.10   working.Checking default router:  192.168.0.1    working.Checking DNS host:        dns1.xxx.xxx.net. working.Checking DNS host:        dns1.xxx.xxx.net. working.&lt;/pre&gt;&lt;p&gt;&lt;b&gt;Bugs:&lt;/b&gt;&lt;li&gt;Addresses for localhost, and router are hard coded.  &lt;li&gt;Not portable.&lt;li&gt;No pod.&lt;li&gt;Would be nice to be able to specify your router from the command line.&lt;li&gt;Not portable.&lt;/p&gt;&lt;p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w## peek.pl - walk up the IP stack looking for problems.## 6/7/06 betuse strict;# Definitionsmy $localnet=&quot;127.0.0.1&quot;;       # Should get this from &quot;hosts&quot; file.my $router = &quot;192.168.0.1&quot;;      # Your DSL gateway here.## Files we need.my $resolv = &quot;/etc/resolv.conf&quot;;        # To get the nameservers from.my $hosts = &quot;/etc/hosts&quot;;               # To get &quot;localhost&quot; from.## Commandsmy $short_ping = &quot;ping -t1 -c1 -n &quot;;my $dig_cmd = &quot;dig&quot;;my $dig_options = &quot;+short +tries=1 +time=2&quot;;my $config_cmd = &quot;ifconfig&quot;;my $status_cmd = &quot;netstat -rn&quot;;## Hold found routes.my  @net;my $token; my $host;## Subroutines#sub check_interface  {          # ( -d|-u , &quot;message&quot; )        my $option = shift @_;        my $message = shift @_;        my $ifs=`                $config_cmd $option |                perl -lne &#39;\$\\=&quot; &quot;; print \$1 if m/^([a-z]+[0-9])[:]/&#39;        `;        my @ifs=split / /, $ifs;        print scalar @ifs, $message;        print &quot;\t$_\n&quot; foreach @ifs;        return @ifs}sub ping_host {        my $host = shift @_;        `$short_ping $host`;        print &#39;&#39;, ($?==0)?&#39;working.&#39;:&#39;***FAILED***&#39;, &quot;\n&quot;;}# Abnomal exit point.  Used for network errors. Use &quot;die&quot; for program errors.sub bail {      # Tell the user we can&#39;t continue.        print shift @_;        print &quot;\nCan&#39;t continue tests.\n&quot;;        exit 1;}## main - program starts here#`uname` =~ /FreeBSD/ or die &quot;Program is for FreeBSD&quot;;# Find any interfaces marked &quot;down&quot;check_interface( &quot;-d&quot;, &quot; interfaces down:\n&quot; );# Find any interfaces marked &quot;up&quot;my @ifs = check_interface( &quot;-u&quot;, &quot; interfaces up:\n&quot; );# Check localhost directprintf &quot;%-26s%-15s&quot;, &quot;Checking localnet:&quot;, $localnet;ping_host ( $localnet );# Check local host via lookupprintf &quot;%-41s&quot;, &#39;Checking &quot;localhost&quot; &#39;;ping_host( &quot;localhost&quot; );# Find address of remaining interfaces.# Check itmy $count=0;foreach my $ifs (@ifs) {        do {                @net = grep m/inet /,  split /\n/, `$config_cmd $ifs`;                if ( scalar @net != 1 ) {                        print &quot;$ifs: No inet record found.\n&quot;; next };                if ($net[0] =~ m/([0-9]+[.][0-9]+[.][0-9]+[.][0-9]+)/) {                        printf &quot;%-26s%-15s&quot;,                                &quot;Checking interface $ifs:&quot;, $1;                        `$short_ping $1`;                        if ($?==0) {                                $count++;                                print &quot;working.\n&quot;;                        } else {                                print &quot;***FAILED***\n&quot;;                        }                }        } unless ( $ifs eq &quot;lo0&quot; );}if($count == 0) { bail  &quot;No external interfaces!!!&quot; };print &quot;$count external interface&quot;, ($count==1)?&#39;&#39;:&#39;s&#39;, &quot; found.\n&quot;;# Find default route and check@net = grep m/default/,  split  &#39;\n&#39;, `$status_cmd`;if ( scalar @net == 0 ) { bail &quot;No default route!&quot; };if ( scalar @net &gt;= 2 ) { bail &quot;Multiple default routes!&quot; };# Could be on different subnets??($token, $host) = split &#39; &#39;, $net[0];           #awk compatible pattern.printf &quot;%-26s%-15s&quot;, &quot;Checking Default route:&quot;, $host;ping_host ( $host );# Ping routerprintf &quot;%-26s%-15s&quot;,  &quot;Checking default router:&quot;, $router;ping_host ( $router );#       Could we maybe use TTL to resolve next hop? So we don&#39;t hardcode it.#               Otherwice we have to get it from the router.#               &quot;ping -m3  $DNS&quot; works, but would break if ??.#       lookup DNS in /etc/resolv.conf#               Check DNS#-e $resolv or die &quot;Resolv.conf file ***missing ***&quot;;open FH, $resolv or die &quot;Open $resolv failed: $!&quot;;while (&lt;FH&gt;) {        next if ( length $_ &lt; 10 );        ($token, $host) = split &#39; &#39;;            #awk compatible pattern.        if ( $token eq &quot;nameserver&quot; ) {                printf &quot;%-26s&quot;, &quot;Checking DNS host:&quot;;                `$dig_cmd \@$host -x $host $dig_options` =~ m/([\w]|[.])+/;                if ($?==0 ) {                        print &quot;$&amp; working.\n&quot;;                } else {                        print &quot;$host ***FAILED***!\n&quot;;                }        }}close FH;# Normal program termination.exit 0;__END__&lt;/pre&gt;&lt;/p&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-433972&quot;&gt;&lt;HR&gt;s//-----&gt;\t/;$~=&quot;JAPH&quot;;s//\r&lt;$~~/;{s|~$~-|-~$~|||s|-$~~|$~~-|||s,&lt;$~~,&lt;~$~,,s,~$~&gt;,$~~&gt;,,$|=1,select$,,$,,$,,1e-1;print;redo}&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Managing BitTorrent traffic (rinceWind)</title>
    <link>http://prlmnks.org/html/567045.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/567045.html</guid>

    <description>
        &lt;p&gt;I&#39;ve recently set up some torrents on my home Linux PC, mainly for Debian and Knoppix distros. I&#39;m quite into the idea of using my spare bandwidth to seed torrents for others, but I find that this chews up my upload potential to the detrement of other things I&#39;m doing.&lt;/p&gt;&lt;p&gt;I&#39;m using the shell to background a number of btdownloadgui processes - these each provide a visual indication of the status of a given torrent. I could click &quot;close&quot; on all of the GUIs whenever I want to use my bandwidth for something else. But, I&#39;ve found a neater solution.&lt;/p&gt;&lt;p&gt;There&#39;s a pair of signals: SIGSTOP and SIGCONT that can be used to suspend and resume processes. On i386 Debian, these signals are 19 and 18 respectively; see &quot;man 7 signal&quot; for the list of signal numbers that apply to your platform. &lt;/p&gt;&lt;p&gt;The shell (bash in this case) doesn&#39;t easily provide lists of PIDs. If there&#39;s exactly one process running a given command, you can use &lt;tt class=&quot;inline_code&quot;&gt;%string&lt;/tt&gt; as a job specifier to find the PID, provided it is a child process of your current shell. As an alternative, here is some perl that will give you a list of matching PIDs to STDOUT:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl# findpidsuse strict;use warnings;use Unix::PID;use Getopt::Long;my $cmd = &#39;&#39;;GetOptions(    &#39;command=s&#39; =&gt; \$cmd,    );my $pobj = Unix::PID-&gt;new;print join(&#39; &#39;,$pobj-&gt;get_pidof($cmd)),&quot;\n&quot;;&lt;/pre&gt;&lt;p&gt;Now, I can easily suspend and resume all my torrent processes, voila:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;ivor@orinoco:~$ kill -19 `findpids --command btdownloadgui`ivor@orinoco:~$ jobs[1]   Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[2]+  Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[3]   Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[5]   Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[6]   Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[7]   Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[8]   Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)[9]-  Stopped                 nice btdownloadgui --saveas ${fil%.torrent} $torrent  (wd: ~)&lt;/pre&gt;&lt;p&gt; The torrents die down and I get to use the bandwidth. Then, when I&#39;ve finished:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;ivor@orinoco:~$ kill -18 `findpids --command btdownloadgui`&lt;/pre&gt;&lt;p&gt;and the lights start flickering on my router again.&lt;/p&gt;&lt;!-- Node text goes above. Div tags should contain sig only --&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-144850&quot;&gt;&lt;p&gt;&lt;small&gt;--&lt;br /&gt;&lt;br /&gt;Oh Lord, wont you burn me a Knoppix CD ?&lt;br /&gt;My friends all rate Windows, I must disagree.&lt;br /&gt;Your powers of persuasion will set them all free,&lt;br /&gt;So oh Lord, wont you burn me a Knoppix CD ? &lt;br /&gt; &lt;em&gt;(Missquoting Janis Joplin)&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Inernet daemon watchdog (sh1tn)</title>
    <link>http://prlmnks.org/html/567036.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/567036.html</guid>

    <description>
        # `inet` is the ip, default gw and dns settings&lt;br&gt;# upon lost connection this daemon renew the settings&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;use POSIX qw(setsid);use Net::Ping;{ fork and exit; setsid }my $router = q{default gw ip};my $net    = Net::Ping-&gt;new;sleep 5, $net-&gt;ping($router, 1) or `inet` while 1&lt;/pre&gt;&lt;!-- Node text goes above. Div tags should contain sig only --&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-416961&quot;&gt;&lt;br&gt;&lt;br&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Dynamic GAIM Info (frew)</title>
    <link>http://prlmnks.org/html/566877.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/566877.html</guid>

    <description>
        So for a long time I had the current song from XMMS in my GAIM info, but I switched to amaroK and I needed to do something else.  So I got the AutoProfile Plugin and it can run any program and use the output from that.  I suggest setting up the program as a cron job and having AutoProfile reading from a file as that won&#39;t cause it to take a long time if you want perl to do something that takes a while.  Anyway, after browsing around on CPAN through the Acme namespace a bit, I came up with this simple and fun info message:&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;#use Acme::Terror::UK;use Acme::Terror;use DCOP;my $t = Acme::Terror-&gt;new();#my $s = Acme::Terror::UK-&gt;new();my $dcop = DCOP-&gt;new();#my $uLevel = $s-&gt;fetch();my $level = $t-&gt;fetch();my $current_song = $dcop-&gt;run( &#39;amarok player nowPlaying&#39; );if ($dcop-&gt;run(&#39;amarok player status&#39;) == 1) {    $current_song = &quot;$current_song [paused]&quot;;}if ($current_song) {    print &quot;I am currently listening to: $current_song\n&quot;;}print &quot;Stay informed!  The current terrorist threat level in the US is: $level\n&quot;;#print &quot;Also: The current terrorist threat level in the UK is: $uLevel\n&quot;;&lt;/pre&gt;The UK stuff is commented out because the server seems to be down.  Probably because of the recent unfortunate events in that area.  Either way, I hope someone finds this cool and useful.  I certainly dig it!&lt;br /&gt;&lt;br /&gt;Note: Because getting the terror level seems to be taking a while I had this delegated to a cron job.  &lt;strike&gt;I can&#39;t figure out how to get the information from amaroK via cron, supposedly because of user permission issues.  If anyone figures out a way, let me know.&lt;/strike&gt;&lt;em&gt;Update:&lt;/em&gt; I figured out what my problem was with getting the music via cron.  The DCOP module uses kde-config to locate the dcop binary, but if kde-config is not in your path, it will just choke.  I added a PATH variable to my cron and that fixed everything.  Now hopefully you will all have the current song in your gaim info as well!
    </description>
</item>

        

<item>
    <title>Very light weight GPS position (GrandFather)</title>
    <link>http://prlmnks.org/html/566778.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/566778.html</guid>

    <description>
        &lt;p&gt;The following code reads data from a serial port connected GPS receiver generating NMEA GGA sentences and shows the current time and location.&lt;/p&gt;&lt;p&gt;Note that this is not a full NMEA sentence parser and it doesn&#39;t show a continuously updated time and position. It was written in a hurry to facilitate observing a lunar graze occultation.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;use strict;use warnings;use Tk;my $fh;my $mw = MainWindow-&gt;new;$mw-&gt;withdraw;if (! open $fh, &#39;&lt;&#39;, &quot;COM4:&quot;) {    $mw-&gt;messageBox (        -message =&gt; &#39;GPS unit not available&#39;,        -title =&gt; &#39;Time and location&#39;, -type =&gt; &#39;Ok&#39;,        -icon =&gt; &#39;info&#39;,        );    exit;}my $location;while (&lt;$fh&gt;) {    next if ! /^\$GPGGA,/;    chomp;        my ($hour, $min, $sec, $latDeg, $latMin, $latHemi, $longDeg, $longMin, $longHem) =        /(\d\d)(\d\d)(\d\d),(\d\d)([\d.]+),(\S),(\d\d)([\d.]+),(\S),/;    next if ! defined ($longHem);        $latMin = toMS ($latMin);    $longMin = toMS ($longMin);    $location = sprintf &quot;%s&quot;, &quot;$hour:$min:$sec, $latDeg $latMin$latHemi, $longDeg $longMin$longHem&quot;;    last;}$mw-&gt;messageBox (    -message =&gt; $location, -title =&gt; &#39;Time and location&#39;, -type =&gt; &#39;Ok&#39;    );sub toMS {    my $x = shift;    my $min = int ($x);    $x = ($x - $min) * 60;    return sprintf &quot;%d %.2f&quot;, $min, $x;}&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-461912&quot;&gt;&lt;hr&gt;DWIM is Perl&#39;s answer to G&amp;ouml;del&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>C comment stripping preprocessor (GrandFather)</title>
    <link>http://prlmnks.org/html/566453.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/566453.html</guid>

    <description>
        &lt;p&gt;In the process of trying to emulate the C pre-processor I had major trouble trying to handle C style /* ... */ comments. There are two issues that cause particular grief - comments can span lines and, at least for some compilers, comments can be nested (and are in the code I need to handle).&lt;/p&gt;&lt;p&gt;An additional gotcha is that things that look like comments in strings need to be retained.&lt;/p&gt;&lt;p&gt;The code below parses an input string and generates an output string comprising the original text sans C style comments. Note that it leaves C++ single line comments however - but they are easily dealt with in the second pass.&lt;/p&gt;&lt;readmore title=&quot;The code&quot;&gt;&lt;pre class=&quot;block_code&quot;&gt;use strict;use warnings;use Parse::RecDescent;my $decommendedText = &#39;&#39;;sub concat ($) {$decommendedText .= $_[0]; 1;}my $decomment = &lt;&lt;&#39;GRAMMAR&#39;;file : block(s)block   : string            {::concat ($item{string}); 1}        | m{((?!/\*|&quot;|&#39;).)+}s            {::concat ($item[-1]); 1}        | comment            {::concat ($item{comment}); 1;}            string  : /&quot;([^&quot;]|\\&quot;)*&quot;/            {$return = $item[-1] . ($text =~ /^\n/ ? &quot;\n&quot; : &#39;&#39;); 1;}        | /&#39;([^&#39;]|\\&#39;)*&#39;/            {$return = $item[-1] . ($text =~ /^\n/ ? &quot;\n&quot; : &#39;&#39;); 1;}        comment : &#39;/*&#39; commentBlock &#39;*/&#39;            {$return = $text =~ /^\n/ ? &quot;\n&quot; : &#39;&#39;; 1;}commentBlock   :  m{((?! \*/ | /\* ).)*}sx comment m{((?! \*/ | /\* ).)*}sx            {$return = &quot;\n&quot;; 1;}        | m{((?! \*/ | /\* ).)+}sx            {$return = &#39;&#39;; 1;}GRAMMARmy $parse = new Parse::RecDescent ($decomment);my $input = &lt;&lt;&#39;DATA&#39;;#include &quot;StdAfx.h&quot; // Tail comment#include &quot;Utility\perftime.h&quot;#pragma hdrstop/* Comment before MACRO *//* Comment /* and nested comment */ lines */#define MACRO 10\              + 3 // Multi line macro with comment#define __DEBUG /* comment */ 1#define STRING &#39;This is a string&#39; /* comment */#define COMMENT &quot;/* comment in \&quot;a\&quot; string */&quot;// c++ comment line/* Comment at start for a number of lines *//* multi-line comment/* nested */block */// cpp blockchar PerfTimer::Buf[64];DATA$parse-&gt;file($input) or die &quot;Parse failed\n&quot;;print $decommendedText;&lt;/pre&gt;&lt;p&gt;Prints:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#include &quot;StdAfx.h&quot;// Tail comment#include &quot;Utility\perftime.h&quot;#pragma hdrstop#define MACRO 10\              + 3 // Multi line macro with comment#define __DEBUG 1#define STRING &#39;This is a string&#39;#define COMMENT &quot;/* comment in \&quot;a\&quot; string */&quot;// c++ comment line// cpp blockchar PerfTimer::Buf[64];&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-461912&quot;&gt;&lt;hr&gt;DWIM is Perl&#39;s answer to G&amp;ouml;del&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Amortization Calculator (liverpole)</title>
    <link>http://prlmnks.org/html/565885.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/565885.html</guid>

    <description>
        This is an amortization calculator I wrote using Perl/Tk.&lt;p&gt;You can use it to calculate, for example, the principal and interest payments you would have to make on a mortgage for a house.&lt;p&gt;The program has 3 basic sections:&amp;nbsp;&amp;nbsp;The upper left shows program variables, the upper right is a graph of the interest/principal paid over the life of the loan, and the bottom is the full payment schedule, which may be saved to disk.&lt;p&gt;Here is the program itself:&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w##  Amortization (loan) calculation program##  060806 by liverpole################### Strict  ##################use strict;use warnings;####################### User-defined #######################my $title     = &quot;Mortgage Calculator V4.0 (060806) liverpole&quot;;my $schedout  = &quot;mortgage.txt&quot;;my @gcolors   = qw( red gold magenta );# Limiting valuesmy $min_years = 1;          # Minimum number of yearsmy $max_years = 100;        # Maximum number of yearsmy $min_rate  = 0.01;       # Minimum interest ratemy $max_rate  = 100.0;      # Maximum interest rate#################### Libraries ####################use FileHandle;use File::Basename;use Tk;use Tk::DialogBox;################## Globals ##################my ($loan, $rate, $period, $years, $npay, $extra, $newyear, $newpay);my ($o_loan, $o_rate, $o_period, $o_years, $o_npay);my ($o_extra, $o_newyear, $o_newpay);my ($payment, $total, $tot_paid, $tot_prin, $tot_int, $pct_int, $maxpay);my $payments = [ ];my @glist;my $use_defaults = 0;my $iam = basename($0);########################## Package textbox ##########################package textbox;our $AUTOLOAD;my $ptags = {    &#39;-1&#39; =&gt; &#39;debug&#39;,     &#39;0&#39; =&gt; &#39;default&#39;,     &#39;1&#39; =&gt; &#39;warning&#39;,     &#39;2&#39; =&gt; &#39;error&#39;,};my $plevels = {    &#39;-1&#39; =&gt; &#39;[debug]&#39;,     &#39;0&#39; =&gt; &#39; [info]&#39;,     &#39;1&#39; =&gt; &#39; [warn]&#39;,     &#39;2&#39; =&gt; &#39;[error]&#39;,};################################################################################  new():  textbox constructor.  Parameters are:##      $1 ... The textbox object#      $2 ... The parent window#      $3 ... The textbox width#      $4 ... The textbox height#      $5 ... The textbox color#      $6 ... The textbox default font#      $7 ... A flag which, if nonzero, disables the textbox#      $8 ... Scrollbar location (defaults to optional-south + optional-east)##  Return value:  The textbox object###############################################################################sub new {    my ($obj, $win, $width, $height, $bg, $font, $b_dis, $sb_loc) = @_;    $win or return;    my $mw = $win-&gt;toplevel();    $width  ||= 0;    $height ||= 0;    $bg     ||= &#39;white&#39;;    $font   ||= 0;    $b_dis  ||= 0;    $sb_loc ||= &quot;osoe&quot;;    my $tbox = $win-&gt;Scrolled(&#39;Text&#39;, -bg =&gt; $bg, -scrollbars =&gt; $sb_loc);    $tbox-&gt;configure(-wrap =&gt; &#39;none&#39;, -takefocus =&gt; 0);    $width  &amp;&amp; $tbox-&gt;configure(-width  =&gt; $width);    $height &amp;&amp; $tbox-&gt;configure(-height =&gt; $height);    my $state = ($b_dis)? &#39;disabled&#39;: &#39;normal&#39;;    $tbox-&gt;configure(-state =&gt; $state);    $tbox-&gt;pack(-side =&gt; &#39;left&#39;, -expand =&gt; &#39;1&#39;, -fill =&gt; &#39;both&#39;);    my $this = {        &#39;mw&#39;      =&gt; $mw,      # Toplevel window        &#39;win&#39;     =&gt; $win,     # Parent window        &#39;widget&#39;  =&gt; $tbox,    # The textbox widget        &#39;font&#39;    =&gt; $font,    # The textbox font        &#39;bg&#39;      =&gt; $bg,      # The textbox background color        &#39;tags&#39;    =&gt; { },      # The defined tags        &#39;state&#39;   =&gt; $state,   # The state (disabled or normal)    };    # Bless the object    bless $this, $obj;    # Create the default fonts    $this-&gt;create_tag(&#39;debug&#39;,   $font, &#39;grey&#39;,       &#39;black&#39;);    $this-&gt;create_tag(&#39;default&#39;, $font, $bg,          &#39;black&#39;);    $this-&gt;create_tag(&#39;error&#39;,   $font, &#39;red3&#39;,       &#39;white&#39;);    $this-&gt;create_tag(&#39;warning&#39;, $font, &#39;sandybrown&#39;, &#39;black&#39;);    return $this;}################################################################################  DESTROY():  textbox destructor.  Parameters are:##      $1 ... The textbox object###############################################################################sub DESTROY {    my ($this) = @_;    my $tbox = $this-&gt;{&#39;widget&#39;};    $tbox and $tbox-&gt;packForget();}################################################################################  AUTOLOAD:  this simply calls the given subroutine on the underlying#  widget, and passes on the passed arguments.  For example, this lets you#  do the following:##        my $tb = new textbox($db, 80, 40, &#39;gray&#39;, 0, 1);#        $tb-&gt;configure(-state =&gt; &#39;normal&#39;);#        $tb-&gt;insert(&#39;end&#39;, &#39;Hello world!&#39;);#        $tb-&gt;configure(-state =&gt; &#39;disabled&#39;);#        $tb-&gt;yview(&#39;1.0&#39;);##     $1 ... The textbox object###############################################################################sub AUTOLOAD {    my ($this, @params) = @_;    my $name = $AUTOLOAD;    $name =~ s/.*:://;    $this-&gt;{&#39;widget&#39;}-&gt;$name(@params);};############################ Other subroutines ##############################  create_tag:  creates the given tag with a specified font, background#  color, and foreground color, for use with the textbox.  The tag is not#  created if it already exists for the object.  If a label is not given#  for the tag, a label is created out of the font, background and foreground#  color names.  Parameters are:##     $1 ... The textbox object#     $2 ... The label for the font#     $3 ... The name of the font (optional)#     $4 ... The background color (optional)#     $5 ... The foreground color (optional)#sub create_tag {    my ($this, $label, $font, $bg, $fg) = @_;    $label or $label = &quot;${font}_${bg}_${fg}&quot;;    return if defined($this-&gt;{&#39;tags&#39;}-&gt;{$label});    $this-&gt;{&#39;tags&#39;}-&gt;{$label} = 1;    my $tbox = $this-&gt;{&#39;widget&#39;};    $font ||= $this-&gt;{&#39;font&#39;};    $bg   ||= 0;    $fg   ||= 0;    $font and $tbox-&gt;tagConfigure($label, -font       =&gt; $font);    $bg   and $tbox-&gt;tagConfigure($label, -background =&gt; $bg);    $fg   and $tbox-&gt;tagConfigure($label, -foreground =&gt; $fg);};##  out:  writes the given text to the text box.  Parameters are:##     $1 ... The textbox object#     $2 ... The text to write (or list of lines of text)#     $3 ... The tag to use (if different from the default)#     $4 ... The level if no tag given (-1=debug, 0=info, 1=warning, 2=error)#     $5 ... An EOL flag which, if nonzero, suppresses the end-of-line#     $6 ... An update flag which, if nonzero, suppresses the gui update.#sub out {    my ($this, $ptext, $tag, $level, $b_no_eol, $b_no_update) = @_;    $level       ||= 0;    $b_no_eol    ||= 0;    $b_no_update ||= 0;    (ref $ptext eq &#39;ARRAY&#39;) or $ptext = [ $ptext ];    if (!$tag) {        $tag = $ptags-&gt;{$level} || &#39;default&#39;;    }    my $tbox = $this-&gt;{&#39;widget&#39;};    $tbox-&gt;configure(-state =&gt; &#39;normal&#39;);    (@$ptext &gt; 0) or return;    ($b_no_eol) and map { $tbox-&gt;insert(&#39;end&#39;, &quot;$_&quot;,   $tag); } @$ptext;    ($b_no_eol)  or map { $tbox-&gt;insert(&#39;end&#39;, &quot;$_\n&quot;, $tag); } @$ptext;    $tbox-&gt;configure(-state =&gt; $this-&gt;{&#39;state&#39;});    $tbox-&gt;yview(&#39;end&#39;);    $b_no_update or $this-&gt;{&#39;mw&#39;}-&gt;update();    return 0;};##  log:  similar to out(), except that the current time and level name are#  displayed as part of the message, and no tag is passed.##     $1 ... The textbox object#     $2 ... The text to write (or list of lines of text)#     $3 ... The tag to use (if different from the default)#     $4 ... The level if no tag given (-1=debug, 0=info, 1=warning, 2=error)#     $5 ... An EOL flag which, if nonzero, suppresses the end-of-line#     $6 ... An update flag which, if nonzero, suppresses the gui update.#sub log {    my ($this, $ptext, $level, $b_no_eol, $b_no_update) = @_;    my $ctime = localtime(time);    $ctime =~ s/\S+\s+(... .. \d+:\d+:\d+).+/$1/;    my $tag   = $ptags-&gt;{$level}   || &#39;default&#39;;    my $ltext = $plevels-&gt;{$level} || $plevels-&gt;{0};    my $p = [ ];    if (ref $ptext eq &#39;ARRAY&#39;) {        map { push @$p, &quot;$ctime $_&quot; } @$ptext;    } else {        $p = [ &quot;$ctime $ltext  $ptext&quot; ];    }    return $this-&gt;out($p, $tag, $level, $b_no_eol, $b_no_update);};##  get:  gets the text in the text box.  Parameters are:##     $1 ... The textbox object#sub get {    my ($this) = @_;    my $tbox = $this-&gt;{&#39;widget&#39;};    my $ptext = $tbox-&gt;get(&#39;1.0&#39;, &#39;end&#39;);    chomp $ptext;    return $ptext;};##  clear:  clears the text box.  Parameters are:##     $1 ... The textbox object#sub clear {    my ($this) = @_;    my $tbox = $this-&gt;{&#39;widget&#39;};    my $mw = $this-&gt;{&#39;mw&#39;};    $tbox-&gt;configure(-state =&gt; &#39;normal&#39;);    $tbox-&gt;delete(&#39;1.0&#39;, &#39;end&#39;);    $tbox-&gt;configure(-state =&gt; $this-&gt;{&#39;state&#39;});    $mw-&gt;update();};##  bind:  simply binds the given key to the textbox widget.  Parameters are:##     $1 ... The textbox object#     $2 ... The key to bind#     $3 ... The subroutine to invoke#sub bind {    my ($this, $key, $psub) = @_;    my $tbox = $this-&gt;{&#39;widget&#39;};    $tbox-&gt;bind($key, $psub);};# End of package &quot;textbox&quot;####################### Package help #######################package help;my $help = &quot;    OVERVIEW    This program calculates an amortization schedule, based on the following    user-supplied variables:        (loan)   L = original loan amount        (rate)   R = yearly interest rate (as a percentage)        (period) P = periods per year (eg. 12 = monthly)        (years)  Y = number of years        (extra)  X = extra amount paid to reduce the mortgage    and the following program-calculated variables:        F = factor loan grows by each period = 1 + (R / P)        N = number of payments = Y * P        A = amount of periodic payment (see formula for &#39;A&#39; below)        T = total amount paid = A * N        M = monthly interest paid        I = total amount of interest paid    The program also calculates how the number of periods can be altered,    depending on an extra amount X paid each period.    The basic formula for calculating A (the amount owed each period) is:        A = L(F^N)(F-1) / (F^N - 1)    The monthly interest M is:        M = L * (R / P)    (which is:  new_balance - previous_balance)    The total amount paid will be:        T = (A * N) + X * (N - S)    of which the total interest paid will be:        I = T - L    Try changing any of the values in the upper-left box of the application,    to see how they affect the entire payment schedule.  Set the desired    values for:        Loan Amount        Annual % interest rate        Period (12 = each month)        Base # of years        Base # of payments        Extra payment amount    and observe the corresponding changes to the values for:        Payment Amount        Total interest payments        Total principal payments        Total amount paid        Interest as % of total    You can change the &#39;Extra payment amount&#39; to see how much earlier the    loan would be paid off when making additional payments each period (the    new values cannot be larger than the values of &#39;Base # of years&#39; and    &#39;Base # of payments&#39;, though).    To reset the program variables, type &#39;^R&#39; or press the button marked    &#39;Reset Variables&#39;.    To save the payment schedule to a file (eg. for printing), type &#39;^S&#39; or    press the button marked &#39;Save Schedule&#39;.    To exit the program, type &lt;Escape&gt;, or press the button marked &#39;Exit&#39;.&quot;;##  new -- help constructor.  Parameters are:##      $1 ... The help object#      $2 ... The parent window#      $3 ... The button text (0 = no button)#      $4 ... The button color (0 = default)#      $5 ... The button pack style#      $6 ... The help message title#      $7 ... The help message text#      $8 ... The help message width (defaults to 40)#      $9 ... The help message height (defaults to 80)##  Return value:  The help object#sub new {    my ($obj, $win, $btext, $bcolor, $pack, $key, $title, $msg, $w, $h) = @_;    my $mw = $win-&gt;toplevel();    $w ||= 80;    $h ||= 40;    my $this = {        &#39;mw&#39;     =&gt; $mw,     # Toplevel window        &#39;win&#39;    =&gt; $win,    # Parent window        &#39;key&#39;    =&gt; $key,    # The accelerator key        &#39;title&#39;  =&gt; $title,  # The help title        &#39;msg&#39;    =&gt; $msg,    # The help message        &#39;width&#39;  =&gt; $w,      # The help message width        &#39;height&#39; =&gt; $h,      # The help message height    };    #    #  Create an anonymous subroutine to display the help dialog box.    #  Create a button to call the function if button text was supplied,    #  and create an accelerator key to call it if a key was supplied.    #    my $pfunc = sub { $this-&gt;give_help() };    $btext and &amp;main::button($win, $btext, $bcolor, $pfunc, 0, $pack);    ($key =~ /^[fF]([0-9]+)$/) and $key = &quot;&lt;Key-F&quot; . $1 . &quot;&gt;&quot;;    $key and $mw-&gt;bind($key, $pfunc);    # Bless the object    bless $this, &#39;help&#39;;    return $this;}##  give_help:  displays the actual help message in a dialog box for#  the user to peruse.#sub give_help {    (@_ &gt; 1) and shift;    # Throw away event, if triggered by a button    my ($this) = @_;    my $mw      = $this-&gt;{&#39;mw&#39;};    my $title   = $this-&gt;{&#39;title&#39;};    my $width   = $this-&gt;{&#39;width&#39;};    my $height  = $this-&gt;{&#39;height&#39;};    my $db = $mw-&gt;DialogBox(-title =&gt; $title, -buttons =&gt; [ &quot;OK&quot; ]);    my $tb = new textbox($db, $width, $height, &#39;gray&#39;, 0, 1);    $tb-&gt;configure(-state =&gt; &#39;normal&#39;);    $tb-&gt;insert(&#39;end&#39;, $this-&gt;{&#39;msg&#39;});    $tb-&gt;configure(-state =&gt; &#39;disabled&#39;);    $tb-&gt;yview(&#39;1.0&#39;);    $db-&gt;Show();    $mw-&gt;update();}# End of package &quot;help&quot;####################### Main program #######################package main;reset_values();gui_mode();###################### Subroutines ######################sub reset_values {    $o_loan    = $loan    = 100000;    $o_rate    = $rate    = 5.0;    $o_period  = $period  = 12;    $o_years   = $years   = 30;    $o_npay    = $npay    = $years * $period;    $o_extra   = $extra   = 0;    $o_newyear = $newyear = $years;    $o_newpay  = $newpay  = $npay;    find_payment();}sub summary {    my ($extra, $np, $P) = @_;    my $years = (0 == $P)? 0: ($np / $P);    printf &quot;\nExtra payment[\$%.2f]:  Nperiods = %d  (%3.1f years)\n\n&quot;,        $extra, $np, $years;}sub calc_payment {    my ($L, $R, $P, $N) = @_;    my $F = 1 + ($R / 100.0 / $P);    my $A = $L * ($F ** $N) * ($F - 1) / ($F ** $N - 1);    my $result = sprintf &quot;%.2f&quot;, $A;    return $result;}sub commas {    my ($val) = @_;    $val =~ s/,//g;    $val = sprintf &quot;%.2f&quot;, $val;    $val =~ s/(?&lt;=\d)(\d{3})(?=(,\d{3}|\.))/,$1/g;    return $val;}sub schedule_header {    my ($rate) = @_;    my $text = sprintf &quot;Year &quot;;    $text   .= sprintf &quot;Period      &quot;;    $text   .= sprintf &quot;Starting     &quot;;    $text   .= sprintf &quot;+ %6.4f%%       &quot;, $rate;    $text   .= sprintf &quot;Payment         &quot;;    $text   .= sprintf &quot;Extra        &quot;;    $text   .= sprintf &quot;Ending       &quot;;    $text   .= sprintf &quot;Interest    &quot;;    $text   .= sprintf &quot;Total int&quot;;    return $text;}sub enforce_limits {    my ($pva