home *** CD-ROM | disk | FTP | other *** search
/ PC World 2003 March / PCWorld_2003-03_cd.bin / Software / Topware / activeperl / ActivePerl / Perl / lib / Locale / Maketext.pm < prev    next >
Encoding:
Text File  |  2002-06-19  |  22.2 KB  |  676 lines

  1.  
  2. # Time-stamp: "2001-06-21 23:09:33 MDT"
  3.  
  4. require 5;
  5. package Locale::Maketext;
  6. use strict;
  7. use vars qw( @ISA $VERSION $MATCH_SUPERS $USING_LANGUAGE_TAGS
  8.              $USE_LITERALS);
  9. use Carp ();
  10. use I18N::LangTags 0.21 ();
  11.  
  12. #--------------------------------------------------------------------------
  13.  
  14. BEGIN { unless(defined &DEBUG) { *DEBUG = sub () {0} } }
  15.  # define the constant 'DEBUG' at compile-time
  16.  
  17. $VERSION = "1.03";
  18. @ISA = ();
  19.  
  20. $MATCH_SUPERS = 1;
  21. $USING_LANGUAGE_TAGS = 1;
  22.  # Turning this off is somewhat of a security risk in that little or no
  23.  # checking will be done on the legality of tokens passed to the
  24.  # eval("use $module_name") in _try_use.  If you turn this off, you have
  25.  # to do your own taint checking.
  26.  
  27. $USE_LITERALS = 1 unless defined $USE_LITERALS;
  28.  # a hint for compiling bracket-notation things.
  29.  
  30. my %isa_scan = ();
  31.  
  32. ###########################################################################
  33.  
  34. sub quant {
  35.   my($handle, $num, @forms) = @_;
  36.  
  37.   return $num if @forms == 0; # what should this mean?
  38.   return $forms[2] if @forms > 2 and $num == 0; # special zeroth case
  39.  
  40.   # Normal case:
  41.   # Note that the formatting of $num is preserved.
  42.   return( $handle->numf($num) . ' ' . $handle->numerate($num, @forms) );
  43.    # Most human languages put the number phrase before the qualified phrase.
  44. }
  45.  
  46.  
  47. sub numerate {
  48.  # return this lexical item in a form appropriate to this number
  49.   my($handle, $num, @forms) = @_;
  50.   my $s = ($num == 1);
  51.  
  52.   return '' unless @forms;
  53.   if(@forms == 1) { # only the headword form specified
  54.     return $s ? $forms[0] : ($forms[0] . 's'); # very cheap hack.
  55.   } else { # sing and plural were specified
  56.     return $s ? $forms[0] : $forms[1];
  57.   }
  58. }
  59.  
  60. #--------------------------------------------------------------------------
  61.  
  62. sub numf {
  63.   my($handle, $num) = @_[0,1];
  64.   if($num < 10_000_000_000 and $num > -10_000_000_000 and $num == int($num)) {
  65.     $num += 0;  # Just use normal integer stringification.
  66.          # Specifically, don't let %G turn ten million into 1E+007
  67.   } else {
  68.     $num = CORE::sprintf("%G", $num);
  69.      # "CORE::" is there to avoid confusion with the above sub sprintf.
  70.   }
  71.   while( $num =~ s/^([-+]?\d+)(\d{3})/$1,$2/s ) {1}  # right from perlfaq5
  72.    # The initial \d+ gobbles as many digits as it can, and then we
  73.    #  backtrack so it un-eats the rightmost three, and then we
  74.    #  insert the comma there.
  75.  
  76.   $num =~ tr<.,><,.> if ref($handle) and $handle->{'numf_comma'};
  77.    # This is just a lame hack instead of using Number::Format
  78.   return $num;
  79. }
  80.  
  81. sub sprintf {
  82.   no integer;
  83.   my($handle, $format, @params) = @_;
  84.   return CORE::sprintf($format, @params);
  85.     # "CORE::" is there to avoid confusion with myself!
  86. }
  87.  
  88. #=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
  89.  
  90. use integer; # vroom vroom... applies to the whole rest of the module
  91.  
  92. sub language_tag {
  93.   my $it = ref($_[0]) || $_[0];
  94.   return undef unless $it =~ m/([^':]+)(?:::)?$/s;
  95.   $it = lc($1);
  96.   $it =~ tr<_><->;
  97.   return $it;
  98. }
  99.  
  100. sub encoding {
  101.   my $it = $_[0];
  102.   return(
  103.    (ref($it) && $it->{'encoding'})
  104.    || "iso-8859-1"   # Latin-1
  105.   );
  106.  
  107. #--------------------------------------------------------------------------
  108.  
  109. sub fallback_languages { return('i-default', 'en', 'en-US') }
  110.  
  111. sub fallback_language_classes { return () }
  112.  
  113. #--------------------------------------------------------------------------
  114.  
  115. sub fail_with { # an actual attribute method!
  116.   my($handle, @params) = @_;
  117.   return unless ref($handle);
  118.   $handle->{'fail'} = $params[0] if @params;
  119.   return $handle->{'fail'};
  120. }
  121.  
  122. #--------------------------------------------------------------------------
  123.  
  124. sub failure_handler_auto {
  125.   # Meant to be used like:
  126.   #  $handle->fail_with('failure_handler_auto')
  127.  
  128.   my($handle, $phrase, @params) = @_;
  129.   $handle->{'failure_lex'} ||= {};
  130.   my $lex = $handle->{'failure_lex'};
  131.  
  132.   my $value;
  133.   $lex->{$phrase} ||= ($value = $handle->_compile($phrase));
  134.  
  135.   # Dumbly copied from sub maketext:
  136.   {
  137.     local $SIG{'__DIE__'};
  138.     eval { $value = &$value($handle, @_) };
  139.   }
  140.   # If we make it here, there was an exception thrown in the
  141.   #  call to $value, and so scream:
  142.   if($@) {
  143.     my $err = $@;
  144.     # pretty up the error message
  145.     $err =~ s<\s+at\s+\(eval\s+\d+\)\s+line\s+(\d+)\.?\n?>
  146.              <\n in bracket code [compiled line $1],>s;
  147.     #$err =~ s/\n?$/\n/s;
  148.     Carp::croak "Error in maketexting \"$phrase\":\n$err as used";
  149.     # Rather unexpected, but suppose that the sub tried calling
  150.     # a method that didn't exist.
  151.   } else {
  152.     return $value;
  153.   }
  154. }
  155.  
  156. #==========================================================================
  157.  
  158. sub new {
  159.   # Nothing fancy!
  160.   my $class = ref($_[0]) || $_[0];
  161.   my $handle = bless {}, $class;
  162.   $handle->init;
  163.   return $handle;
  164. }
  165.  
  166. sub init { return } # no-op
  167.  
  168. ###########################################################################
  169.  
  170. sub maketext {
  171.   # Remember, this can fail.  Failure is controllable many ways.
  172.   Carp::croak "maketext requires at least one parameter" unless @_ > 1;
  173.  
  174.   my($handle, $phrase) = splice(@_,0,2);
  175.  
  176.   # Look up the value:
  177.  
  178.   my $value;
  179.   foreach my $h_r (
  180.     @{  $isa_scan{ref($handle) || $handle} || $handle->_lex_refs  }
  181.   ) {
  182.     print "* Looking up \"$phrase\" in $h_r\n" if DEBUG;
  183.     if(exists $h_r->{$phrase}) {
  184.       print "  Found \"$phrase\" in $h_r\n" if DEBUG;
  185.       unless(ref($value = $h_r->{$phrase})) {
  186.         # Nonref means it's not yet compiled.  Compile and replace.
  187.         $value = $h_r->{$phrase} = $handle->_compile($value);
  188.       }
  189.       last;
  190.     } elsif($phrase !~ m/^_/s and $h_r->{'_AUTO'}) {
  191.       # it's an auto lex, and this is an autoable key!
  192.       print "  Automaking \"$phrase\" into $h_r\n" if DEBUG;
  193.       
  194.       $value = $h_r->{$phrase} = $handle->_compile($phrase);
  195.       last;
  196.     }
  197.     print "  Not found in $h_r, nor automakable\n" if DEBUG > 1;
  198.     # else keep looking
  199.   }
  200.  
  201.   unless(defined($value)) {
  202.     print "! Lookup of \"$phrase\" in/under ", ref($handle) || $handle,
  203.       " fails.\n" if DEBUG;
  204.     if(ref($handle) and $handle->{'fail'}) {
  205.       print "WARNING0: maketext fails looking for <$phrase>\n" if DEBUG;
  206.       my $fail;
  207.       if(ref($fail = $handle->{'fail'}) eq 'CODE') { # it's a sub reference
  208.         return &{$fail}($handle, $phrase, @_);
  209.          # If it ever returns, it should return a good value.
  210.       } else { # It's a method name
  211.         return $handle->$fail($phrase, @_);
  212.          # If it ever returns, it should return a good value.
  213.       }
  214.     } else {
  215.       # All we know how to do is this;
  216.       Carp::croak("maketext doesn't know how to say:\n$phrase\nas needed");
  217.     }
  218.   }
  219.  
  220.   return $$value if ref($value) eq 'SCALAR';
  221.   return $value unless ref($value) eq 'CODE';
  222.   
  223.   {
  224.     local $SIG{'__DIE__'};
  225.     eval { $value = &$value($handle, @_) };
  226.   }
  227.   # If we make it here, there was an exception thrown in the
  228.   #  call to $value, and so scream:
  229.   if($@) {
  230.     my $err = $@;
  231.     # pretty up the error message
  232.     $err =~ s<\s+at\s+\(eval\s+\d+\)\s+line\s+(\d+)\.?\n?>
  233.              <\n in bracket code [compiled line $1],>s;
  234.     #$err =~ s/\n?$/\n/s;
  235.     Carp::croak "Error in maketexting \"$phrase\":\n$err as used";
  236.     # Rather unexpected, but suppose that the sub tried calling
  237.     # a method that didn't exist.
  238.   } else {
  239.     return $value;
  240.   }
  241. }
  242.  
  243. ###########################################################################
  244.  
  245. sub get_handle {  # This is a constructor and, yes, it CAN FAIL.
  246.   # Its class argument has to be the base class for the current
  247.   # application's l10n files.
  248.   my($base_class, @languages) = @_;
  249.   $base_class = ref($base_class) || $base_class;
  250.    # Complain if they use __PACKAGE__ as a project base class?
  251.  
  252.   unless(@languages) {  # Calling with no args is magical!  wooo, magic!
  253.     if(length( $ENV{'REQUEST_METHOD'} || '' )) { # I'm a CGI
  254.       my $in = $ENV{'HTTP_ACCEPT_LANGUAGE'} || '';
  255.         # supposedly that works under mod_perl, too.
  256.       $in =~ s<\([\)]*\)><>g; # Kill parens'd things -- just a hack.
  257.       @languages = &I18N::LangTags::extract_language_tags($in) if length $in;
  258.         # ...which untaints, incidentally.
  259.       
  260.     } else { # Not running as a CGI: try to puzzle out from the environment
  261.       if(length( $ENV{'LANG'} || '' )) {
  262.     push @languages, split m/[,:]/, $ENV{'LANG'};
  263.          # LANG can be only /one/ locale as far as I know, but what the hey.
  264.       }
  265.       if(length( $ENV{'LANGUAGE'} || '' )) {
  266.     push @languages, split m/[,:]/, $ENV{'LANGUAGE'};
  267.       }
  268.       print "Noting ENV LANG ", join(',', @languages),"\n" if DEBUG;
  269.       # Those are really locale IDs, but they get xlated a few lines down.
  270.       
  271.       if(&_try_use('Win32::Locale')) {
  272.         # If we have that module installed...
  273.         push @languages, Win32::Locale::get_language()
  274.          if defined &Win32::Locale::get_language;
  275.       }
  276.     }
  277.   }
  278.  
  279.   #------------------------------------------------------------------------
  280.   print "Lgs1: ", map("<$_>", @languages), "\n" if DEBUG;
  281.  
  282.   if($USING_LANGUAGE_TAGS) {
  283.     @languages = map &I18N::LangTags::locale2language_tag($_), @languages;
  284.      # if it's a lg tag, fine, pass thru (untainted)
  285.      # if it's a locale ID, try converting to a lg tag (untainted),
  286.      # otherwise nix it.
  287.  
  288.     push @languages, map I18N::LangTags::super_languages($_), @languages
  289.      if $MATCH_SUPERS;
  290.  
  291.     @languages =  map { $_, I18N::LangTags::alternate_language_tags($_) }
  292.                       @languages;    # catch alternation
  293.  
  294.     push @languages, I18N::LangTags::panic_languages(@languages)
  295.       if defined &I18N::LangTags::panic_languages;
  296.     
  297.     push @languages, $base_class->fallback_languages;
  298.      # You are free to override fallback_languages to return empty-list!
  299.  
  300.     @languages =  # final bit of processing:
  301.       map {
  302.         my $it = $_;  # copy
  303.         $it =~ tr<-A-Z><_a-z>; # lc, and turn - to _
  304.         $it =~ tr<_a-z0-9><>cd;  # remove all but a-z0-9_
  305.         $it;
  306.       } @languages
  307.     ;
  308.   }
  309.   print "Lgs2: ", map("<$_>", @languages), "\n" if DEBUG > 1;
  310.  
  311.   push @languages, $base_class->fallback_language_classes;
  312.    # You are free to override that to return whatever.
  313.  
  314.  
  315.   my %seen = ();
  316.   foreach my $module_name ( map { $base_class . "::" . $_ }  @languages )
  317.   {
  318.     next unless length $module_name; # sanity
  319.     next if $seen{$module_name}++        # Already been here, and it was no-go
  320.             || !&_try_use($module_name); # Try to use() it, but can't it.
  321.     return($module_name->new); # Make it!
  322.   }
  323.  
  324.   return undef; # Fail!
  325. }
  326.  
  327. ###########################################################################
  328. #
  329. # This is where most people should stop reading.
  330. #
  331. ###########################################################################
  332.  
  333. sub _compile {
  334.   # This big scarp routine compiles an entry.
  335.   # It returns either a coderef if there's brackety bits in this, or
  336.   #  otherwise a ref to a scalar.
  337.   
  338.   my $target = ref($_[0]) || $_[0];
  339.   
  340.   my(@code);
  341.   my(@c) = (''); # "chunks" -- scratch.
  342.   my $call_count = 0;
  343.   my $big_pile = '';
  344.   {
  345.     my $in_group = 0; # start out outside a group
  346.     my($m, @params); # scratch
  347.     
  348.     while($_[1] =~  # Iterate over chunks.
  349.      m<\G(
  350.        [^\~\[\]]+  # non-~[] stuff
  351.        |
  352.        ~.       # ~[, ~], ~~, ~other
  353.        |
  354.        \[          # [ presumably opening a group
  355.        |
  356.        \]          # ] presumably closing a group
  357.        |
  358.        ~           # terminal ~ ?
  359.        |
  360.        $
  361.      )>xgs
  362.     ) {
  363.       print "  \"$1\"\n" if DEBUG > 2;
  364.  
  365.       if($1 eq '[' or $1 eq '') {       # "[" or end
  366.         # Whether this is "[" or end, force processing of any
  367.         #  preceding literal.
  368.         if($in_group) {
  369.           if($1 eq '') {
  370.             $target->_die_pointing($_[1], "Unterminated bracket group");
  371.           } else {
  372.             $target->_die_pointing($_[1], "You can't nest bracket groups");
  373.           }
  374.         } else {
  375.           if($1 eq '') {
  376.             print "   [end-string]\n" if DEBUG > 2;
  377.           } else {
  378.             $in_group = 1;
  379.           }
  380.           die "How come \@c is empty?? in <$_[1]>" unless @c; # sanity
  381.           if(length $c[-1]) {
  382.             # Now actually processing the preceding literal
  383.             $big_pile .= $c[-1];
  384.             if($USE_LITERALS and (
  385.               (ord('A') == 65)
  386.                ? $c[-1] !~ m<[^\x20-\x7E]>s
  387.                   # ASCII very safe chars
  388.                : $c[-1] !~ m/[^ !"\#\$%&'()*+,\-.\/0-9:;<=>?\@A-Z[\\\]^_`a-z{|}~\x07]/s
  389.                   # EBCDIC very safe chars
  390.             )) {
  391.               # normal case -- all very safe chars
  392.               $c[-1] =~ s/'/\\'/g;
  393.               push @code, q{ '} . $c[-1] . "',\n";
  394.               $c[-1] = ''; # reuse this slot
  395.             } else {
  396.               push @code, ' $c[' . $#c . "],\n";
  397.               push @c, ''; # new chunk
  398.             }
  399.           }
  400.            # else just ignore the empty string.
  401.         }
  402.  
  403.       } elsif($1 eq ']') {  # "]"
  404.         # close group -- go back in-band
  405.         if($in_group) {
  406.           $in_group = 0;
  407.           
  408.           print "   --Closing group [$c[-1]]\n" if DEBUG > 2;
  409.           
  410.           # And now process the group...
  411.           
  412.           if(!length($c[-1]) or $c[-1] =~ m/^\s+$/s) {
  413.             DEBUG > 2 and print "   -- (Ignoring)\n";
  414.             $c[-1] = ''; # reset out chink
  415.             next;
  416.           }
  417.           
  418.            #$c[-1] =~ s/^\s+//s;
  419.            #$c[-1] =~ s/\s+$//s;
  420.           ($m,@params) = split(",", $c[-1], -1);  # was /\s*,\s*/
  421.           
  422.           # A bit of a hack -- we've turned "~,"'s into DELs, so turn
  423.           #  'em into real commas here.
  424.           if (ord('A') == 65) { # ASCII, etc
  425.             foreach($m, @params) { tr/\x7F/,/ } 
  426.           } else {              # EBCDIC (1047, 0037, POSIX-BC)
  427.             # Thanks to Peter Prymmer for the EBCDIC handling
  428.             foreach($m, @params) { tr/\x07/,/ } 
  429.           }
  430.           
  431.           # Special-case handling of some method names:
  432.           if($m eq '_*' or $m =~ m<^_(-?\d+)$>s) {
  433.             # Treat [_1,...] as [,_1,...], etc.
  434.             unshift @params, $m;
  435.             $m = '';
  436.           } elsif($m eq '*') {
  437.             $m = 'quant'; # "*" for "times": "4 cars" is 4 times "cars"
  438.           } elsif($m eq '#') {
  439.             $m = 'numf';  # "#" for "number": [#,_1] for "the number _1"
  440.           }
  441.  
  442.           # Most common case: a simple, legal-looking method name
  443.           if($m eq '') {
  444.             # 0-length method name means to just interpolate:
  445.             push @code, ' (';
  446.           } elsif($m =~ m<^\w+(?:\:\:\w+)*$>s
  447.             and $m !~ m<(?:^|\:)\d>s
  448.              # exclude starting a (sub)package or symbol with a digit 
  449.           ) {
  450.             # Yes, it even supports the demented (and undocumented?)
  451.             #  $obj->Foo::bar(...) syntax.
  452.             $target->_die_pointing(
  453.               $_[1], "Can't (yet?) use \"SUPER::\" in a bracket-group method",
  454.               2 + length($c[-1])
  455.             )
  456.              if $m =~ m/^SUPER::/s;
  457.               # Because for SUPER:: to work, we'd have to compile this into
  458.               #  the right package, and that seems just not worth the bother,
  459.               #  unless someone convinces me otherwise.
  460.             
  461.             push @code, ' $_[0]->' . $m . '(';
  462.           } else {
  463.             # TODO: implement something?  or just too icky to consider?
  464.             $target->_die_pointing(
  465.              $_[1],
  466.              "Can't use \"$m\" as a method name in bracket group",
  467.              2 + length($c[-1])
  468.             );
  469.           }
  470.           
  471.           pop @c; # we don't need that chunk anymore
  472.           ++$call_count;
  473.           
  474.           foreach my $p (@params) {
  475.             if($p eq '_*') {
  476.               # Meaning: all parameters except $_[0]
  477.               $code[-1] .= ' @_[1 .. $#_], ';
  478.                # and yes, that does the right thing for all @_ < 3
  479.             } elsif($p =~ m<^_(-?\d+)$>s) {
  480.               # _3 meaning $_[3]
  481.               $code[-1] .= '$_[' . (0 + $1) . '], ';
  482.             } elsif($USE_LITERALS and (
  483.               (ord('A') == 65)
  484.                ? $p !~ m<[^\x20-\x7E]>s
  485.                   # ASCII very safe chars
  486.                : $p !~ m/[^ !"\#\$%&'()*+,\-.\/0-9:;<=>?\@A-Z[\\\]^_`a-z{|}~\x07]/s
  487.                   # EBCDIC very safe chars            
  488.             )) {
  489.               # Normal case: a literal containing only safe characters
  490.               $p =~ s/'/\\'/g;
  491.               $code[-1] .= q{'} . $p . q{', };
  492.             } else {
  493.               # Stow it on the chunk-stack, and just refer to that.
  494.               push @c, $p;
  495.               push @code, ' $c[' . $#c . "], ";
  496.             }
  497.           }
  498.           $code[-1] .= "),\n";
  499.  
  500.           push @c, '';
  501.         } else {
  502.           $target->_die_pointing($_[1], "Unbalanced ']'");
  503.         }
  504.         
  505.       } elsif(substr($1,0,1) ne '~') {
  506.         # it's stuff not containing "~" or "[" or "]"
  507.         # i.e., a literal blob
  508.         $c[-1] .= $1;
  509.         
  510.       } elsif($1 eq '~~') { # "~~"
  511.         $c[-1] .= '~';
  512.         
  513.       } elsif($1 eq '~[') { # "~["
  514.         $c[-1] .= '[';
  515.         
  516.       } elsif($1 eq '~]') { # "~]"
  517.         $c[-1] .= ']';
  518.  
  519.       } elsif($1 eq '~,') { # "~,"
  520.         if($in_group) {
  521.           # This is a hack, based on the assumption that no-one will actually
  522.           # want a DEL inside a bracket group.  Let's hope that's it's true.
  523.           if (ord('A') == 65) { # ASCII etc
  524.             $c[-1] .= "\x7F";
  525.           } else {              # EBCDIC (cp 1047, 0037, POSIX-BC)
  526.             $c[-1] .= "\x07";
  527.           }
  528.         } else {
  529.           $c[-1] .= '~,';
  530.         }
  531.         
  532.       } elsif($1 eq '~') { # possible only at string-end, it seems.
  533.         $c[-1] .= '~';
  534.         
  535.       } else {
  536.         # It's a "~X" where X is not a special character.
  537.         # Consider it a literal ~ and X.
  538.         $c[-1] .= $1;
  539.       }
  540.     }
  541.   }
  542.  
  543.   if($call_count) {
  544.     undef $big_pile; # Well, nevermind that.
  545.   } else {
  546.     # It's all literals!  Ahwell, that can happen.
  547.     # So don't bother with the eval.  Return a SCALAR reference.
  548.     return \$big_pile;
  549.   }
  550.  
  551.   die "Last chunk isn't null??" if @c and length $c[-1]; # sanity
  552.   print scalar(@c), " chunks under closure\n" if DEBUG;
  553.   if(@code == 0) { # not possible?
  554.     print "Empty code\n" if DEBUG;
  555.     return \'';
  556.   } elsif(@code > 1) { # most cases, presumably!
  557.     unshift @code, "join '',\n";
  558.   }
  559.   unshift @code, "use strict; sub {\n";
  560.   push @code, "}\n";
  561.  
  562.   print @code if DEBUG;
  563.   my $sub = eval(join '', @code);
  564.   die "$@ while evalling" . join('', @code) if $@; # Should be impossible.
  565.   return $sub;
  566. }
  567.  
  568. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  569.  
  570. sub _die_pointing {
  571.   # This is used by _compile to throw a fatal error
  572.   my $target = shift; # class name
  573.   # ...leaving $_[0] the error-causing text, and $_[1] the error message
  574.   
  575.   my $i = index($_[0], "\n");
  576.  
  577.   my $pointy;
  578.   my $pos = pos($_[0]) - (defined($_[2]) ? $_[2] : 0) - 1;
  579.   if($pos < 1) {
  580.     $pointy = "^=== near there\n";
  581.   } else { # we need to space over
  582.     my $first_tab = index($_[0], "\t");
  583.     if($pos > 2 and ( -1 == $first_tab  or  $first_tab > pos($_[0]))) {
  584.       # No tabs, or the first tab is harmlessly after where we will point to,
  585.       # AND we're far enough from the margin that we can draw a proper arrow.
  586.       $pointy = ('=' x $pos) . "^ near there\n";
  587.     } else {
  588.       # tabs screw everything up!
  589.       $pointy = substr($_[0],0,$pos);
  590.       $pointy =~ tr/\t //cd;
  591.        # make everything into whitespace, but preseving tabs
  592.       $pointy .= "^=== near there\n";
  593.     }
  594.   }
  595.   
  596.   my $errmsg = "$_[1], in\:\n$_[0]";
  597.   
  598.   if($i == -1) {
  599.     # No newline.
  600.     $errmsg .= "\n" . $pointy;
  601.   } elsif($i == (length($_[0]) - 1)  ) {
  602.     # Already has a newline at end.
  603.     $errmsg .= $pointy;
  604.   } else {
  605.     # don't bother with the pointy bit, I guess.
  606.   }
  607.   Carp::croak( "$errmsg via $target, as used" );
  608. }
  609.  
  610. ###########################################################################
  611.  
  612. my %tried = ();
  613.   # memoization of whether we've used this module, or found it unusable.
  614.  
  615. sub _try_use {   # Basically a wrapper around "require Modulename"
  616.   # "Many men have tried..."  "They tried and failed?"  "They tried and died."
  617.   return $tried{$_[0]} if exists $tried{$_[0]};  # memoization
  618.  
  619.   my $module = $_[0];   # ASSUME sane module name!
  620.   { no strict 'refs';
  621.     return($tried{$module} = 1)
  622.      if defined(%{$module . "::Lexicon"}) or defined(@{$module . "::ISA"});
  623.     # weird case: we never use'd it, but there it is!
  624.   }
  625.  
  626.   print " About to use $module ...\n" if DEBUG;
  627.   {
  628.     local $SIG{'__DIE__'};
  629.     eval "require $module"; # used to be "use $module", but no point in that.
  630.   }
  631.   if($@) {
  632.     print "Error using $module \: $@\n" if DEBUG > 1;
  633.     return $tried{$module} = 0;
  634.   } else {
  635.     print " OK, $module is used\n" if DEBUG;
  636.     return $tried{$module} = 1;
  637.   }
  638. }
  639.  
  640. #--------------------------------------------------------------------------
  641.  
  642. sub _lex_refs {  # report the lexicon references for this handle's class
  643.   # returns an arrayREF!
  644.   no strict 'refs';
  645.   my $class = ref($_[0]) || $_[0];
  646.   print "Lex refs lookup on $class\n" if DEBUG > 1;
  647.   return $isa_scan{$class} if exists $isa_scan{$class};  # memoization!
  648.  
  649.   my @lex_refs;
  650.   my $seen_r = ref($_[1]) ? $_[1] : {};
  651.  
  652.   if( defined( *{$class . '::Lexicon'}{'HASH'} )) {
  653.     push @lex_refs, *{$class . '::Lexicon'}{'HASH'};
  654.     print "%" . $class . "::Lexicon contains ",
  655.          scalar(keys %{$class . '::Lexicon'}), " entries\n" if DEBUG;
  656.   }
  657.  
  658.   # Implements depth(height?)-first recursive searching of superclasses.
  659.   # In hindsight, I suppose I could have just used Class::ISA!
  660.   foreach my $superclass (@{$class . "::ISA"}) {
  661.     print " Super-class search into $superclass\n" if DEBUG;
  662.     next if $seen_r->{$superclass}++;
  663.     push @lex_refs, @{&_lex_refs($superclass, $seen_r)};  # call myself
  664.   }
  665.  
  666.   $isa_scan{$class} = \@lex_refs; # save for next time
  667.   return \@lex_refs;
  668. }
  669.  
  670. sub clear_isa_scan { %isa_scan = (); return; } # end on a note of simplicity!
  671.  
  672. ###########################################################################
  673. 1;
  674.  
  675.