<?xml version="1.0" encoding="windows-1252"?>
<node id="574265" title="Get your Daily Tao Te Ching fix" created="2006-09-21 16:56:42" updated="2006-09-21 12:56:42">
<type id="1042">
CUFP</type>
<author id="375088">
radiantmatrix</author>
<data>
<field name="doctext">
&lt;p&gt;I'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 '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.

&lt;p&gt;--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.

&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 '--usage', '--help', '-h', or '-?' 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='dailytao.pl'&gt;
&lt;code&gt;
#!/usr/local/bin/perl
use strict;
use warnings;

BEGIN {
  # Try to load YAML::Syck (faster, preferred), fall back to YAML otherwise
  # thanks to PerlMonks' 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(
  'help|usage|?|h' =&gt; \&amp;usage,
  'reset'          =&gt; sub { for (&lt;*.yaml&gt;) { unlink while -f } },
  'today'          =&gt; \&amp;daily_tao,
  'chapter'        =&gt; \&amp;my_tao,
  'license'        =&gt; sub { print join('',&lt;DATA&gt;); exit; },
);

#========================================================================
# load_tao - loads or regenerates cache of tao chapters, returns as list
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 &gt; $#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 number
#          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 &gt; $#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-&gt;new( -f 'taote-v4.html' 
    ?(file =&gt; 'taote-v4.html')
    :(url =&gt; 'http://acc6.its.brooklyn.cuny.edu/~phalsall/texts/taote-v4.html')
  );
  
  # 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('h2');
    if ($token-&gt;is_start_tag('pre')) {
      # start of license text and copyright notice
      open $LIC, '&gt;license.txt' or die ("Can't write license.txt: $!");
    }
    elsif ($token-&gt;is_end_tag('pre')) {
      # 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('h3'); #title start
    (my $title = $p-&gt;peek(1)) =~ s{^(.+?)\.\s}{}g;
    my $num = $1;
    my $text = '';
    
    #finish end of title
    while ($token = $p-&gt;get_token) { last if $token-&gt;is_end_tag('h3') }
    
    #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('tao.yaml', \@chapter);
  return \@chapter;
}

#========================================================================
# usage - prints a nice usage message and exits the app
sub 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.html
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 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 "Software"), 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 "AS IS", 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;/code&gt;
&lt;/readmore&gt;


&lt;p&gt;&lt;small&gt;&lt;b&gt;Updates:&lt;/b&gt;&lt;ul type='square'&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="pmsig"&gt;&lt;div class="pmsig-375088"&gt;
&lt;small&gt;
&lt;small&gt;&lt;font color='#000000'&gt;&amp;lt;&lt;/font&gt;&lt;font color='#1a1a1a'&gt;&amp;ndash;&lt;/font&gt;&lt;font color='#343434'&gt;r&lt;/font&gt;&lt;font color='#4e4e4e'&gt;a&lt;/font&gt;&lt;font color='#686868'&gt;d&lt;/font&gt;&lt;font color='#828282'&gt;i&lt;/font&gt;&lt;font color='#9c9c9c'&gt;a&lt;/font&gt;&lt;font color='#b6b6b6'&gt;n&lt;/font&gt;&lt;font color='#d0d0d0'&gt;t&lt;/font&gt;&lt;font color='#eaeaea'&gt;.&lt;/font&gt;&lt;font color='#d0d0d0'&gt;m&lt;/font&gt;&lt;font color='#b6b6b6'&gt;a&lt;/font&gt;&lt;font color='#9c9c9c'&gt;t&lt;/font&gt;&lt;font color='#828282'&gt;r&lt;/font&gt;&lt;font color='#686868'&gt;i&lt;/font&gt;&lt;font color='#4e4e4e'&gt;x&lt;/font&gt;&lt;font color='#343434'&gt;&amp;ndash;&lt;/font&gt;&lt;font color='#1a1a1a'&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='http://radiantmatrix.org/'&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't found a problem yet that can'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;</field>
</data>
</node>
