Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid

Comment on

by gods
on Feb 11, 2000 at 00:06 UTC ( #3333=superdoc: print w/replies, xml ) Need Help??

I'm a big fan of the Tao te Ching, and after finding a free version (called 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.

I personally use this in a cron job to change my /etc/motd message.

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 'license.txt'), and store a YAML cache of that work. If you'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 'taote-v4.html' -- when put in the exec directory, the app will prefer that local copy.

--today causes the program to print the next chapter of the TTC if it's not the same day as the last time it was run.

--chapter lets you get a specific chapter (see usage for details)

To clean up and start fresh, use the --reset parameter.

Run without parameters or with '--usage', '--help', '-h', or '-?' for a usage message.

Depends on CPAN modules YAML-Syck (fast libsyck) or YAML (slower, pure-perl), and HTML-TokeParser-Simple (for parsing the HTML version of GNL). The last dependency is only needed for parsing the HTML document -- it is not needed once tao.yaml exists.

#!/usr/local/bin/perl use strict; use warnings; BEGIN { # Try to load YAML::Syck (faster, preferred), fall back to YAML othe +rwise # thanks to PerlMonks' shmem for pointing out potential YAML::Syck t +rouble eval { require YAML::Syck; YAML::Syck->import( qw[LoadFile DumpFile] ); }; if ($@) { # fall back to YAML require YAML; YAML->import( qw[LoadFile DumpFile] ); } } use Getopt::Long; # how do we want to operate? usage() unless @ARGV; GetOptions( 'help|usage|?|h' => \&usage, 'reset' => sub { for (<*.yaml>) { unlink while -f } }, 'today' => \&daily_tao, 'chapter' => \&my_tao, 'license' => sub { print join('',<DATA>); exit; }, ); #===================================================================== +=== # load_tao - loads or regenerates cache of tao chapters, returns as li +st sub load_tao { if ( -f 'tao.yaml') { # load chapters from disk cache, if we have them return @{ LoadFile('tao.yaml') }; } 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 'day.yaml' 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 'day.yaml' ) { ($day_index, $time) = @{ LoadFile('day.yaml') } } my @now = localtime($now); my @last = localtime($time); if ( $last[7] != $now[7] || $last[5] != $now[5]) { # it's not the same day as last we ran $day_index++; if ($day_index > $#chapter) { $day_index = 0 } # roll over $time = $now; # we will record the *current* time } printf "%s: %s\n%s", @{ $chapter[$day_index] }; DumpFile('day.yaml', [ $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 numbe +r # with a paramter ending in '.', shows record with that chap. + id # Dies if there'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 "$chapter[$_][0]." eq $chapter; } die "$chapter is not a valid chapter spec\n" if "$chapter[$chindex][0]." ne $chapter; $chapter = $chindex; } elsif ($chapter =~ /\D/) { # doesn't look like a spec, but isn't just a number - invalid die "'$chapter' is neither a valid spec nor a valid numeric id\n"; } die "The last record is no.$#chapter, but you asked for no.$chapter\ +n" if $chapter > $#chapter; printf "%s: %s\n%s", @{$chapter[$chapter]}; } #===================================================================== +=== # get_web_tao - retrieves the "GNL" tao te ching from the web and # creates the disk cache of the chapters in 'tao.yaml' # if a local file 'taote-v4.html' exists, will use that # instead of fetching from the web sub get_web_tao { require HTML::TokeParser::Simple; my $p = HTML::TokeParser::Simple->new( -f 'taote-v4.html' ?(file => 'taote-v4.html') :(url => ' +v4.html') ); # skip notices and such, but record them to license.txt my $LIC; while (my $token = $p->get_token) { last if $token->is_end_tag('h2'); if ($token->is_start_tag('pre')) { # start of license text and copyright notice open $LIC, '>license.txt' or die ("Can't write license.txt: $!") +; } elsif ($token->is_end_tag('pre')) { # end of license text and copyright notice close $LIC; undef $LIC; } elsif ($token->is_text && defined $LIC) { # body text for license and copyright print $LIC $token->as_is; } } # add chapters to data struct in form [id, title, text] my @chapter; while (my $token = $p->get_token) { next unless $token->is_start_tag('h3'); #title start (my $title = $p->peek(1)) =~ s{^(.+?)\.\s}{}g; my $num = $1; my $text = ''; #finish end of title while ($token = $p->get_token) { last if $token->is_end_tag('h3') +} #grab text *as* text while ($token = $p->get_token) { last if $p->peek =~ /<h3>/i; next if $token->is_tag; $text.=$token->as_is; } push @chapter, [ $num, $title, $text ]; } DumpFile('tao.yaml', \@chapter); return \@chapter; } #===================================================================== +=== # usage - prints a nice usage message and exits the app sub usage { print <<USAGE_DOC; Daily Tao te Ching chapter printer. Uses the GNL version of the Tao Te Ching (TTC) from: See 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 recor +d id (0-81) or by chapter number followed by a period. For ex +ample, to see TTC chapter 64b, use --chapter 64b. (including th +e period). Displays the last chapter if an invalid chapte +r 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 st +art at the beginning of the TTC and fetch a new copy from th +e web (or from the local file taote-v4.html) --license Display the license for this application (license for th +e 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 ( Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or s +ell 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 include +d in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRES +S OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILIT +Y, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHAL +L THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISIN +G 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))


  • 2004-09-22 : shmem noted that YAML::Syck might be problematic for some. Updated code to prefer YAML::Syck, but fall back on YAML if Syck is not installed.

A collection of thoughts and links from the minds of geeks
The Code that can be seen is not the true Code
I haven't found a problem yet that can't be solved by a well-placed trebuchet

In reply to Get your Daily Tao Te Ching fix by radiantmatrix

Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":

  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?

    What's my password?
    Create A New User
    and the web crawler heard nothing...

    How do I use this? | Other CB clients
    Other Users?
    Others examining the Monastery: (3)
    As of 2016-08-30 21:54 GMT
    Find Nodes?
      Voting Booth?
      The best thing I ever won in a lottery was:

      Results (423 votes). Check out past polls.