<?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 ($pvar, $min, $max) = @_;    if ($$pvar &lt; $min) {        $$pvar = $min;    } elsif ($$pvar &gt; $max) {        $$pvar = $max;    }}sub schedule {    my ($out, $L, $R, $P, $N, $X) = @_;    my $F = 1 + ($R / 100.0 / $P);    my $start = $L;    my $b_is_fh = (ref($out) =~ /FileHandle/);    my $pout = sub {        my ($msg) = @_;        $b_is_fh and print $out &quot;$msg\n&quot;;        $b_is_fh  or $out-&gt;out($msg);    };    if ($b_is_fh) {        my $shdr = schedule_header($R);        $pout-&gt;($shdr);    }    my ($i, $text);    my $fmt = &quot;%4s %6d %13s %13s %13s %13s %13s %13s %13s\n&quot;;    my ($int, $A) = (0, 0);    $tot_int = 0;    $tot_paid = 0;    $payments = [ ];    for ($i = $maxpay = 0; $start &gt; 0; $i++, $N--) {        my $nyear = int($i / $period);        my $extra = $X;        my $w_int = sprintf &quot;%.2f&quot;, ($start * $F);        my $end = $w_int;        $int = $w_int - $start;        # Interest payment        $tot_int += $int;        (!$A || $extra &lt;= 0) and $A = calc_payment($start, $R, $P, $N);        $end -= $extra;        ($end &lt;= $A) and $A = $end;        $end -= $A;        $tot_paid += ($A + $extra);        # Save payment information        $payments-&gt;[$nyear]-&gt;[0] += $int;        $payments-&gt;[$nyear]-&gt;[1] += $A - $int;        $payments-&gt;[$nyear]-&gt;[2] += $extra;        my $totpay = $payments-&gt;[$nyear]-&gt;[0] +                     $payments-&gt;[$nyear]-&gt;[1] +                     $payments-&gt;[$nyear]-&gt;[2];        ($totpay &gt; $maxpay) and $maxpay = $totpay;        $text .= sprintf $fmt,                    (0 == ($i % $P)? ($i / $P): &quot; &quot;), $i+1,                    commas($start), commas($w_int), commas($A),                    commas($extra), commas($end), commas($int),                    commas($tot_int);        $start = $end;    }    $pct_int = sprintf &quot;%8.3f%%&quot;, (100.0 * $tot_int / $tot_paid);    $tot_prin = $tot_paid - $tot_int;    # Make these values &#39;pretty&#39;, as they aren&#39;t directly changed by the user    dollar_amount(\$tot_prin, 1);    dollar_amount(\$tot_paid, 1);    dollar_amount(\$tot_int,  1);    if ($out &amp;&amp; 0 == ($i % $P)) {        $text .= sprintf &quot;%4s %s\n&quot;, ($i / $P), (&#39;-&#39; x 3);    }    $pout-&gt;($text);    return $i;}sub configure_relief {    my ($widget, $rel) = @_;    if ($rel) {        ($rel eq &#39;n&#39;) and $rel = &#39;none&#39;;        ($rel eq &#39;f&#39;) and $rel = &#39;flat&#39;;        ($rel eq &#39;g&#39;) and $rel = &#39;groove&#39;;        ($rel eq &#39;r&#39;) and $rel = &#39;raised&#39;;        ($rel eq &#39;R&#39;) and $rel = &#39;ridge&#39;;        ($rel eq &#39;s&#39;) and $rel = &#39;solid&#39;;        ($rel eq &#39;S&#39;) and $rel = &#39;sunken&#39;;        $widget-&gt;configure(-relief =&gt; $rel);    }}sub configure_packing {    my ($widget, $pack) = @_;    if ($pack =~ s/([&lt;|&gt;])//) {        my $just = $1;        if ($widget =~ /Entry/) {            ($just eq &#39;&lt;&#39;) and $widget-&gt;configure(-justify =&gt; &#39;left&#39;);            ($just eq &#39;|&#39;) and $widget-&gt;configure(-justify =&gt; &#39;center&#39;);            ($just eq &#39;&gt;&#39;) and $widget-&gt;configure(-justify =&gt; &#39;right&#39;);        }    }    if ($pack) {        my $fill   = ($pack =~ /([bxyn])/)? $1: &#39;n&#39;;        my $expand = ($pack =~ /([01])/)?   $1: &#39;0&#39;;        my $side   = ($pack =~ /([TBLR])/)? $1: &#39;top&#39;;        ($fill eq &#39;b&#39;) and $fill = &#39;both&#39;;        ($fill eq &#39;n&#39;) and $fill = &#39;none&#39;;        ($side eq &#39;T&#39;) and $side = &#39;top&#39;;        ($side eq &#39;B&#39;) and $side = &#39;bottom&#39;;        ($side eq &#39;L&#39;) and $side = &#39;left&#39;;        ($side eq &#39;R&#39;) and $side = &#39;right&#39;;        $widget-&gt;pack(-expand =&gt; $expand, -fill =&gt; $fill, -side =&gt; $side);    }    return $widget;}## frame:  Creates a Tk Frame widget##     $1 ... The parent window#     $2 ... The frame background color#     $3 ... The frame relief#     $4 ... The frame border width#     $5 ... The frame pack style, which may include the side (T=top,#            B=bottom, L=left, R=right), the fill flag (n=none, x, y,#            or b=both) and/or the expand flag (0 or 1).#sub frame {    my ($w, $bg, $rel, $bw, $pack) = @_;    $bg   ||= 0;    $rel  ||= 0;    $bw   ||= 5;    $pack ||= &#39;T&#39;;    my $f = $w-&gt;Frame(-borderwidth =&gt; $bw);    $bg and $f-&gt;configure(-bg =&gt; $bg);    configure_relief($f, $rel);    return configure_packing($f, $pack);}## button:  Creates a Tk Button widget##     $1 ... The parent window#     $2 ... The button text#     $3 ... The button background color#     $4 ... The associated command#     $5 ... The width of the button#     $6 ... Where/how to place the button.  If it contains one#            of [TBLR], packs it (top, bottom, left or right).#            If it contains the format &quot;r,c&quot;, grids it at the#            given row, column.  If it contains &#39;d&#39;, disables the#            button.#     $7 ... An optional key to bind to this button from the main#            window.  If the key is in the format &#39;F[0-9]*&#39;, it#            is converted to the appropriate function key name.#sub button {    my ($win, $txt, $bg, $pcmd, $width, $where, $key) = @_;    $txt   ||= &#39;&#39;;    $bg    ||= &#39;green&#39;;    $pcmd  ||= 0;    $width ||= 0;    $where ||= 0;    $key   ||= 0;    ($key =~ /^[fF]([0-9]+)$/) and $key = &quot;&lt;Key-F&quot; . $1 . &quot;&gt;&quot;;    ($key =~ /^esc(ape)?$/i)   and $key = &quot;&lt;Key-Escape&gt;&quot;;    ($key =~ /^\^(.+)/)        and $key = &quot;&lt;Control-&quot; . (lc $1) . &quot;&gt;&quot;;    my $mw = $win-&gt;toplevel();    my $b = $win-&gt;Button(-text =&gt; $txt, -bg =&gt; $bg);    $pcmd  and $b-&gt;configure(-command =&gt; $pcmd);    $width and $b-&gt;configure(-width   =&gt; $width);    ($where =~ /d/) and $b-&gt;configure(-state   =&gt; &#39;disabled&#39;);    $key   and $mw-&gt;bind($key, sub { $b-&gt;invoke(); });    if ($where) {        my $anch   = ($where =~ s/([&lt;|&gt;])//)?  $1: 0;        ($anch eq &#39;&lt;&#39;) and $b-&gt;configure(-anchor =&gt; &#39;w&#39;);        ($anch eq &#39;|&#39;) and $b-&gt;configure(-anchor =&gt; &#39;center&#39;);        ($anch eq &#39;&gt;&#39;) and $b-&gt;configure(-anchor =&gt; &#39;e&#39;);        if ($where =~ /(\d+),(\d+)/) {            my ($row, $col) = ($1, $2);            $b-&gt;grid(-row =&gt; $row, -col =&gt; $col);        } elsif ($where =~ /[TBLR]/) {            my $side = &#39;L&#39;;            ($where =~ /T/) and $side = &#39;top&#39;;            ($where =~ /B/) and $side = &#39;bottom&#39;;            ($where =~ /L/) and $side = &#39;left&#39;;            ($where =~ /R/) and $side = &#39;right&#39;;            $b-&gt;pack(-side =&gt; $side);        }    }    return $b;}## entry:  Creates a Tk Entry widget:##     $1 ... The parent window#     $2 ... The entry background color#     $3 ... The entry width#     $4 ... The entry relief#     $5 ... The associated scalar variable#     $6 ... Special flags.  If it contains &#39;d&#39;, disables the widget#            from input.  If it contains &#39;*&#39;, hides text.#     $7 ... The entry pack style, which may include the justification#            (&lt;=left, |=center, &gt;=right), side (T=top, B=bottom, L=left,#            R=right), the fill flag (n=none, x, y, or b=both) and/or#            the expand flag (0 or 1).#     $8 ... Callback routine when text is entered in the Enter widget.#sub entry {    my ($w, $bg, $width, $rel, $pvar, $flags, $pack, $pcback) = @_;    $bg     ||= &#39;white&#39;;    $width  ||= 0;    $rel    ||= 0;    $pvar   ||= 0;    $flags  ||= 0;    $pack   ||= 0;    $pcback ||= 0;    my $e = $w-&gt;Entry();    $bg     and $e-&gt;configure(-bg      =&gt; $bg);    $width  and $e-&gt;configure(-width   =&gt; $width);    $pvar   and $e-&gt;configure(-textvar =&gt; $pvar);    if ($flags) {        ($flags =~ /d/)  and $e-&gt;configure(-state =&gt; &#39;disabled&#39;);        ($flags =~ /\*/) and $e-&gt;configure(-show  =&gt; &#39;*&#39;);    }    configure_relief($e, $rel);    if ($pcback) {        # Allow &#39;&lt;Return&gt;&#39; and loss-of-focus to trigger this callback        $e-&gt;bind(&#39;&lt;Return&gt;&#39;, $pcback);        $e-&gt;bind(&#39;&lt;FocusOut&gt;&#39;, $pcback);    }    return configure_packing($e, $pack);}## labent:  Creates a Tk Label/Entry widget:##     $1 ... The parent window#     $2 ... The label background color#     $3 ... The label width#     $4 ... The label relief#     $5 ... The text (or text variable) of the label#     $6 ... The entry background color#     $7 ... The entry width#     $8 ... Various flags, in which the following characters are valid:##               Flags     Meaning#                 d ..... The widget is disabled#                 * ..... The text appears as &#39;*&#39; (eg. for passwords)##             Anchors     Meaning#                 &lt; ..... The widget is anchored left#                 | ..... The widget is anchored middle#                 &gt; ..... The widget is anchored right##              Pack       Meaning#                 T ..... Pack the widget to the top#                 B ..... Pack the widget to the bottom#                 L ..... Pack the widget to the left#                 R ..... Pack the widget to the right##              Fill       Meaning#                 N ..... No fill#                 X ..... Fill in the X-direction#                 Y ..... Fill in the Y-direction#                 B ..... Fill in both the X and Y directions#                 0 ..... Do NOT expand#                 1 ..... Expand##     $9 ... Callback routine when text is entered in the Enter widget.#sub labent {    my ($w, $label, $bg1, $w1, $pvar, $bg2, $w2, $flags, $pvalid) = @_;    $label  ||= &#39;&#39;;    $bg1    ||= &#39;gray&#39;;    $w1     ||= &#39;10&#39;;    $pvar   ||= 0;    $bg2    ||= &#39;white&#39;;    $w2     ||= 10;    $flags  ||= 0;    $pvalid ||= 0;    my $lpack = &#39;Lb1&#39;;    my $epack = &#39;Lb1&#39;;    ($flags =~ /([&lt;|&gt;])/) and $epack .= $1;    my $fr_bg = $w-&gt;cget(-bg);    my $f = frame($w, $fr_bg, &#39;g&#39;, 3, 0);    my $l = label($f, $bg1, $w1, 0, $label, $lpack);    my $e = entry($f, $bg2, $w2, 0, $pvar,  $flags, $epack, $pvalid);    return [ $l, $e ];}## label:  Creates a Tk Label widget##     $1 ... The parent window#     $2 ... The label background color#     $3 ... The label width#     $4 ... The label relief#     $5 ... The text (or text variable) of the label#     $6 ... The entry pack style, which may include the anchor position#            (&lt;=left, |=center, &gt;=right), side (T=top, B=bottom, L=left,#            R=right), the fill flag (n=none, x, y, or b=both) and/or#            the expand flag (0 or 1).#sub label {    my ($w, $bg, $width, $rel, $pvar, $pack) = @_;    $bg    ||= 0;    $width ||= 0;    $rel   ||= 0;    $pvar  ||= 0;    $pack  ||= 0;    my $l = $w-&gt;Label();    $bg    and $l-&gt;configure(-bg    =&gt; $bg);    $width and $l-&gt;configure(-width =&gt; $width);    if ($pvar) {        if (ref $pvar eq &#39;&#39;) {            $l-&gt;configure(-text =&gt; $pvar);        } else {            $l-&gt;configure(-textvar =&gt; $pvar);        }    }    configure_relief($l, $rel);    return configure_packing($l, $pack);}GUI: {    my $mw;    my $te;    my $tb = 0;    my $graph = 0;    my ($f1, $f2, $f3, $f4, $f5, $f6, $f7, $f8, $f9);    my ($le1, $le2, $le3, $le4, $le5, $le6, $le7);    my $bg1 = &#39;peachpuff&#39;;            # Label/Entry color    sub exit_gui {        $mw-&gt;exit;    }    sub make_float {        my ($pval) = @_;        $$pval =~ s/[^\d\.]//g;        $$pval =~ s/(\..*)\./$1/g;    }    sub dollar_amount {        my ($pval, $pretty) = @_;        $pretty ||= 0;        make_float($pval);        $$pval = sprintf &quot;%.2f&quot;, $$pval;        $pretty and $$pval = &#39;$&#39; . commas($$pval);        return $$pval;    }    sub adjust_for_extra {        my ($np) = @_;        $o_newpay = $newpay = int($np);        $o_newyear = $newyear = sprintf &quot;%.3f&quot;, ($newpay / $period);        show_graph();    }    sub find_payment {        $payment = calc_payment($loan, $rate, $period, $npay);        dollar_amount(\$payment, 1);        return unless $tb;        $tb-&gt;clear();        my $np = schedule($tb, $loan, $rate, $period, $npay, $extra);        adjust_for_extra($np);    }    sub do_principal {        return if ($loan eq $o_loan);        dollar_amount(\$loan);        find_payment();        $o_loan = $loan;    }    sub do_rate {        return if ($rate eq $o_rate);        enforce_limits(\$rate, $min_rate, $max_rate);        ($rate &lt; 0.1) and $rate = 0.1;        make_float(\$rate);        find_payment();        create_schedule_header();        $o_rate = $rate;    }    sub do_period {        return if ($period eq $o_period);        make_float(\$period);        ($period &lt; 1) and $period = 12;        $period = int($period);        find_payment();        $o_period = $period;    }    sub do_years {        return if ($years eq $o_years);        enforce_limits(\$years, $min_years, $max_years);        make_float(\$years);        $npay = int($years * $period);        do_npay();        $o_years = $years;    }    sub do_npay {        return if ($npay eq $o_npay);        $npay = int($npay);        $years = sprintf &quot;%.3f&quot;, ($npay / $period);        find_payment();        $o_npay = $npay;    }    sub do_extra {        return if ($extra eq $o_extra);        dollar_amount(\$extra);        find_payment();        $o_extra = $extra;    }    sub clear_graph {        while (my $id = shift @glist) {            $graph-&gt;delete($id);        }    }    sub save_schedule {        my $dir = $ENV{&#39;USERPROFILE&#39;};        defined($dir) and $dir .= &quot;\\Desktop&quot;;        $dir ||= &quot;.&quot;;        my @opts = (            -title            =&gt; &quot;Save Mortgage Information&quot;,            -defaultextension =&gt; &#39;txt&#39;,            -initialdir       =&gt; $dir,            -initialfile      =&gt; $schedout,        );        my $fname = $mw-&gt;getSaveFile(@opts);        $fname or return;        my $fh = new FileHandle;        open($fh, &quot;&gt;&quot;, $fname) or return;        schedule($fh, $loan, $rate, $period, $npay, $extra);        close $fh;    }    sub plot_points {        my ($x, $xinc, $y, $yext, $p) = @_;        my $x0 = $x;        my $x1 = $x + $xinc;        my $y0 = $y;        for (my $i = 0; $i &lt; @$p; $i++) {            my $pay = $p-&gt;[$i] / $maxpay;            my $y1 = $y0 - ($pay * $yext);            my $bg = $gcolors[$i];            my $id = $graph-&gt;createRectangle($x0, $y0, $x1, $y1, -fill =&gt; $bg);            push @glist, $id;            $y0 = $y1;        }        return $x1;    }    sub show_graph {        return unless $graph;        # Clear the previous graph        clear_graph();        # Get the number of payments saved (and make sure it&#39;s more than 1)        my $n = @$payments;        return if ($n &lt; 1);        # Get the absolute pixel width and height of the canvas        my $width = $graph-&gt;Width();        my $height = $graph-&gt;Height();        # Draw the graph        my ($xmargin, $ymargin) = (5, 5);        my $xinc = ($width - 2 * $xmargin) / $n;        my $yext = ($height - 2 * $ymargin);        my $x = $xmargin;        my $y = $height - $ymargin;        for (my $i = 0; $i &lt; @$payments; $i++) {            $x = plot_points($x, $xinc, $y, $yext, $payments-&gt;[$i]);        }    }    sub create_schedule_header {        my $shdr = schedule_header($rate);        $te and $te-&gt;packForget();        $te = $f8-&gt;Text(-height =&gt; 1);        $te-&gt;pack(-expand =&gt; 0, -fill =&gt; &#39;x&#39;);        $te-&gt;insert(&#39;end&#39;, &quot;$shdr&quot;);        $te-&gt;configure(-state =&gt; &#39;disabled&#39;, -takefocus =&gt; 0);    }    sub gui_mode {        $mw = new MainWindow(-title =&gt; $title);        $mw-&gt;fontCreate(&#39;vals&#39;, -family =&gt; &#39;arial&#39;, -size =&gt; 12);        my $top = frame($mw, 0, 0, 0, &#39;Tb1&#39;);        $f1 = frame($top, &#39;pink&#39;, &#39;g&#39;, 0, &#39;x0&#39;);        button($f1, &quot;Exit (Esc)&quot;, 0, \&amp;exit_gui, 0, &#39;R&#39;, &#39;esc&#39;);        new help($f1, &#39;Help (F1)&#39;, 0, &#39;L&#39;, &#39;&lt;Key-F1&gt;&#39;, &#39;Program Help&#39;, $help);        button($f1, &#39;Reset Variables (^R)&#39;, 0, \&amp;reset_values,  0, &#39;L&#39;, &#39;^R&#39;);        button($f1, &quot;Save Schedule (^S)&quot;,   0, \&amp;save_schedule, 0, &#39;L&#39;, &#39;^S&#39;);        $f2 = frame($top,      0, &#39;g&#39;, 0, &#39;Tx0&#39;);        $f3 = frame($f2,  &#39;cyan&#39;,  0,  0, &#39;Ln0&#39;);    # Variables frame        $f4 = frame($f2,       0, &#39;g&#39;, 0, &#39;Lb1&#39;);    # Graph frame        $f5 = frame($top, &#39;cyan&#39;, &#39;g&#39;, 0, &#39;Tb1&#39;);    # Schedule frame        my $labents = [            [ &#39;Loan Amount&#39;,              \$loan,     &#39;T&gt;&#39;,   \&amp;do_principal ],            [ &#39;Annual % interest rate&#39;,   \$rate,     &#39;T&gt;&#39;,   \&amp;do_rate ],            [ &#39;Period (eg. 12=monthly)&#39;,  \$period,   &#39;T&gt;&#39;,   \&amp;do_period ],            [ &#39;Base # of years&#39;,          \$years,    &#39;T&gt;&#39;,   \&amp;do_years ],            [ &#39;Base # of payments &#39;,      \$npay,     &#39;T&gt;&#39;,   \&amp;do_npay  ],            [ &#39;Extra payment amount&#39;,     \$extra,    &#39;T&gt;&#39;,   \&amp;do_extra ],            [ &#39;Payment Amount&#39;,           \$payment,  &#39;T&gt;d&#39;,  0 ],            [ &#39;Total interest payments&#39;,  \$tot_int,  &#39;T&gt;d&#39;,  0 ],            [ &#39;Total principal payments&#39;, \$tot_prin, &#39;T&gt;d&#39;,  0 ],            [ &#39;Total amount paid&#39;,        \$tot_paid, &#39;T&gt;d&#39;,  0 ],            [ &#39;Interest as % of total&#39;,   \$pct_int,  &#39;T&gt;d&#39;,  0 ],        ];        foreach my $p (@$labents) {            my $p = labent($f3, $p-&gt;[0], $bg1, 24, $p-&gt;[1],                            0, 12, $p-&gt;[2], $p-&gt;[3]);            $p-&gt;[1]-&gt;configure(-font =&gt; &#39;vals&#39;);        }        # Graph frame        $f6 = frame($f4, 0, &#39;g&#39;, 0, &#39;Tx0&#39;);        $f7 = frame($f4, 0, &#39;g&#39;, 0, &#39;Tb1&#39;);        label($f6, &#39;white&#39;,     0, &#39;g&#39;, &#39;            Key              &#39;, &#39;L&#39;);        label($f6, $gcolors[0], 0, &#39;g&#39;, &#39;     Interest payments       &#39;, &#39;L&#39;);        label($f6, $gcolors[1], 0, &#39;g&#39;, &#39;   Basic principal payments  &#39;, &#39;L&#39;);        label($f6, $gcolors[2], 0, &#39;g&#39;, &#39;   Extra principal payments  &#39;, &#39;L&#39;);        $graph = $f7-&gt;Canvas(-bg =&gt; &#39;gray&#39;);        $graph-&gt;pack(-expand =&gt; 1, -fill =&gt; &#39;both&#39;);        $f7-&gt;bind(&#39;&lt;Configure&gt;&#39;, \&amp;show_graph);        # Schedule frame        $f8 = frame($f5,  &#39;gold&#39;,  0,  0, &#39;Tx0&#39;);        $f9 = frame($f5,  &#39;gold&#39;,  0,  0, &#39;Tb1&#39;);        create_schedule_header();        $tb = new textbox($f9, 115, 16, 0, 0, 0, &quot;e&quot;);        $mw-&gt;after(100, \&amp;find_payment);        MainLoop;    }}&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-465654&quot;&gt;&lt;hr /&gt;&lt;font size=&quot;1&quot;&gt;s&#39;&#39;(q.S:$/9=(T1&#39;;s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/&lt;/font&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>RFC: LaTeX::Table (lima1)</title>
    <link>http://prlmnks.org/html/565704.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/565704.html</guid>

    <description>
        Well, it is not really cool, but useful (at least it was for me :-p)&lt;p&gt;&lt;a href=&quot;http://avimanager.sourceforge.net/LaTeX-Table-v0.0.1.tar.gz&quot;&gt;Download&lt;/a&gt;&lt;p&gt;The SYNOPSIS demonstrates the main functionality:&lt;pre class=&quot;block_code&quot;&gt;        use LaTeX::Table;         my $data = [               [ &#39;Lisa&#39;,   &#39;0&#39;, &#39;0&#39; ],               [ &#39;Marge&#39;,  &#39;0&#39;, &#39;1&#39; ],               [ &#39;Wiggum&#39;, &#39;0&#39;, &#39;5&#39; ],               [ &#39;Otto&#39;,   &#39;1&#39;, &#39;3&#39; ],               [ &#39;Homer&#39;,  &#39;2&#39;, &#39;6&#39; ],               [ &#39;Barney&#39;, &#39;8&#39;, &#39;16&#39; ],         ];         my $header               = [ [ &#39;Name&#39;, &#39;Beers:2|c|&#39; ], [ &#39;&#39;, &#39;before 4pm&#39;, &#39;after 4pm&#39; ] ];         my $table = LaTeX::Table-&gt;new(               {                  filename    =&gt; &#39;counter.tex&#39;,                  caption     =&gt; &#39;Number of beers before and after 4pm.&#39;,                  maincaption =&gt; &#39;Beer Counter&#39;,                  label       =&gt; &#39;table_beercounter&#39;,                  theme       =&gt; &#39;Houston&#39;,                  tablepos    =&gt; &#39;htb&#39;,               }         );         $table-&gt;generate( $header, $data )&lt;/pre&gt;It supports multipage tables (e.g. for appendices) and some predefined good looking table themes.Some feedback is highly appreciated before putting it on CPAN.&lt;p&gt;&lt;small&gt;2006-08-04 Retitled by [Arunbear], as per Monastery [id://341118|guidelines] &lt;br /&gt;Original title: &#39;LaTeX::Table&#39;&lt;/small&gt;&lt;/p&gt;
    </description>
</item>

        

<item>
    <title>New Graphing Module (aplonis)</title>
    <link>http://prlmnks.org/html/565126.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/565126.html</guid>

    <description>
        &lt;p&gt;There is one important instance where &lt;tt class=&quot;inline_code&quot;&gt;GD::Graph::lines&lt;/tt&gt; does not suffice. That is the case where you require two Y axes (left &amp;amp; right) with more than two data curves to trace.&lt;/p&gt;&lt;p&gt;So I have cobbled together a module (my first ever OO) which covers that instance where &lt;tt class=&quot;inline_code&quot;&gt;GD::Graph::lines&lt;/tt&gt; is lacking. May it serve as a tide-over until the day (month?, year?) when &lt;tt class=&quot;inline_code&quot;&gt;GD::Graph::lines&lt;/tt&gt; shall be repaired.&lt;/p&gt;&lt;p&gt;This is a self-actualizing demo of the module, part of a utility I&#39;m working on to graph strain gage rosette data. It generates fake data, saves it to a file, reads it back in, adds some calcualted-result channels, graphs it both in GD as &lt;tt class=&quot;inline_code&quot;&gt;*.png&lt;/tt&gt; and in PostScript as an &lt;tt class=&quot;inline_code&quot;&gt;*.eps&lt;/tt&gt; file&lt;/p&gt;&lt;p&gt;That&#39;s right, the output is PostScript. So you&#39;ll need a viewer (&lt;tt class=&quot;inline_code&quot;&gt;gv&lt;/tt&gt; on UNIX, &lt;tt class=&quot;inline_code&quot;&gt;GSView&lt;/tt&gt; on Win32) to view it. Running the file also calls up whichever of those is appropriate for your OS to display the graph.&lt;/p&gt;&lt;p&gt;It only does line graphs but does them quite prettily...except for the example given...which is deliberately hideous to show off user options for colors, fonts, etc. (I&#39;m not really color-blind.)&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;http://starling.us/tet/gus_perl/gus_strain_pl/gus_ue_rosette_cli_demo.txt&quot;&gt;Graphing Module Demo&lt;/a&gt;&lt;/p&gt;&lt;p&gt;There isn&#39;t any real Perl magic here, though. It&#39;s PostScript which performs all the tricks. I dredged up an old standalone PostScript graphing program which I&#39;d written way back in 1992, tidied it a bit, broke it into pieces and married those pieces to Perl to make this happen.&lt;/p&gt;&lt;p&gt;You can choose any web safe color for back- and fore-grounds, lines, etc. You can choose any of the 35 fonts. And it auto-reconciles the Y1 and Y2 axes for best resolution of the data traces. That is to say, for each axis, at least one trace will course nearly the whole vertical area.&lt;/p&gt;&lt;p&gt;Its one big flaw is that no single data trace may number more than 65,535 data points. This is due to PostScript&#39;s internal addressing limitation...which I have yet to creatively overcome. At some point later on, after I&#39;ve tinkered with it a bit more, I&#39;ll put in in CPAN. For now it&#39;s embeded in the demo script.&lt;/p&gt;&lt;p&gt;Gan Uesli Starling&lt;br/&gt;Kalamazoo MI USA&lt;/p&gt;
    </description>
</item>

        

<item>
    <title>(Apache::Session) Detect a Failure to Save Session (Mr. Muskrat)</title>
    <link>http://prlmnks.org/html/565084.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/565084.html</guid>

    <description>
        &lt;p&gt;You&#39;re using Apache::Session and sometimes the data doesn&#39;t stay saved when you think it should (like mentioned in [id://435977]).  Chances are it&#39;s a closure that is to blame.&lt;/p&gt;&lt;p&gt;Just add the DESTROY method to your derived class and it will spew a warning anytime it detects this happening.  (YMMV of course.)&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;package Apache::Session::Derived::Class;use strict;our $VERSION = &#39;1.00&#39;;our @ISA     = qw(Apache::Session);use Apache::Session;...sub populate {    my $self = shift;    ...    return $self;}sub DESTROY {    my $self = shift;    local $@;    eval {        $self-&gt;save;    };    if ( $@ ) {        my $error = $@;        my $msg = q{Can&#39;t call method &quot;.*?&quot; on an undefined value at }            . q{.*?/Apache/Session\.pm line \d+ during global destruction.};        if ( $error =~ m/$msg/ ) {          warn &quot;Unable to save session changes! (Closure around session?)\n&quot;;        } else {          warn $error;        }    }    $self-&gt;release_all_locks;}1;&lt;/pre&gt;&lt;p&gt;I would like to thank [diotalevi] for pointing out the need for localizing $@.&lt;/p&gt;
    </description>
</item>

        

<item>
    <title>Stealth Raven Shield launcher - Switching DNS server under Win32 (GrandFather)</title>
    <link>http://prlmnks.org/html/564868.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/564868.html</guid>

    <description>
        &lt;p&gt;A number of us indulge in a little mayhem during lunchtime at work in the form of running around with guns shooting each other. Or at least, we play Raven Shield. To avoid network issues we turn off DNS and have to remember to turn it back on after we have beaten each other to a pulp, or worse. If we dont PerlMonks doesnt work any more :(&lt;/p&gt;&lt;p&gt;A workmate wrote the code below and I added to it slightly. It changes the DNS server address to the local machine, launches Raven Shield, then restores the DNS server when Raven Shield exits. Enjoy.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;use strict;use warnings;# change these to work with your machine.my $interface_name = &quot;Local Area Connection&quot;;my $RSPath         = &#39;C:\games~~\Red Storm Entertainment\RavenShield&#39;;# Get current IPmy $config = `ipconfig`;$config =~ /IP Address[. :]+([\d.]*)/;my $myIP = $1;munt();show();launch();unmunt();show();sub launch{print `$RSPath/system/RavenShield.exe`;}sub munt{print `netsh interface ip set dns &quot;$interface_name&quot; static $myIP`;}sub unmunt{print `netsh interface ip set dns &quot;$interface_name&quot; dhcp`;}sub show{print `netsh interface ip show dns`;}&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>Giving birth with perl (jasonk)</title>
    <link>http://prlmnks.org/html/564258.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/564258.html</guid>

    <description>
        &lt;p&gt;So my wife and I are expecting our first baby.  Today in fact, although it looks as though she will be a bit late.&lt;/p&gt;&lt;p&gt;I got tired of trying to figure out which button did what on the little digital watch we were trying to use to time contractions, so I threw this together.  There is obviously room for improvement (or even golfing), but something tells me that I should be doing something else right about now...&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -wuse strict;use warnings;my $last = time;while( &lt;&gt; ) {    my $time = time;    my $interval = $time - $last;    print localtime($time).&quot; ($interval)&quot;;    $last = $time;}&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-231445&quot;&gt;&lt;hr&gt;&lt;table border=0 width=100%&gt;&lt;tr&gt;&lt;th&gt;&lt;small&gt;We&#39;re not surrounded, we&#39;re in a target-rich environment!&lt;/small&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>downloader (fighting with missing urls) (sh1tn)</title>
    <link>http://prlmnks.org/html/564015.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/564015.html</guid>

    <description>
        &lt;pre class=&quot;block_code&quot;&gt;use strict;use warnings;use WWW::Mechanize;use LWP::Simple;use Data::Dumper;use Time::HiRes qw( usleep tv_interval gettimeofday );$|++;my $url  = shift || die &quot;no url given!\n&quot;;my $ext  = shift || &#39;mp3&#39;; #die &quot;no file extention given!\n&quot;;my $mech = WWW::Mechanize-&gt;new(agent=&gt;&quot;Mozilla/5.0&quot;);my $ua   = LWP::UserAgent-&gt;new;$mech-&gt;get($url);$ua-&gt;timeout(5);my $links = $mech-&gt;links;no warnings;for my $link ( @{$links} ) {        my $url  = $link-&gt;url_abs;        my $res  = $ua-&gt;head($url);        my $http_res   = HTTP::Response-&gt;new($res);        my $abs_name   = $http_res -&gt;{&#39;_rc&#39;}-&gt;{&#39;_previous&#39;}-&gt;{&#39;_headers&#39;}-&gt;{&#39;location&#39;};        my ($rel_name) = $abs_name =~ /.+\/(.+)$/;        my $local_name = $rel_name;        if( $rel_name =~ /\Q$ext\E$/i ){                $rel_name =~ s/\%\d+/ /g;                $rel_name =~ s/(.{35}).+/$1.../;                print(pack(&#39;A45&#39;, &quot;[$rel_name]&quot;), &quot;  is being downloaded ... &quot;);                my $s_time = q{};                my $e_time = q{};                my $flag = [];                for( 1..5 ){                        local $SIG{ALRM} = sub { die(&quot;timeout&quot;) };                        eval {                           alarm(2);                           $flag = [head($abs_name)];                           alarm(0);                        };                        next if $@ =~ m|timeout|;                        last if $@ !~ m|timeout|;                }                if( $flag-&gt;[0] ){                        $s_time = [gettimeofday];                        getstore($abs_name, $local_name);                        $e_time = tv_interval ($s_time, [gettimeofday]);                }                $e_time =  $e_time ? $e_time . &quot; seconds&quot; : &#39;less than 1/100 second&#39;;                print &quot;timeout failure in $e_time seconds :(\n&quot; and next if $@;                print &quot;done in $e_time\n&quot;;        }}&lt;/pre&gt;&lt;br&gt;Update: for example - download.pl &#39;http://10.10.0.100/show_file.php?id=F-S26-10410041&#39; mp3 # or jpeg, or whatever&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>Closures are Easy in Perl (ruoso)</title>
    <link>http://prlmnks.org/html/562451.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/562451.html</guid>

    <description>
        &lt;P&gt;My today&#39;s CUFP won&#39;t have any code, just a huge &lt;strong&gt;Thanks&lt;/strong&gt; to Perl for supporting closures.&lt;/P&gt;&lt;P&gt;Today I was dealing with some C code, this code was using dlopen to get some shared object and call some function to it. I couldn&#39;t change the API, but needed the called function of some specific plugin to behave differently according to some data I have only when getting the pointer to that function. I thought: well, a simple closure will solve the problem, but no... C doesn&#39;t support closures...&lt;/P&gt;&lt;P&gt;Fortunally, some guys at ##C at freenode helped me and pointed me to &lt;a href=&quot;http://www.haible.de/bruno/packages-ffcall.html&quot;&gt;ffcal&lt;/a&gt;and I could find a way to solve my problem. But I&#39;m still surprised by the fact this library is almost never used. Searching for reverse depends on the Debian archive, gnustep  and gnustep libraries are the only packages to link against this library...&lt;/p&gt;&lt;P&gt;This made me think closure *is* a Cool Use For Perl... &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>Decisiveness for Gamers (Panda)</title>
    <link>http://prlmnks.org/html/561120.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/561120.html</guid>

    <description>
        &lt;i&gt;When all questions of space, time, matter and the nature of being have been resolved, only one question remains-&quot;What game shall we play?&quot;&lt;p&gt;The Perlmatic Automated Game Picker provides the ultimate gaming experience and for once there are no options to worry about.&lt;/i&gt;&lt;p&gt;This newly improved edition will pick a game for you to play based on your mood. So until PAGP 3.0 comes out, Have FUN!&lt;p&gt;&lt;pre class=&quot;block_code&quot;&gt;use warnings;use strict;{my @HappyGamesAndThings = (&quot;nfs&quot;, &quot;nfsII&quot;, &quot;With the PerlMonks&quot;, &quot;Morrowind&quot;, &quot;TES Constructor Set&quot;);my @SadGamesAndThings = qw(RedFactionII CCG RavenShield AGG);print &quot;How has your day been? (Answer &#39;good or &#39;bad&#39;).\n&quot;;my $mood = &lt;STDIN&gt;;chomp $mood;if (&quot;bad&quot; eq lc ($mood))   {   my $randgame = $SadGamesAndThings [rand (@SadGamesAndThings)];   print &quot;You should play:&quot;;   print $randgame;   }if (&quot;good&quot; eq lc ($mood))   {   my $randgame = $HappyGamesAndThings [rand (@HappyGamesAndThings)];   print &quot;You should play: &quot;;   print $randgame;   }   if (lc ($mood) ne &quot;bad&quot; and lc ($mood) ne &quot;good&quot;)   {   print &quot;Error, please re-type answer. \n&quot;;   redo   }}exit;&lt;/pre&gt;Please feel free to substitute your own games, use this wisely, and you will never have to make a decision again, well, almost.
    </description>
</item>

        

<item>
    <title>Perl test - Make sure all perl files will compile (skx)</title>
    <link>http://prlmnks.org/html/560574.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/560574.html</guid>

    <description>
        &lt;p&gt;I&#39;ve had this code in one of my projects for a while, but after reading [id://506585] and point 2 in particular &quot;Everything has an automated test that, at the bare minimum, verifies that it will compile.&quot; I figured it was worth sharing.&lt;/p&gt;&lt;p&gt;The code is designed to be dropped into a &lt;tt&gt;test/&lt;/tt&gt; directory which &quot;&lt;tt&gt;make test&lt;/tt&gt;&quot; will then run.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w##  Test that every perl file we have passes the syntax check.## Steve# --# $Id: perl-syntax.t,v 1.2 2006/06/13 13:26:00 steve Exp $use strict;use File::Find;use Test::More qw( no_plan );##  Find all the files beneath the current directory,# and call &#39;checkFile&#39; with the name.#find( { wanted =&gt; \&amp;checkFile, no_chdir =&gt; 1 }, &#39;.&#39; );##  Check a file.##  If this is a perl file then call &quot;perl -c $name&quot;, otherwise# return#sub checkFile{    # The file.    my $file = $File::Find::name;    # We don&#39;t care about directories    return if ( ! -f $file );    # `modules.sh` is a false positive.    return if ( $file =~ /modules.sh$/ );    # See if it is a perl file.    my $isPerl = 0;    # Read the file.    open( INPUT, &quot;&lt;&quot;, $file );    foreach my $line ( &lt;INPUT&gt; )    {        if ( ( $line =~ /\/usr\/bin\/perl/ ) ||             ( $line =~ /\/usr\/local\/bin\/perl/ ) )        {            $isPerl = 1;        }    }    close( INPUT );    #    #  Return if it wasn&#39;t a perl file.    #    return if ( ! $isPerl );    #    #  Now run &#39;perl -c $file&#39; to see if we pass the syntax    # check    #    my $retval = system( &quot;perl -c $file 2&gt;/dev/null &gt;/dev/null&quot; );    is( $retval, 0, &quot;Perl file passes our syntax check: $file&quot; );}&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-194370&quot;&gt;&lt;a href=&quot;http://www.steve.org.uk/&quot;&gt;Steve&lt;/a&gt;&lt;br/&gt;-- &lt;br/&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Google Earth Monks - Part II (The Code) (McDarren)</title>
    <link>http://prlmnks.org/html/560490.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/560490.html</guid>

    <description>
        Greetings!&lt;p&gt;This is the &quot;final&quot; (..heh) version of the code that&#39;s used to generate the Google Earth KMZ files referred to in [id://558846]. It&#39;s presented here partly for posterity, and partly to elicit some feedback and (hopefully) some suggestions for improvement. &lt;p&gt;For some background, read the [id://558846|original thread], or visit the &quot;googlemonks&quot; [http://mcdarren.perlmonk.org/googlemonks/|homepage].&lt;p&gt;&lt;font size=-1&gt;&lt;em&gt;Note: I&#39;ve posted the code here in CUFP (rather than in the original thread) after seeking advice in the CB.&lt;p&gt;Edit: s/illicit/elicit/ (thanks, [GrandFather])&lt;/em&gt;&lt;/font&gt;&lt;p&gt;&lt;readmore title=&quot;the code....&quot;&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w## pmgoogle.pl# Generates compressed KMZ (Google Earth) files# with placemarks for Perlmonks monks# See: earth.google.com## Darren - July 2006use strict;use XML::Simple;use LWP::UserAgent;use Storable;use Time::HiRes qw( time );my $start = time();say(&quot;$0 started at &quot;, scalar localtime($start));# Where everything livesmy $monkfile = &#39;/home/mcdarren/scripts/monks.store&#39;;my $kmlfile = &#39;/home/mcdarren/temp.kml&#39;;my $www_dir = &#39;/home/mcdarren/var/www/googlemonks&#39;;my $palette_url = &#39;http://mcdarren.perlmonk.org/googlemonks/img/monk-palette.png&#39;;my $monks;  # hashref$|++;# Uncomment this for testing# Avoids re-fetching the data#if (! -f $monkfile) {    # Fetch and parse the XML from tinymicros    $monks = get_monk_data();    store $monks, $monkfile;#}$monks = retrieve($monkfile)    or die &quot;Could not retrieve $monkfile:$!\n&quot;;# A pretty lousy attempt at abstraction :/my %types = (    by_level    =&gt; {        desc        =&gt; &#39;By Level&#39;,        outfile     =&gt; &#39;perlmonks_by_level.kmz&#39;,        },    by_name     =&gt; {        desc        =&gt; &#39;By Monk&#39;,        outfile     =&gt; &#39;perlmonks_by_monk.kmz&#39;,        });my @levels = qw(    Initiate Novice Acolyte Sexton    Beadle Scribe Monk Pilgrim    Friar Hermit Chaplain Deacon    Curate Priest Vicar Parson    Prior Monsignor Abbot Canon    Chancellor Bishop Archbishop Cardinal    Sage Saint Apostle Pope    );# Create a reference to a LoL,# which represents xy offsets to each of the# icons on the palette image# The palette consists of 28 icons in a 7x4 gridmy $xy_data = get_xy();my @t = time();print &quot;Writing and compressing output files...&quot;;for (keys %types) {    open OUT, &quot;&gt;&quot;, $kmlfile        or die &quot;Could not open $kmlfile:$!\n&quot;;    my $kml = build_kml($monks, $_);    print OUT $kml;    close OUT;    write_zip($kmlfile, &quot;$www_dir/$types{$_}{outfile}&quot;);}$t[1] = time();say(&quot;done (&quot;, formatted_time_diff(@t), &quot; secs)&quot;);my $end = time();say(&quot;Total run time &quot;, formatted_time_diff($start, $end), &quot; secs&quot;);say(&quot;Total monks: &quot;, scalar keys %{$monks-&gt;{monk}});exit;##################################### End of main - subs below####################################sub say {    # Perl Hacks #86    print @_, &quot;\n&quot;;}sub formatted_time_diff {    return sprintf(&quot;%.2f&quot;, $_[1]-$_[0])}sub by_level {    return $monks-&gt;{monk}{$b}{level} &lt;=&gt; $monks-&gt;{monk}{$a}{level}    || lc($a) cmp lc($b);}sub by_name {    return lc($a) cmp lc($b);}sub write_zip {    my ($infile, $outfile) = @_;    use Archive::Zip qw( :ERROR_CODES :CONSTANTS );    my $zip = Archive::Zip-&gt;new();    my $member = $zip-&gt;addFile($infile);    return undef unless $zip-&gt;writeToFileNamed($outfile) == AZ_OK;}sub build_kml {    # This whole subroutine is pretty fugly    # I really wanted to do it without an if/elsif,    # but I couldn&#39;t figure out how    my $ref = shift;    my $type = shift;    my $kml = qq(&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;        &lt;kml xmlns=&quot;http://earth.google.com/kml/2.1&quot;&gt;        &lt;Folder&gt;        &lt;name&gt;Perl Monks - $types{$type}{desc}&lt;/name&gt;        &lt;open&gt;1&lt;/open&gt;);    if ($type eq &#39;by_level&#39;) {        my $level = 28;        $kml .= qq(&lt;Folder&gt;&lt;name&gt;Level $level - Pope&lt;/name&gt;&lt;open&gt;0&lt;/open&gt;\n);        for my $id (sort by_level keys %{$ref-&gt;{monk}}) {            my $mlevel = $ref-&gt;{monk}{$id}{level};            if ($mlevel &lt; $level) {                $level = $mlevel;                my $level_name = $levels[$level-1];                $kml .= qq(&lt;/Folder&gt;&lt;Folder&gt;&lt;name&gt;Level $level - $level_name&lt;/name&gt;&lt;open&gt;0&lt;/open&gt;\n);            }            $kml .= mk_placemark($id,$mlevel);        }        $kml .= q(&lt;/Folder&gt;);    }    elsif ($type eq &#39;by_name&#39;) {        my @monks = sort by_name keys %{$ref-&gt;{monk}};        my $nummonks = scalar @monks;        my $mpf = 39; # monks-per-folder        my $start = 0;        while ($start &lt; $nummonks) {            my $first = lc(substr($monks[$start],0,2));            my $last = defined $monks[$start+$mpf]                     ? lc(substr($monks[$start+$mpf],0,2))                     : lc(substr($monks[-1],0,2));            $kml .= qq(&lt;Folder&gt;&lt;name&gt;Monks $first-$last&lt;/name&gt;&lt;open&gt;0&lt;/open&gt;\n);            MONK:            for my $cnt ($start .. $start+$mpf) {                last MONK if !$monks[$cnt];                my $monk = $monks[$cnt];                my $mlevel = $ref-&gt;{monk}{$monk}{level};                $kml .= mk_placemark($monk,$mlevel);            }            $start += ($mpf + 1);            $kml .= q(&lt;/Folder&gt;);        }    }    $kml .= q(&lt;/Folder&gt;&lt;/kml&gt;);    return $kml;}sub mk_placemark {    my $id = shift;    my $mlevel = shift;    my $p;    $p = qq(    &lt;Placemark&gt;        &lt;description&gt;        &lt;![CDATA[            Level: $mlevel&lt;br \\&gt;            Experience: $monks-&gt;{monk}{$id}{xp}&lt;br \\&gt;            Writeups: $monks-&gt;{monk}{$id}{writeups}&lt;br \\&gt;            User Since: $monks-&gt;{monk}{$id}{since}&lt;br \\&gt;            http://www.perlmonks.org/?node_id=$monks-&gt;{monk}{$id}{id}            ]]&gt;        &lt;/description&gt;        &lt;Snippet&gt;&lt;/Snippet&gt;        &lt;name&gt;$id&lt;/name&gt;        &lt;LookAt&gt;            &lt;longitude&gt;$monks-&gt;{monk}{$id}{location}{longitude}&lt;/longitude&gt;            &lt;latitude&gt;$monks-&gt;{monk}{$id}{location}{latitude}&lt;/latitude&gt;            &lt;altitude&gt;0&lt;/altitude&gt;            &lt;range&gt;10000&lt;/range&gt;            &lt;tilt&gt;0&lt;/tilt&gt;            &lt;heading&gt;0&lt;/heading&gt;        &lt;/LookAt&gt;        &lt;Style&gt;            &lt;IconStyle&gt;                &lt;Icon&gt;                    &lt;href&gt;$palette_url&lt;/href&gt;                    &lt;x&gt;$xy_data-&gt;[$mlevel-1][0]&lt;/x&gt;                    &lt;y&gt;$xy_data-&gt;[$mlevel-1][1]&lt;/y&gt;                    &lt;w&gt;32&lt;/w&gt;                    &lt;h&gt;32&lt;/h&gt;                &lt;/Icon&gt;            &lt;/IconStyle&gt;        &lt;/Style&gt;        &lt;Point&gt;            &lt;coordinates&gt;$monks-&gt;{monk}{$id}{location}{longitude},$monks-&gt;{monk}{$id}{location}{latitude},0&lt;/coordinates&gt;        &lt;/Point&gt;    &lt;/Placemark&gt;    );    return $p;}sub get_xy {    # This returns an AoA, which represents xy-offsets    # to each of the monk level icons on the image palette    my @xy;    for my $y (qw(96 64 32 0)) {        for my $x (qw(0 32 64 96 128 160 192)) {            push @xy, [ $x, $y ];        }    }    return \@xy;}sub get_monk_data {    my $monk_url = &#39;http://tinymicros.com/pm/monks.xml&#39;;    my @t = time();    print &quot;Fetching data....&quot;;    my $ua = LWP::UserAgent-&gt;new();    my $req = HTTP::Request-&gt;new(GET=&gt;&quot;$monk_url&quot;);    my $result = $ua-&gt;request($req);    return 0 if !$result-&gt;is_success;    my $content = $result-&gt;content;    $t[1] = time();    say(&quot;done (&quot;, formatted_time_diff(@t), &quot; secs)&quot;);    print &quot;Parsing XML....&quot;;    my $monks = XMLin($content, Cache =&gt; &#39;storable&#39;);    $t[2] = time();    say(&quot;done (&quot;, formatted_time_diff(@t[1,2]), &quot; secs)&quot;);    return $monks;}&lt;/pre&gt;&lt;p&gt;Cheers,&lt;br&gt;Darren :)
    </description>
</item>

        

<item>
    <title>Image combinations (jimt)</title>
    <link>http://prlmnks.org/html/560282.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/560282.html</guid>

    <description>
        &lt;p&gt;I wrote this up a few years ago, figure I might as well share it on here. It will create a PNG which contains all of the possible image combinations for the given set of parameters. So you specify a 2x3 pixel image with 5 possible colors (or whatever) and you&#39;ll get a huge image output the other end that contains all the variations.&lt;/p&gt;&lt;p&gt;The fun thing about image generation in this manner is that you can theoretically generate photographs of anything this way, if you had sufficient time and horsepower. Of course, a 10x10 pixel image with 10 colors has one googol possible combinations, so it quickly becomes intractable.&lt;/p&gt;&lt;p&gt;I have a more thorough write up here: &lt;a href = &quot;http://www.jimandkoka.com/m.cgi/Journal.mchn?state=display_entry&amp;journal_entry_id=139&amp;view=1&quot; target = &quot;_blank&quot;&gt;http://www.jimandkoka.com/&lt;/a&gt;&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl# Copyright and (c) 2002, 2003 Jim Thomason. jim@jimandkoka.com# http://www.jimandkoka.com# distributed under the artistic license or something## Note that this software requires the GD module, available from CPAN# http://search.cpan.org/## Usage - ./imagemaker.pl width height colors ?countonly? &gt; some.png## width is the image width# height is the image height# colors is the number of possible colors allowed in the image# countonly is an optional param. If passed, the program will just #   print out a count of the number of combinations and then quit.#use strict;use warnings;use GD;our $VERSION = 2.00;#snag our command line argumentsmy $w  = shift @ARGV or die &#39;Cannot run w/o width&#39;;my $h  = shift @ARGV or die &#39;Cannot run w/o height&#39;;my $c  = shift @ARGV or die &#39;Cannot run w/o colors&#39;;my $s  = shift @ARGV || 1;my $countonly = shift @ARGV || 0;die &quot;Cannot support more than 7 colors&quot; if $c &gt; 7 &amp;&amp; ! $countonly;#find out the number of pixel combinationsmy $combos =  $c  ** ($w * $h);#find out the unscaled area of each image setmy $unscaled_each_area = $w * $h;#find out the area of each image set, taking the scaling into accountmy $each_area = $unscaled_each_area * $s * $s;#calculate how many we&#39;d like to display per line.my $perline = int sqrt $combos;#round up if it&#39;s not an even number.$perline += 1 unless $perline ** 2 == $combos;# calculate the actual width and height of the image we&#39;re going # to generate. Each image will take up $w * $s pixels width + # $s pixels padding to its right but, the last image doesn&#39;t # require padding, so we subtract off its padding amount## ditto for the heightmy $width  = $perline * ($w * $s + $s) - $s;my $height  = $perline * ($h * $s + $s) - $s;#display some numbersprint STDERR &quot;for a $w x $h image @ $c colors, &quot;  . &quot;there are $combos combinations\n&quot;;#and bow out if we&#39;re just countingexit if $countonly;#warn them about how big this thing is gonna getprint STDERR &quot;your output will be $width x $height\n&quot;;#create our new imagemy $im = new GD::Image($width,$height);# allocate some colors## This is silly, we need to allocate red first, so we get that as # the background for our image. Bleh.my $red    = $im-&gt;colorAllocate(255,0,0);my $green  = $im-&gt;colorAllocate(0,255,0);my $blue  = $im-&gt;colorAllocate(0,0,255);my $cyan  = $im-&gt;colorAllocate(0,255,255);my $magenta  = $im-&gt;colorAllocate(255,0,255);my $yellow  = $im-&gt;colorAllocate(0,255,255);my $black  = $im-&gt;colorAllocate(0,0,0);my $white  = $im-&gt;colorAllocate(255,255,255);# this is the order we want to use the colorsmy @colors= ($white, $black, $green, $blue, $cyan, $magenta, $yellow);#our padding starts at 0.my $xpad = 0;my $ypad = 0;# and we&#39;re off to the races.## It&#39;s pretty easy. iterate through all the numbers from# 0 to the number of combinations we have. At each number,# convert it from base 10 to the number base of the number# of colors we have. So a B/W image is 2 colors is base 2.# a black, white, and green image is 3 colors, is base 3# and so on.## With each base(x) number, each digit corresponds to a color# value in our colors array. So use that color value to fill# in the pixel at the appropriate square.for (my $num = 0; $num &lt; $combos; $num++) {  # convert to the appropriate number base  my @num = basemaster($num, 10, $c);    # now, if we have fewer digits in the new base than we&#39;ll have  # pixels in the image, zero pad the number until we reach the  # appropriate size.  if (@num &lt; $unscaled_each_area) {    @num = ((0) x ($unscaled_each_area - @num), @num);  };  {    #keep track of which pixel we&#39;re on.    my $pix    = 0;    #we&#39;ll start drawing this image at the appropriate xpad    # and ypad values    my $x    = $xpad;    my $y    = $ypad;        #as long as we have pixels left to draw...    while ($pix &lt; $unscaled_each_area) {            #grab our next color      my $col = $colors[$num[$pix] || 0];            #increment our pixel count      # (yeah, I could&#39;ve done the inc up in that last line,      # but I didn&#39;t want it to get lost in the muck)      $pix++;      # and draw our pixel, scaled appropriately.      # GD is a little silly, the arguments are the upper left      # coordinates of the upper left pixel and the upper left      # coordinates of the lower right pixel. So, to actually      # set the lower right pixel to where we want, we need to back      # up by one pixel to get it positioned properly      $im-&gt;filledRectangle($x, $y, $x + $s - 1, $y + $s - 1, $col);            # slide our y coordinate over by the scale value      $y += $s;            # okay, if $pix % $h == 0, then we&#39;re at the end of the row, so       # we&#39;ll need to drop to the next row by incrementing the xpad,      # and go back to the first column by resetting the ypad.      unless ($pix % $h) {        $xpad += $s;        $x = $xpad;        $y = $ypad;      };      };  };  # okay, we&#39;ve now finished drawing out the individual image, so  # we&#39;re going to move onto the next in the sequence. So we&#39;re going  # to slide our xpadding over by $s pixels to be  # ready to start the next one. FYI, this is starting the image to  # the right of the one just drawn  $xpad += $s;    # the one exception is if we&#39;ve reached the perline limit. In that  # case, we&#39;re at the end of the row, so we&#39;ll reset our xpad back  # to zero and increment our ypad instead by the appropriate amount  unless (($num + 1) % $perline) {    $ypad += $h * $s + $s;    $xpad = 0;  };};#be nice and binmode it. Stupid dos.binmode STDOUT;#printprint $im-&gt;png;exit;  #and we&#39;re done!# basemaster takes 3 arguments.# the number you&#39;re converting, the base you&#39;re converting from, and# the base you&#39;re converting to# Always returns an array of numbers in order, higher digits in the# front, lower at the back.# # so basemaster(13, 10, 2) returns (1, 1, 0, 1)sub basemaster {  my $num    = shift || return 0;  my $from  = shift;  my $to    = shift;  # we allow it to accept a comma delimited number to allow for higher  # bases  my @num = reverse $num =~ /,/ ? split(/,/, $num) : split(//, $num);    # starting power is 0  my $pow = 0;  # and we don&#39;t know it in base10  my $base10 = undef;  # if we&#39;re converting from base ten, then do it.  if ($from != 10) {    foreach my $digit (@num){      die &quot;Invalid number -- $digit &gt;= $from&quot; if $digit &gt;= $from;      $base10 += $digit * $from ** $pow++;    };  }  # otherwise, we have a base 10 number, so there&#39;s no work to do.  else {    $base10 = $num;  };    #reset our power to 1  $pow = 1;  # and find the highest power of the number in the base we&#39;re  # converting to  $pow++ while $to ** $pow &lt;= $base10;  #we&#39;ll return our array here.  my @return = ();  #convert to the new base  while ($pow &gt;= 0){    push @return, int ($base10 / $to ** $pow);    $base10 %= $to ** $pow--;  };    #trash leading zeroes  shift @return while $return[0] == 0;  #and we are done.  return @return;};&lt;/pre&gt;
    </description>
</item>

        

<item>
    <title>Google Checkout? (qbxk)</title>
    <link>http://prlmnks.org/html/559704.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/559704.html</guid>

    <description>
        Well, I notice the new Google Checkout has now just plunked onto the scene, and their [http://code.google.com/apis/checkout/samplecode.html|code samples] page has a conspicuous absence of perl.  The [http://code.google.com/apis/checkout/developer/index.html|developer docs] appear to be quite thorough, seems like it&#39;s just a bunch of http POSTs using a well structured XML schema.  CPAN turns up missing the Google::Checkout module, so I may get to it, but the project I&#39;d need it for is starting some months from now... &lt;br&gt;&lt;br&gt;Off the top of my head, you&#39;d want the full CRUD interface for the whole object model they defined, I think, a la&lt;pre class=&quot;block_code&quot;&gt;   my $cart = Google::Checkout::Cart;   $cart-&gt;add_item(        name =&gt; &#39;Bill&#39;,       description =&gt; &#39;a pal&#39;,       price =&gt; &#39;priceless&#39;, quantity =&gt; 2   );   $cart-&gt;post(       merchant_auth_encoded =&gt;                 q[MTIzNDU2Nzg5MDpIc1lYRm9aZkhBcXlMY0NSWWVIOHFR]   );&lt;/pre&gt; but personally, I&#39;d (probably) never use that, instead opting for the magical &quot;hash to google checkout&quot; function (..&lt;tt class=&quot;inline_code&quot;&gt;Google::Checkout::from_hash(%args)&lt;/tt&gt; ? )&lt;br&gt;&lt;br&gt;maybe it&#39;s not too bad of an idea to implement that function as a wrapper to XML::Simple with the right config, and running the result against &lt;a href=&quot;/out/http/?url=code.google.com%2Fapis%2Fcheckout%2Fapiv2.xsd&quot;&gt;google&#39;s schema&lt;/a&gt; for verification?  Then doing the post as per google &#39;s spec.  &lt;br&gt;Anyhow, sorry I&#39;ve got no code, I&#39;m probably not qualified to write such an &quot;industrial strength&quot; module yet (don&#39;t make mistakes with banking code!), but I&#39;d like to help if any were interested in leading the way.  Or at least hear about your progress.  Thanks!&lt;p&gt;&lt;small&gt;Edit: &lt;a href=&quot;/out/node/g0n&quot;&gt;g0n&lt;/a&gt; - removed readmore tags&lt;/small&gt;&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-487158&quot;&gt;&lt;br&gt;&lt;font size=-2&gt;It&#39;s not what you look like, when you&#39;re doin&#39; what youre doin&#39;.&lt;br&gt;It&#39;s what youre doin&#39; when youre doin&#39; what you look like youre doin&#39;! &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;- Charles Wright &amp;amp; the Watts 103rd Street Rhythm Band, &lt;i&gt;Express yourself&lt;/i&gt;&lt;/font&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Measuring MySQL resources consumption on Linux (gmax)</title>
    <link>http://prlmnks.org/html/559540.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/559540.html</guid>

    <description>
        &lt;p&gt;This script finds how much a MySQL process is using on a Linux box. It can be used, with slight modification, to measure any other threading process. Just find a way of passing a PID to the script.&lt;/p&gt;&lt;p&gt;For a detailed explanation of what this script does, you can read this &lt;a href=&quot;http://www.oreillynet.com/databases/blog/2006/07/measuring_resources_for_a_mysq_1.html&quot;&gt;article&lt;/a&gt;.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;                       use warnings;                     use English qw( -no_match_vars ); use Getopt::Long;                 use Data::Dumper;                our $VERSION = q{1.0};$ENV{ &#39;PATH&#39; } = &#39;~/bin:&#39; . $ENV{ &#39;PATH&#39; };my $DEBUG = $ENV{ &#39;DEBUG&#39; } || 0;## The program options, for command line processing#my %parse_options = (    output_format   =&gt; {             value =&gt; &#39;plain&#39;,             parse =&gt; &#39;f|format=s&#39;,             so    =&gt; 1,             help  =&gt; [                         &#39;The output format. {plain|perl} (Default: plain)&#39;                      ],        },    mysql_user      =&gt; {             value =&gt;  undef,             parse =&gt;  &#39;u|user=s&#39;,             so    =&gt;  2,             help  =&gt; [                         &#39;The user name used with mysql client. (Default: none)&#39;                      ],        },    mysql_password  =&gt; {             value =&gt;  undef,             parse =&gt;  &#39;p|password=s&#39;,             so    =&gt;  3,             help  =&gt; [                         &#39;The password used with mysql client. (Default: none)&#39;                      ],        },    mysql_client    =&gt; {             value =&gt;  &#39;mysql&#39;,             parse =&gt;  &#39;m|mysql=s&#39;,             so    =&gt;  4,             help  =&gt; [                         &#39;The mysql client. You may add options after the file name (default: &quot;mysql&quot;)&#39;                      ],        },    print_pstree    =&gt; {             value =&gt;  0,             parse =&gt;  &#39;pstree&#39;,             so    =&gt;  5,             help  =&gt; [                         &#39;Print a Process tree after the normal output. (Default: No)&#39;                      ],        },    help      =&gt; {             value =&gt;  0,             parse =&gt;  &#39;help&#39;,             so    =&gt;  6,             help  =&gt; [                         &#39;Display this help&#39;                      ],        },);my %options = map { $_ , $parse_options{$_}{&#39;value&#39;}}  keys %parse_options;GetOptions (    map { $parse_options{ $_ }{ &#39;parse&#39; }, \$options{ $_ } }        grep { $parse_options{ $_ }{ &#39;parse&#39; }}        keys %parse_options) or get_help( q{!} );get_help() if $options{&#39;help&#39;};## Check if we are running under Linux.# This method of collecting performance data does not# work under other operating systems#unless ($OSNAME =~ /linux/i) {    die &quot;This program can only work with a GNU/Linux OS\n&quot;;}## The client used to connect to the database server.# You can add appropriate username and password with# the options -u and -p, unless you have a .my.cnf# file in your home directory#my $MYSQL_CLIENT = sprintf &quot;%s %s %s&quot;,        $options{ &#39;mysql_client&#39; },        $options{ &#39;mysql_user&#39; } ? &quot; -u $options{ &#39;mysql_user&#39; } &quot; : q{},        $options{ &#39;mysql_password&#39; } ? &quot; -p$options{ &#39;mysql_password&#39; } &quot; : q{};## Get the hostname, so your information will be tagged# to the appropriate host, if you collect this data from# several servers.#my $hostname = get_from_shell(q{ hostname });## Check if mysqld is running#get_from_shell(q{ pgrep mysqld }, 1)    or die &quot;mysqld is not running\n&quot;;## Get the PID file name#my $pid_info = get_from_shell(        qq($MYSQL_CLIENT -N -e &#39;show variables like &quot;pid_file&quot;&#39;) );$pid_info or die &quot;unable to get mysqld pid file\n&quot;;my ($pid_file) =  $pid_info =~ m/            ^          # start of string            \s*        # blanks (if any)            \w+        # skip the first word            \s*        # ignore any spaces            (\S+)      # get a chunk of non-blanks (the file name)            /x;## Get mysqld PID#my ($mysqld_pid) =  file_slurp( $pid_file );unless ($mysqld_pid        and ( $mysqld_pid =~ m/                ^     # start of string                \d+   # all numeric                $     # end of string                /x ) ){    die &quot;could not get a valid PID\n&quot;;}my $statinfo = get_pid_stats($mysqld_pid);## Get the user (ID and name) under which mysqld is running#my $UID =  $statinfo-&gt;{ &#39;Uid&#39; };my $USER = getpwuid( $UID );## Recursively search for mysqld_safe PID.# This loop stops when either a process named mysqlmanager, or mysqld_safe,# or safe_mysqld is found (MySQL 3.23 uses a different name)# or a parent with PID 1 is found (this should account for a starting# script with a non-standard name)# You may get you an incorrect result if you started mysqld manually# from the command line.#while ( !(          ($statinfo-&gt;{ &#39;PPid&#39; } == 1)        # parent is /sbin/init          or ( $statinfo-&gt;{ &#39;Name&#39; } =~ /                    ^               # start of string                    (?:             # name is one of the following                    safe_mysqld     # ... the standard name                    | mysqld_safe   # ... or the old name                    | mysqlmanager  # ... or the new manager                    )               # end of group                    $               # end of string                    /x) )){    $statinfo = get_pid_stats($statinfo-&gt;{ &#39;PPid&#39; });}my $mysqldsafe_pid = $statinfo-&gt;{ &#39;Pid&#39; };my %INFO = (        hostname    =&gt; $hostname,        mysqld_pid  =&gt; $mysqld_pid,        pid_file    =&gt; $pid_file,        user        =&gt; { name =&gt; $USER, UID =&gt; $UID },        mysqld_safe =&gt; $mysqldsafe_pid,);## Get information about CPU and memory usage# for the mysqld process#my $process_list = get_from_shell ( &quot;ps -o pcpu,pmem,vsz,rss,dsiz $mysqld_pid&quot; );## Get the CPU load for all the processes under mysqld_safe# and calculate the average#my $pstree = `pstree -p $mysqldsafe_pid`;my $total_cpu_load = 0;my $active_pid_num = 0;my @pids = $pstree =~ m/\( (\d+) \)/xg;my $pid_num = @pids;my $plist = get_from_shell( &quot;ps -o pcpu @pids&quot;, 1);## Get the load for each PID#while ( $plist =~ /^\s* (\d+\.\d+)/gmx) {    $total_cpu_load += $1;    $active_pid_num++;}## calculate the average, avoiding any division by zero traps#my $avg_cpu_load = $total_cpu_load ? $total_cpu_load / $active_pid_num : 0 ;## Extract process details#if ( $process_list =~ m/        \s* (\d+\.\d+) # percentage cpu        \s+ (\d+\.\d+) # percentage memory        \s+ (\d+)      # virtual memory size        \s+ (\d+)      # resident set size        \s+ (\d+)      # data size        /x){    $INFO{ &#39;mysql_load&#39; } = {        avg_perc_cpu    =&gt; $avg_cpu_load,        perc_memory     =&gt; $2,        vmem_size       =&gt; $3,        res_set_size    =&gt; $4,        data_size       =&gt; $5,        pids            =&gt; $pid_num,        active_pids     =&gt; $active_pid_num,    };}## Get the global server load info#my ($server_load) = file_slurp( &#39;/proc/loadavg&#39; );if ( $server_load =~ m/        \s* (\d+\.\d+) # cpu load average last  5 minutes        \s+ (\d+\.\d+) # cpu load average last 10 minutes        \s+ (\d+\.\d+) # cpu load average last 15 minutes        /x ){    $INFO{ &#39;server_load&#39; } = {        cpu5    =&gt; $1,        cpu10   =&gt; $2,        cpu15   =&gt; $3,    };}## Get the number of open files for the mysqld process.# Notice that you won&#39;t be able to access this list as# a normal user if mysqld_safe was launched by root.#my @file_descriptors = glob &quot;/proc/$mysqld_pid/fd/*&quot;;$INFO{ &#39;open_files&#39; } = scalar @file_descriptors;if ($options{ &#39;output_format&#39; } eq &#39;perl&#39;) {    perl_output( \%INFO );}elsif ($options{ &#39;output_format&#39; } eq &#39;plain&#39;) {    plain_output(\%INFO);}else {    die &quot;unrecognized output format ($options{ &#39;output_format&#39; })\n&quot;;}if ($options{ &#39;print_pstree&#39; }) {    print &quot;\n&quot;, $pstree;}## subs follow### the plain text report#sub plain_output {    my ($info) = @_;    my $template = &lt;&lt;&#39;END_PLAIN&#39;;    hostname         : %10s    mysql_pid        : %10d    pid_file         : %s    mysqld_safe      : %10d    open_files       : %10d    user    ----        name         : %10s        UID          : %10d    mysql_load    ----------        avg_perc_cpu : %10.2f        perc_memory  : %10.2f        vmem_size    : %10d        res_set_size : %10d        data_size    : %10d        pids         : %10d        active_pids  : %10d    server_load    -----------        cpu5         : %10.2f        cpu10        : %10.2f        cpu15        : %10.2fEND_PLAIN    print credits(), &quot;\n&quot;;    printf $template,        @$info{qw( hostname mysqld_pid pid_file mysqld_safe open_files ) },        $info-&gt;{ &#39;user&#39; }{ &#39;name&#39; },        $info-&gt;{ &#39;user&#39; }{ &#39;UID&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;avg_perc_cpu&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;perc_memory&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;vmem_size&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;res_set_size&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;data_size&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;pids&#39; },        $info-&gt;{ &#39;mysql_load&#39; }{ &#39;active_pids&#39; },        $info-&gt;{ &#39;server_load&#39; }{ &#39;cpu5&#39; },        $info-&gt;{ &#39;server_load&#39; }{ &#39;cpu10&#39; },        $info-&gt;{ &#39;server_load&#39; }{ &#39;cpu15&#39; };}## the report in Perl format.# Useful for loading it from another program, or to# pipe it (see help for examples)#sub perl_output {    my ($info) = @_;    print Data::Dumper-&gt;Dump( [ $info ], [&#39;INFO&#39;] );}## reads the file /proc/PID/status# and returns a hash with its values#sub get_pid_stats {    my ($pid) = @_;    my %stat_info;    my @pid_status = file_slurp ( &quot;/proc/$pid/status&quot; );    for my $line (@pid_status) {        my ($key, $value) = $line =~ m/                    ^       # start of string                    (\w+)   # first word                    :       # a colon                    \s+     # skip any blanks                    (\S+)   # the first chunk of non-blank characters                    /x ;        next unless $key;        $stat_info{$key} = $value;    }    return \%stat_info;}## reads a file and returns an array# (one element for each line)#sub file_slurp {    my ($filename) = @_;    print &quot;reading $filename\n&quot; if $DEBUG;    $filename        or die &quot;can&#39;t open a empty filename\n&quot;;    open my $FD, q{&lt;}, $filename        or die &quot;can&#39;t open $filename ($ERRNO)\n&quot;;    my @contents = &lt;$FD&gt;;    chomp @contents;    close $FD;    return @contents;}## executes a shell command# and returns its output#sub get_from_shell {    my ($cmd, $ignore_error) = @_;    my $result;    print &quot;executing $cmd\n&quot; if $DEBUG &gt;= 1;    $result = qx{$cmd} ;    if ( $CHILD_ERROR and ! $ignore_error ) {        warn &quot;&#39;$cmd&#39; returned error $CHILD_ERROR.\n&quot;;    }    chomp $result;    print &quot;$result\n&quot; if $DEBUG &gt;= 2;    return $result;}## Help for this program#sub get_help {    my ($msg) = @_;    my $HELP_MSG = q{};    for my $op (                sort { $parse_options{$a}{ &#39;so&#39; } &lt;=&gt; $parse_options{$b}{ &#39;so&#39; } }                grep { $parse_options{$_}{ &#39;parse&#39; }}  keys %parse_options )    {        my $param =  $parse_options{$op}{ &#39;parse&#39; };        my $param_str = q{    };        my ($short, $long ) = $param =~ / (?: (\w) \| )? (\S+) /x;        if ($short) {            $param_str .= q{-} . $short . q{ };        }        $long =~ s/ = s \@? / = name/x;        $long =~ s/ = i / = number/x;        $param_str .= q{--} . $long;        $param_str .= (q{ } x (40 - length($param_str)) );        my $text_items = $parse_options{$op}{ &#39;help&#39; };        for my $titem (@{$text_items}) {            $HELP_MSG .= $param_str . $titem . &quot;\n&quot;;            $param_str = q{ } x 40;        }        if (@{$text_items} &gt; 1) {            $HELP_MSG .= &quot;\n&quot;;        }        # $HELP_MSG .= &quot;\n&quot;;    }    if ($msg) {        warn &quot;[***] $msg\n\n&quot;;    }    print credits(),&quot;\n&quot;,        &quot;syntax: $PROGRAM_NAME [options] \n&quot;,        $HELP_MSG;    print &quot;\n    You may get output in other formats, by piping the &#39;perl&#39; output\n&quot;,          &quot;    for example: \n    &quot;,          $PROGRAM_NAME,          q{ -f perl | perl -MYAML -0ne &#39;eval $_; print Dump $INFO&#39;},          &quot;\n&quot;,          &quot;    or: \n    &quot;,          $PROGRAM_NAME,          q{ -f perl | perl -MXML::Simple -0ne &#39;eval $_; print XMLout $INFO, AttrIndent=&gt;1&#39;},          &quot;\n\n&quot;;    if ($msg) {        exit 1;    }    else {        exit 0;    }}## Who made it#sub credits {    my $CREDITS =          qq(    The MySQL Resource Locator,  version $VERSION\n)        . qq(    (C) 2006 Giuseppe Maxia, Stardata s.r.l.\n);    return $CREDITS;}&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-127116&quot;&gt;&lt;pre&gt; _  _ _  _  (_|| | |(_|&gt;&lt; _|   &lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>perror.pl (Hue-Bond)</title>
    <link>http://prlmnks.org/html/559156.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/559156.html</guid>

    <description>
        &lt;p&gt;At work I learned about a tool called &lt;tt&gt;perror&lt;/tt&gt;, that showed the literal string corresponding to a numeric UNIX error. So if you get a message from a program that says &quot;errno 13&quot;, you can run &lt;tt&gt;perror&lt;/tt&gt; to easily translate that into &quot;Permission denied&quot;. When I went to install it at home, I found out that it&#39;s in Debian package mysql-server (!). So I made my own version:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;foreach (grep /^\d+$/, @ARGV) {  $! = $_;  print &quot;$_: $!&quot;;}&lt;/pre&gt;&lt;p&gt;Which can be turned into a oneliner:&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;$ perl -le&#39;print &quot;$_: &quot;, $! = $_ foreach grep /^\d+$/, @ARGV&#39; 13 20   ## It even respects your locale settings13: Permiso denegado20: No es un directorio&lt;/pre&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-489671&quot;&gt;&lt;p&gt;-- &lt;br /&gt;David Serrano&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>quotes from the great master (sh1tn)</title>
    <link>http://prlmnks.org/html/558848.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/558848.html</guid>

    <description>
        &lt;pre class=&quot;block_code&quot;&gt;# random Larry Wall quote# quotes can be taken from:# http://www.perl.com/CPAN/misc/lwall-quotes.txt.gz# Sun Jul  2 15:25:40use File::Slurp;$_      = read_file(q{lwall-quotes.txt});$quote  = [];push @$quote, $2 while /(\%\%)(.+?)\1/gs;print $quote-&gt;[rand(@$quote)], $/&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>web_diff.pl (ciderpunx)</title>
    <link>http://prlmnks.org/html/558367.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/558367.html</guid>

    <description>
        Its a simple script to diff the text on web pages.&lt;br /&gt; &lt;br /&gt; Someone used the phrase somewhere on the perlmonks site which made me think it&#39;d be a handy thing to have - thanks to them.&lt;br /&gt; &lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -wuse strict;use LWP::Simple;use Text::Diff;use HTML::Strip;require 5.008_000;my $STORE=&quot;/home/charlie/diffs&quot;;die (&quot;Usage: $0 &lt;URL_TO_DIFF&gt;&quot;) unless ($#ARGV==0);my $url=$ARGV[0];# &#39;nice&#39; URLmy $n_url=$url;$n_url=~s/^http:\/\///;$n_url=~s/\//_/g;my $store_as = (-e &quot;$STORE/$n_url&quot; )        ? &quot;$STORE/$n_url.new&quot;        : &quot;$STORE/$n_url&quot;;if (is_success(getstore($url,$store_as)))  {        unless ($store_as eq &quot;$STORE/$url&quot;) {                my $diff = diff $store_as, &quot;$STORE/$n_url&quot;;                print $diff . &quot;\n&quot;;                rename $store_as, &quot;$STORE/$n_url&quot;;        }}else {  warn &quot;Storing $store_as failed. Life sucks.&quot;  }__END__=head1 NAMEweb_diff.pl  =head2 VERSION0.1=head1 SYNOPSISdiff text from a page retrieved off interweb and page stored locally=head1 DESCRIPTIONRetrieve a pageIf we have a previously stored local copy,        compare retrieved and local page        If they are not identical                strip html from them                do a diffIf we have no locally stored page, store retrieved page locally=head2 OPTIONS=over=item  C&lt;URL TO DIFF&gt;This isn&#39;t sanitized in properly, this code is not for use by peopleyou don&#39;t trust implicitly :-)=back=head1 REQUIREMENTS=over=item Perl &gt;= 5.8.0 (not tested on earlier versions)=item HTML::Strip=item Text::Diff=item LWP::Simple=back=head1 COPYRIGHT AND LICENCE               Copyright (C)2006  Charlie Harvey This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. Also available on line: http://www.gnu.org/copyleft/gpl.html=head1 SEE ALSO=cut&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-373188&quot;&gt;--&lt;br /&gt;&lt;a href=&quot;http://charlieharvey.org.uk/cgi-bin/ctrl.pl&quot;&gt;charlieharvey.org.uk&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Regex Tool (Yunus)</title>
    <link>http://prlmnks.org/html/558260.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/558260.html</guid>

    <description>
        Hi everyone!New tool to build Perl-compatible Regex (mine actually)http://www.geocities.com/unouse_80/app/builder.htmlI would like to have some comments/feedback.TQ
    </description>
</item>

        

<item>
    <title>Play and win the word morph game with the help of Perl :) (Ieronim)</title>
    <link>http://prlmnks.org/html/558123.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/558123.html</guid>

    <description>
        There is a popular word game caled &quot;[lucky://Word Morph]&quot;. In this game you need to go from one word to another by modifying  one letter in each word to form a new word. for example (head to foot):&lt;pre class=&quot;block_code&quot;&gt;HEADbeadbeatboatbootFOOT&lt;/pre&gt;One of my English-speaking friends told me about this game. As my English is quite bad, I could not play this game fairly; but i wrote a perl script to solve the problem :)&lt;p&gt;This command-line tool finds a shortest way from one word to another using the given dictionary. I used the 2of12 dictonary from the 12Dicts project ([href://http://wordlist.sourceforge.net]), but any newline-character-delimited wordlist of any language can be used.&lt;p&gt;I don&#39;t know how &#39;cool&#39; is this usage, but i mean that it is quite interesting :)&lt;p&gt;It is of course not ideal, so I am open for any suggestions  :)&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse warnings;use strict;my $dict  = &#39;2of12.txt&#39;;die &lt;&lt;HELP unless @ARGV == 2;usage: transform.pl &lt;word1&gt; &lt;word2&gt;The program finds a way from one word to other, like this:% transform.pl love shitlove-lose-lost-loot-soot-shot-shitHELPmy $left  =  shift(@ARGV) || &#39;love&#39;;my $right =  shift(@ARGV) || &#39;shit&#39;;for ($left, $right) {    $_ = lc;    tr/A-Za-z//cd;}die &quot;the length of given words is not equal!\n&quot; if length($left) != length $right;open DICT, $dict or die &quot;Cannot open dictionary $dict: $!&quot;;my @words;while (&lt;DICT&gt;) {    chomp;    push @words, $_ if length == length $left;}eval {    my @ways = ([transform($left, $right, \@words)], [reverse transform($right, $left, \@words)]);    if (@{$ways[0]} != @{$ways[1]}) {        printway( @{$ways[0]} &gt; @{$ways[1]} ? $ways[0] : $ways[1] );    }    elsif (grep {$ways[0]-&gt;[$_] ne $ways[1]-&gt;[$_]} (0..(scalar(@{$ways[0]}) - 1) )) {        printway($ways[0]);        printway($ways[1]);    }    else {printway($ways[0])}    1;} or print $@;sub transform {    my $left = shift;    my $right = shift;    my @words = @{+shift};    my (@left, %left, @right, %right);      # @left and @right- arrays containing word relation trees: ([foo], [0, foe], [0, fou], [0, 1, fie] ...)                                            # %left and %right - indices containing word offsets in arrays @left and @right    $left[0] = [$left];    $right[0] = [$right];    my $leftstart  = 0;    my $rightstart = 0;    my @way;    my (%leftstarts, %rightstarts);    SEARCH:     for (;;) {        my @left_ids = $leftstart..$#left;                              # choose array of indices of new words        $leftstart = $#left;        die &quot;Cannot solve! Bad word &#39;$left&#39; :(\n&quot; if $leftstarts{$leftstart}++ &gt;2;  # finish search if the way could not be found        for my $id (@left_ids) {                                        # come through all new words            my @prefix   = @{$left[$id]};            my @patterns = wordpattern(pop @prefix);                    # build patterns to find related words: foo -&gt; (/^.oo$/,/^f.o$/, /^fo.$/)            push @prefix, $id;            foreach my $word (@words) {                next if $left{$word};                                   # skip words which are already in the tree                if (scalar grep {$word =~ /$_/} @patterns) {            # if matched...                     push @left, [@prefix, $word];                    $left{$word} = $#left;                              # add new word to array and index                    #print join &quot; &quot;, @{$left[-1]}, &quot;\n&quot;; #debugging                    if ( defined(my $r_id = $right{$word}) ) {          # and check if the word appears in right index. if yes...                        my @end = reverse(print_rel($r_id, \@right));                        shift @end;                        @way = (print_rel($#left, \@left), @end);       # build the way between the words                        last SEARCH;                                    # and finish the search                    }                 }            }        }        my @right_ids = $rightstart..$#right;                           # all the same :) the tree is build from both ends to speed up the process        $rightstart = $#right;        die &quot;Cannot solve! Bad word &#39;$right&#39; :(\n&quot; if $rightstarts{$rightstart}++ &gt; 2;        for my $id (@right_ids) {      # build right relational table            my @prefix   = @{$right[$id]};            my @patterns = wordpattern(pop @prefix);            push @prefix, $id;            foreach my $word (@words) {                next if $right{$word};                if (scalar grep {$word =~ /$_/} @patterns) {                    push @right, [@prefix, $word];                    $right{$word} = $#right;                    # print join &quot; &quot;, @{$right[-1]}, &quot;\n&quot;; #debugging                    if ( defined(my $l_id = $left{$word}) ) {                        my @end = reverse print_rel($#right, \@right);                        shift @end;                        @way = (print_rel($l_id, \@left), @end);                        last SEARCH;                    }                 }            }        }    }    return @way;}sub wordpattern {    my $word = shift;    my @patterns;    for my $i (0..(length($word)-1)) {        substr((my $pat = $word), $i, 1) = &#39;.&#39;;        push @patterns, qr/^$pat$/;    }    return @patterns;}sub print_rel {    my $id = shift;    my $ary = shift;    my @line;    my @rel = @{$ary-&gt;[$id]};    push @line, (pop @rel);    foreach my $ref_id (reverse @rel) {        unshift @line, $ary-&gt;[$ref_id]-&gt;[-1];    }    return wantarray ? @line : join &quot;\n&quot;, @line, &quot;&quot;;}sub printway {    my @way = @{+shift};    print join &quot;-&quot;, @way;    print &quot;\n&quot;;}&lt;/pre&gt;
    </description>
</item>

        

<item>
    <title>String eval is cool (DrHyde)</title>
    <link>http://prlmnks.org/html/556884.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/556884.html</guid>

    <description>
        Recently on one of my mailing lists, someone wanted some code to translate CIDR-style ip.add.re.ss/bits descriptions into the more traditional network address and netmask pairs.  Another list member posted some perl code which was basically 30 lines of C but with extra $ signs sprinkled throughout.  My version was (stripped of data validity checking etc) this:&lt;pre class=&quot;block_code&quot;&gt;my ($addr, $bits, $network, $mask) = split(&quot;/&quot;, $cidr);$network = $network * 256 + $_ foreach(split(/\./, $addr);$mask = eval &#39;0b&#39;.(&#39;1&#39; x $bits).(&#39;0&#39; x (32 - $bits));print int2ip($network &amp; $mask).&quot;\t&quot;.int2ip($mask).&quot;\n&quot;;&lt;/pre&gt;&lt;p&gt;(the int2ip function was the same as that the original poster had used, so I don&#39;t bother reproducing it here)&lt;p&gt;The string eval - abhorred by many - meant I could avoid several lines of fairly opaque bit-banging and I think it&#39;s *very* clear what that line does, and it also makes it obvious how netmasks are related to the /bits in CIDR notation.
    </description>
</item>

        

<item>
    <title>Sudoku Solver, and web interface. (JediWizard)</title>
    <link>http://prlmnks.org/html/556520.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/556520.html</guid>

    <description>
        &lt;p&gt;While I was bored the other day, I decided to see if I could come up with a way to programatically solve sudoku puzzles in perl. I have a sudoku game on my palm, and I wanted a program that could solve puzzles at all four difficulty levels. Although I was able to solve puzzles at the first three difficulty setting with little trouble, the &quot;expert&quot; level puzzle forced me to use an algorithm with which I am not satisfied. Below you will find my code.&lt;/p&gt;&lt;p&gt;Comments welcome.&lt;/p&gt;&lt;p&gt;If you know a better algorithm to relace my poorly named &quot;level4&quot; logic, I&#39;d love to hear about it.&lt;/p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/local/bin/perl -wuse strict;use CGI qw(:standard);print header();print &quot;&lt;html&gt;\n&lt;head&gt;\n&lt;title&gt;Sudoku Solver&lt;/title&gt;\n&quot;;print &quot;&lt;link rel=&#39;stylesheet&#39; type=&#39;text/css&#39; href=&#39;/sudoku.css&#39; /&gt;&lt;/head&gt;&lt;body&gt;\n&lt;center&gt;&lt;div class=&#39;header&#39;&gt;&lt;span style=&#39;align:center;position:relative;top:20%&#39;&gt;Sudoku Solver&lt;/span&gt;&lt;/div&gt;\n&quot;;my(%ParamHash) = ();foreach my $param (param()){$ParamHash{$param} = param($param);}if(exists($ParamHash{action}) &amp;&amp; $ParamHash{action} eq &#39;solve&#39;){my $board = Sudoku::Board-&gt;new();foreach my $sq (grep(/^sq/, keys %ParamHash)){next if($ParamHash{$sq} &lt; 1);$sq=~m/(\d+)/;my $sqn = $1;print STDERR &quot;$sqn $ParamHash{$sq}\n&quot;;$board-&gt;get_square($sqn)-&gt;assign_value($ParamHash{$sq});}&amp;level1($board);print STDERR &quot;Level 1 logic complete\n&quot;;if(! $board-&gt;is_solved){    print STDERR &quot;Begining level 2 logic\n&quot;;    &amp;level2($board);    &amp;level1($board);}if(! $board-&gt;is_solved){    &amp;level3($board);    &amp;level2($board);    &amp;level1($board);}if(! $board-&gt;is_solved){    &amp;level4($board);    &amp;level3($board);    &amp;level2($board);    &amp;level1($board);}# Display Puzzle print &quot;&lt;div class=&#39;board&#39;&gt;\n&quot;;my $sqn = 0;for(my $r=1; $r&lt;10; $r++){print &quot;&lt;div class=&#39;r$r&#39;&gt;\n&quot;;for(my $i = 1; $i&lt;10; $i++){my $sq = $board-&gt;get_square($sqn);print &quot;&lt;div class=&#39;c$i&#39;&gt;&lt;span style=&#39;align:center;position:relative;top:30%&#39;&gt;&quot;;if(exists($sq-&gt;{value})){print $sq-&gt;{value};}else{print &quot;&amp;nbsp;&quot;;}print &quot;&lt;/span&gt;&lt;/div&gt;\n&quot;;$sqn++;}print &quot;&lt;/div&gt;\n&quot;;}print &quot;&lt;/div&gt;\n&quot;;}else{print &quot;&lt;form name=&#39;board&#39; method=&#39;post&#39;&gt;\n&quot;;print &lt;&lt;EOF;&lt;script language=&#39;javascript&#39;&gt;function incrimentSquare(field, square){var val = field.value;val++;if(val == 10){val = 0;square.innerHTML=&#39;&#39;;}else{square.innerHTML = val;}field.value = val;}&lt;/script&gt;EOFprint &quot;&lt;div class=&#39;board&#39;&gt;\n&quot;;my $sqid=0;print &quot;&lt;span id=&#39;davey&#39;&gt;&lt;/span&gt;\n&quot;;for(my $r=1; $r&lt;10; $r++){print &quot;&lt;div class=&#39;r$r&#39;&gt;\n&quot;;for(my $i = 1; $i&lt;10; $i++){print &quot;&lt;div class=&#39;c$i&#39; onclick=\&quot;javascript:incrimentSquare(document.forms.board.sq$sqid, document.getElementById(&#39;sq$sqid&#39;))\&quot;&gt;&lt;span id=&#39;sq$sqid&#39; style=&#39;align:center;position:relative;top:30%&#39;&gt;&lt;/span&gt;&quot;;print &quot;&lt;input type=&#39;hidden&#39; name=&#39;sq$sqid&#39; value=&#39;0&#39; /&gt;&quot;;print &quot;&lt;/div&gt;\n&quot;;$sqid++;}print &quot;&lt;/div&gt;\n&quot;;}print &quot;&lt;/div&gt;\n&quot;;print &quot;&lt;input type=&#39;hidden&#39; name=&#39;action&#39; value=&#39;solve&#39; /&gt;\n&quot;;print &quot;&lt;input type=&#39;button&#39; value=&#39;Solve it&#39; onclick=&#39;javascript:document.forms.board.submit();&#39; /&gt;\n&quot;;print &quot;&lt;/form&gt;\n&quot;;}print &quot;&lt;/center&gt;\n&lt;/body&gt;\n&lt;/html&gt;\n&quot;;sub level1{    my $board = shift;    my $action = 1;    while($action){        $action = 0;        foreach my $offset (0 .. 80){            my $sq = $board-&gt;get_square($offset);            next if($sq-&gt;{value});            my(@ava) = $sq-&gt;available_values();            if(scalar(@ava) == 1){                $sq-&gt;assign_value($ava[0]);                $action++            }        }    }}sub level2{    my $board = shift;    my $action = 1;    INFI: while($action){        $action = 0;        my(@units) = ($board-&gt;get_rows, $board-&gt;get_columns, $board-&gt;get_cubes);        UNI: foreach my $unit (sort({$a-&gt;available_values &lt;=&gt; $b-&gt;available_values} @units)){            my(%ava) = $unit-&gt;get_squares_by_number();            my(@one) = grep({ scalar(@{ $ava{$_} }) == 1 } keys %ava);            if(scalar(@one)){                $action++;                foreach my $val (@one){                    if(! $ava{$val}[0]-&gt;assign_value($val)){                        print STDERR &quot;Warning Assign Value Failed!\n&quot;;                    }                }                &amp;level1($board);                last INFI if($board-&gt;is_solved());                next INFI;            }        }    }}sub level3{    my $board = shift;    my(@squares) = grep({scalar($_-&gt;available_values) &lt; 3} $board-&gt;get_all_squares());    my(%table, %groups);    foreach my $sq (@squares){        push @{ $table{ join(&#39;;&#39;, $sq-&gt;available_values) } }, $sq;    }    foreach my $combo (grep({scalar(@{ $table{$_} }) &gt; 1} keys %table)){        COMBO: for(my $si=0; $si&lt;$#{ $table{$combo} }; $si++){            for(0 .. 2){                if($table{$combo}[$si]{groups}[$_] == $table{$combo}[($si+1)]{groups}[$_]){                    push @{ $groups{$combo} }, $table{$combo}[$si]{groups}[$_];                    last COMBO;                }            }        }    }    foreach my $cm (keys %groups){        my($num1, $num2) = split(/;/, $cm);        foreach my $gr (@{ $groups{$cm} }){            foreach my $sq ($gr-&gt;get_members()){                my(@left) = grep({$_ != $num1 &amp;&amp; $_ != $num2} $sq-&gt;available_values);                if(scalar(@left) == 1){                    $sq-&gt;assign_value($left[0]);                }            }        }    }}sub level4{    my $board = shift;    my(@units) = sort({$a-&gt;available_values &lt;=&gt; $b-&gt;available_values} ($board-&gt;get_rows, $board-&gt;get_columns, $board-&gt;get_cubes));        foreach my $unit (@units){        foreach my $sq (grep({! exists($_-&gt;{value}) } $unit-&gt;get_members)){            my(@values) = $sq-&gt;available_values();            my(@groups) = @{ $sq-&gt;{groups} };            foreach my $val (@values){                my $gcc = 0;                GROUP: foreach my $gr (@groups){                    my(%vbn) = $gr-&gt;get_squares_by_number();                    foreach my $osq (grep({$_ != $sq} @{$vbn{$val}})){                        if(scalar($osq-&gt;available_values) &lt; 3){                            $gcc++;                            next GROUP;                        }                    }                    foreach my $v (keys %vbn){                        next if($v == $val);                        next if(scalar(grep({$_ != $sq} @{ $vbn{$v} })) &gt; 1);                        $gcc++;                        next GROUP;                    }                }                if($gcc == 3){                    $sq-&gt;assign_value($val);                    return 1;                }            }        }    }    return 0;}package Sudoku::Square;sub new{    my $proto = shift;    my(@groups) = @_;    $proto = ref($proto) || $proto;        my $self = { groups =&gt; \@groups };        foreach (@{ $self-&gt;{groups} }){        $_-&gt;add_square($self);    }        return bless $self, $proto;}sub available_values{    my $self = shift;    if($self-&gt;{value}){        return $self-&gt;{value};    }        my(%values);    foreach my $gr (@{ $self-&gt;{groups} }){        foreach ($gr-&gt;available_values()){            $values{$_}++;            #print STDERR &quot;$_ == $values{$_}\n&quot;;        }    }    #print STDERR join(&quot;, &quot;, grep({$values{$_} == 3 } keys %values)).&quot;\n\n&quot;;    return grep({$values{$_} == 3 } keys %values);}sub assign_value{    my $self = shift;    my ($value) = @_;    my @assigned = ();    foreach my $gr (@{ $self-&gt;{groups} }){        if($gr-&gt;take_value($value)){            push @assigned, $gr;        }else{            foreach (@assigned){                $_-&gt;relinquish_value($value);            }            return 0;        }    }        $self-&gt;{value} = $value;        return 1;}package Sudoku::Group;sub new{    my $proto = shift;    $proto = ref($proto) || $proto;    my $self = {};    my(%values);    @values{ 1 .. 9 } = (1 .. 9);    $self-&gt;{Values} = \%values;        return bless $self, $proto;}sub add_square{    my $self = shift;    push @{ $self-&gt;{squares} }, shift;    return 1;}sub get_square{    my $self = shift;    return $self-&gt;{squares}[ $_[0] ];}sub take_value{    my $self = shift;    my($value) = @_;        if(exists($self-&gt;{Values}{$value})){        delete($self-&gt;{Values}{$value});        return 1;    }else{        return 0;    }    return 0;}sub available_values{    my $self = shift;    return keys %{ $self-&gt;{Values} };}sub relinquish_value{    my $self = shift;    my($value) = @_;    $self-&gt;{Values}{$value} = $value;        return 1;}sub get_squares_by_number{    my $self = shift;    my(%ava);    foreach my $sq ($self-&gt;get_members()){        next if($sq-&gt;{value});        foreach my $val ($sq-&gt;available_values){            push @{ $ava{$val} }, $sq;        }    }    return %ava;}sub get_members{    my $self = shift;    return @{ $self-&gt;{squares} };}package Sudoku::Board;sub new{    my $proto = shift;    $proto = ref($proto) || $proto;    my $self = {};    $self-&gt;{Rows}    = [new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group];    $self-&gt;{Columns} = [new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group];    $self-&gt;{Cubes}   = [new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group, new Sudoku::Group];        for(my $cu=0; $cu &lt; 9; $cu++){        my $cube = $self-&gt;{Cubes}[$cu];        my $col_off = (($cu % 3) * 3);        my $row_off = (int($cu/3) * 3);        for(my $r = 0; $r &lt; 3; $r++){            my $row = $self-&gt;{Rows}[($r + $row_off)];            for(my $c = 0; $c &lt; 3; $c++){                my $sq = Sudoku::Square-&gt;new($row, $self-&gt;{Columns}[($c + $col_off)], $cube);            }        }    }        return bless $self, $proto;}sub get_square{    my $self = shift;    my($sq_num) = @_;    return $self-&gt;{Rows}[(int($sq_num/9))]-&gt;get_square(($sq_num % 9));}sub get_all_squares{    my $self = shift;    return map({ $_-&gt;get_members } $self-&gt;get_rows);}sub get_rows{    my $self = shift;    return @{ $self-&gt;{Rows} };}sub get_columns{    my $self = shift;    return @{ $self-&gt;{Columns} };}sub get_cubes{    my $self = shift;    return @{ $self-&gt;{Cubes} };}sub is_solved{    my $self = shift;        foreach my $row (@{ $self-&gt;{Rows} }){        if(scalar($row-&gt;available_values) &gt; 1){            return 0;        }    }    return 1;}&lt;/pre&gt;&lt;p&gt;The web interface is fairly easy to use. It was tested with Firefox on windows. Please forgive the distortionsd when the window is scaled.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Caveat&lt;/b&gt;: If only given a few squares as a starting point, it will hang. For best results, give it a puzzle with only one possible solution.&lt;/p&gt;&lt;div class=&quot;pmsig&quot;&gt;&lt;div class=&quot;pmsig-391471&quot;&gt;&lt;hr /&gt;&lt;p&gt;They say that time changes things, but you actually have to change them yourself.&lt;/p&gt;&lt;p class=&#39;indented&#39;&gt;&amp;#0151;Andy Warhol&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>MoveCeiling (bpoag)</title>
    <link>http://prlmnks.org/html/555652.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/555652.html</guid>

    <description>
        Wrote a script at work today, one that will monitor a filesystem in AIX, and increase it&#39;s size if it begins to run out of room...Added alot of neat little trick to it, including email notification. Here&#39;s the goods:&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl## MoveCeiling v0.2 written 061206:1122 by BJP## MoveCeiling will watch and automatically increase the size of an AIX filesystem# given user-specified parameters.## Usage: moveceiling &lt;ceiling in MB&gt; &lt;filesystem&gt; &lt;increment size&gt; &lt;sleep time&gt; &lt;space to preserve&gt; &lt;notify counter&gt; &lt;email address&gt;# Example: moveceiling 500 /tmp 64 3200 5 foo@bar.com# English: Keep 500MB free in /tmp. If you have to add space, add it in 64MB chunks. Preserve 3200MB in the volume group.#          Remind me every 5 times you have to increase space, and send that reminder to foo@bar.com#use Mail::Sendmail;$ceilingSize=$ARGV[0];$targetFS=$ARGV[1];$incrementSize=$ARGV[2];$sleepTime=$ARGV[3];$preserveAmount=$ARGV[4];$notifyCounter=$ARGV[5];$mailRecipient=$ARGV[6];$mailSender=$ENV{&quot;USER&quot;}.&quot;\@&quot;.&quot;localhost&quot;;if ($#ARGV!=6){        showUsage();        exit();}spinUpRoutine();mainRoutine();spinDownRoutine();sub showUsage(){        print &quot;\n MoveCeiling&#39;s job is to watch a filesystem, and increase it&#39;s size if the\n&quot;;        print &quot; amount of free space in that filesystem falls below threshold. MoveCeiling\n&quot;;        print &quot; is much smarter than it used to be, but you should still use it with caution. \n&quot;;        print &quot;\n&quot;;        print &quot;      Usage: moveceiling &lt;ceiling in MB&gt; &lt;filesystem&gt; &lt;increment size&gt; &lt;sleep time&gt; &lt;space to preserve&gt; &lt;notify counter&gt; &lt;email address&gt;\n&quot;;        print &quot;    Example: moveceiling 500 /tmp 64 30 5 foo\@bar.com\n&quot;;        print &quot; In English: Every 30 seconds, check to see if /tmp has at least 500MB free. \n&quot;;        print &quot;             If it sinks below 500MB, add another 64MB. For every 5 times I \n&quot;;        print &quot;             add more space, i&#39;ll send a status message to foo\@bar.com.\n&quot;;        print &quot;\n\n&quot;;        exit();}sub spinUpRoutine(){        print &quot;\nMoveCeiling: I will make sure $targetFS has at least $ceilingSize MB free.&quot;;        print &quot;\nMoveCeiling: If I have to increase it&#39;s size, I will do so in $incrementSize MB increments.&quot;;        print &quot;\nMoveCeiling: Every $notifyCounter times I do so, i&#39;ll drop a note to $mailRecipient.&quot;;        print &quot;\nMoveCeiling: I&#39;ll stop adding storage if the volume group has less than $preserveAmount MB remaining.&quot;;        print &quot;\nMoveCeiling: Starting up..\n&quot;;        ## Check to see if the filesystem actually exists...        @dfContents=split(/\s+/,`df -m | grep &quot; $targetFS\$&quot;`);        if(length($targetFS)==length($dfContents[6]))        {                print &quot;MoveCeiling: Examining $dfContents[6]..\n&quot;;        }        else        {                print &quot;MoveCeiling: Uh-oh.. Can&#39;t find the filesystem you specified ($targetFS)..!  Exiting.\n\n&quot;;                exit();        }        ## So far so good. Pull up the VG info for this filesystem..        $dfContents[0]=~/\/dev\//;        $targetLV=$&#39;;        @lslvContents=split(/\s+/,`lslv $targetLV|head -n1` );        $targetVG=$lslvContents[5];        print &quot;MoveCeiling: $targetFS belongs to logical volume $targetLV inside $targetVG.\n&quot;;        @lsvgDump=split(/\s+/,`lsvg -L $targetVG | head -n4 | tail -n1`);        $lsvgDump[6]=~/\(/;        $targetVGSpaceRemaining=$&#39;;        print &quot;MoveCeiling: $targetVG has $targetVGSpaceRemaining MB remaining, of which $preserveAmount MB will be preserved.\n\n&quot;;}sub mainRoutine(){        while(true)        {                @dfContents=split(/\s+/,`df -m | grep &quot; $targetFS\$&quot;`);                chomp($dateString=`date`);                print &quot;MoveCeiling: $dfContents[2] MB remaining in $targetFS as of $dateString.\n&quot;;                @lsvgDump=split(/\s+/,`lsvg -L $targetVG | head -n4 | tail -n1`);                $lsvgDump[6]=~/\(/;                $targetVGSpaceRemaining=$&#39;;                if($dfContents[2] &lt; $ceilingSize) # Tricky logic time..                {                        if($targetVGSpaceRemaining &gt;= ($preserveAmount+$incrementSize))                        {                                print &quot;MoveCeiling: Less than $ceilingSize MB remaining! Adding $incrementSize MB to $targetFS..\n&quot;;                                system(&quot;echo `date` -- MoveCeiling: Less than $ceilingSize MB remaining! Adding $incrementSize MB to $targetFS.. &gt;&gt;/tmp/moveceiling.log&quot;);                                `chfs -a size=+&quot;$incrementSize&quot;M $targetFS`;                                $incrementCounter++;                                if($incrementCounter==$notifyCounter)                                {                                        sendNotification();                                        $incrementCounter=0;                                }                        }                        else                        {                                system(&quot;echo `date` -- MoveCeiling: Preserve limit reached. Committing suicide. $targetFS last seen with $dfContents[2] MB free, $targetVGSpaceRemaiing MB remaining in $targetVG.  &gt;&gt;/tmp/moveceiling.log&quot;);                                print &quot;MoveCeiling: Increasing $targetFS by $incrementSize MB would eat into the preserved area! \n&quot;;                                print &quot;MoveCeiling: Preparing suicide note to $mailRecipient..\n&quot;;                                sendSuicideNote();                                print &quot;MoveCeiling: My hands are tied! I&#39;m sorry!\n&quot;;                                spinDownRoutine();                                exit();                        }                }               else                {                        print &quot;MoveCeiling: More than $ceilingSize MB remaining..No increase needed.\n&quot;;                }                print &quot;MoveCeiling: $targetVGSpaceRemaining MB remaining in $targetVG. Sleeping for $sleepTime seconds..\n\n&quot;;                sleep($sleepTime);        }}sub sendSuicideNote(){        %mail=( To =&gt; $mailRecipient,                From =&gt; $mailSender,                Subject =&gt; &quot;MoveCeiling Suicide Notice&quot;,                Message =&gt; &quot;Preserve limit reached -- Data loss may be imminent. $targetFS last seen with $dfContents[2] MB free, $targetVGSpaceRemaining MB remaining in $targetVG. );                sendmail(%mail);        print &quot;MoveCeiling: Suicide note sent.\n&quot;;}sub sendNotification(){        print &quot;MoveCeiling: Sending a friendly reminder to $mailRecipient..\n&quot;;        %mail=( To =&gt; $mailRecipient,                From =&gt; $mailSender,                Subject =&gt; &quot;MoveCeiling Update&quot;,                Message =&gt; &quot;$targetFS still growing. Now at $dfContents[2] MB in size with $targetVGSpaceRemaining MB remaining in $targetVG.&quot; );                sendmail(%mail);        print &quot;MoveCeiling: Mail sent.\n&quot;;}sub spinDownRoutine(){        print &quot;MoveCeiling: Spinning down..\n\n&quot;;}&lt;/pre&gt;
    </description>
</item>

        

<item>
    <title>startup scripts under Slackware (sh1tn)</title>
    <link>http://prlmnks.org/html/554670.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/554670.html</guid>

    <description>
        It&#39;s  because I get crazy when I have to type one milion times per day &lt;tt class=&quot;inline_code&quot;&gt;cd /etc/rc.d/--bla--bla--&lt;/tt&gt; or just execute the startup script with the absolute path which is even more exhaustive.&lt;pre class=&quot;block_code&quot;&gt;### daemon - lists and starts scripts from /etc/rc.d### may be useful under Slackwareuse strict;my $rc_dir = q|/etc/rc.d|;my @coms   = grep {-f &amp;&amp; -x} glob &quot;$rc_dir/*&quot;;my %coms;my $com;@coms =        grep{/.{2,}/}        map {s|.+/rc\.(.+?)\W*$|$1| and $_}@coms;$&quot; = $/ and die &quot;@coms&quot;, $/ unless @ARGV;$com = shift;@coms{@coms} = ();exists $coms{$com} or die qx{&quot;$rc_dir/rc.$com&quot;},$/;print for qx{&quot;$rc_dir/rc.$com&quot; &quot;@ARGV&quot;};&lt;/pre&gt;&lt;br&gt;__END__&lt;br&gt;#cp daemon.pl /usr/bin/daemon; chmod +x !$&lt;br&gt;#daemon sshd start (or restart or whatever args /etc/rc.d/rc.sshd script takes)&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>pass command line parameters (sombhotla)</title>
    <link>http://prlmnks.org/html/554247.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/554247.html</guid>

    <description>
        hi,i call a perl script passing some parameters to the script one of which being a another script name which also needs parameters.but when i pass the parameters to script which is to be called from the main script, the main script shows me some warnings.ex:perl a.pl -a 1 -b b.pl -a 2 -b 2 -c 1where the second a and b parameters are for the script b.pl and not a.pl, where as c is parameter to a.pl.Is there a way so that i can tell perl to take all this as single parameter.thanks for any help in advance
    </description>
</item>

        

<item>
    <title>Perl and multimedia (tbone1)</title>
    <link>http://prlmnks.org/html/553485.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/553485.html</guid>

    <description>
        I didn&#39;t do this myself, but &lt;A HREF=&quot;http://www.kusnetz.net/prius/&quot;&gt;a guy put a Mac Mini in his Prius&lt;/A&gt; to use as a multimedia center and used Perl to run the touch-screen for the thing. (To drive the multimedia center, not the car.) Now &lt;I&gt;this&lt;/I&gt; is a cool use of Perl! Maybe not as cool as doing this in Danica Patrick&#39;s Indy car, but cool nonetheless.&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>Translate military paygrades to rank salutations (girarde)</title>
    <link>http://prlmnks.org/html/553157.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/553157.html</guid>

    <description>
        This is a module that simplifies translating US Military Paygrades (E-1, W-3, O-4) into the proper salutation for that rank, given the internet domain of the service of interest (af, army, navy, usmc, uscg).  For generating mail in response to a form, it beats remembering.  It has some historical notes as well.&lt;pre class=&quot;block_code&quot;&gt;package Paygrades;require Exporter;our @ISA        = qw(Exporter);our @EXPORT     = qw(paygrade);our @EXPORT_OK  = qw(%navy %army %af %usmc);=historical notesSome of the paygrades listed below are no longer in use, the statutoryauthority for them having expired upon the death of GENA Bradley,others representing special conferrals on extraordinary commanders,and one established to ensure the seniority of George Washington toall United States military personnel, forever.  Additionally, theUnited States Air Force no longer has Warrant Officers.The first officer promoted to Fleet Admiral was Leahy, who wasPresident Roosevelt&#39;s Chief of Staff.  He was joined by Ernest King(Chief of Naval Operations), Chester Nimitz (CinC, Pacific OceanAreas) and William Halsey (COM7THFLT).The first officer promoted to General of the Army was George Marshall(Chief of Staff of the Army) joined by Douglas MacArthur (CinC,Southwest Pacific), Dwight Eisenhower (Supreme Commander, AlliedForces Europe) and Omar Bradley (US Army Europe).The only General of the Air Force to date was Harold &#39;Hap&#39; Arnold,Chief of Staff, Army Air Forces.Admiral of the Navy was conferred on George Dewey for the victory atManila Bay in the Spanish American War.General of the Armies was conferred on John &#39;Blackjack&#39; Pershing forhis command of the American Expeditionary Force during World War I. Itis said that George Marshall was very apprehensive about beingpromoted to five stars, for fear of offending him.George Washington is sui generis, and the perpetual General of theArmies of the United States of America.=cut%navy = (   &#39;E-1&#39;     =&gt; &#39;Seaman&#39;,            &#39;E-2&#39;     =&gt; &#39;Seaman&#39;,            &#39;E-3&#39;     =&gt; &#39;Seaman&#39;,            &#39;E-4&#39;     =&gt; &#39;Petty Officer&#39;,            &#39;E-5&#39;     =&gt; &#39;Petty Officer&#39;,            &#39;E-6&#39;     =&gt; &#39;Petty Officer&#39;,            &#39;E-7&#39;     =&gt; &#39;Chief&#39;,            &#39;E-8&#39;     =&gt; &#39;Senior Chief&#39;,            &#39;E-9&#39;     =&gt; &#39;Master Chief&#39;,            &#39;W-1&#39;     =&gt; &#39;Warrant Officer&#39;,            &#39;W-2&#39;     =&gt; &#39;Chief Warrant Officer&#39;,            &#39;W-3&#39;     =&gt; &#39;Chief Warrant Officer&#39;,            &#39;W-4&#39;     =&gt; &#39;Chief Warrant Officer&#39;,            &#39;W-5&#39;     =&gt; &#39;Chief Warrant Officer&#39;,            &#39;O-1&#39;     =&gt; &#39;Ensign&#39;,            &#39;O-2&#39;     =&gt; &#39;Lieutenant (j.g.)&#39;,            &#39;O-3&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-4&#39;     =&gt; &#39;Lieutenant Commander&#39;,            &#39;O-5&#39;     =&gt; &#39;Commander&#39;,            &#39;O-6&#39;     =&gt; &#39;Captain&#39;,            &#39;O-7&#39;     =&gt; &#39;Admiral&#39;,            &#39;O-8&#39;     =&gt; &#39;Admiral&#39;,            &#39;O-9&#39;     =&gt; &#39;Admiral&#39;,            &#39;O-10&#39;    =&gt; &#39;Admiral&#39;,            &#39;O-11&#39;    =&gt; &#39;Fleet Admiral&#39;,            &#39;O-12&#39;    =&gt; &#39;Admiral of the Navy&#39;);%usmc = (   &#39;E-1&#39;     =&gt; &#39;Private&#39;,            &#39;E-2&#39;     =&gt; &#39;PFC&#39;,            &#39;E-3&#39;     =&gt; &#39;Lance Corporal&#39;,            &#39;E-4&#39;     =&gt; &#39;Corporal&#39;,            &#39;E-5&#39;     =&gt; &#39;Sergeant&#39;,            &#39;E-6&#39;     =&gt; &#39;Staff Sergeant&#39;,            &#39;E-7&#39;     =&gt; &#39;Gunny&#39;,            &#39;E-8&#39;     =&gt; &#39;Master Sergeant&#39;,            &#39;E-9&#39;     =&gt; &#39;Master Guns&#39;,            &#39;W-1&#39;     =&gt; &#39;Gunner&#39;,            &#39;W-2&#39;     =&gt; &#39;Gunner&#39;,            &#39;W-3&#39;     =&gt; &#39;Gunner&#39;,            &#39;W-4&#39;     =&gt; &#39;Gunner&#39;,            &#39;W-5&#39;     =&gt; &#39;Gunner&#39;,            &#39;O-1&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-2&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-3&#39;     =&gt; &#39;Captain&#39;,            &#39;O-4&#39;     =&gt; &#39;Major&#39;,            &#39;O-5&#39;     =&gt; &#39;Lieutenant Colonel&#39;,            &#39;O-6&#39;     =&gt; &#39;Colonel&#39;,            &#39;O-7&#39;     =&gt; &#39;General&#39;,            &#39;O-8&#39;     =&gt; &#39;General&#39;,            &#39;O-9&#39;     =&gt; &#39;General&#39;,            &#39;O-10&#39;    =&gt; &#39;General&#39;);%army = (   &#39;E-1&#39;     =&gt; &#39;Private&#39;,            &#39;E-2&#39;     =&gt; &#39;Private&#39;,            &#39;E-3&#39;     =&gt; &#39;PFC&#39;,            &#39;E-4&#39;     =&gt; &#39;Corporal&#39;,            &#39;E-5&#39;     =&gt; &#39;Sergeant&#39;,            &#39;E-6&#39;     =&gt; &#39;Staff Sergeant&#39;,            &#39;E-7&#39;     =&gt; &#39;Sergeant&#39;,            &#39;E-8&#39;     =&gt; &#39;Master Sergeant&#39;,            &#39;E-9&#39;     =&gt; &#39;Sergeant Major&#39;,            &#39;W-1&#39;     =&gt; &#39;Chief&#39;,            &#39;W-2&#39;     =&gt; &#39;Chief&#39;,            &#39;W-3&#39;     =&gt; &#39;Chief&#39;,            &#39;W-4&#39;     =&gt; &#39;Chief&#39;,            &#39;W-5&#39;     =&gt; &#39;Chief&#39;,            &#39;O-1&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-2&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-3&#39;     =&gt; &#39;Captain&#39;,            &#39;O-4&#39;     =&gt; &#39;Major&#39;,            &#39;O-5&#39;     =&gt; &#39;Lieutenant Colonel&#39;,            &#39;O-6&#39;     =&gt; &#39;Colonel&#39;,            &#39;O-7&#39;     =&gt; &#39;General&#39;,            &#39;O-8&#39;     =&gt; &#39;General&#39;,            &#39;O-9&#39;     =&gt; &#39;General&#39;,            &#39;O-10&#39;    =&gt; &#39;General&#39;,            &#39;O-11&#39;    =&gt; &#39;General of the Army&#39;,            &#39;O-12&#39;    =&gt; &#39;General of the Armies&#39;,            &#39;O-13&#39;    =&gt; &#39;General of the Armies of the United States of America&#39;);%usaf = (   &#39;E-1&#39;     =&gt; &#39;Airman&#39;,            &#39;E-2&#39;     =&gt; &#39;Airman&#39;,            &#39;E-3&#39;     =&gt; &#39;Senior Airman&#39;,            &#39;E-4&#39;     =&gt; &#39;Sergeant&#39;,            &#39;E-5&#39;     =&gt; &#39;Staff Sergeant&#39;,            &#39;E-6&#39;     =&gt; &#39;Tech Sergeant&#39;,            &#39;E-7&#39;     =&gt; &#39;Master Sergeant&#39;,            &#39;E-8&#39;     =&gt; &#39;Senior Master Sergeant&#39;,            &#39;E-9&#39;     =&gt; &#39;Chief Master Sergeant&#39;,            &#39;W-1&#39;     =&gt; &#39;&#39;,            &#39;W-2&#39;     =&gt; &#39;&#39;,            &#39;W-3&#39;     =&gt; &#39;&#39;,            &#39;W-4&#39;     =&gt; &#39;&#39;,            &#39;W-5&#39;     =&gt; &#39;&#39;,            &#39;O-1&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-2&#39;     =&gt; &#39;Lieutenant&#39;,            &#39;O-3&#39;     =&gt; &#39;Captain&#39;,            &#39;O-4&#39;     =&gt; &#39;Major&#39;,            &#39;O-5&#39;     =&gt; &#39;Lieutenant Colonel&#39;,            &#39;O-6&#39;     =&gt; &#39;Colonel&#39;,            &#39;O-7&#39;     =&gt; &#39;General&#39;,            &#39;O-8&#39;     =&gt; &#39;General&#39;,            &#39;O-9&#39;     =&gt; &#39;General&#39;,            &#39;O-10&#39;    =&gt; &#39;General&#39;,            &#39;O-11&#39;    =&gt; &#39;General of the Air Force&#39;);sub paygrade {my ($title, $domain) = @_;my $paygrade;unless ($title =~ /(^W)|(^O)|(^E)/) {    return $title;#    print $title;}$domain = uc($domain);if ($domain =~ /NAVY/) {    $paygrade =  $navy{$title};}elsif ($domain =~ /USCG/) {    $paygrade =  $navy{$title};}elsif ($domain =~ /USMC/) {    $paygrade =  $usmc{$title};}elsif ($domain =~ /AF\.MIL/) {    $paygrade =  $usaf{$title};}elsif ($domain =~ /ARMY/) {    $paygrade =  $army{$title};}else {    $paygrade =  &quot;$army{$title}/$navy{$title}&quot;;}#print $paygrade;return $paygrade;}&lt;/pre&gt;&lt;p&gt;&lt;small&gt;2006-06-02 [id://340870|Retitled] by [planetscape], as per Monastery [id://341118|guidelines] &lt;readmore title=&quot;view votes&quot;&gt;( keep:0 edit:10 reap:1 ) &lt;br /&gt;Original title: &#39;Paygrades&#39;&lt;/small&gt;&lt;/p&gt;
    </description>
</item>

        

<item>
    <title>#perl.br calc - A Perl Shell (ruoso)</title>
    <link>http://prlmnks.org/html/552887.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/552887.html</guid>

    <description>
        &lt;P&gt;Using a trick by [node://fglock], and with the help of the guys at #perl.br (at freenode.org), here is the #perl.br calc, which I now use in place of bc...&lt;/P&gt;&lt;P&gt;Obviously, you&#39;ll see it&#39;s just a Perl Shell that have some calculator shortcuts, so, you can do whatever you want with it...&lt;/P&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;use warnings;use Term::ReadLine;my $name = &#39;#perl.br calc&#39;;print $name.$/;my $term = Term::ReadLine-&gt;new($name);$term-&gt;Features()-&gt;{&#39;autohistory&#39;} = 1;my $prompt = &quot;&gt; &quot;;my $OUT = $term-&gt;OUT || \*STDOUT;my $___eval_str___;my $M = 0;my $scale = 2;sub ler {        my $a = $term-&gt;readline($prompt);        unless (defined $a) {                print $/;                exit(0);        }        return $a;}sub substituir {        my $a = shift;        $a =~ s/^(.+)(\*\*)$/\$M $2 $1/;        $a =~ s/^(.+)(\+|\-|\*|\/)$/\$M $2 $1/;        $a =~ s/\;\s*$//;        if ($a =~ /^\s*do {\s*$/) {                my $block = &#39;&#39;;                my $count = 1;                while (defined($_=$term-&gt;readline(&#39; &#39;.(&#39;.&#39;x$count).&#39; &#39;))) {                        while ($_ =~ m/\{/g) {                                $count++;                        }                        while ($_ =~ m/\}/g) {                                $count--;                        }                        last if $count &lt; 1;                        $block .= $_;                }                $a = &#39;do {&#39;.$block.&#39;}&#39;;        }        return $a;}sub falar {        my $a = shift;        if ($@) {                print &quot;! &quot;.$@;        } else {                if (defined $a &amp;&amp; $a =~ /^[\d\.]$/) {                        $M=sprintf(&quot;%.&quot;.$scale.&quot;f&quot;,$a);                } elsif ($a) {                        $M=$a;                }                print $OUT &quot;= &quot;.$M.$/ if defined $M;        }}sub doit {        $___eval_str___ = &#39;eval(&quot;   falar(&quot;.substituir(ler()).&quot;);   $___eval_str___;&quot;);if($@){   print &quot;! &quot;.$@;   eval($___eval_str___)}&#39;;        eval $___eval_str___;}doit();&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>Football World Cup Group Stage Predictor. (perlmoth)</title>
    <link>http://prlmnks.org/html/552645.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/552645.html</guid>

    <description>
        With less than two weeks before the Football World Cup kicks off, the office sweep has appeared. I wrote the following script to help me with the group stage predictions. It makes its predictions based on the FIFA rankings:&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perluse strict;use warnings;# The following statistics are from# http://www.fifa.com/en/mens/statistics/index/0,2548,All-May-2006,00.htmlmy %FIFA_points =  (&quot;Brazil&quot;              =&gt; 827,   &quot;Czech Republic&quot;      =&gt; 772,   &quot;Holland&quot;             =&gt; 768,   &quot;Mexico&quot;              =&gt; 758,   &quot;Spain&quot;               =&gt; 756,   &quot;USA&quot;                 =&gt; 756,   &quot;Portugal&quot;            =&gt; 750,   &quot;France&quot;              =&gt; 749,   &quot;Argentina&quot;           =&gt; 746,   &quot;England&quot;             =&gt; 741,   &quot;Italy&quot;               =&gt; 728,   &quot;Sweden&quot;              =&gt; 709,   &quot;Japan&quot;               =&gt; 705,   &quot;Germany&quot;             =&gt; 696,   &quot;Tunisia&quot;             =&gt; 693,   &quot;Iran&quot;                =&gt; 686,   &quot;Croatia&quot;             =&gt; 686,   &quot;Costa Rica&quot;          =&gt; 683,   &quot;Poland&quot;              =&gt; 677,   &quot;South Korea&quot;         =&gt; 677,   &quot;Ivory Coast&quot;         =&gt; 669,   &quot;Paraguay&quot;            =&gt; 653,   &quot;Saudi Arabia&quot;        =&gt; 651,   &quot;Switzerland&quot;         =&gt; 648,   &quot;Ecuador&quot;             =&gt; 631,   &quot;Australia&quot;           =&gt; 612,   &quot;Serbia &amp; Montenegro&quot; =&gt; 610,   &quot;Ukraine&quot;             =&gt; 609,   &quot;Trinidad &amp; Tobago&quot;   =&gt; 604,   &quot;Ghana&quot;               =&gt; 600,   &quot;Angola&quot;              =&gt; 581,   &quot;Togo&quot;                =&gt; 569  );my %groups = (A =&gt; [&quot;Germany&quot;,     &quot;Costa Rica&quot;,     &quot;Poland&quot;,              &quot;Ecuador&quot;],  B =&gt; [&quot;England&quot;,     &quot;Paraguay&quot;,       &quot;Trinidad &amp; Tobago&quot;,   &quot;Sweden&quot;],  C =&gt; [&quot;Argentina&quot;,   &quot;Ivory Coast&quot;,    &quot;Serbia &amp; Montenegro&quot;, &quot;Holland&quot;],  D =&gt; [&quot;Mexico&quot;,      &quot;Iran&quot;,           &quot;Angola&quot;,              &quot;Portugal&quot;],  E =&gt; [&quot;USA&quot;,         &quot;Czech Republic&quot;, &quot;Italy&quot;,               &quot;Ghana&quot;],  F =&gt; [&quot;Australia&quot;,   &quot;Japan&quot;,          &quot;Brazil&quot;,              &quot;Croatia&quot;],  G =&gt; [&quot;South Korea&quot;, &quot;Togo&quot;,           &quot;France&quot;,              &quot;Switzerland&quot;],  H =&gt; [&quot;Spain&quot;,       &quot;Ukraine&quot;,        &quot;Tunisia&quot;,             &quot;Saudi Arabia&quot;] );my @combinations = ([0, 1], [2, 3], [0, 2], [3, 1], [3, 0], [1, 2]);for (&quot;A&quot; .. &quot;H&quot;) {  print STDERR &quot;\nGroup $_:\n&quot;;  my $group = $groups{$_};  foreach (@combinations) {    my $a = $group-&gt;[$_-&gt;[0]];    my $b = $group-&gt;[$_-&gt;[1]];    my $home_advantage = $FIFA_points{$a} - $FIFA_points{$b};    my $score = get_score($home_advantage);    print STDERR &quot;$a vs $b ($score)\n&quot;;  }}sub get_score {  my ($points_diff) = @_;  my $score = 0;  my $other = 0;  if (abs $points_diff &lt;= 30) {    $score = 0;  }  elsif (abs $points_diff &lt;= 80) {    $score = 1;  }  elsif (abs $points_diff &lt;= 130) {    $score = 2;  }  elsif (abs $points_diff &lt;= 200) {    $score = 3;  }  else {    $score = 4;  }  my $random_factor = rand;  if ($random_factor &gt; 0.9) {    $score += 2;    $other += 2;  }  elsif ($random_factor &lt;= 0.9 and $random_factor &gt; 0.75) {    $score += 1;    $other += 1;  }  if ($points_diff == abs $points_diff) {    return &quot;${score}-${other}&quot;;  }  else {    return &quot;${other}-${score}&quot;;  }}&lt;/pre&gt;&lt;br&gt;&lt;br&gt;Any comments or suggested improvements would be gratefully received.
    </description>
</item>

        

<item>
    <title>Show mbox files with unread mail messages in them (skx)</title>
    <link>http://prlmnks.org/html/552218.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/552218.html</guid>

    <description>
        A simple script which will process all the mbox files beneath a given directory root and show those which contain unread mail.&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w=head1 NAMEunread-mail - Display mbox files which contain unread messages.=head1 SYNOPSIS  unread-mail [options]  Help Options:   --help     Show this scripts help information.   --manual   Read this scripts manual.   --version  Show the version number and exit.   --verbose  Show verbose information useful for debugging problems.  General Options:   --dir      Specify the root mail directory to search beneath   --recent   Only process mbox files modified in the most recent N days.   --exclude  Comma-seperated list of mbox files to exclude from processing.=cut=head1 AUTHOR Steve -- http://www.steve.org.uk/ $Id: unread-mail,v 1.6 2006/05/28 21:50:13 steve Exp $=cut=head1 LICENSECopyright (c) 2005 by Steve Kemp.  All rights reserved.This module is free software;you can redistribute it and/or modify it underthe same terms as Perl itself.The LICENSE file contains the full text of the license.=cutuse strict;use File::Find;use Getopt::Long;use Mail::MboxParser;use Pod::Usage;##  Global options#my %CONFIG;##  Defaults##  Mail directory$CONFIG{&#39;maildir&#39;} = $ENV{&#39;HOME&#39;} . &quot;/mail&quot; if ( -d $ENV{&#39;HOME&#39;} . &quot;/mail&quot; );$CONFIG{&#39;maildir&#39;} = $ENV{&#39;HOME&#39;} . &quot;/Mail&quot; if ( -d $ENV{&#39;HOME&#39;} . &quot;/Mail&quot; );##  Exclusion list$CONFIG{&#39;exclude&#39;} = &quot;backup,build,logs,sent,pyzor,spam&quot;;##  Number of days.$CONFIG{&#39;time&#39;} = 2;##  Verbosity$CONFIG{&#39;verbose&#39;} = 0;##  Parse the arguments.#parseCommandLineArguments();##  Show unread mail#findUnreadMail( $CONFIG{&#39;maildir&#39;} );##  Finished#exit;=head2 parseCommandLineArguments  Process the command line arguments.=cutsub parseCommandLineArguments{    my $HELP= 0;    my $MANUAL= 0;    my $VERSION= 0;    #  Parse options.    #    GetOptions(       &quot;dir=s&quot;,        \$CONFIG{&#39;maildir&#39;},       &quot;exclude=s&quot;,    \$CONFIG{&#39;exclude&#39;},       &quot;recent=s&quot;,     \$CONFIG{&#39;time&#39;},       &quot;help&quot;,         \$HELP,       &quot;manual&quot;,       \$MANUAL,       &quot;version&quot;,      \$VERSION,       &quot;verbose&quot;,      \$CONFIG{&#39;verbose&#39;}      );    pod2usage(1) if $HELP;    pod2usage(-verbose =&gt; 2 ) if $MANUAL;    if ( $VERSION )    {my $REVISION      = &#39;$Revision: 1.6 $&#39;;if ( $REVISION =~ /1.([0-9.]+) / ){    $REVISION = $1;}print &quot;unread-mail CVS: $REVISION\n&quot;;exit;    }    if ( $CONFIG{&#39;verbose&#39;} )    {print &quot;Configuration:\n&quot;;foreach my $key ( sort keys( %CONFIG ) ){    print $key . &quot; =&gt; &#39;&quot; . $CONFIG{$key} . &quot;&#39;\n&quot;;}    }}=head2 wanted  Called as a result of the File::Find module.  Process every given file beneath the $CONFIG{&#39;maildir&#39;} root directory and look for unread mail.  Mailboxes contained in the $CONFIG{&#39;exclude&#39;} setting are ignored.=cutsub wanted{    my $file = $File::Find::name;    #    # We only care about files.    #    return if ( -d $file );    #    # Skip some mailboxes.    #    foreach my $exclude ( split( /,/, $CONFIG{&#39;exclude&#39;} ) )    {if ( $file =~ /$exclude/i ){    $CONFIG{&#39;verbose&#39;} &amp;&amp; print &quot;Excluded mail file: $file\n&quot;;    return;}    }    #    #  Skip files if they&#39;ve not been modified too recently.    #    my $time = -M $file;    if ( defined( $time ) )     {if ( $time &gt; $CONFIG{&#39;time&#39;} ){    $CONFIG{&#39;verbose&#39;} &amp;&amp; print &quot;Mail file not modified recently: $file\n&quot;;    return;}    }    else    {        $CONFIG{&#39;verbose&#39;} &amp;&amp; print &quot;Failed to stat: $file - $!\n&quot;;return;    }    my $parseropts = {      enable_cache    =&gt; 1,      enable_grep     =&gt; 1,      cache_file_name =&gt; &#39;/tmp/mail-cache&#39;,     };    my $mb = Mail::MboxParser-&gt;new( $file,    decode     =&gt; &#39;NONE&#39;,    parseropts =&gt; $parseropts );    my $count = 0;    for my $msg ($mb-&gt;get_messages)    {my $status = $msg-&gt;header-&gt;{status};if ( !defined( $status ) ){    $count += 1;}    }    if ( $CONFIG{&#39;verbose&#39;} )    {print &quot;$file ($count)\n&quot;;    }    else    {print &quot;$file ($count)\n&quot; if ($count &gt; 1 );    }}=head2 findUnreadMail  Look for files and directories beneath the maildir root and call &#39;wanted&#39; on them.=cutsub findUnreadMail{    my $dir = $CONFIG{&#39;maildir&#39;};    die &quot;$CONFIG{&#39;maildir&#39;} doesn&#39;t exist&quot; unless ( -d $CONFIG{&#39;maildir&#39;} );    $CONFIG{&#39;verbose&#39;} &amp;&amp; print &quot;Staring search for unread mail in: $dir\n&quot;;    #    #  Do the searching.    #    find( { wanted =&gt; \&amp;wanted, no_chdir =&gt; 1 }, $dir );    $CONFIG{&#39;verbose&#39;} &amp;&amp; print &quot;Finished search for unread mail in: $dir\n&quot;;}&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-194370&quot;&gt;&lt;a href=&quot;http://www.steve.org.uk/&quot;&gt;Steve&lt;/a&gt;&lt;br/&gt;-- &lt;br/&gt;&lt;/div&gt;&lt;/div&gt;
    </description>
</item>

        

<item>
    <title>Patchy pattern using a cellular automaton (ambrus)</title>
    <link>http://prlmnks.org/html/551051.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/551051.html</guid>

    <description>
        &lt;p&gt;This one-liner draws a patchy pattern using a simple game-of-life-like cellular automaton.  You can see the pattern evolving.  &lt;p&gt;If you use a larger terminal window, the pattern is larger as well, but the program gets slower.  In general, this program isn&#39;t written with speed in mind, it could be made much faster.&lt;pre class=&quot;block_code&quot;&gt;perl -we &#39;($H,$W)=`stty size`=~/^(\d+) (\d+)/?($1-1,$2):(24,80);$w=$W+20;@a = map { rand() &lt; 1/3 } 0 .. ($H+20)*$w; print &quot;\e[H\e[J&quot;; for (0 .. 30) { print &quot;\e[H&quot;; for (0 .. 1) { @a = map { $c = $_; $s = 0; $s += $a[($c + $_) % @a] for -$w-1, -$w, -$w+1, -1, 1, $w-1, $w, $w+1; $s &lt; 3 } 0 .. @a - 1; } for $y (10 .. $H + 9) { for $x (10 .. $W + 9) { print $a[$y*$w + $x] ? &quot;#&quot; : &quot; &quot;; } print &quot;\n&quot;; } }&#39;&lt;/pre&gt;
    </description>
</item>

        

<item>
    <title>Create and edit new scripts (johngg)</title>
    <link>http://prlmnks.org/html/545414.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/545414.html</guid>

    <description>
        To save myself some typing I have written a small script to make the creation of new Perl scripts simpler. I called it newscript (it does what it says on the tin) and you run it with an argument of the script name you want to create. It checks that the file does not already exist then creates a new file with execute permissions and places&lt;p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl#use strict;use warnings;&lt;/pre&gt;&lt;p&gt;at the top so I have no excuse for not using strict and warnings. It then asks the user if they would like to start editing the new file; the script sets up a timeout for the user to answer so that it doesn&#39;t sit there waiting forever. The editor I prefer to use is &lt;tt class=&quot;inline_code&quot;&gt;nedit&lt;/tt&gt; but you could substitute your favourite in &lt;tt class=&quot;inline_code&quot;&gt;$editor&lt;/tt&gt;. If your preference is something like &lt;tt class=&quot;inline_code&quot;&gt;vi&lt;/tt&gt; then you could start an &lt;tt class=&quot;inline_code&quot;&gt;xterm&lt;/tt&gt; with &lt;tt class=&quot;inline_code&quot;&gt;vi&lt;/tt&gt; as it&#39;s command to run. This is rather *nix-centric but that&#39;s mostly what I use. Here&#39;s the script. I hope that it is of interest.&lt;p&gt;&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl#use strict;use warnings;# Get modules, set autoflush on STDOUT.#use IO::File;use Term::ReadKey;STDOUT-&gt;autoflush(1);# Get name of script we want to create, die if it already# exists.#my $newfile = shift or   die &quot;Usage: newscript &lt;filename&gt;\n&quot;;die &quot;newscript: $newfile already exists\n&quot; if   -e $newfile or -l $newfile;# Make new IO::File handle to create script file with execute# permissions, die on failure.#my $scriptFH = IO::File-&gt;new($newfile,   O_WRONLY|O_CREAT|O_TRUNC, 0755);die &quot;newscript: open: $!\n&quot; unless $scriptFH;# Set up hash-bang line and strictures for top of script, write# them to the new file and then close.#my $hashBang =   &quot;#!/usr/bin/perl\n#\n\nuse strict;\nuse warnings;\n\n&quot;;$scriptFH-&gt;print($hashBang);$scriptFH-&gt;close();# Set up editor command, prompts asking user if they want to# edit the new script, and a time-out value of five seconds.#my $editor = &quot;/usr/local/bin/nedit&quot;;our $prompt1 = &quot;Start editing $newfile? (&quot;;our $prompt2 = &quot;) (y/n) : &quot;;our $timeOut = 5;# Set up subroutine reference that will be used as the handler# for $SIG{ALRM}.#our $rcCountdown = sub{    # If there is still time left, print prompt (decrementing    # time-out value) and set alarm for one second.    #    if($timeOut)    {        print &quot;\r&quot;, $prompt1, $timeOut --, $prompt2;alarm(1);    }    # Time is up, restore read mode and die.    #    else    {        ReadMode(0);die &quot;Timed out\n&quot;    }};# Install handler, call handler for the first time to prompt# and set alarm.#$SIG{ALRM} = $rcCountdown;$rcCountdown-&gt;();# Set read mode to raw and await a key press.#ReadMode(4);my $resp = ReadKey(0);# If we got here we got a key press, reset read mode, unset# alarm and print a newline to move off prompt.#ReadMode(0);alarm(0);print &quot;\n&quot;;# If key pressed was &#39;y&#39; then start the editor in the# background via exec, new editor window will appear and# shell will prompt for next command.#if($resp =~ /^y$/i){    exec &quot;$editor $newfile &amp;&quot; or       die &quot;Couldn&#39;t invoke $editor: $!\n&quot;;}# User doesn&#39;t want to edit so just exit.#exit;&lt;/pre&gt;&lt;p&gt;Cheers,&lt;p&gt;JohnGG
    </description>
</item>

        

<item>
    <title>Parse and Summarise Big Brother logs (McDarren)</title>
    <link>http://prlmnks.org/html/543818.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/543818.html</guid>

    <description>
        At my workplace we use &lt;a href=&quot;http://www.bb4.org/&quot;&gt;Big Brother&lt;/a&gt; for Network Monitoring. We have dispensed with the default BB web interface and replaced it with a home-grown Perl/CGI interface...&lt;p&gt;This is &lt;b&gt;good&lt;/b&gt; because:&lt;p&gt;&lt;ul&gt;&lt;li&gt;It is more suited to our specific needs. That is, we&#39;ve added lots of funky CGI options to allow sorting/filtering/etc.&lt;li&gt;It is more &quot;real-time&quot;. The default BB interface is basically a static HTML page, which is re-generated every 5 minutes. Whereas our CGI interface is generated every time the page is loaded.&lt;/ul&gt;&lt;p&gt;However, the 2nd point above is a double-edged sword. Because with around 15 tests running on 500-odd hosts - that&#39;s around 7500 files that must be parsed &lt;b&gt;every&lt;/b&gt; time the page is loaded. And when there are 15-20 users constantly hitting the page - things start to get a bit bogged down.&lt;p&gt;So as a compromise, I decided to write a separate script that would parse all of the BB logs, and summarise them into a single file. And then modify the CGI script so that instead of reading all 7500 logs, it just reads the single summary file.&lt;p&gt;The summarising script is invoked via crond every minute, and takes less than a second to run. The script is shown below. There is nothing particularly clever about it, but in our case it has proved quite effective. So I thought it may be useful for others.&lt;pre class=&quot;block_code&quot;&gt;#!/usr/bin/perl -w# parse_bblogs.pl# Darren - 15th Feb 2006## Simple script to parse and summarise Big Brother logs# Would generally be invoked via crond# Will produce a summary of all logs found in $logs_dir#  and write to $outfile in the following format:##  hostname:test:status:duration#use strict;use Fcntl qw(:DEFAULT :flock);use Readonly;use Log::Trivial;################################### Adjust the following to suit# regex to match the filename format of the bb logsReadonly my $LOGFILE_MATCH =&gt; qr/-\d\./;my $logs_dir = qw( /home/bb/bbvar/logs );my $outfile = qw( /home/bb/bb/www/bbstatus.current );my $debug = 0;################################### Should be no need to change anything below here# Create a new logfile each daychomp(my $today = `date +%Y-%m-%d`);my $logfile = qq(/var/log/bb/$0-$today);my $logger = Log::Trivial-&gt;new(        log_file =&gt; &quot;$logfile&quot;,        log_mode =&gt; &quot;single&quot;        );opendir(DIR, $logs_dir)   or die &quot;Cannot opendir $logs_dir:$!&quot;;my @files = grep { /$LOGFILE_MATCH/ &amp;&amp; -f &quot;$logs_dir/$_&quot; } readdir DIR;closedir DIR;# Need an exclusive lock on the output file# (Assuming of course, that everybody else is playing by the rules :)sysopen(OUT, $outfile, O_WRONLY | O_CREAT)   or die &quot;Cannot open $outfile for writing:$!\n&quot;;flock(OUT, LOCK_EX)   or die &quot;Cannot get a lock on $outfile:$!\n&quot;;truncate(OUT, 0)   or die &quot;Cannot truncate $outfile:$!\n&quot;;FILE:for my $file (@files) {    debug(&quot;Processing $file&quot;) if $debug;    # Because the logs are automagically created by BB,    # we can be quite strict about the format we expect to see    # ie: hostname.test    my ($host, $test) = split(/\./, $file, 2)        or $logger-&gt;write(&quot;WARNING: Skipping unrecognised logfile: $file&quot;)        and next FILE;    open BBLOG, &quot;&lt;&quot;, &quot;$logs_dir/$file&quot;        or $logger-&gt;write(&quot;ERROR: I could not open $file: $!&quot;)        and next FILE;    chomp(my @lines = &lt;BBLOG&gt;);    # The test status should ALWAYS be the first &quot;word&quot; on the first line    # And the status duration should ALWAYS be contained on the 2nd last line    debug($lines[0], $lines[-2]) if $debug;    my ($status) = ($lines[0] =~ /^([a-z]+)/);    my ($dur) = ($lines[-2] =~ /Status unchanged in (.*)$/);    if (!defined $status &amp;&amp; !defined $dur) {        $logger-&gt;write(&quot;WARNING: Skipping malformed logfile: $file&quot;);        next FILE;    }    close BBLOG;    # If we get to here, all is good and so we write to the output file    print OUT &quot;$host:$test:$status:$dur\n&quot;;}close OUT;sub debug {    $logger-&gt;write(&quot;DEBUG: $_&quot;) for @_;}&lt;/pre&gt;&lt;p&gt;As always, any pointers for improving the script, making it more efficient, less brain-dead, etc. are most welcome.&lt;p&gt;Cheers,&lt;br&gt;Darren :)
    </description>
</item>

        

<item>
    <title>winamp lyrics script (rush3k)</title>
    <link>http://prlmnks.org/html/543599.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/543599.html</guid>

    <description>
        Started learning Perl two weeks ago .. this is my first script .. it reads current song playing on Winamp and displays lyrics from the &lt;a href=&quot;http://www.azlyrics.com/&quot;target=&quot;_blank&quot;&gt;azlyrics&lt;/a&gt; website on the DOS terminal.However its dependent on &lt;a href=&quot;http://httpq.sourceforge.net/&quot;target=&quot;_blank&quot;&gt;httpq&lt;/a&gt; installation ... is there a way I can get the current song playing information from Windows?&lt;pre class=&quot;block_code&quot;&gt;use strict;use Winamp::Control;use WWW::Mechanize;use warnings;my $agent = Winamp::Control -&gt; new (host =&gt; &#39;127.0.0.1&#39;, port =&gt; &#39;4800&#39;, passwd =&gt; &#39;pass&#39;);my $playing = $agent-&gt;getcurrenttitle();my $artistsong = $agent-&gt;getcurrenttitle();   $artistsong =~ s/\d.*?\s//s;my $firstletter = $artistsong;   $firstletter = substr($artistsong,0,1);my $artistname = $artistsong;   $artistname =~ s/ -.*//s;   $artistname =~ tr/a-z/A-Z/;   my $songtitle = $artistsong;   $songtitle =~ s/.*- //s;   my $lyrics = WWW::Mechanize -&gt; new (autocheck =&gt; 1);   $lyrics -&gt; get(&#39;http://www.azlyrics.com/&#39;);   $lyrics -&gt; follow_link(text =&gt; $firstletter, n =&gt; 1);   $lyrics -&gt; follow_link(text =&gt; $artistname, n =&gt; 1);   $lyrics -&gt; follow_link(text =&gt; $songtitle, n =&gt; 1);my $content1 = $lyrics -&gt; content(format =&gt; &quot;text&quot;);   print &quot;$artistname&quot;;print &quot;$songtitle \n&quot;;print $content1;&lt;/pre&gt;      
    </description>
</item>

        

<item>
    <title>Redirecting output across Oracle DB link (jeremyh)</title>
    <link>http://prlmnks.org/html/543446.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/543446.html</guid>

    <description>
        To be used in conjunction with Grant McLean&#39;s Sprog::PrintProxy&lt;p&gt;Sends output for a given file handle to Oracle&#39;s utl_file.put()&lt;p&gt;You invoke it like this:&lt;p&gt;&lt;pre class=&quot;block_code&quot;&gt;use DBI;use Sprog::PrintProxy;use DBPrinter;my $dbh = DBI-&gt;connect(&quot;dbi:Oracle:&quot;, &quot;uid&quot;, &quot;passwd&quot;)           or die &quot;$!:  Can&#39;t connect to DB&quot;;my $fh = Sprog::PrintProxy-&gt;new( DBPrinter-&gt;new($dbh, &quot;/some/directory&quot;, &quot;outfile.txt&quot;, &quot;dblinkname&quot;) );&lt;/pre&gt;&lt;p&gt;Now all output to $fh will end up in /some/directory/outfile.txt on the server where dblinkname runs.  You can also simply output to a directory seen by the DB from your DBI connection by leaving out the dblinkname parameter.  Can&#39;t think of a reason this would be useful, but there might be.&lt;p&gt;Note:  Oracle requires that utl_file output directories be declared in init.ora, and the directory in the utl_file call must be &quot;spelled&quot; exactly the same as the directory listed in init.ora.&lt;p&gt;here&#39;s the code&lt;pre class=&quot;block_code&quot;&gt;# ============================================================================#  DBPrinter##  Intercepts output to tie()&#39;ed filehandle and writes it to Oracle#  UTL_FILE.FILE_TYPE filehandle.  Allows perl output to be redirected#  from one server to another over a DB link.  Max length per write (line)#  is 2000 chars.##  Jeremy Hickerson, 4/13/2006# ============================================================================package DBPrinter;use strict;use DBI;# ============================================================================#                           Package level vars# ============================================================================my (%glb_prepared_sql, $sth_pls_write_DBfh, $DBH, $DB_fh, $Filedir, $Filename,    $DB_linkname);# Constantsmy $SQL_CHAR             = 1;my $SQL_NUMERIC          = 2;my $SQL_DECIMAL          = 3;my $SQL_INTEGER          = 4;my $SQL_SMALLINT         = 5;my $SQL_FLOAT            = 6;my $SQL_REAL             = 7;my $SQL_DOUBLE           = 8;my $SQL_DATE             = 9;my $SQL_TIME            = 10;my $SQL_TIMESTAMP       = 11;my $SQL_VARCHAR         = 12;my $SQL_LONGVARCHAR     = -1;my $SQL_BINARY          = -2;my $SQL_VARBINARY       = -3;my $SQL_LONGVARBINARY   = -4;my $SQL_BIGINT          = -5;my $SQL_TINYINT         = -6;my $SQL_BIT             = -7;my $SQL_WCHAR           = -8;my $SQL_WVARCHAR        = -9;my $SQL_WLONGVARCHAR   = -10;# ============================================================================#                             Constructor# ============================================================================sub new {    my ($class, $self);    ($class, $DBH, $Filedir, $Filename, $DB_linkname) = @_;    if ($DB_linkname) { $DB_linkname = &quot;\@$DB_linkname&quot; }    else { $DB_linkname = &quot;&quot; }    open_DBfh();    $self = { };    bless($self, $class);    return $self;}# ============================================================================#                              Methods# ============================================================================sub print {    my ($class, $data) = @_;    # from Oracle&#39;s utl_file package:    #    # FILE_TYPE - File handle    #     # TYPE file_type IS RECORD (id BINARY_INTEGER);    # we can populate/save id field of outfile with a bind var    # for performance we will only prepare repeated sql once    if (exists $glb_prepared_sql{&quot;sth_pls_write_DBfh&quot;} ) {    $sth_pls_write_DBfh = $glb_prepared_sql{&quot;sth_pls_write_DBfh&quot;};    }    else {    $sth_pls_write_DBfh = $DBH-&gt;prepare( qq{    DECLARE        outfile          sys.UTL_FILE.FILE_TYPE$DB_linkname;    BEGIN        outfile.id := :DB_fh;        sys.utl_file.put$DB_linkname(outfile, :data);    END; } );    $sth_pls_write_DBfh-&gt;bind_param_inout(&quot;:DB_fh&quot;, \$DB_fh, $SQL_BINARY);    $glb_prepared_sql{&quot;sth_pls_write_DBfh&quot;} = $sth_pls_write_DBfh;    }    $sth_pls_write_DBfh-&gt;bind_param_inout(&quot;:data&quot;, \$data, $SQL_VARCHAR);    $sth_pls_write_DBfh-&gt;execute();}sub open_DBfh {    my $sth_pls_open_DBfh = $DBH-&gt;prepare( qq{DECLARE    outfile              sys.UTL_FILE.FILE_TYPE$DB_linkname;    MAX_LINESIZE         integer := 2000;BEGIN    outfile := sys.utl_file.fopen$DB_linkname(:Filedir, :Filename, &#39;w&#39;, MAX_LINESIZE);    :DB_fh  := outfile.id;END; } );$sth_pls_open_DBfh-&gt;bind_param_inout(&quot;:DB_fh&quot;,    \$DB_fh,    $SQL_BINARY);$sth_pls_open_DBfh-&gt;bind_param_inout(&quot;:Filename&quot;, \$Filename, $SQL_VARCHAR);$sth_pls_open_DBfh-&gt;bind_param_inout(&quot;:Filedir&quot;,  \$Filedir,  $SQL_VARCHAR);$sth_pls_open_DBfh-&gt;execute();}sub close_DBfh {    my $sth_pls_close_DBfh = $DBH-&gt;prepare( qq{DECLARE    outfile              sys.UTL_FILE.FILE_TYPE$DB_linkname;BEGIN    outfile.id := :DB_fh;    sys.utl_file.fclose$DB_linkname(outfile);END; } );    $sth_pls_close_DBfh-&gt;bind_param_inout(&quot;:DB_fh&quot;,    \$DB_fh,    $SQL_BINARY);    $sth_pls_close_DBfh-&gt;execute();}# ============================================================================#                                 Destructor# ============================================================================sub DESTROY {    %glb_prepared_sql = ();    close_DBfh();}1;&lt;/pre&gt;
    </description>
</item>

        

<item>
    <title>PerlMonks Editor (GrandFather)</title>
    <link>http://prlmnks.org/html/543242.html</link>
    <guid isPermaLink="true">http://prlmnks.org/html/543242.html</guid>

    <description>
        &lt;p&gt;This is the first cut of a PerlMonks offline wysiwyg editor. It may be used to wysiwig edit material for posting on PerlMonks and generates the common HTML and PerlMonks special tags used in PerlMonks node markup.&lt;/p&gt;&lt;p&gt;The code uses Tk and should be reasonably cross platform.&lt;/p&gt;&lt;p&gt;This first cut is missing a lot of functionality but is being posted to garner initial reactions and make it available to the curious (I&#39;m off on holiday for a few days and won&#39;t be able to work on it for another week).&lt;/p&gt;&lt;p&gt;Important missing stuff includes - no readmore handling, save/open partially implemented (and disabled), keyboard accelerators not working (and disabled), much markup not hooked up.&lt;/p&gt;&lt;readmore title=&quot;the code&quot;&gt;&lt;pre class=&quot;block_code&quot;&gt;use strict;use warnings;use diagnostics;use Tk;#use Tk::TextUndo;use Tk::Balloon;use Tk::Clipboard;use Tk::FBox;use Clone qw(clone);=head TodoProvide file open and saveManage B, F, I, P, R and U flagsTranslate entities (note U flag)Handle accelerator keysSupport snippet text (with formatting)Disallow relinkCheckmarks for menushandle tableshook up keysAdd Edit menu to access find and replace etc=cutuse constant kParaSpace =&gt; 4;my $currentFile = &#39;&#39;;my %tagTypes;       # Style tag datamy %formatFonts;    # Fonts used in style tags. Keyed by tagmy %bindings;       # Key, menu and toolbar bindings. Tag is valuemy %menuItems;      # Child menu widgets keyed by menu label pathmy %entities =      # Entities we need to use outside code blocks    (    &#39;&amp;&#39;, &#39;&amp;amp;&#39;,    &#39;&lt;&#39;, &#39;&amp;lt;&#39;,    &#39;&gt;&#39;, &#39;&amp;gt;&#39;,    &#39;[&#39;, &#39;&amp;#91;&#39;,    &#39;]&#39;, &#39;&amp;#93;&#39;,    );my @stdFlags = (    &#39;B&#39;, # Block level element    &#39;C&#39;, # Clear all or specified tags: C or Ctag (note lower case)    &#39;F&#39;, # Format tag (inline element)    &#39;I&#39;, # Item in a list. Implies B    &#39;L&#39;, # Link    &#39;P&#39;, # Applies to whole paragraph    &#39;R&#39;, # Readmore text    &#39;S&#39;, # Single spaced text    &#39;U&#39;, # Untranslated - don&#39;t translate entities    &#39;X&#39;, # Exclude all or specified tags: X or Xtag (note lower case)    );while (&lt;DATA&gt;) {    # Load the default configuration stuff    chomp;    next if ! length;    last if /^#key /;    next if /^#/;        my ($tag, $htmlTag, $name, $flagsField, @options) = split /\s*,\s*/;    (print &quot;Missing entries in tag line ($.): $_&quot;), next if ! defined $flagsField;    # pull out flags and handle X and C special case flags    my %flags;    @flags{@stdFlags} = (0) x @stdFlags; # Preset flags off    $flags{&#39;C&#39;} = {};    $flags{&#39;X&#39;} = {};    for (split /(?=[A-Z][a-z]*)/, $flagsField) {        my ($flag, $value) = split /(?&lt;=[A-Z])/, $_;                print &quot;Unhandled flag &#39;$flag&#39; used\n&quot; if ! exists $flags{$flag};        if (-1 != index &#39;XC&#39;, $flag) {            $flags{$flag}{$value || &#39;ALL&#39;} = 1;            $flags{&#39;C&#39;}{$value || &#39;ALL&#39;} = 1 if $flag eq &#39;X&#39;; # X implies C        } else {            $flags{$flag} = $value || 1;            $flags{&#39;B&#39;} = $value || 1 if $flag eq &#39;I&#39;;        }    }    #Fix up options    my $optionStr = join &#39;, &#39;, @options;    my %optionHash;        while ($optionStr =~ /\G,?\s*((?:(?!=&gt;).)*)=&gt;\s*(\[[^\]]*\]|[^,]*),?\s*/g) {        my ($option, $value) = ($1, $2);                trim (\$option, \$value);                if ($value =~ s/\[|\]//g) {            # Nested options. Turn them into a hash            my @options = split &#39;,&#39;, $value;            my %optionHash;                        for (@options) {                my ($suboption, $subvalue) = split /\s*=&gt;\s*/;                                last if ! defined $subvalue;                trim (\$suboption, \$subvalue);                $optionHash{$suboption} = $subvalue;            }                        $value = \%optionHash;        }                $optionHash{$option} = $value;    }        $tagTypes{$tag} = [$htmlTag, $name, \%flags, \%optionHash];}while (&lt;DATA&gt;) {    # Load key binding information    next if /^#/;    chomp;    next if ! length;        my ($tag, $key, $menuItem, $toolbarItem, $rightClickItem) = split /\s*,\s*/;    (print &quot;Missing tag in binding line ($.): $_&quot;), next if ! defined $tag;    $bindings{$tag} = [$key, $menuItem, $toolbarItem, $rightClickItem];}my $mw = MainWindow-&gt;new (-title =&gt; &quot;PerlMonks node editor&quot;);my $text = $mw-&gt;Scrolled    (&#39;Text&#39;, -font =&gt; &#39;normal&#39;, -wrap =&gt; &#39;word&#39;, -scrollbars =&gt; &#39;e&#39;,);my $status = $mw-&gt;Label(-width =&gt; 60, -relief =&gt; &quot;sunken&quot;, -bd =&gt; 1, -anchor =&gt; &#39;w&#39;);my $balloon = $mw-&gt;Balloon(-statusbar =&gt; $status);my $msg = &#39;&#39;;my $balloonCharIndex = &#39;&#39;;my $balloonLastIndex = &#39;&#39;;$status-&gt;pack(-side =&gt; &quot;bottom&quot;, -fill =&gt; &quot;both&quot;, -padx =&gt; 2, -pady =&gt; 1);#$balloon-&gt;attach#    (#    $text, -msg =&gt; \$msg,#    -balloonposition =&gt; &#39;mouse&#39;,  # Not really used since the postcommand returns the real position.#    -postcommand =&gt; \&amp;balloonPostCommand,#    -motioncommand =&gt; \&amp;balloonMotionCommand,#    );my $menuBar = $mw-&gt;Menu (-type =&gt; &#39;menubar&#39;);$mw-&gt;configure(-menu =&gt; $menuBar);$text-&gt;pack (-expand =&gt; &#39;yes&#39;, -fill =&gt; &#39;both&#39;);# Build file menu$menuItems{&#39;~File&#39;} = $menuBar-&gt;cascade(-label =&gt; &#39;~File&#39;, -tearoff =&gt; 0);$menuItems{&#39;~File&#39;}-&gt;command (-label =&gt; &#39;~Render&#39;, -command =&gt; \&amp;fileRender);#$menuItems{&#39;~File&#39;}-&gt;command (-label =&gt; &#39;~Open...&#39;, -command =&gt; \&amp;fileOpen);#$menuItems{&#39;~File&#39;}-&gt;command (-label =&gt; &#39;~Save&#39;, -command =&gt; \&amp;fileSave);#$menuItems{&#39;~File&#39;}-&gt;command (-label =&gt; &#39;Save ~As...&#39;, -command =&gt; \&amp;fileSaveAs);$menuItems{&#39;~File&#39;}-&gt;command (-label =&gt; &#39;E~xit&#39;, -command =&gt; \&amp;fileExit);# Build menus and bind keysfor my $tag (keys %bindings) {    my $menuPath = $bindings{$tag}[1];    next if ! defined $menuPath;    my ($top, $item) = split &#39;/&#39;, $menuPath;        next if ! defined $item;    if (! defined $menuItems{$top}) {        $menuItems{$top} = $menuBar-&gt;cascade(-label =&gt; $top, -tearoff =&gt; 0);    }        my $newItem = $menuItems{$top}-&gt;command        (-label =&gt; $item, -command =&gt; [\&amp;doCommand, $tag]);        if (defined $bindings{$tag}[0]) {        #Set up accelerator bindings                #my $key = $bindings{$tag}[0];        #        #$mw-&gt;bind (&quot;&lt;$key&gt;&quot; =&gt; [\&amp;keyCommand, $tag]);        #        #$key =~ s/^Control/ctrl/;        #$newItem-&gt;configure (-accelerator =&gt; $key);    }}$menuItems{&#39;~Help&#39;} = $menuBar-&gt;cascade(-label =&gt; &#39;~Help&#39;, -tearoff =&gt; 0);$menuItems{&#39;~Help&#39;}-&gt;command (-label =&gt; &#39;~PerlMonks Editor Help&#39;, -command =&gt; \&amp;help);$menuItems{&#39;~Help&#39;}-&gt;command (-label =&gt; &#39;~About&#39;, -command =&gt; \&amp;about);# A couple of phantom paragraph spacing tags to ease calculating paragraph spacing$text-&gt;tagConfigure(&quot;!para_start&quot;, -spacing1 =&gt; 0, -spacing3 =&gt; -(kParaSpace));$text-&gt;tagConfigure(&quot;!para_end&quot;, -spacing1 =&gt; -(kParaSpace), -spacing3 =&gt; 0);$text-&gt;insert (&#39;end&#39;, &quot;Some text to play with.\n&quot;, &#39;!para&#39;);$text-&gt;insert (&#39;end&#39;, &quot;Some more text to play with. Some more text to play with.\n&quot;, &#39;!para&#39;);#$mw-&gt;bind (&quot;&lt;$key&gt;&quot; =&gt; [\&amp;keyCommand, $tag]);MainLoop ();sub balloonPostCommand {    return 0 if ! length $balloonCharIndex;        my %balloonCharTags;    my  $charIndex = $text-&gt;index (&quot;$balloonCharIndex +1 char&quot;);        @balloonCharTags{$text-&gt;tagNames()} = ($balloonCharIndex);        # If no tags under mouse don&#39;t post the balloon.    return 0 if ! %balloonCharTags;        if (exists $balloonCharTags{name}) {        my ($start, $end) = $text-&gt;tagPrevrange (&#39;name&#39;, $balloonCharIndex);        my $name = $text-&gt;get($start, $end);                $name =~ s/\|.*//;        $msg = &quot;link to [${name}]&#39;s home node&quot;;    } elsif (exists $balloonCharTags{node}) {        my ($start, $end) = $text-&gt;tagPrevrange (&#39;node&#39;, $balloonCharIndex);        my $node = $text-&gt;get($start, $end);                $node =~ s/\|.*//;        $msg = &quot;link to node id $node&quot;;        $msg .= &#39; (badly formed - digits only allowed)&#39; if $node !~ /^\d+$/;    } else {        return 0;    }        my @p = $text-&gt;bbox($balloonCharIndex);    my $x = $text-&gt;rootx + $p[0] + $p[2] - 4;    my $y = $text-&gt;rooty + $p[1] + $p[3] + 2;    print &quot;-$x,$y-\n&quot;;    return &quot;$x,$y&quot;;}sub balloonMotionCommand {    my $x = $text-&gt;pointerx - $text-&gt;rootx;    my $y = $text-&gt;pointery - $text-&gt;rooty;        $balloonCharIndex = $text-&gt;index (&quot;\@$x,$y&quot;);    # If the same char don&#39;t cancel the balloon.    return 0 if $balloonLastIndex eq $balloonCharIndex;        # New char under mouse - cancel it so a new balloon will be posted.    $balloonLastIndex = $balloonCharIndex;    print &quot;&gt;$balloonLastIndex&lt;\n&quot;;    return 1;}sub fileRender {    $text-&gt;clipboardClear ();    $text-&gt;clipboardAppend (render ());}sub fileOpen {    $currentFile = $text-&gt;FBox(-type =&gt; &#39;open&#39;, -filter =&gt; &#39;*.PMEdit&#39;)-&gt;Show;        open inFile, &#39;&lt;&#39;, $currentFile or        $text-&gt;messageBox            (            -title =&gt; &#39;Save failed&#39;, -icon =&gt; &#39;error&#39;,            -type =&gt; &#39;Ok&#39;,            -message =&gt; &quot;Unable to open &#39;$currentFile&#39; - $!&quot;            );    my ($html, $name, $mode, $params);               while (&lt;inFile&gt;) {        my ($type, $index, $item) = /(\S+)\s(\S+)\s(.*)/;                if ($type =~ /^tago(?:n|ff)$/) {            next if $item =~ /^_/;            ($html, $name, $mode, $params) = @{$tagTypes{$item}};        }        if ($type eq &#39;-tagon&#39;) {            print outFile &quot;-tagon $item $index\n&quot;;        } elsif ($type eq &#39;-tagoff&#39;) {            print outFile &quot;-tagoff $item $index\n&quot;;        } elsif ($type eq &#39;-text&#39;) {            print outFile &quot;-text $item\n&quot;;        } else {            print &quot;Token type $type at $index not handled.\n&quot;;        }    }        close inFile;}sub fileSave {    if (defined $currentFile and length $currentFile) {        doSave ($currentFile);    } else {        fileSaveAs ();    }}sub fileSaveAs {    my $filename = $text-&gt;FBox(-type =&gt; &#39;save&#39;, -filter =&gt; &#39;*.PMEdit&#39;)-&gt;Show;    doSave ($filename);}sub doSave {    my $filename = shift;        return if ! defined $filename or ! length $filename;        open outFile, &#39;&gt;&#39;, $filename or        $text-&gt;messageBox            (            -title =&gt; &#39;Save failed&#39;, -icon =&gt; &#39;error&#39;,            -type =&gt; &#39;Ok&#39;,            -message =&gt; &quot;Unable to create &#39;$filename&#39; - $!&quot;            );    my @dumpText = $text-&gt;dump (&#39;-tag&#39;, &#39;-text&#39;, &#39;start&#39;, &#39;end&#39;);    my ($html, $name, $mode, $params);        while (@dumpText) {        my ($type, $item, $index) = splice @dumpText, 0, 3;                my $segEnd = exists $dumpText[2] ? $dumpText[2] : &#39;end&#39;;        if ($type =~ /^tago(?:n|ff)$/) {            next if $item =~ /^_/;            ($html, $name, $mode, $params) = @{$tagTypes{$item}};        }        if ($type eq &#39;tagon&#39;) {            print outFile &quot;-tagon $index $item\n&quot;;        } elsif ($type eq &#39;tagoff&#39;) {            print outFile &quot;-tagoff $index $item\n&quot;;        } elsif ($type eq &#39;text&#39;) {            print outFile &quot;-text - $item\n&quot;;        } else {            print &quot;Token type $type at $index not handled.\n&quot;;        }    }        close outFile;    $currentFile = $filename;}sub fileExit {    exit 1;}sub render {    my $result;    my $paragraph;    my $inCode = 0;    my @dumpText = $text-&gt;dump (&#39;-tag&#39;, &#39;-text&#39;, &#39;1.0&#39;, &#39;end&#39;);    my ($html, $name, $mode, $params);        while (@dumpText) {        my ($type, $item, $index) = splice @dumpText, 0, 3;                next if $item =~ m&#39;^(?:sel|para)&#39;;                my $segEnd = exists $dumpText[2] ? $dumpText[2] : &#39;end&#39;;        if ($type =~ /^tago(?:n|ff)$/) {            next if $item =~ /^(?:_|!)/;            ($html, $name, $mode, $params) = @{$tagTypes{$item}};        }                    if ($type eq &#39;tagon&#39;) {            if ($mode-&gt;{&#39;L&#39;}) {                my ($linkCode) = $html =~ /^\S*\s*(.*)/;                $paragraph .= &quot;[$linkCode&quot;;                next;            }            $inCode = 1 if $item eq &#39;code&#39;;            $paragraph .= &quot;&lt;$tagTypes{$item}[0]&gt;&quot;;        } elsif ($type eq &#39;tagoff&#39;) {            if ($mode-&gt;{&#39;L&#39;}) {                $paragraph .= &#39;]&#39;;                next;            }                        $paragraph .= &quot;&lt;/$tagTypes{$item}[0]&gt;&quot;;            if ($item eq &#39;code&#39;) {                $inCode = 0;            } else {            }        } elsif ($type eq &#39;text&#39;) {            $paragraph .= $item;            if ($paragraph =~ /\n/) {                if ($inCode) {                    $result .= $paragraph;                } else {                    $paragraph =~ tr/\n//d;                    if ($paragraph eq &#39;&lt;/pre&gt;&#39;) {                        $result .= $paragraph;                        $paragraph = &#39;&#39;;                    }                                        $result .= &quot;&lt;p&gt;$paragraph&lt;/p&gt;\n&quot;;                }                                $paragraph = &#39;&#39;;            }        } else {            print &quot;Token type $type at $index not handled.\n&quot;;        }    }        $result =~ s|&lt;p&gt;&lt;/p&gt;|&lt;br&gt;|g;    return $result;}sub keyCommand {    &amp;doCommand ();}sub doCommand {    my %newTag = (tag =&gt; shift);    my @selections = $text-&gt;tagRanges(&#39;sel&#39;);    @newTag{&#39;name&#39;, &#39;html&#39;, &#39;flags&#39;, &#39;params&#39;} = @{$tagTypes{$newTag{tag}}};            do {        if (@selections) {            my %tags;            @tags{$text-&gt;tagNames($selections[0])} = (); # Preset current tags            $newTag{isOn} = ! exists $tags{$newTag{tag}}; # Complement new tag&#39;s curr state            $tags{$newTag{tag}} ||= $newTag{isOn};                        @newTag{&#39;start&#39;, &#39;end&#39;} = splice @selections, 0, 2;        } else {            my %activeTags;            @activeTags{$text-&gt;tagNames(&#39;insert&#39;)} = ();            return if ! exists $activeTags{$newTag{tag}};            @newTag{&#39;start&#39;, &#39;end&#39;} = $text-&gt;tagPrevrange ($newTag{tag}, &#39;insert&#39;);            $newTag{isOn} = 0;        }                return if ! defined $newTag{end};                my $msg = $newTag{flags}{L} ? manageLink (%newTag) : updateTextTags (%newTag);        if (length $msg) {            $status-&gt;configure (-text =&gt; $msg);            return;        }            } while (@selections);}sub updateTextTags {    my %newTag = @_;    my @dumpText = $text-&gt;dump (&#39;-tag&#39;, &#39;-text&#39;, $newTag{start}, $newTag{end});    my @activeTags = $text-&gt;tagNames($newTag{start});    my %tags;        @tags{@activeTags} = (1) x @activeTags; # Preset current tags    $tags{$newTag{tag}} = $newTag{isOn};    TOKEN: while (@dumpText) {        my ($type, $item, $index) = splice @dumpText, 0, 3;        my $segEnd = exists $dumpText[2] ? $dumpText[2] : $newTag{end};        if ($type eq &#39;tagon&#39;) {            $tags{$item} = 1 if $item ne $newTag{tag};        } elsif ($type eq &#39;tagoff&#39;) {            $tags{$item} = 0 if $item ne $newTag{tag};        } elsif ($type eq &#39;text&#39;) {            my @tagList = grep {! /^_|^sel$/ &amp;&amp; $tags{$_}} keys %tags;            my @removeList = grep {! $tags{$_} || /^_/} keys %tags;            # Bail if current tags preclude new tag            for (@tagList) {                next if ! exists $tagTypes{$_} or $newTag{tag} eq $_;                my ($Ihtml, $Iname, $Iflags, $Iparams) = @{$tagTypes{$_}};                                # Check for existing tag that precludes all new tags                if ($Iflags-&gt;{&#39;X&#39;}{&#39;ALL&#39;}) {                    next TOKEN                }                                # Check for existing tag that precludes $newTag                if ($Iflags-&gt;{&#39;X&#39;}{$newTag{tag}}) {                    next TOKEN;                }            }            if ($newTag{isOn}) {                if ($newTag{flags}-&gt;{&#39;C&#39;}{&#39;ALL&#39;}) {                    # Strip all other tags                    push @removeList, @tagList;                } elsif (%{$newTag{flags}-&gt;{&#39;C&#39;}}) {                    # Clear specific tags                    push @removeList, keys %{$newTag{flags}-&gt;{&#39;C&#39;}};                }                push @tagList, $newTag{tag};            }            $text-&gt;tagRemove ($_, $index, $segEnd) for @removeList;                        @tagList = buildTag (@tagList);            $text-&gt;tagAdd ($_, $index, $segEnd) for @tagList;            fixParaSpacing ($index);        } else {            print &quot;Token type $type at $index not handled.\n&quot;;        }    }        return &#39;&#39;;}sub manageLink {    my %newTag = @_;    my @activeTags = $text-&gt;tagNames($newTag{start});    my %tags;        if (! $newTag{isOn}) {        # Remove the link        $text-&gt;tagRemove ($newTag{tag}, $newTag{start}, $newTag{end});        updateTextTags (%newTag);        return &#39;&#39;;    }        @tags{@activeTags} = (1) x @activeTags; # Preset current tags    for (keys %tags) {        next if ! exists $tagTypes{$_};        return 1 if $newTag{tag} eq $_ and $newTag{isOn}; # Link already                my ($Ihtml, $Iname, $Iflags, $Iparams) = @{$tagTypes{$_}};        return &quot;Can&#39;t link inside $Iname&quot; if $Iflags-&gt;{&#39;X&#39;}{&#39;ALL&#39;};        return &quot;Can&#39;t link inside $Iname&quot; if $Iflags-&gt;{&#39;X&#39;}{&#39;link&#39;};    }        return &#39;Links must not span line ends.&#39;        if int ($newTag{start}) != int ($newTag{end});            # Get the link text    my $orgLinkText = $text-&gt;get($newTag{start}, $newTag{end});    my ($linkStr, $textStr) = $orgLinkText =~ /^([~|]*\|?)(.*)/;    my $indexStr = &quot;$newTag{start} +&quot; . length ($linkStr) . &#39;chars&#39;;    my $linkEnd = $text-&gt;index ($indexStr);    my %linkTag = %{clone (\%newTag)};    my %textTag = %{clone (\%newTag)};    $linkTag{end} = $linkEnd;    $textTag{start} = $linkEnd;        updateTextTags (%linkTag);    updateTextTags (%textTag);    return &#39;&#39;;}sub buildTag {    my %tags;        @tags{@_} = ();        my @tagList = sort keys %tags;    my $newFormatTag = &#39;_&#39; . join &#39;_&#39;, @tagList;    my %options;    my %fontParams;        for (@tagList) {        next if ! exists $tagTypes{$_} || ! ref $tagTypes{$_};        my ($html, $name, $mode, $params) = @{$tagTypes{$_}};        next if ! ref $params;                for my $type (keys %$params) {            if ($type =~ /-font/) {                for my $subType (keys %{$params-&gt;{$type}}) {                    $fontParams{$subType} = $params-&gt;{$type}{$subType};                }            } else {                $options{$type} = $params-&gt;{$type};            }        }    }        $options{-font} = buildFont (%fontParams) if %fontParams;    $text-&gt;tagConfigure ($newFormatTag, %options);    push @tagList, $newFormatTag;    return @tagList;}sub buildFont {    my %options = @_;    my $fontName = &#39;&#39;;        $fontName .= &quot;$_|$options{$_},&quot; for sort keys %options;    $fontName =~ tr/-+/mp/;    $fontName =~ tr/a-zA-Z0-9/mp_/c;    $mw-&gt;fontCreate($fontName, %options) if ! $formatFonts{$fontName}++;    return $fontName;}sub fixParaSpacing {    my $lastLine = ($text-&gt;index (&#39;end&#39;) =~ /(\d+)/)[0];    my $lastTailSpace = -(kParaSpace);    my @paraTags;        push @paraTags, &quot;!para_$_&quot; for (1..$lastLine);    $text-&gt;tagDelete (@paraTags); # Clear current spacing tags        for my $line (1..$lastLine) {        my $headSpace = kParaSpace;        my $tailSpace = kParaSpace;        my @activeTags = $text-&gt;tagNames(&quot;$line.0&quot;);                # Note that this is currently broken if the first character happens to be a        # part of a single spaced style applied to a partial line        for (@activeTags) {            next if ! exists $tagTypes{$_} || ! ref $tagTypes{$_};                my ($html, $name, $mode, $params) = @{$tagTypes{$_}};            next if ! ref $params;                for my $type (keys %$params) {                $headSpace = $params-&gt;{$type} if $headSpace &amp;&amp; $type =~ /-spacing1/;                $tailSpace = $params-&gt;{$type} if $tailSpace &amp;&amp; $type =~ /-spacing3/;            }        }        if ($lastTailSpace == -(kParaSpace)) {            $headSpace = 0;        } elsif ($lastTailSpace == 0 &amp;&amp; $headSpace &gt; 0) {            $headSpace += kParaSpace;        } elsif ($lastTailSpace &gt; 0 &amp;&amp; $headSpace == 0) {            $headSpace += kParaSpace;        }        $text-&gt;tagConfigure(&quot;!para_$line&quot;, -spacing1 =&gt; $headSpace, -spacing3 =&gt; $tailSpace);        $text-&gt;tagAdd (&quot;!para_$line&quot;, &quot;$line.0&quot;);        $text-&gt;tagRaise (&quot;!para_$line&quot;);        $lastTailSpace = $tailSpace;    }}sub trim {    for (@_) {        $$_ =~ s/^\s+//;        $$_ =~ s/\s+$//;    }}sub help {    my $msg = &lt;&lt;MSG;This editor is designed to provide wysiwyg editing for PerlMonks.org nodes. Thecontents of the node is edited off-line and rendered (File|Render) to theclipboard for pasting into a node&#39;s text edit field.Feedback can be /msged to GrandFather in the first instance. If you provide anemail address in your /msg, GrandFather will most likely reply to the emailaddress.MSG    $mw-&gt;messageBox (        -icon =&gt; &#39;info&#39;,        -message =&gt; $msg, -title =&gt; &#39;PerlMonks Editor Help&#39;,        -type =&gt; &#39;Ok&#39;,        );}sub about {    my $msg = &lt;&lt;MSG;PerlMonks EditorWritten by GrandFather for the assistance, pleasure and edification of othermonks.MSG    $mw-&gt;messageBox (        -icon =&gt; &#39;info&#39;,        -message =&gt; $msg, -title =&gt; &#39;About PerlMonks Editor&#39;,        -type =&gt; &#39;Ok&#39;,        );}__DATA__#tag style definitions#tag name,HTML tag, UI text, flags, modifiers as key value pairsbig,big,Big font,F,-font =&gt; [-size =&gt; 16]bold,b,Bold,F,-font =&gt; [-weight =&gt; bold]center,center,Centered text,P,code,code,Code block,BFXCU,-spacing1 =&gt; 0,-spacing3 =&gt; 0,-background =&gt; #e0e0ff,-font =&gt; [-family =&gt; courier, -weight =&gt; bold]cpan,link id://,CPAN link,L, -background =&gt; #c0c0c0, -foreground =&gt; #40e040,dd,dd,Definition Description,B,del,del,Deleted Text,F,dl,dl,Definition List,B,-lmargin1 =&gt; 20m, -lmargin2 =&gt; 20m, -rmargin =&gt; 20mdt,dt,Definition Term,B,-lmargin1 =&gt; 10m, -lmargin2 =&gt; 10m, -rmargin =&gt; 10m, -font =&gt; [-weight =&gt; bold]emphasis,em,Emphasis,F,-font =&gt; [-weight =&gt; bold]h3,h3,Header level 3,B,-font =&gt; [-size =&gt; 24], -background =&gt; #c0c0c0,-spacing1 =&gt; 14h4,h4,Header level 4,B,-font =&gt; [-size =&gt; 24], -background =&gt; #8080c0,-spacing1 =&gt; 10h5,h5,Header level 5,B,-font =&gt; [-size =&gt; 16], -background =&gt; #c0c0c0,-spacing1 =&gt; 10h6,h6,Header level 6,B,-font =&gt; [-size =&gt; 16], -background =&gt; #8080c0,-spacing1 =&gt; 8hrule,hr,Horizontal rule,BX,inserted,ins,ins,BF, -background =&gt; #ffffc0,italic,i,Italic,F,-font =&gt; [-slant =&gt; italic]item,li,List item,I,olist,ol,Ordered list,B,-lmargin1 =&gt; 20m, -lmargin2 =&gt; 20m, -rmargin =&gt; 20mquote,blockquote,Quoted block,P,-lmargin1 =&gt; 15m,-lmargin2 =&gt; 15m,-rmargin =&gt; 15mreadmore,readmore,Read more block,BR,-background =&gt; #a0b7cesmall,small,small,F,-font =&gt; [-size =&gt; 8]spoiler,spoiler,Spoiler,F, -background =&gt; #000000, -foreground =&gt; #404040,strike,strike,Strike Out,F,-overstrike =&gt; onstrong,strong,Strong emphasis,F,sub,sub,Sub script,FCsuper,-offset =&gt; -2p,-font =&gt; [-size =&gt; 8]super,sup,Super script,FCsub,-offset =&gt; 4p,-font =&gt; [-size =&gt; 8]teletype,Teletype text,tt,F,-font =&gt; [-family =&gt; courier], -background =&gt; #FFFFc0ulist,ul,Unordered list,B,-lmargin1 =&gt; 20m, -lmargin2 =&gt; 20m, -rmargin =&gt; 20munderline,u,Underline,F,[-underline =&gt; on]],#links - still tag style definitionsacronym,link acronym://,Acronym link,L, -background =&gt; #f0f0f0, -foreground =&gt; #0060c0,cpan,link cpan://,Cpan link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,dict,link dict://,Dictionary link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,dist,link dist://,CPAN Distro link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,doc,link doc://,perldoc link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,ftp,link ftp://,Ftp link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,google,link google://,Google link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,href,link href://,Href link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,http,link http://,Http link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,https,link https://,Https link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,id,link id://,Node id link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,isbn,link isbn://,Isbn link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,jargon,link jargon://,Jargon link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,kobes,link kobes://,Kobes link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,lj,link lj://,Live journal link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,lucky,link lucky://,Google lucky link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,mod,link mod://,Mod link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,module,link module://,Module link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,name,link,Node name link,L, -background =&gt; #f0f0f0, -foreground =&gt; #0060c0,pad,link pad://,Scratchpad link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,perldoc,link perldoc://,Perldoc link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,pmdev,link pmdev://,Pmdev link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,wp,link wp://,Wp link,L, -background =&gt; #f0f0f0, -foreground =&gt; #00a0a0,#key bindings, menu items and tool bar items#tag,key,menu item,toolbar item,right click itembig,Control 2,Format/Big,,Bigbold,Control Shift b,Format/Bold,,Bolditalic,Control i,Format/Italic,,Italicstrike,Control s,Format/Strike out,,Strike outsub,Control u,Format/Subscript,,Subscriptsuper,Control s,Format/Superscript,,Superscriptcode,Control c,Format/Code,,Codeid,,Links/Node,,Node id linkname,,Links/Name,,Name link&lt;/c&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>

    </channel>
</rss>
