File Coverage

blib/lib/Term/Query.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             # Term::Query.pm -*- perl -*-
2             #
3             # Copyright (C) 1995 Alan K. Stebbens
4             #
5             # This program is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by
7             # the Free Software Foundation; either version 2 of the License, or
8             # (at your option) any later version.
9             #
10             # This program is distributed in the hope that it will be useful,
11             # but WITHOUT ANY WARRANTY; without even the implied warranty of
12             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13             # GNU General Public License for more details.
14             #
15             # You should have received a copy of the GNU General Public License
16             # along with this program; if not, write to the Free Software
17             # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18             #
19             # $Id: Query.pm,v 1.1.1.1 1996/08/09 21:39:25 stebbens Exp $
20             # Author: Alan K. Stebbens
21             #
22             #
23             # query -- generalized query routine
24             #
25             # query_table -- perform multiple queries (given an array of info)
26             #
27             # query_table_set_defaults
28             # -- set all named variable's to their default values.
29             #
30             # query_table_process
31             # -- process a table of queries
32             #
33             # Note: This module uses the Array::PrintCols module (by the same author).
34              
35             package Term::Query;
36              
37             require 5.001;
38              
39 4     4   767 use Exporter;
  4         9  
  4         315  
40              
41             @ISA = (Exporter);
42             @EXPORT_OK = qw( query
43             query_table
44             query_table_set_defaults
45             query_table_process
46             );
47              
48 4     4   26 use Carp;
  4         8  
  4         299  
49 4     4   1470 use Array::PrintCols;
  0            
  0            
50              
51             ###############
52             #
53             # $result = query($prompt, $flags, [optional fields])
54             #
55             # Ask a question, prompting with $prompt (unless STDIN is not tty).
56             # Validate the answer based on $flags below.
57             #
58             #
59             # The following flags indicate the type or attribute of the value
60             # r - an answer is required
61             # Y - the question requires a "yes" or "no", defaulting to "yes"
62             # N - the question requires a "yes" or "no", defaulting to "no"
63             # i - the input is an integer
64             # n - the input is a number (possibly a real number)
65             # H - do *not* treat '?' as a request for help (note, this disables
66             # any help, unless implemented in the "after" subroutine).
67             #
68             # The following flags indicate that the next argument is:
69             # a - a subroutine which is invoked *After* the input is read, but
70             # prior to doing any other checks; if it returns false, the input
71             # is rejected.
72             # b - a subroutine which is invoked *Before* the input is read, which
73             # generally prepares for the input; if it returns false, then no
74             # input is accepted; if it returns undef, an EOF is assumed.
75             # d - the next argument is a Default input, used if the actual input
76             # is the empty string.
77             # h - the next argument is a Help string to print in response to "?"
78             # I - the next argument is the input "method" ref: if it is a scalar
79             # value, then no read is performed and this value is used as if
80             # it has been entered by the user; if it is a CODE ref, then the
81             # sub is invoked to obtain its return value as the input.
82             # J - same as I, except that if the initial value returned by the
83             # next argument reference is unacceptable for any reason,
84             # solicit a new, proper value from STDIN. (Mnemonic: "jump" into
85             # query with an initial value).
86             # k - the next argument is a table reference of allowable keywords
87             # (mnemonic: check a Keywword list).
88             # K - the next argument is a table reference of disallowed keywords
89             # (mnemonic: check against a Keyword list).
90             # m - the next argument is a Match pattern (regexp)
91             # l - the next argument is a maximum Length value
92             # V - the next argument is a variable name or *reference* to receive
93             # the value; if it is a name (a string) and unqualified, it is
94             # qualified at the package level outside of Query.pm.
95             #
96             # The ordering of the arguments must match the ordering of their
97             # corresponding flags.
98             #
99             # The ordering of the flags is also important -- it determines the order in
100             # which the various checks are made. For example, if the flags are
101             # given in this order: 'alm', then the $after sub is invoked first, then the
102             # length check is made, then the $match test is made.
103             #
104             # Of course, the 'b' flag (and the corresponding $before sub) is always
105             # invoked before doing any input.
106             #
107             # Returns undef on EOF.
108             # Otherwise, the result is the input.
109              
110             %query_flags = (
111             'a', sub { $after = shift(@_);
112             &add_check("after"); },
113              
114             'b', sub { $before = shift(@_); },
115              
116             'd', sub { $default = shift(@_);
117             &add_check(qw(default null)); },
118              
119             'h', sub { $help = shift(@_);
120             &add_check(qw(help null)); },
121              
122             'H', sub { $nohelp++;
123             $check_done{"help"}++;}, # don't do any help
124              
125             'i', sub { $integer++;
126             &add_check(qw(int null strip default help)); },
127              
128             'I', sub { $inref = shift(@_);
129             $inref_flag++; },
130              
131             'J', sub { $inref = shift(@_);
132             $inref_flag++;
133             $inref_once++; },
134              
135             'k', sub { $keys = shift(@_);
136             ref($keys) eq 'ARRAY' or
137             (croak "query: The k flag needs an array reference argument.\n");
138             &add_check(qw(key null strip default help)); },
139              
140             'K', sub { $notkeys = shift(@_);
141             ref($notkeys) eq 'ARRAY' or
142             (croak "query: The K flag needs an array reference argument.\n");
143             &add_check(qw(nonkey null strip default help)); },
144              
145             'l', sub { $maxlen = shift(@_);
146             &add_check(qw(maxlen null default help)); },
147              
148             'm', sub { $match = shift(@_);
149             &add_check(qw(match null default help)); },
150              
151             'n', sub { $number++;
152             &add_check(qw(num null strip default help)); },
153              
154             'N', sub { $no++;
155             &add_check(qw(yesno null strip default help)); },
156              
157             'r', sub { $required++;
158             &add_check(qw(req null default help)); },
159              
160             's', sub { $strip++;
161             &add_check(qw(strip null default help)); },
162              
163             'V', sub { $variable = shift(@_);
164             ref($variable) eq 'SCALAR' or
165             (ref($variable) eq '' &&
166             $variable =~ /^((\w+)?(::|'))?\w+$/) or
167             (croak "query: The V flag needs a variable name or reference.\n"); },
168              
169             'Y', sub { $yes++;
170             &add_check(qw(yesno null strip default help)); },
171              
172             );
173              
174             $need_arg_codes = 'abdhIkKlmV'; # list of codes which need an argument
175              
176             # This is an array of check "codes", and corresponding anonymous subs
177             # which, when invoked, should return one of the values (undef, '', 1)
178             # indicating how to proceed with the input.
179             #
180             # The sub "add_check", when invoked as part of the flag parsing (see
181             # above), causes the codes below to be inserted into the @checks array,
182             # which is then processed for each input.
183              
184             %check_code = (
185             'after', \&check_after,
186             'default', \&check_default,
187             'help', \&check_help,
188             'int', \&check_integer,
189             'key', \&check_keys,
190             'maxlen', \&check_length,
191             'match', \&check_match,
192             'nonkey', \&check_nonkeys,
193             'num', \&check_number,
194             'req', \&check_required,
195             'strip', \&strip_input,
196             'yesno', \&check_yesorno,
197             'null', \&check_null,
198             );
199              
200             # This variable controls how the keyword matching is done.
201              
202             $Case_sensitive = '';
203              
204             $Force_Interactive = ''; # set to force interactive behaviour
205              
206             ##################################################################################
207             #
208             # &query($prompt, $flags, @optional_args)
209             #
210             # Returns
211             #
212             # undef EOF on input
213             #
214             #
215              
216             sub query {
217             local( $prompt ) = shift;
218             local( $flags ) = shift; # there may be other arguments
219             local( $help, $required, $default, $match, $maxlen, $keys, $notkeys );
220             local( $yes, $no, $integer, $number, $strip, $after, $before, $inref );
221             local( $inref_once, $inref_flag, $variable );
222             local( $c, $ev, $input );
223             local( @flags ) = split(//,$flags);
224             local( @checks, $check, $result );
225             local( %check_done ); # make sure this gets reset
226              
227             foreach $c ( @flags ) {
228             $ev = $query_flags{$c} or
229             (croak "query: Unknown query flag '$c'\n");
230             &$ev; # set a flag, or get the next argument
231             }
232              
233             &add_check(qw( help null default )); # these checks are done
234             # by default (unless disabled)
235              
236             # setup a default, depending on type
237             $default = $yes ? 'yes' : 'no' if $yes or $no;
238              
239             $help .= "\n" if $help && substr($help,-1) ne "\n";
240              
241             Query:while (1) {
242             if (length($before)) {
243             &$before or last; # check $before sub first
244             }
245             if ($inref_flag) { # do we have a reference?
246             $input = &deref($inref);
247             $inref_flag = '' if $inref_once; # kill flag if "once"
248             } else {
249             if (-t STDIN or $Force_Interactive) { # interactive?
250             print $prompt;
251             print " " unless substr($prompt, -1) eq ' ';
252             if ($default ne '') {
253             my($def) = &deref($default);
254             print "[$def] ";
255             }
256             }
257             $input = ;
258             print "\n" if !-t STDIN and $Force_Interactive;
259             }
260              
261             # Now process all the check expressions. If any return undef, then
262             # return from this routine with undef. If a null or zero return is
263             # made, then reject the input. Otherwise, it passes.
264              
265             foreach $check ( @checks ) {
266             $result = &$check; # process the check
267             return undef unless defined($result); # was the result undef?
268              
269             # Perform the next test if this one was okay
270             next if $result;
271              
272             # If $inref_flag is set (I flag), don't loop
273             return undef if $inref_flag;
274              
275             # don't try looping on non-interactive input
276             return undef unless -t STDIN or $Force_Interactive;
277              
278             print "Please try again, or enter \"?\" for help.\n";
279             next Query; # do another query
280             }
281             last Query; # all tests passed
282             }
283             &define_var($variable, $input); # assign a variable (maybe)
284             return $input; # return with input
285             }
286              
287             #############################
288             #
289             # &deref ($possible_ref)
290             #
291             # If the $possible_ref is a reference, dereference it
292             # correctly.
293              
294             sub deref {
295             my($ref) = shift;
296             my($type) = ref($ref);
297             return $ref if $type eq ''; # not a reference
298             return $$ref if $type eq 'SCALAR'; # a scalar
299             return &$ref if $type eq 'CODE'; # a subroutine
300             return @$ref if $type eq 'ARRAY'; # an array
301             return %$ref if $type eq 'HASH'; # a hashed array
302             return &deref($$ref) if $type eq 'REF'; # recursive reference
303             $ref; # whatever..
304             }
305              
306              
307             #############################
308             #
309             # &add_check($code, @precedes)
310             #
311             # Add the check code for $code, after ensuring that all codes
312             # in @precedes have already been done.
313             #
314             # In other words, if a particular check should be done *after*
315             # some other test, place the other check code(s) as one of the
316             # elements in the @precedes array.
317             #
318             # Add_check ensures that no check is scheduled twice.
319              
320             sub add_check {
321             local($code,@precedes) = @_;
322             return if $check_done{$code}; # don't make the same check twice
323             local($c); # ensure predecessors are done first
324             foreach $c (@precedes) { # see if others are done
325             &add_check($c) unless $check_done{$c};
326             }
327             push(@checks,$check_code{$code});
328             $check_done{$code}++;
329             }
330              
331             #################################
332             #
333             # These are the "check" routines.
334             #
335             # They are all called without arguments, and have full access to the
336             # variables of the &query routine.
337              
338             # They all should check $input and return either:
339             #
340             # undef -return from query with undef
341             # '' -fail the input, and force another query
342             # 1 -input is okay, do the next check
343              
344              
345             # &check_after
346             #
347             # If $after is a CODE ref, invoke it to
348             # allow the sub to validate the input.
349              
350             sub check_after {
351             return 1 unless length($after); # default is okay
352             &$after(\$input); # invoke the sub
353             }
354              
355             # &check_default
356             #
357             # If $default is a CODE ref, invoke it to
358             # get the default value, otherwise just use
359             # the value as is.
360              
361             sub check_default {
362             $input = &deref($default) if !length($input);
363             1;
364             }
365              
366             #
367             # &check_keys
368             #
369              
370             sub check_keys {
371             local( @exact );
372             if ($Case_sensitive) {
373             @exact = grep($input eq $_, @$keys);
374             } else {
375             @exact = grep(/^\Q$input\E$/i, @$keys);
376             }
377             if ($#exact == 0) {
378             $input = $exact[0]; # it matches -- return the keyword
379             return 1; # yea!
380             }
381             local( @matches );
382             if ($Case_sensitive) {
383             @matches = grep(/^\Q$input\E/, @$keys);
384             } else {
385             @matches = grep(/^\Q$input\E/i, @$keys);
386             }
387             if ($#matches == 0) { # exactly one match?
388             $input = $matches[0];
389             return 1; # return success
390             }
391             if ($#matches > 0) { # ambiguous?
392             print "The input \"$input\" is ambiguous; it matches the following:\n";
393             print_cols \@matches;
394             } else {
395             print "The input \"$input\" fails to match any of the allowed keywords:\n";
396             print_cols $keys;
397             }
398             ''; # fail the input
399             }
400              
401             #
402             # &check_nonkeys
403             #
404              
405             sub check_nonkeys {
406             local( @matches );
407             if ($Case_sensitive) {
408             @matches = grep($_ eq $input, @$notkeys);
409             } else {
410             @matches = grep(/^\Q$input\E$/i, @$notkeys);
411             }
412             @matches || return 1; # no matches -- it's okay
413             printf("The input \"%s\" matches a disallowed keyword \"%s\".\n",
414             $input, $matches[0]);
415             return '';
416             }
417              
418             #
419             # &check_number
420             #
421              
422             sub check_number {
423             if ($input !~ /^(\d+(\.\d*)?|\.\d+)(e\d+)?$/i) {
424             print "Please enter a number, real or integer.\n";
425             return '';
426             }
427             $input = 0.0 + $input; # convert to numeric
428             1; # and it's okay
429             }
430              
431             #
432             # &check_integer
433             #
434              
435             sub check_integer {
436             if ($input !~ /^(\d+|0x[0-9a-f]+)$/i) {
437             print "Please enter an integer number.\n";
438             return '';
439             }
440             $input = 0 + $input; # conver to integer
441             1;
442             }
443              
444             #
445             # &check_yesorno
446             #
447              
448             sub check_yesorno {
449             if ($input !~ /^(y(es?)?|no?)$/i) {
450             print "Please answer with \"yes\" or \"no\".\n";
451             return '';
452             }
453             # Coerce input to 'yes' or 'no'
454             # Fixed by markw@temple.dev.wholesale.nbnz.co.nz (Mark Wright)
455             $input = $input =~ /^y(es?)?$/i ? 'yes' : 'no';
456             }
457              
458             #
459             # &check_match
460             #
461              
462             sub check_match {
463             return 1 if $match eq '' or $input =~ m/$match/;
464             printf "\"%s\" fails to match \"%s\"\n", $input, $match;
465             ''; # fail the input
466             }
467              
468             #
469             # &check_length
470             #
471              
472             sub check_length {
473             return 1 if $maxlen <= 0 or length($input) <= $maxlen;
474             printf "Input is %d characters too long; cannot exceed %d characters.\n",
475             (length($input) - $maxlen), $maxlen;
476             ''; # fail the input
477             }
478              
479             #
480             # &check_required
481             #
482             sub check_required {
483             return 1 if length($input);
484             print "Input is required.\n";
485             ''; # fail the input
486             }
487              
488             #
489             # &check_null
490             #
491              
492             sub check_null {
493             return undef unless length($input); # a null input is an EOF
494             chomp($input); # trim trailing newline
495             1; # always succeed
496             }
497              
498             #
499             # &strip_input
500              
501             sub strip_input {
502             $input =~ s/^\s+//;
503             $input =~ s/\s+$//;
504             $input =~ s/\s+/ /g; # squeeze blanks
505             1; # always ok
506             }
507              
508             #
509             # &check_help
510             #
511             # Check for help trigger '?'
512              
513             sub check_help {
514             $input =~ /^\s*\?\s*$/ || return 1; # if not '?', its okay
515             print ($help || "You are being asked \"$prompt\"\n");
516             print "Input is required.\n" if $required;
517             printf "The input should be %s.\n",($integer ? 'an integer' : 'a number')
518             if $integer || $number;
519             print "The input should be either \"yes\" or \"no\".\n" if $yes || $no;
520             if ($default) {
521             my($def) = &deref($default);
522             print "If you enter nothing, the default answer will be \"$def\".\n";
523             } else {
524             print "There is no default input.\n";
525             }
526             printf "The input cannot exceed %d characters in length.\n", $maxlen
527             if $maxlen;
528             printf "The input must match the pattern \"%s\".\n",$match if $match;
529             if (@$keys) {
530             print "The input must match one of the following keywords:\n";
531             print_cols $keys, 0, 0, 1;
532             print "The keyword matching is case-sensitive.\n" if $Case_sensitive;
533             }
534             if (@$notkeys) {
535             print "The input cannot match one of the following keywords:\n";
536             print_cols $notkeys, 0, 0, 1;
537             print "The keyword matching is case-sensitive.\n" if $Case_sensitive;
538             }
539             print "\n";
540             ''; # cause another query
541             }
542              
543            
544             ###############
545             #
546             # query_table_process \@array, \&flagsub, \&querysub.
547             #
548             # Given an array suitable for query_table, run through the table and
549             # perform &querysub on each query definition, invoking &flagsub on each
550             # flag character.
551             #
552             # The local variables available to the subs are:
553             # $table - the array reference
554             # $flags - all the flags
555             # $flag - the current flag being processed (&flagsub only)
556             # $arg - the argument for the current flag, if appropriate.
557             # $prompt - the prompt for the current query
558             #
559             # When the &querysub is invoked, if it returns UNDEF, then the query
560             # table processing stops immediately, with an UNDEF return.
561              
562             sub query_table_process {
563             local( $table ) = shift; # the query table
564             local( $flagsub ) = shift; # sub to perform on each flag
565             local( $querysub ) = shift; # sub to perform for each query
566             local( $x, $prompt, $flags, $query_args, $argx, $flag, $_, $arg);
567              
568             ref($table) eq 'ARRAY' or
569             (croak "query_table_process: Need an array reference argument.\n");
570             (ref($flagsub) eq 'CODE' or $flagsub eq '') and
571             (ref($querysub) eq 'CODE' or $querysub eq '') or
572             (croak "query_table_process: Need a code reference argument.\n");
573              
574             for ($x = 0; $x <= $#$table; $x += 3) {
575             $prompt = $table->[$x]; # get the prompt
576             $flags = $table->[$x+1]; # get the flags
577             $query_args = $table->[$x+2]; # get the arguments (if any)
578             $argx = 0; # initialize arg index
579             foreach $flag ( split(//, $flags) ) {
580             $query_flags{$flag} or
581             (croak "query_table_set_defaults: Unknown query flag: '$flag'\n");
582             $arg = ''; # set arg to null by default
583             if (index($need_arg_codes, $flag) >= 0) {
584             $arg = $query_args->[$argx++]; # get the next arg
585             }
586             &$flagsub if $flagsub ne ''; # run the flag sub
587             }
588             if ($querysub ne '') { # is there a querysub?
589             &$querysub or return undef; # run the query
590             }
591             }
592             1;
593             }
594              
595             ###############
596             #
597             # $ok = query_table \@array;
598             #
599             # $ok == undef if EOF returned
600             # == 1 if all queries completed ok
601             # == 0 if not.
602             #
603             # Run multiple queries given "query" entries in the @array.
604             #
605             # The array is organized like this:
606             #
607             # @array = ( prompt1, flags1, [ arglist1, ... ],
608             # prompt2, flags2, [ arglist2, ... ],
609             # ...
610             # promptN, flagsN, [ arglistN, ...] )
611             #
612             # Note: the query table is a N x 3 array, with the 3rd column being
613             # itself arrays of varying lengths, depending upon the corresponding
614             # flags.
615             #
616             # Of course, this routine is more useful if the flags contain the 'V'
617             # flag, and the arglist has a correspoinding variable name.
618             #
619              
620             sub query_table {
621             local( $table ) = shift;
622             local( @args );
623              
624             query_table_process $table, # process the query table
625             sub { # flagsub
626             push(@args, $arg) if index($need_arg_codes, $flag) >= 0;
627             },
628             sub { # querysub
629             defined(query $prompt, $flags, @args) or return undef;
630             @args = (); # reset the args array
631             1;
632             };
633             1;
634             }
635              
636             ###############
637             #
638             # query_table_set_defaults \@array;
639             #
640             # Given an array suitable for query_table, run through the table and
641             # initialize any variables mentioned with the provided defaults, if any.
642             #
643             # This routine is suitable for preinitializing variables using the
644             # same query table as would be used to query for their values.
645             #
646              
647             sub query_table_set_defaults {
648             local( $table ) = shift; # the query table
649             local( $var, $def );
650              
651             query_table_process $table,
652             sub { # flag sub
653             $var = $arg if $flag eq 'V'; # look for the variable arg
654             $def = $arg if $flag eq 'd'; # look for the default arg
655             },
656             sub { &define_var($var, $def); }; # define a variable (maybe)
657             1;
658             }
659              
660             #######################
661             #
662             # define_var $var, $ref
663             #
664             # Define $var outside of this package.
665             #
666             # $var can be a reference to a variable, or it can be a string name.
667             # If it is the latter and not already qualified, it will be
668             # qualified at the package level outside of the Query.pm module.
669             #
670              
671             sub define_var {
672             my( $var ) = shift; # the variable name
673             my( $ref ) = shift; # the value to define
674             return 1 unless length($var); # don't work with nulls
675             if (!(ref($var) or $var =~ /::/)) { # variable already qualified?
676             my( $pkg, $file ) = (caller)[0,1]; # get caller info
677             my( $i );
678             # Walk the stack until we get the first level outside of Query.pm
679             for ($i = 1; $file =~ /Query\.pm/; $i++) {
680             ($pkg, $file) = (caller $i)[0,1];
681             }
682             $pkg = 'main' unless $pkg ne ''; # default package
683             $var = "${pkg}::${var}"; # qualify the variable's scope
684             }
685             $$var = &deref($ref); # assign a deref'ed value
686             1; # always return good stuff
687             }
688              
689             1;
690              
691             __END__