This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Integrate:
[perl5.git] / wince / bin / search.pl
1 #!/usr/local/bin/perl -w
2 'di';
3 'ig00';
4 ##############################################################################
5 ##
6 ## search
7 ##
8 ## Jeffrey Friedl (jfriedl@omron.co.jp), Dec 1994.
9 ## Copyright 19.... ah hell, just take it.
10 ##
11 ## BLURB:
12 ## A combo of find and grep -- more or less do a 'grep' on a whole
13 ## directory tree. Fast, with lots of options. Much more powerful than
14 ## the simple "find ... | xargs grep ....". Has a full man page.
15 ## Powerfully customizable.
16 ##
17 ## This file is big, but mostly comments and man page.
18 ##
19 ## See man page for usage info.
20 ## Return value: 2=error, 1=nothing found, 0=something found.
21 ##
22
23 $version = "950918.5";
24 ##
25 ## "950918.5";
26 ##      Changed all 'sysread' to 'read' because Linux perl's don't seem
27 ##      to like sysread()
28 ##
29 ## "941227.4";
30 ##      Added -n, -u
31 ##
32 ## "941222.3"
33 ##      Added -nice (due to Lionel Cons <Lionel.Cons@cern.ch>)
34 ##      Removed any leading "./" from name.
35 ##      Added default flags for ~/.search, including TTY, -nice, -list, etc.
36 ##      Program name now has path removed when printed in diagnostics.
37 ##      Added simple tilde-expansion to -dir arg.
38 ##      Added -dskip, etc. Fixed -iregex bug.
39 ##      Changed -dir to be additive, adding -ddir.
40 ##      Now screen out devices, pipes, and sockets.
41 ##      More tidying and lots of expanding of the man page
42 ##
43 ##
44 ## "941217.2";
45 ##      initial release.
46
47 $stripped=0;
48
49 &init;
50 $rc_file = join('/', $ENV{'HOME'}, ".search");
51
52 &check_args;
53
54 ## Make sure we've got a regex.
55 ## Don't need one if -find or -showrc was specified.
56 $!=2, die "expecting regex arguments.\n"
57         if $FIND_ONLY == 0 && $showrc == 0 && @ARGV == 0;
58
59 &prepare_to_search($rc_file);
60
61 &import_program if !defined &dodir; ## BIG key to speed.
62
63 ## do search while there are directories to be done.
64 &dodir(shift(@todo)) while @todo;
65
66 &clear_message if $VERBOSE && $STDERR_IS_TTY;
67 exit($retval);
68 ###############################################################################
69
70 sub init
71 {
72   ## initialize variables that might be reset by command-line args
73   $DOREP=0;             ## set true by -dorep (redo multi-hardlink files)
74   $DOREP=1 if $^O eq 'MSWin32';
75   $DO_SORT=0;           ## set by -sort (sort files in a dir before checking)
76   $FIND_ONLY=0;         ## set by -find (don't search files)
77   $LIST_ONLY=0;         ## set true by -l (list filenames only)
78   $NEWER=0;             ## set by -newer, "-mtime -###"
79   $NICE=0;              ## set by -nice (print human-readable output)
80   $NOLINKS=0;           ## set true by -nolinks (don't follow symlinks)
81   $OLDER=0;             ## set by -older, "-mtime  ###"
82   $PREPEND_FILENAME=1;  ## set false by -h (don't prefix lines with filename)
83   $REPORT_LINENUM=0;    ## set true by -n (show line numbers)
84   $VERBOSE=0;           ## set to a value by -v, -vv, etc. (verbose messages)
85   $WHY=0;               ## set true by -why, -vvv+ (report why skipped)
86   $XDEV=0;              ## set true by -xdev (stay on one filesystem)
87   $all=0;               ## set true by -all (don't skip many kinds of files)
88   $iflag = '';          ## set to 'i' by -i (ignore case);
89   $norc=0;              ## set by -norc (don't load rc file)
90   $showrc=0;            ## set by -showrc (show what happens with rc file)
91   $underlineOK=0;       ## set true by -u (watch for underline stuff)
92   $words=0;             ## set true by -w (match whole-words only)
93   $DELAY=0;             ## inter-file delay (seconds)
94   $retval=1;            ## will set to 0 if we find anything.
95
96   ## various elements of stat() that we might access
97   $STAT_DEV   = 1;
98   $STAT_INODE = 2;
99   $STAT_MTIME = 9;
100
101   $VV_PRINT_COUNT = 50;  ## with -vv, print every VV_PRINT_COUNT files, or...
102   $VV_SIZE = 1024*1024;  ## ...every VV_SIZE bytes searched
103   $vv_print = $vv_size = 0; ## running totals.
104
105   ## set default options, in case the rc file wants them
106   $opt{'TTY'}= 1 if -t STDOUT;
107   
108   ## want to know this for debugging message stuff
109   $STDERR_IS_TTY = -t STDERR ? 1 : 0;
110   $STDERR_SCREWS_STDOUT = ($STDERR_IS_TTY && -t STDOUT) ? 1 : 0;
111
112   $0 =~ s,.*/,,;  ## clean up $0 for any diagnostics we'll be printing.
113 }
114
115 ##
116 ## Check arguments.
117 ##
118 sub check_args
119 {
120   while (@ARGV && $ARGV[0] =~ m/^-/)
121   {
122       $arg = shift(@ARGV);
123
124       if ($arg eq '-version' || ($VERBOSE && $arg eq '-help')) {
125           print qq/Jeffrey's file search, version "$version".\n/;
126           exit(0) unless $arg eq '-help';
127       }
128       if ($arg eq '-help') {
129           print <<INLINE_LITERAL_TEXT;
130 usage: $0 [options] [-e] [PerlRegex ....]
131 OPTIONS TELLING *WHERE* TO SEARCH:
132   -dir DIR       start search at the named directory (default is current dir).
133   -xdev          stay on starting file system.
134   -sort          sort the files in each directory before processing.
135   -nolinks       don't follow symbolic links.
136 OPTIONS TELLING WHICH FILES TO EVEN CONSIDER:
137   -mtime #       consider files modified > # days ago (-# for < # days old)
138   -newer FILE    consider files modified more recently than FILE (also -older)
139   -name GLOB     consider files whose name matches pattern (also -regex).
140   -skip GLOB     opposite of -name: identifies files to not consider.
141   -path GLOB     like -name, but for files whose whole path is described.
142   -dpath/-dregex/-dskip versions for selecting or pruning directories.
143   -all           don't skip any files marked to be skipped by the startup file.
144   -x<SPECIAL>    (see manual, and/or try -showrc).
145   -why           report why a file isn't checked (also implied by -vvvv).
146 OPTIONS TELLING WHAT TO DO WITH FILES THAT WILL BE CONSIDERED:
147   -f  | -find    just list files (PerlRegex ignored). Default is to grep them.
148   -ff | -ffind   Does a faster -find (implies -find -all -dorep)
149 OPTIONS CONTROLLING HOW THE SEARCH IS DONE (AND WHAT IS PRINTED):
150   -l | -list     only list files with matches, not the lines themselves.
151   -nice | -nnice print more "human readable" output.
152   -n             prefix each output line with its line number in the file.
153   -h             don't prefix output lines with file name.
154   -u             also look "inside" manpage-style underlined text
155   -i             do case-insensitive searching.
156   -w             match words only (as defined by perl's \\b).
157 OTHER OPTIONS:
158   -v, -vv, -vvv  various levels of message verbosity.
159   -e             end of options (in case a regex looks like an option).
160   -showrc        show what the rc file sets, then exit.
161   -norc          don't load the rc file.
162   -dorep         check files with multiple hard links multiple times.
163 INLINE_LITERAL_TEXT
164         print "Use -v -help for more verbose help.\n" unless $VERBOSE;
165         print "This script file is also a man page.\n" unless $stripped;
166         print <<INLINE_LITERAL_TEXT if $VERBOSE;
167
168 If -f (or -find) given, PerlRegex is optional and ignored.
169 Otherwise, will search for files with lines matching any of the given regexes.
170
171 Combining things like -name and -mtime implies boolean AND.
172 However, duplicating things (such as -name '*.c' -name '*.txt') implies OR.
173
174 -mtime may be given floating point (i.e. 1.5 is a day and a half).
175 -iskip/-idskip/-ipath/... etc are case-insensitive versions.
176
177 If any letter in -newer/-older is upper case, "or equal" is
178 inserted into the test.
179
180 You can always find the latest version on the World Wide Web in
181    http://www.wg.omron.co.jp/~jfriedl/perl/
182 INLINE_LITERAL_TEXT
183           exit(0);
184       }
185       $DOREP=1,             next if $arg eq '-dorep';   ## do repeats
186       $DO_SORT=1,           next if $arg eq '-sort';    ## sort files
187       $NOLINKS=1,           next if $arg eq '-nolinks'; ## no sym. links
188       $PREPEND_FILENAME=0,  next if $arg eq '-h';       ## no filename prefix
189       $REPORT_LINENUM=1,    next if $arg eq '-n';       ## show line numbers
190       $WHY=1,               next if $arg eq '-why';     ## tell why skipped
191       $XDEV=1,              next if $arg eq '-xdev';    ## don't leave F.S.
192       $all=1,$opt{'-all'}=1,next if $arg eq '-all';     ## don't skip *.Z, etc
193       $iflag='i',           next if $arg eq '-i';       ## ignore case
194       $norc=1,              next if $arg eq '-norc';    ## don't load rc file
195       $showrc=1,            next if $arg eq '-showrc';  ## show rc file
196       $underlineOK=1,       next if $arg eq '-u';       ## look throuh underln.
197       $words=1,             next if $arg eq '-w';       ## match "words" only
198       &strip                     if $arg eq '-strip';   ## dump this program
199       last                       if $arg eq '-e';
200       $DELAY=$1,            next if $arg =~ m/-delay(\d+)/;
201
202       $FIND_ONLY=1,         next if $arg =~/^-f(ind)?$/;## do "find" only
203
204       $FIND_ONLY=1, $DOREP=1, $all=1,
205                             next if $arg =~/^-ff(ind)?$/;## fast -find
206       $LIST_ONLY=1,$opt{'-list'}=1,
207                             next if $arg =~/^-l(ist)?$/;## only list files
208
209       if ($arg =~ m/^-(v+)$/) { ## verbosity
210         $VERBOSE =length($1);
211         foreach $len (1..$VERBOSE) { $opt{'-'.('v' x $len)}=1 }
212         next;
213       }
214       if ($arg =~ m/^-(n+)ice$/) { ## "nice" output
215         $NICE =length($1);
216         foreach $len (1..$NICE) { $opt{'-'.('n' x $len).'ice'}=1 }
217         next;
218       }
219
220       if ($arg =~ m/^-(i?)(d?)skip$/) {
221           local($i) = $1 eq 'i';
222           local($d) = $2 eq 'd';
223           $! = 2, die qq/$0: expecting glob arg to -$arg\n/ unless @ARGV;
224           foreach (split(/\s+/, shift @ARGV)) {
225               if ($d) {
226                   $idskip{$_}=1 if $i;
227                    $dskip{$_}=1;
228               } else {
229                   $iskip{$_}=1 if $i;
230                    $skip{$_}=1;
231               }
232           }
233           next;
234       }
235
236
237       if ($arg =~ m/^-(i?)(d?)(regex|path|name)$/) {
238           local($i) = $1 eq 'i';
239           $! = 2, die qq/$0: expecting arg to -$arg\n/ unless @ARGV;
240           foreach (split(/\s+/, shift @ARGV)) {
241               $iname{join(',', $arg, $_)}=1 if $i;
242                $name{join(',', $arg, $_)}=1;
243           }
244           next;
245       }
246
247       if ($arg =~ m/^-d?dir$/) {
248           $opt{'-dir'}=1;
249           $! = 2, die qq/$0: expecting filename arg to -$arg\n/ unless @ARGV;
250           $start = shift(@ARGV);
251           $start =~ s#^~(/+|$)#$ENV{'HOME'}$1# if defined $ENV{'HOME'};
252           $! = 2, die qq/$0: can't find ${arg}'s "$start"\n/ unless -e $start;
253           $! = 2, die qq/$0: ${arg}'s "$start" not a directory.\n/ unless -d _;
254           undef(@todo), $opt{'-ddir'}=1 if $arg eq '-ddir';
255           push(@todo, $start);
256           next;
257       }
258
259       if ($arg =~ m/^-(new|old)er$/i) {
260           $! = 2, die "$0: expecting filename arg to -$arg\n" unless @ARGV;
261           local($file, $time) = shift(@ARGV);
262           $! = 2, die qq/$0: can't stat -${arg}'s "$file"./
263                   unless $time = (stat($file))[$STAT_MTIME];
264           local($upper) = $arg =~ tr/A-Z//;
265           if ($arg =~ m/new/i) {
266              $time++ unless $upper;
267              $NEWER = $time if $NEWER < $time;
268           } else {
269              $time-- unless $upper;
270              $OLDER = $time if $OLDER == 0 || $OLDER > $time;
271           }
272           next;
273       }
274
275       if ($arg =~ m/-mtime/) {
276           $! = 2, die "$0: expecting numerical arg to -$arg\n" unless @ARGV;
277           local($days) = shift(@ARGV);
278           $! = 2, die qq/$0: inappropriate arg ($days) to $arg\n/ if $days==0;
279           $days *= 3600 * 24;
280           if ($days < 0) {
281               local($time) = $^T + $days;
282               $NEWER = $time if $NEWER < $time;
283           } else {
284               local($time) = $^T - $days;
285               $OLDER = $time if $OLDER == 0 || $OLDER > $time;
286           }
287           next;
288       }
289
290       ## special user options
291       if ($arg =~ m/^-x(.+)/) {
292           foreach (split(/[\s,]+/, $1)) {  $user_opt{$_} = $opt{$_}= 1;  }
293           next;
294       }
295
296       $! = 2, die "$0: unknown arg [$arg]\n";
297   }
298 }
299
300 ##
301 ## Given a filename glob, return a regex.
302 ## If the glob has no globbing chars (no * ? or [..]), then
303 ## prepend an effective '*' to it.
304 ##
305 sub glob_to_regex
306 {
307     local($glob) = @_;
308     local(@parts) = $glob =~ m/\\.|[*?]|\[]?[^]]*]|[^[\\*?]+/g;
309     local($trueglob)=0;
310     foreach (@parts) {
311         if ($_ eq '*' || $_ eq '?') {
312             $_ = ".$_";
313             $trueglob=1;  ## * and ? are a real glob
314         } elsif (substr($_, 0, 1) eq '[') {
315             $trueglob=1;  ## [..] is a real glob
316         } else {
317             s/^\\//;     ## remove any leading backslash;
318             s/\W/\\$&/g; ## now quote anything dangerous;
319         }
320     }
321     unshift(@parts, '.*') unless $trueglob;
322     join('', '^', @parts, '$');
323 }
324
325 sub prepare_to_search
326 {
327   local($rc_file) = @_;
328
329   $HEADER_BYTES=0;          ## Might be set nonzero in &read_rc;
330   $last_message_length = 0; ## For &message and &clear_message.
331
332   &read_rc($rc_file, $showrc) unless $norc;
333   exit(0) if $showrc;
334
335   $NEXT_DIR_ENTRY = $DO_SORT ? 'shift @files' : 'readdir(DIR)';
336   $WHY = 1 if $VERBOSE > 3; ## Arg -vvvv or above implies  -why.
337   @todo = ('.') if @todo == 0; ## Where we'll start looking
338
339   ## see if any user options were specified that weren't accounted for
340   foreach $opt (keys %user_opt) {
341       next if defined $seen_opt{$opt};
342       warn "warning: -x$opt never considered.\n";
343   }
344
345   die "$0: multiple time constraints exclude all possible files.\n"
346       if ($NEWER && $OLDER) && ($NEWER > $OLDER);
347
348   ##
349   ## Process any -skip/-iskip args that had been given
350   ##
351   local(@skip_test);
352   foreach $glob (keys %skip) {
353       $i = defined($iskip{$glob}) ? 'i': '';
354       push(@skip_test, '$name =~ m/'. &glob_to_regex($glob). "/$i");
355   }
356   if (@skip_test) {
357       $SKIP_TEST = join('||',@skip_test);
358       $DO_SKIP_TEST = 1;
359   } else {
360       $DO_SKIP_TEST = $SKIP_TEST = 0;
361   }
362
363   ##
364   ## Process any -dskip/-idskip args that had been given
365   ##
366   local(@dskip_test);
367   foreach $glob (keys %dskip) {
368       $i = defined($idskip{$glob}) ? 'i': '';
369       push(@dskip_test, '$name =~ m/'. &glob_to_regex($glob). "/$i");
370   }
371   if (@dskip_test) {
372       $DSKIP_TEST = join('||',@dskip_test);
373       $DO_DSKIP_TEST = 1;
374   } else {
375       $DO_DSKIP_TEST = $DSKIP_TEST = 0;
376   }
377
378
379   ##
380   ## Process any -name, -path, -regex, etc. args that had been given.
381   ##
382   undef @name_test;
383   undef @dname_test;
384   foreach $key (keys %name) {
385       local($type, $pat) = split(/,/, $key, 2);
386       local($i) = defined($iname{$key}) ? 'i' : '';
387       if ($type =~ /regex/) {
388           $pat =~ s/!/\\!/g;
389           $test = "\$name =~ m!^$pat\$!$i";
390       } else {
391           local($var) = $type eq 'name' ? '$name' : '$file';
392           $test = "$var =~ m/". &glob_to_regex($pat). "/$i";
393       }
394       if ($type =~ m/^-i?d/) {
395           push(@dname_test, $test);
396       } else {
397           push(@name_test, $test);
398       }
399   }
400   if (@name_test) {
401       $GLOB_TESTS = join('||', @name_test);
402
403       $DO_GLOB_TESTS = 1;
404   } else {
405       $GLOB_TESTS = $DO_GLOB_TESTS = 0;
406   }
407   if (@dname_test) {
408       $DGLOB_TESTS = join('||', @dname_test);
409       $DO_DGLOB_TESTS = 1;
410   } else {
411       $DGLOB_TESTS = $DO_DGLOB_TESTS = 0;
412   }
413
414
415   ##
416   ## Process any 'magic' things from the startup file.
417   ##
418   if (@magic_tests && $HEADER_BYTES) {
419       ## the $magic' one is for when &dodir is not inlined
420       $tests = join('||',@magic_tests);
421       $MAGIC_TESTS = " { package magic; \$val = ($tests) }";
422       $DO_MAGIC_TESTS = 1;
423   } else {
424       $MAGIC_TESTS = 1;
425       $DO_MAGIC_TESTS = 0;
426   }
427
428   ##
429   ## Prepare regular expressions.
430   ##
431   {
432       local(@regex_tests);
433
434       if ($LIST_ONLY) {
435          $mflag = '';
436          ## need to have $* set, but perl5 just won''t shut up about it.
437          if ($] >= 5) {
438               $mflag = 'm';
439          } else {
440               eval ' $* = 1 ';
441          }
442       }
443
444       ##
445       ## Until I figure out a better way to deal with it,
446       ## We have to worry about a regex like [^xyz] when doing $LIST_ONLY.
447       ## Such a regex *will* match \n, and if I'm pulling in multiple
448       ## lines, it can allow lines to match that would otherwise not match.
449       ##
450       ## Therefore, if there is a '[^' in a regex, we can NOT take a chance
451       ## an use the fast listonly.
452       ##
453       $CAN_USE_FAST_LISTONLY = $LIST_ONLY;
454
455       local(@extra);
456       local($underline_glue) = ($] >= 5) ? '(:?_\cH)?' : '(_\cH)?';
457       while (@ARGV) {
458           $regex = shift(@ARGV);
459           ##
460           ## If watching for underlined things too, add another regex.
461           ##
462           if ($underlineOK) {
463              if ($regex =~ m/[?*+{}()\\.|^\$[]/) {
464                 warn "$0: warning, can't underline-safe ``$regex''.\n";
465              } else {
466                 $regex = join($underline_glue, split(//, $regex));
467              }
468           }
469
470           ## If nothing special in the regex, just use index...
471           ## is quite a bit faster.
472           if (($iflag eq '') && ($words == 0) &&
473                         $regex !~ m/[?*+{}()\\.|^\$[]/)
474           {
475               push(@regex_tests, "(index(\$_, q+$regex+)>=0)");
476
477           } else {
478               $regex =~ s#[\$\@\/]\w#\\$&#;
479               if ($words) {
480                   if ($regex =~ m/\|/) {
481                       ## could be dangerous -- see if we can wrap in parens.
482                       if ($regex =~ m/\\\d/) {
483                           warn "warning: -w and a | in a regex is dangerous.\n"
484                       } else {
485                           $regex = join($regex, '(', ')');
486                       }
487                   }
488                   $regex = join($regex, '\b', '\b');
489               }
490               $CAN_USE_FAST_LISTONLY = 0 if substr($regex, "[^") >= 0;
491               push(@regex_tests, "m/$regex/$iflag$mflag");
492           }
493
494           ## If we're done, but still have @extra to do, get set for that.
495           if (@ARGV == 0 && @extra) {
496               @ARGV = @extra;   ## now deal with the extra stuff.
497               $underlineOK = 0; ## but no more of this.
498               undef @extra;     ## or this.
499           }
500       }
501       if (@regex_tests) {
502           $REGEX_TEST = join('||', @regex_tests);
503           ## print STDERR $REGEX_TEST, "\n"; exit;
504       } else {
505           ## must be doing -find -- just give something syntactically correct.
506           $REGEX_TEST = 1;
507       }
508   }
509
510   ##
511   ## Make sure we can read the first item(s).
512   ##
513   foreach $start (@todo) {
514       $! = 2, die qq/$0: can't stat "$start"\n/
515           unless ($dev,$inode) = (stat($start))[$STAT_DEV,$STAT_INODE];
516
517       if (defined $dir_done{"$dev,$inode"}) {
518           ## ignore the repeat.
519           warn(qq/ignoring "$start" (same as "$dir_done{"$dev,$inode"}").\n/)
520                 if $VERBOSE;
521           next;
522       }
523
524       ## if -xdev was given, remember the device.
525       $xdev{$dev} = 1 if $XDEV;
526
527       ## Note that we won't want to do it again
528       $dir_done{"$dev,$inode"} = $start;
529   }
530 }
531
532
533 ##
534 ## See the comment above the __END__ above the 'sub dodir' below.
535 ##
536 sub import_program
537 {
538     sub bad {
539         print STDERR "$0: internal error (@_)\n";
540         exit 2;
541     }
542
543     ## Read from data, up to next __END__. This will be &dodir.
544     local($/) = "\n__END__";
545     $prog = <DATA>;
546     close(DATA);
547
548     $prog =~ s/\beval\b//g;       ## remove any 'eval'
549
550     ## Inline uppercase $-variables by their current values.
551     if ($] >= 5) {
552         $prog =~ s/\$([A-Z][A-Z0-9_]{2,}\b)/
553                     &bad($1) if !defined ${$main::{$1}}; ${$main::{$1}};/eg;
554     } else {
555         $prog =~ s/\$([A-Z][A-Z0-9_]{2,}\b)/local(*VAR) = $_main{$1};
556                     &bad($1) if !defined $VAR; $VAR;/eg;
557     }
558
559     eval $prog;  ## now do it. This will define &dodir;
560     $!=2, die "$0 internal error: $@\n" if $@;
561 }
562
563 ###########################################################################
564
565 ##
566 ## Read the .search file:
567 ##    Blank lines and lines that are only #-comments ignored.
568 ##    Newlines may be escaped to create long lines
569 ##    Other lines are directives.
570 ##
571 ##    A directive may begin with an optional tag in the form <...>
572 ##    Things inside the <...> are evaluated as with:
573 ##         <(this || that) && must>
574 ##    will be true if
575 ##       -xmust -xthis   or   -xmust -xthat
576 ##    were specified on the command line (order doesn't matter, though)
577 ##    A directive is not done if there is a tag and it's false.
578 ##    Any characters but whitespace and &|()>,! may appear after an -x
579 ##    (although "-xdev" is special).  -xmust,this is the same as -xmust -xthis.
580 ##    Something like -x~ would make <~> true, and <!~> false.
581 ##
582 ##    Directives are in the form:
583 ##      option: STRING
584 ##      magic : NUMBYTES : EXPR
585 ##
586 ##    With option:
587 ##      The STRING is parsed like a Bourne shell command line, and the
588 ##      options are used as if given on the command line.
589 ##      No comments are allowed on 'option' lines.
590 ##      Examples:
591 ##          # skip objects and libraries
592 ##          option: -skip '.o .a'
593 ##          # skip emacs *~ and *# files, unless -x~ given:
594 ##          <!~> option: -skip '~ #'
595 ##
596 ##    With magic:
597 ##      EXPR can be pretty much any perl (comments allowed!).
598 ##      If it evaluates to true for any particular file, it is skipped.
599 ##      The only info you'll have about a file is the variable $H, which
600 ##      will have at least the first NUMBYTES of the file (less if the file
601 ##      is shorter than that, of course, and maybe more). You'll also have
602 ##      any variables you set in previous 'magic' lines.
603 ##      Examples:
604 ##          magic: 6 : ($x6 = substr($H, 0, 6)) eq 'GIF87a'
605 ##          magic: 6 :  $x6                     eq 'GIF89a'
606 ##
607 ##          magic: 6 : (($x6 = substr($H, 0, 6)) eq 'GIF87a' ## old gif \
608 ##                                       || $x6  eq 'GIF89a' ## new gif
609 ##      (the above two sets are the same)
610 ##          ## Check the first 32 bytes for "binarish" looking bytes.
611 ##          ## Don't blindly dump on any high-bit set, as non-ASCII text
612 ##          ## often has them set. \x80 and \xff seem to be special, though.
613 ##          ## Require two in a row to not get things like perl's $^T.
614 ##          ## This is known to get *.Z, *.gz, pkzip, *.elc and about any
615 ##          ## executable you'll find.
616 ##          magic: 32 : $H =~ m/[\x00-\x06\x10-\x1a\x1c-\x1f\x80\xff]{2}/
617 ##
618 sub read_rc
619 {
620     local($file, $show) = @_;
621     local($line_num, $ln, $tag) = 0;
622     local($use_default, @default) = 0;
623
624     { package magic; $\17 = 0; } ## turn off warnings for when we run EXPR's
625
626     unless (open(RC, "$file")) {
627         $use_default=1;
628         $file = "<internal default startup file>";
629         ## no RC file -- use this default.
630         @default = split(/\n/,<<'--------INLINE_LITERAL_TEXT');
631             magic: 32 : $H =~ m/[\x00-\x06\x10-\x1a\x1c-\x1f\x80\xff]{2}/
632             option: -skip '.a .COM .elc .EXE .gz .o .pbm .xbm .dvi'
633             option: -iskip '.tarz .zip .z .lzh .jpg .jpeg .gif .uu'
634             <!~> option: -skip '~ #'
635 --------INLINE_LITERAL_TEXT
636     }
637
638     ##
639     ## Make an eval error pretty.
640     ##
641     sub clean_eval_error {
642         local($_) = @_;
643         s/ in file \(eval\) at line \d+,//g; ## perl4-style error
644         s/ at \(eval \d+\) line \d+,//g;     ## perl5-style error
645         $_ = $` if m/\n/;                    ## remove all but first line
646         "$_\n";
647     }
648
649     print "reading RC file: $file\n" if $show;
650
651     while (defined($_ = ($use_default ? shift(@default) : <RC>))) {
652         $ln = ++$line_num;                           ## note starting line num.
653         $_ .= <RC>, $line_num++ while s/\\\n?$/\n/;  ## allow continuations
654         next if /^\s*(#.*)?$/;          ## skip blank or comment-only lines.
655         $do = '';
656         
657         ## look for an initial <...> tag.
658         if (s/^\s*<([^>]*)>//) {
659             ## This simple s// will make the tag ready to eval.
660             ($tag = $msg = $1) =~
661                 s/[^\s&|(!)]+/
662                         $seen_opt{$&}=1;         ## note seen option
663                         "defined(\$opt{q>$&>})"  ## (q>> is safe quoting here)
664                 /eg;
665             
666             ## see if the tag is true or not, abort this line if not.
667             $dothis = (eval $tag);
668             $!=2, die "$file $ln <$msg>: $_".&clean_eval_error($@) if $@;
669
670             if ($show) {
671                 $msg =~ s/[^\s&|(!)]+/-x$&/;
672                 $msg =~ s/\s*!\s*/ no /g;
673                 $msg =~ s/\s*&&\s*/ and /g;
674                 $msg =~ s/\s*\|\|\s*/ or /g;
675                 $msg =~ s/^\s+//; $msg =~ s/\s+$//;
676                 $do = $dothis ? "(doing because $msg)" :
677                                 "(do if $msg)";
678             } elsif (!$dothis) {
679                 next;
680             }
681         }
682
683         if (m/^\s*option\s*:\s*/) {
684             next if $all && !$show; ## -all turns off these checks;
685             local($_) = $';
686             s/\n$//;
687             local($orig) = $_;
688             print " $do option: $_\n" if $show;
689             local($0) = "$0 ($file)"; ## for any error message.
690             local(@ARGV);
691             local($this);
692             ##
693             ## Parse $_ as a Bourne shell line -- fill @ARGV
694             ##
695             while (length) {
696                 if (s/^\s+//) {
697                     push(@ARGV, $this) if defined $this;
698                     undef $this;
699                     next;
700                 }
701                 $this = '' if !defined $this;
702                 $this .= $1 while s/^'([^']*)'// ||
703                                   s/^"([^"]*)"// ||
704                                   s/^([^'"\s\\]+)//||
705                                   s/^(\\[\D\d])//;
706                 die "$file $ln: error parsing $orig at $_\n" if m/^\S/;
707             }
708             push(@ARGV, $this) if defined $this;
709             &check_args;
710             die qq/$file $ln: unused arg "@ARGV".\n/ if @ARGV;
711             next;
712         }
713
714         if (m/^\s*magic\s*:\s*(\d+)\s*:\s*/) {
715             next if $all && !$show; ## -all turns off these checks;
716             local($bytes, $check) = ($1, $');
717
718             if ($show) {
719                 $check =~ s/\n?$/\n/;
720                 print " $do contents: $check";
721             }
722             ## Check to make sure the thing at least compiles.
723             eval  "package magic; (\$H = '1'x \$main'bytes) && (\n$check\n)\n";
724             $! = 2, die "$file $ln: ".&clean_eval_error($@) if $@;
725
726             $HEADER_BYTES = $bytes if $bytes > $HEADER_BYTES;
727             push(@magic_tests, "(\n$check\n)");
728             next;
729         }
730         $! = 2, die "$file $ln: unknown command\n";
731     }
732     close(RC);
733 }
734
735 sub message
736 {
737     if (!$STDERR_IS_TTY) {
738         print STDERR $_[0], "\n";
739     } else {
740         local($text) = @_;
741         $thislength = length($text);
742         if ($thislength >= $last_message_length) {
743             print STDERR $text, "\r";
744         } else {
745             print STDERR $text, ' 'x ($last_message_length-$thislength),"\r";
746         }       
747         $last_message_length = $thislength;
748     }
749 }
750
751 sub clear_message
752 {
753     print STDERR ' ' x $last_message_length, "\r" if $last_message_length;
754     $vv_print = $vv_size = $last_message_length = 0;
755 }
756
757 ##
758 ## Output a copy of this program with comments, extra whitespace, and
759 ## the trailing man page removed. On an ultra slow machine, such a copy
760 ## might load faster (but I can't tell any difference on my machine).
761 ##
762 sub strip {
763     seek(DATA, 0, 0) || die "$0: can't reset internal pointer.\n";
764     while(<DATA>) {
765       print, next if /INLINE_LITERAL_TEXT/.../INLINE_LITERAL_TEXT/;
766       ## must mention INLINE_LITERAL_TEXT on this line!
767       s/\#\#.*|^\s+|\s+$//; ## remove cruft
768       last if $_ eq '.00;';
769       next if ($_ eq '') || ($_ eq "'di'") || ($_ eq "'ig00'");
770       s/\$stripped=0;/\$stripped=1;/;
771       s/\s\s+/ /;  ## squish multiple whitespaces down to one.
772       print $_, "\n";
773     }
774     exit(0);
775 }
776
777 ##
778 ## Just to shut up -w. Never executed.
779 ##
780 sub dummy {
781
782     1 || &dummy || &dir_done || &bad || &message || $NEXT_DIR_ENTRY ||
783     $DELAY || $VV_SIZE || $VV_PRINT_COUNT || $STDERR_SCREWS_STDOUT ||
784     @files || @files || $magic'H || $magic'H || $xdev{''} || &clear_message;
785
786 }
787
788 ##
789 ## If the following __END__ is in place, what follows will be
790 ## inlined when the program first starts up. Any $ variable name
791 ## all in upper case, specifically, any string matching
792 ##      \$([A-Z][A-Z0-9_]{2,}\b
793 ## will have the true value for that variable inlined. Also, any 'eval' is
794 ## removed
795 ##
796 ## The idea is that when the whole thing is then eval'ed to define &dodir,
797 ## the perl optimizer will make all the decisions that are based upon
798 ## command-line options (such as $VERBOSE), since they'll be inlined as
799 ## constants
800 ##
801 ## Also, and here's the big win, the tests for matching the regex, and a
802 ## few others, are all inlined. Should be blinding speed here.
803 ##
804 ## See the read from <DATA> above for where all this takes place.
805 ## But all-in-all, you *want* the __END__ here. Comment it out only for
806 ## debugging....
807 ##
808
809 __END__
810
811 ##
812 ## Given a directory, check all "appropriate" files in it.
813 ## Shove any subdirectories into the global @todo, so they'll be done
814 ## later.
815 ##
816 ## Be careful about adding any upper-case variables, as they are subject
817 ## to being inlined. See comments above the __END__ above.
818 ##
819 sub dodir
820 {
821   local($dir) = @_;
822   $dir =~ s,/+$,,; ## remove any trailing slash.
823   unless (opendir(DIR, "$dir/.")) {
824       &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
825       warn qq($0: can't opendir "$dir/".\n);
826       return;
827   }
828
829   if ($VERBOSE) {
830       &message($dir);
831       $vv_print = $vv_size = 0;
832   }
833
834   @files = sort readdir(DIR) if $DO_SORT;
835
836   while (defined($name = eval $NEXT_DIR_ENTRY))
837   {
838     next if $name eq '.' || $name eq '..'; ## never follow these.
839
840     ## create full relative pathname.
841     $file = $dir eq '.' ? $name : "$dir/$name";
842
843     ## if link and skipping them, do so.
844     if ($NOLINKS && -l $file) {
845         warn qq/skip (symlink): $file\n/ if $WHY;
846         next;
847     }
848
849     ## skip things unless files or directories
850     unless (-f $file || -d _) {
851         if ($WHY) {
852             $why = (-S _ && "socket")       ||
853                    (-p _ && "pipe")         ||
854                    (-b _ && "block special")||
855                    (-c _ && "char special") || "somekinda special";
856             warn qq/skip ($why): $file\n/;
857         }
858         next;
859     }
860
861     ## skip things we can't read
862     unless (-r _) {
863         if ($WHY) {
864             $why = (-l $file) ? "follow" : "read";
865             warn qq/skip (can't $why): $file\n/;
866         }
867         next;
868     }
869
870     ## skip things that are empty
871     unless (-s _ || -d _) {
872         warn qq/skip (empty): $file\n/ if $WHY;
873         next;
874     }
875
876     ## Note file device & inode. If -xdev, skip if appropriate.
877     ($dev, $inode) = (stat(_))[$STAT_DEV, $STAT_INODE];
878     if ($XDEV && defined $xdev{$dev}) {
879         warn qq/skip (other device): $file\n/ if $WHY;
880         next;
881     }
882     $id = "$dev,$inode";
883
884     ## special work for a directory
885     if (-d _) {
886         ## Do checks for directory file endings.
887         if ($DO_DSKIP_TEST && (eval $DSKIP_TEST)) {
888             warn qq/skip (-dskip): $file\n/ if $WHY;
889             next;
890         }
891         ## do checks for -name/-regex/-path tests
892         if ($DO_DGLOB_TESTS && !(eval $DGLOB_TESTS)) {
893             warn qq/skip (dirname): $file\n/ if $WHY;
894             next;
895         }
896
897         ## _never_ redo a directory
898         if (defined $dir_done{$id} and $^O ne 'MSWin32') {
899             warn qq/skip (did as "$dir_done{$id}"): $file\n/ if $WHY;
900             next;
901         }
902         $dir_done{$id} = $file;     ## mark it done.
903         unshift(@todo, $file);      ## add to the list to do.
904         next;
905     }
906     if ($WHY == 0  && $VERBOSE > 1) {
907       if ($VERBOSE>2||$vv_print++>$VV_PRINT_COUNT||($vv_size+=-s _)>$VV_SIZE){
908           &message($file);
909           $vv_print = $vv_size = 0;
910       }
911     }
912
913     ## do time-related tests
914     if ($NEWER || $OLDER) {
915         $_ = (stat(_))[$STAT_MTIME];
916         if ($NEWER && $_ < $NEWER) {
917             warn qq/skip (too old): $file\n/ if $WHY;
918             next;
919         }
920         if ($OLDER && $_ > $OLDER) {
921             warn qq/skip (too new): $file\n/ if $WHY;
922             next;
923         }
924     }
925
926     ## do checks for file endings
927     if ($DO_SKIP_TEST && (eval $SKIP_TEST)) {
928         warn qq/skip (-skip): $file\n/ if $WHY;
929         next;
930     }
931
932     ## do checks for -name/-regex/-path tests
933     if ($DO_GLOB_TESTS && !(eval $GLOB_TESTS)) {
934         warn qq/skip (filename): $file\n/ if $WHY;
935         next;
936     }
937
938
939     ## If we're not repeating files,
940     ##  skip this one if we've done it, or note we're doing it.
941     unless ($DOREP) {
942         if (defined $file_done{$id}) {
943             warn qq/skip (did as "$file_done{$id}"): $file\n/ if $WHY;
944             next;
945         }
946         $file_done{$id} = $file;
947     }
948
949     if ($DO_MAGIC_TESTS) {
950         if (!open(FILE_IN, $file)) {
951             &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
952             warn qq/$0: can't open: $file\n/;
953             next;
954         }
955         unless (read(FILE_IN, $magic'H, $HEADER_BYTES)) {
956             &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
957             warn qq/$0: can't read from "$file"\n"/;
958             close(FILE_IN);
959             next;
960         }
961
962         eval $MAGIC_TESTS;
963         if ($magic'val) {
964             close(FILE_IN);
965             warn qq/skip (magic): $file\n/ if $WHY;
966             next;
967         }
968         seek(FILE_IN, 0, 0);  ## reset for later <FILE_IN>
969     }
970
971     if ($WHY != 0  && $VERBOSE > 1) {
972       if ($VERBOSE>2||$vv_print++>$VV_PRINT_COUNT||($vv_size+=-s _)>$VV_SIZE){
973           &message($file);
974           $vv_print = $vv_size = 0;
975       }
976     }
977
978     if ($DELAY) {
979         sleep($DELAY);
980     }
981
982     if ($FIND_ONLY) {
983         &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
984         print $file, "\n";
985         $retval=0; ## we've found something
986         close(FILE_IN) if $DO_MAGIC_TESTS;
987         next;
988     } else {
989         ## if we weren't doing magic tests, file won't be open yet...
990         if (!$DO_MAGIC_TESTS && !open(FILE_IN, $file)) {
991             &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
992             warn qq/$0: can't open: $file\n/;
993             next;
994         }
995         if ($LIST_ONLY && $CAN_USE_FAST_LISTONLY) {
996             ##
997             ## This is rather complex, but buys us a LOT when we're just
998             ## listing files and not the individual internal lines.
999             ##
1000             local($size) = 4096;  ## block-size in which to do reads
1001             local($nl);           ## will point to $_'s ending newline.
1002             local($read);         ## will be how many bytes read.
1003             local($_) = '';       ## Starts out empty
1004             local($hold);         ## (see below)
1005
1006             while (($read = read(FILE_IN,$_,$size,length($_)))||length($_))
1007             {
1008                 undef @parts;
1009                 ## if read a full block, but no newline, need to read more.
1010                 while ($read == $size && ($nl = rindex($_, "\n")) < 0) {
1011                     push(@parts, $_);                    ## save that part
1012                     $read = read(FILE_IN, $_, $size); ## keep trying
1013                 }
1014
1015                 ##
1016                 ## If we had to save parts, must now combine them together.
1017                 ## adjusting $nl to reflect the now-larger $_. This should
1018                 ## be a lot more efficient than using any kind of .= in the
1019                 ## loop above.
1020                 ##
1021                 if (@parts) {
1022                     local($lastlen) = length($_); #only need if $nl >= 0
1023                     $_ = join('', @parts, $_);
1024                     $nl = length($_) - ($lastlen - $nl) if $nl >= 0;
1025                 }
1026
1027                 ##
1028                 ## If we're at the end of the file, then we can use $_ as
1029                 ## is.  Otherwise, we need to remove the final partial-line
1030                 ## and save it so that it'll be at the beginning of the
1031                 ## next read (where the rest of the line will be layed in
1032                 ## right after it).  $hold will be what we should save
1033                 ## until next time.
1034                 ##
1035                 if ($read != $size || $nl < 0) {
1036                     $hold = '';
1037                 } else {
1038                     $hold = substr($_, $nl + 1);
1039                     substr($_, $nl + 1) = '';
1040                 }
1041
1042                 ##
1043                 ## Now have a bunch of full lines in $_. Use it.
1044                 ##
1045                 if (eval $REGEX_TEST) {
1046                     &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
1047                     print $file, "\n";
1048                     $retval=0; ## we've found something
1049
1050                     last;
1051                 }
1052
1053                 ## Prepare for next read....
1054                 $_ = $hold;
1055             }
1056
1057         } else {  ## else not using faster block scanning.....
1058
1059             $lines_printed = 0 if $NICE;
1060             while (<FILE_IN>) {
1061                 study;
1062                 next unless (eval $REGEX_TEST);
1063
1064                 ##
1065                 ## We found a matching line.
1066                 ##
1067                 $retval=0;
1068                 &clear_message if $VERBOSE && $STDERR_SCREWS_STDOUT;
1069                 if ($LIST_ONLY) {
1070                     print $file, "\n";
1071                     last;
1072                 } else {
1073                     ## prepare to print line.
1074                     if ($NICE && $lines_printed++ == 0) {
1075                         print '-' x 70, "\n" if $NICE > 1;
1076                         print $file, ":\n";
1077                     }
1078
1079                     ##
1080                     ## Print all the prelim stuff. This looks less efficient
1081                     ## than it needs to be, but that's so that when the eval
1082                     ## is compiled (and the tests are optimized away), the
1083                     ## result will be less actual PRINTs than the more natural
1084                     ## way of doing these tests....
1085                     ##
1086                     if ($NICE) {
1087                         if ($REPORT_LINENUM) {
1088                             print " line $.:  ";
1089                         } else {
1090                             print "  ";
1091                         }
1092                     } elsif ($REPORT_LINENUM && $PREPEND_FILENAME) {
1093                         print "$file,:$.: ";
1094                     } elsif ($PREPEND_FILENAME) {
1095                         print "$file: ";
1096                     } elsif ($REPORT_LINENUM) {
1097                         print "$.: ";
1098                     }
1099                     print $_;
1100                     print "\n" unless m/\n$/;
1101                 }
1102             }
1103             print "\n" if ($NICE > 1) && $lines_printed;
1104         }
1105         close(FILE_IN);
1106     }
1107   }
1108   closedir(DIR);
1109 }
1110
1111 __END__
1112 .00;                    ## finish .ig
1113  
1114 'di                     \" finish diversion--previous line must be blank
1115 .nr nl 0-1              \" fake up transition to first page again
1116 .nr % 0                 \" start at page 1
1117 .\"__________________NORMAL_MAN_PAGE_BELOW_________________
1118 .ll+10n
1119 .TH search 1 "Dec 17, 1994"
1120 .SH SEARCH
1121 search \- search files (a'la grep) in a whole directory tree.
1122 .SH SYNOPSIS
1123 search [ grep-like and find-like options] [regex ....]
1124 .SH DESCRIPTION
1125 .I Search
1126 is more or less a combo of 'find' and 'grep' (although the regular
1127 expression flavor is that of the perl being used, which is closer to
1128 egrep's than grep's).
1129
1130 .I Search
1131 does generally the same kind of thing that
1132 .nf
1133    find <blah blah> | xargs egrep <blah blah>
1134 .fi
1135 does, but is
1136 .I much
1137 more powerful and efficient (and intuitive, I think).
1138
1139 This manual describes
1140 .I search
1141 as of version "941227.4". You can always find the latest version at
1142 .nf
1143    http://www.wg.omron.co.jp/~jfriedl/perl/index.html
1144 .fi
1145
1146 .SH "QUICK EXAMPLE"
1147 Basic use is simple:
1148 .nf
1149     % search jeff
1150 .fi
1151 will search files in the current directory, and all sub directories, for
1152 files that have "jeff" in them. The lines will be listed with the
1153 containing file's name prepended.
1154 .PP
1155 If you list more than one regex, such as with
1156 .nf
1157     % search jeff Larry Randal+ 'Stoc?k' 'C.*son'
1158 .fi
1159 then a line containing any of the regexes will be listed.
1160 This makes it effectively the same as
1161 .nf
1162     % search 'jeff|Larry|Randal+|Stoc?k|C.*son'
1163 .fi
1164 However, listing them separately is much more efficient (and is easier
1165 to type).
1166 .PP
1167 Note that in the case of these examples, the
1168 .B \-w
1169 (list whole-words only) option would be useful.
1170 .PP
1171 Normally, various kinds of files are automatically removed from consideration.
1172 If it has has a certain ending (such as ".tar", ".Z", ".o", .etc), or if
1173 the beginning of the file looks like a binary, it'll be excluded.
1174 You can control exactly how this works -- see below. One quick way to
1175 override this is to use the
1176 .B \-all
1177 option, which means to consider all the files that would normally be
1178 automatically excluded.
1179 Or, if you're curious, you can use
1180 .B \-why
1181 to have notes about what files are skipped (and why) printed to stderr.
1182
1183 .SH "BASIC OVERVIEW"
1184 Normally, the search starts in the current directory, considering files in
1185 all subdirectories.
1186
1187 You can use the
1188 .I ~/.search
1189 file to control ways to automatically exclude files.
1190 If you don't have this file, a default one will kick in, which automatically
1191 add
1192 .nf
1193     -skip .o .Z .gif
1194 .fi
1195 (among others) to exclude those kinds of files (which you probably want to
1196 skip when searching for text, as is normal).
1197 Files that look to be be binary will also be excluded.
1198
1199 Files ending with "#" and "~" will also be excluded unless the
1200 .B -x~
1201 option is given. 
1202
1203 You can use
1204 .B -showrc
1205 to show what kinds of files will normally be skipped.
1206 See the section on the startup file
1207 for more info.
1208
1209 You can use the
1210 .B -all
1211 option to indicate you want to consider all files that would otherwise be
1212 skipped by the startup file.
1213
1214 Based upon various other flags (see "WHICH FILES TO CONSIDER" below),
1215 more files might be removed from consideration. For example
1216 .nf
1217     -mtime 3
1218 .fi
1219 will exclude files that aren't at least three days old (change the 3 to -3
1220 to exclude files that are more than three days old), while
1221 .nf
1222     -skip .*
1223 .fi
1224 would exclude any file beginning with a dot (of course, '.' and '..'  are
1225 special and always excluded).
1226
1227 If you'd like to see what files are being excluded, and why, you can get the
1228 list via the
1229 .B \-why
1230 option.
1231
1232 If a file makes it past all the checks, it is then "considered".
1233 This usually means it is greped for the regular expressions you gave
1234 on the command line.
1235
1236 If any of the regexes match a line, the line is printed.
1237 However, if
1238 .B -list
1239 is given, just the filename is printed. Or, if
1240 .B -nice
1241 is given, a somewhat more (human-)readable output is generated.
1242
1243 If you're searching a huge tree and want to keep informed about how
1244 the search is progressing,
1245 .B -v
1246 will print (to stderr) the current directory being searched.
1247 Using
1248 .B -vv
1249 will also print the current file "every so often", which could be useful
1250 if a directory is huge. Using
1251 .B -vvv
1252 will print the update with every file.
1253
1254 Below is the full listing of options.
1255
1256 .SH "OPTIONS TELLING *WHERE* TO SEARCH"
1257 .TP
1258 .BI -dir " DIR"
1259 Start searching at the named directory instead of the current directory.
1260 If multiple
1261 .B -dir
1262 arguments are given, multiple trees will be searched.
1263 .TP
1264 .BI -ddir " DIR"
1265 Like
1266 .B -dir
1267 except it flushes any previous
1268 .B -dir
1269 directories (i.e. "-dir A -dir B -dir C" will search A, B, and C, while
1270 "-dir A -ddir B -dir C" will search only B and C. This might be of use
1271 in the startup file (see that section below).
1272 .TP
1273 .B -xdev
1274 Stay on the same filesystem as the starting directory/directories.
1275 .TP
1276 .B -sort
1277 Sort the items in a directory before processing them.
1278 Normally they are processed in whatever order they happen to be read from
1279 the directory.
1280 .TP
1281 .B -nolinks
1282 Don't follow symbolic links. Normally they're followed.
1283
1284 .SH "OPTIONS CONTROLLING WHICH FILES TO CONSIDER AND EXCLUDE"
1285 .TP
1286 .BI -mtime " NUM"
1287 Only consider files that were last changed more than
1288 .I NUM
1289 days ago
1290 (less than
1291 .I NUM
1292 days if
1293 .I NUM
1294 has '-' prepended, i.e. "-mtime -2.5" means to consider files that
1295 have been changed in the last two and a half days).
1296 .TP
1297 .B -older FILE
1298 Only consider files that have not changed since
1299 .I FILE
1300 was last changed.
1301 If there is any upper case in the "-older", "or equal" is added to the sense
1302 of the test.  Therefore, "search -older ./file regex" will never consider
1303 "./file", while "search -Older ./file regex" will.
1304
1305 If a file is a symbolic link, the time used is that of the file and not the
1306 link.
1307 .TP
1308 .BI -newer " FILE"
1309 Opposite of
1310 .BR -older .
1311 .TP
1312 .BI -name " GLOB"
1313 Only consider files that match the shell filename pattern
1314 .IR GLOB .
1315 The check is only done on a file's name (use
1316 .B -path
1317 to check the whole path, and use
1318 .B -dname
1319 to check directory names).
1320
1321 Multiple specifications can be given by separating them with spaces, a'la
1322 .nf
1323     -name '*.c *.h'
1324 .fi
1325 to consider C source and header files.
1326 If
1327 .I GLOB
1328 doesn't contain any special pattern characters, a '*' is prepended.
1329 This last example could have been given as
1330 .nf
1331    -name '.c .h'
1332 .fi
1333 It could also be given as
1334 .nf
1335     -name .c -name .h
1336 .fi
1337 or
1338 .nf
1339     -name '*.c' -name '*.h'
1340 .fi
1341 or
1342 .nf
1343     -name '*.[ch]'
1344 .fi
1345 (among others)
1346 but in this last case, you have to be sure to supply the leading '*'.
1347 .TP
1348 .BI -path " GLOB"
1349 Like
1350 .B -name
1351 except the entire path is checked against the pattern.
1352 .TP
1353 .B -regex " REGEX"
1354 Considers files whose names (not paths) match the given perl regex
1355 exactly.
1356 .TP
1357 .BI -iname " GLOB"
1358 Case-insensitive version of
1359 .BR -name .
1360 .TP
1361 .BI -ipath " GLOB"
1362 Case-insensitive version of
1363 .BR -path .
1364 .TP
1365 .BI -iregex " REGEX"
1366 Case-insensitive version of
1367 .BR -regex .
1368
1369 .TP
1370 .BI -dpath " GLOB"
1371 Only search down directories whose path matches the given pattern (this
1372 doesn't apply to the initial directory given by
1373 .BI -dir ,
1374 of course).
1375 Something like
1376 .nf
1377     -dir /usr/man -dpath /usr/man/man*
1378 .fi
1379 would completely skip
1380 "/usr/man/cat1", "/usr/man/cat2", etc.
1381 .TP
1382 .BI -dskip " GLOB"
1383 Skips directories whose name (not path) matches the given pattern.
1384 Something like
1385 .nf
1386     -dir /usr/man -dskip cat*
1387 .fi
1388 would completely skip any directory in the tree whose name begins with "cat"
1389 (including "/usr/man/cat1", "/usr/man/cat2", etc.).
1390 .TP
1391 .BI -dregex " REGEX"
1392 Like
1393 .BI -dpath ,
1394 but the pattern is a full perl regex. Note that this quite different
1395 from
1396 .B -regex
1397 which considers only file names (not paths). This option considers
1398 full directory paths (not just names). It's much more useful this way.
1399 Sorry if it's confusing.
1400 .TP
1401 .BI -dpath " GLOB"
1402 This option exists, but is probably not very useful. It probably wants to
1403 be like the '-below' or something I mention in the "TODO" section.
1404 .TP
1405 .BI -idpath " GLOB"
1406 Case-insensitive version of
1407 .BR -dpath .
1408 .TP
1409 .BI -idskip " GLOB"
1410 Case-insensitive version of
1411 .BR -dskip .
1412 .TP
1413 .BI -idregex " REGEX"
1414 Case-insensitive version of
1415 .BR -dregex .
1416 .TP
1417 .B -all
1418 Ignore any 'magic' or 'option' lines in the startup file.
1419 The effect is that all files that would otherwise be automatically
1420 excluded are considered.
1421 .TP
1422 .BI -x SPECIAL
1423 Arguments starting with
1424 .B -x
1425 (except
1426 .BR -xdev ,
1427 explained elsewhere) do special interaction with the
1428 .I ~/.search
1429 startup file. Something like
1430 .nf
1431         -xflag1 -xflag2
1432 .fi
1433 will turn on "flag1" and "flag2" in the startup file (and is
1434 the same as "-xflag1,flag2"). You can use this to write your own
1435 rules for what kinds of files are to be considered.
1436
1437 For example, the internal-default startup file contains the line
1438 .nf
1439         <!~> option: -skip '~ #'
1440 .fi
1441 This means that if the
1442 .B -x~
1443 flag is
1444 .I not
1445 seen, the option
1446 .nf
1447     -skip '~ #'
1448 .fi
1449 should be done.
1450 The effect is that emacs temp and backup files are not normally
1451 considered, but you can included them with the -x~ flag.
1452
1453 You can write your own rules to customize
1454 .I search
1455 in powerful ways. See the STARTUP FILE section below.
1456 .TP
1457 .B -why
1458 Print a message (to stderr) when and why a file is not considered.
1459
1460 .SH "OPTIONS TELLING WHAT TO DO WITH FILES THAT WILL BE CONSIDERED"
1461 .TP
1462 .B -find
1463 (you can use
1464 .B -f
1465 as well).
1466 This option changes the basic action of
1467 .IR search .
1468
1469 Normally, if a file is considered, it is searched
1470 for the regular expressions as described earlier. However, if this option
1471 is given, the filename is printed and no searching takes place. This turns
1472 .I search
1473 into a 'find' of some sorts.
1474
1475 In this case, no regular expressions are needed on the command line
1476 (any that are there are silently ignored).
1477
1478 This is not intended to be a replacement for the 'find' program,
1479 but to aid
1480 you in understanding just what files are getting past the exclusion checks.
1481 If you really want to use it as a sort of replacement for the 'find' program,
1482 you might want to use
1483 .B -all
1484 so that it doesn't waste time checking to see if the file is binary, etc
1485 (unless you really want that, of course).
1486
1487 If you use
1488 .BR -find ,
1489 none of the "GREP-LIKE OPTIONS" (below) matter.
1490
1491 As a replacement for 'find',
1492 .I search
1493 is probably a bit slower (or in the case of GNU find, a lot slower --
1494 GNU find is
1495 .I unbelievably
1496 fast).
1497 However, "search -ffind"
1498 might be more useful than 'find' when options such as
1499 .B -skip
1500 are used (at least until 'find' gets such functionality).
1501 .TP
1502 .B -ffind
1503 (or
1504 .BR -ff )
1505 A faster more 'find'-like find. Does
1506 .nf
1507     -find  -all -dorep
1508 .fi
1509 .SH "GREP-LIKE OPTIONS"
1510 These options control how a searched file is accessed,
1511 and how things are printed.
1512 .TP
1513 .B -i
1514 Ignore letter case when matching.
1515 .TP
1516 .B -w
1517 Consider only whole-word matches ("whole word" as defined by perl's "\\b"
1518 regex).
1519 .TP
1520 .B -u
1521 If the regex(es) is/are simple, try to modify them so that they'll work
1522 in manpage-like underlined text (i.e. like _^Ht_^Hh_^Hi_^Hs).
1523 This is very rudimentary at the moment.
1524 .TP
1525 .B -list
1526 (you can use
1527 .B -l
1528 too).
1529 Don't print matching lines, but the names of files that contain matching
1530 lines. This will likely be *much* faster, as special optimizations are
1531 made -- particularly with large files.
1532 .TP
1533 .B -n
1534 Pepfix each line by its line number.
1535 .TP
1536 .B -nice
1537 Not a grep-like option, but similar to
1538 .BR -list ,
1539 so included here.
1540 .B -nice
1541 will have the output be a bit more human-readable, with matching lines printed
1542 slightly indented after the filename, a'la
1543 .nf
1544
1545    % search foo
1546    somedir/somefile: line with foo in it
1547    somedir/somefile: some food for thought
1548    anotherdir/x: don't be a buffoon!
1549    %
1550
1551 .fi
1552 will become
1553 .nf
1554
1555    % search -nice foo
1556    somedir/somefile:
1557      line with foo in it
1558      some food for thought
1559    anotherdir/x:
1560      don't be a buffoon!
1561    %
1562
1563 .fi
1564 This option due to Lionel Cons.
1565 .TP
1566 .B -nnice
1567 Be a bit nicer than
1568 .BR -nice .
1569 Prefix each file's output by a rule line, and follow with an extra blank line.
1570 .TP
1571 .B -h
1572 Don't prepend each output line with the name of the file
1573 (meaningless when
1574 .B -find
1575 or
1576 .B -l
1577 are given).
1578
1579 .SH "OTHER OPTIONS"
1580 .TP
1581 .B -help
1582 Print the usage information.
1583 .TP
1584 .B -version
1585 Print the version information and quit.
1586 .TP
1587 .B -v
1588 Set the level of message verbosity.
1589 .B -v
1590 will print a note whenever a new directory is entered.
1591 .B -vv
1592 will also print a note "every so often". This can be useful to see
1593 what's happening when searching huge directories.
1594 .B -vvv
1595 will print a new with every file.
1596 .B -vvvv
1597 is
1598 -vvv
1599 plus
1600 .BR -why .
1601 .TP
1602 .B -e
1603 This ends the options, and can be useful if the regex begins with '-'.
1604 .TP
1605 .B -showrc
1606 Shows what is being considered in the startup file, then exits.
1607 .TP
1608 .B -dorep
1609 Normally, an identical file won't be checked twice (even with multiple
1610 hard or symbolic links). If you're just trying to do a fast
1611 .BR -find ,
1612 the bookkeeping to remember which files have been seen is not desirable,
1613 so you can eliminate the bookkeeping with this flag.
1614
1615 .SH "STARTUP FILE"
1616 When
1617 .I search
1618 starts up, it processes the directives in
1619 .IR ~/.search .
1620 If no such file exists, a default
1621 internal version is used.
1622
1623 The internal version looks like:
1624 .nf
1625
1626    magic: 32 : $H =~ m/[\ex00-\ex06\ex10-\ex1a\ex1c-\ex1f\ex80\exff]{2}/
1627    option: -skip '.a .COM .elc .EXE .gz .o .pbm .xbm .dvi'
1628    option: -iskip '.tarz .zip .z .lzh .jpg .jpeg .gif .uu'
1629    <!~> option: -skip '~ #'
1630
1631 .fi
1632 If you wish to create your own "~/.search",
1633 you might consider copying the above, and then working from there.
1634
1635 There are two kinds of directives in a startup file: "magic" and "option".
1636 .RS 0n
1637 .TP
1638 OPTION
1639 Option lines will automatically do the command-line options given.
1640 For example, the line
1641 .nf
1642         option: -v
1643 .fi
1644 in you startup file will turn on -v every time, without needing to type it
1645 on the command line.
1646
1647 The text on the line after the "option:" directive is processed
1648 like the Bourne shell, so make sure to pay attention to quoting.
1649 .nf
1650         option: -skip .exe .com
1651 .fi
1652 will give an error (".com" by itself isn't a valid option), while
1653 .nf
1654         option: -skip ".exe .com"
1655 .fi
1656 will properly include it as part of -skip's argument.
1657
1658 .TP
1659 MAGIC
1660 Magic lines are used to determine if a file should be considered a binary
1661 or not (the term "magic" refers to checking a file's magic number).  These
1662 are described in more detail below.
1663 .RE
1664
1665 Blank lines and comments (lines beginning with '#') are allowed.
1666
1667 If a line begins with  <...>, then it's a check to see if the
1668 directive on the line should be done or not. The stuff inside the <...>
1669 can contain perl's && (and), || (or), ! (not), and parens for grouping,
1670 along with "flags" that might be indicated by the user with
1671 .BI -x flag
1672 options.
1673
1674 For example, using "-xfoo" will cause "foo" to be true inside the <...>
1675 blocks. Therefore, a line beginning with "<foo>" would be done only when
1676 "-xfoo" had been specified, while a line beginning with "<!foo>" would be
1677 done only when "-xfoo" is not specified (of course, a line without any <...>
1678 is done in either case).
1679
1680 A realistic example might be
1681 .nf
1682         <!v> -vv
1683 .fi
1684 This will cause -vv messages to be the default, but allow "-xv" to override.
1685
1686 There are a few flags that are set automatically:
1687 .RS
1688 .TP
1689 .B TTY
1690 true if the output is to the screen (as opposed to being redirected to a file).
1691 You can force this (as with all the other automatic flags) with -xTTY.
1692 .TP
1693 .B -v
1694 True if -v was specified. If -vv was specified, both 
1695 .B -v
1696 and
1697 .B -vv
1698 flags are true (and so on).
1699 .TP
1700 .B -nice
1701 True if -nice was specified. Same thing about -nnice as for -vv.
1702 .PP
1703 .TP
1704 .B -list
1705 true if -list (or -l) was given.
1706 .TP
1707 .B -dir
1708 true if -dir was given.
1709 .RE
1710
1711 Using this info, you might change the last example to
1712 .nf
1713
1714     <!v && !-v> option: -vv
1715
1716 .fi
1717 The added "&& !-v" means "and if the '-v' option not given".
1718 This will allow you to use "-v" alone on the command line, and not
1719 have this directive add the more verbose "-vv" automatically.
1720
1721 .RS 0
1722 Some other examples:
1723 .TP
1724 <!-dir && !here> option: -dir ~/
1725 Effectively make the default directory your home directory (instead of the
1726 current directory). Using -dir or -xhere will undo this.
1727 .TP
1728 <tex> option: -name .tex -dir ~/pub
1729 Create '-xtex' to search only "*.tex" files in your ~/pub directory tree.
1730 Actually, this could be made a bit better. If you combine '-xtex' and '-dir'
1731 on the command line, this directive will add ~/pub to the list, when you
1732 probably want to use the -dir directory only. You could do
1733 .nf
1734
1735    <tex> option: -name .tex
1736    <tex && !-dir> option: -dir ~/pub
1737 .fi
1738
1739 to will allow '-xtex' to work as before, but allow a command-line "-dir"
1740 to take precedence with respect to ~/pub.
1741 .TP
1742 <fluff> option: -nnice -sort -i -vvv
1743 Combine a few user-friendly options into one '-xfluff' option.
1744 .TP
1745 <man> option: -ddir /usr/man -v -w
1746 When the '-xman' option is given, search "/usr/man" for whole-words
1747 (of whatever regex or regexes are given on the command line), with -v.
1748 .RE
1749
1750 The lines in the startup file are executed from top to bottom, so something
1751 like
1752 .nf
1753
1754    <both> option: -xflag1 -xflag2
1755    <flag1> option: ...whatever...
1756    <flag2> option: ...whatever...
1757
1758 .fi
1759 will allow '-xboth' to be the same as '-xflag1 -xflag2' (or '-xflag1,flag2'
1760 for that matter). However, if you put the "<both>" line below the others,
1761 they will not be true when encountered, so the result would be different
1762 (and probably undesired).
1763
1764 The "magic" directives are used to determine if a file looks to be binary
1765 or not. The form of a magic line is
1766 .nf
1767     magic: \fISIZE\fP : \fIPERLCODE\fP
1768 .fi
1769 where
1770 .I SIZE
1771 is the number of bytes of the file you need to check, and
1772 .I PERLCODE
1773 is the code to do the check. Within
1774 .IR PERLCODE ,
1775 the variable $H will hold at least the first
1776 .I SIZE
1777 bytes of the file (unless the file is shorter than that, of course).
1778 It might hold more bytes. The perl should evaluate to true if the file
1779 should be considered a binary.
1780
1781 An example might be
1782 .nf
1783     magic: 6 : substr($H, 0, 6) eq 'GIF87a'
1784 .fi
1785 to test for a GIF ("-iskip .gif" is better, but this might be useful
1786 if you have images in files without the ".gif" extension).
1787
1788 Since the startup file is checked from top to bottom, you can be a bit
1789 efficient:
1790 .nf
1791     magic: 6 : ($x6 = substr($H, 0, 6)) eq 'GIF87a'
1792     magic: 6 :  $x6                     eq 'GIF89a'
1793 .fi
1794 You could also write the same thing as
1795 .nf
1796   magic: 6 : (($x6 = substr($H, 0, 6)) eq 'GIF87a') || ## an old gif, or.. \e
1797                $x6                     eq 'GIF89a'     ## .. a new one.
1798 .fi
1799 since newlines may be escaped.
1800
1801 The default internal startup file includes
1802 .nf
1803    magic: 32 : $H =~ m/[\ex00-\ex06\ex10-\ex1a\ex1c-\ex1f\ex80\exff]{2}/
1804 .fi
1805 which checks for certain non-printable characters, and catches a large
1806 number of binary files, including most system's executables, linkable
1807 objects, compressed, tarred, and otherwise folded, spindled, and mutilated
1808 files.
1809
1810 Another example might be
1811 .nf
1812     ## an archive library
1813     magic: 17 : substr($H, 0, 17) eq "!<arch>\en__.SYMDEF"
1814 .fi
1815
1816 .SH "RETURN VALUE"
1817 .I Search
1818 returns zero if lines (or files, if appropriate) were found,
1819 or if no work was requested (such as with
1820 .BR -help ).
1821 Returns 1 if no lines (or files) were found.
1822 Returns 2 on error.
1823
1824 .SH TODO
1825 Things I'd like to add some day:
1826 .nf
1827   + show surrounding lines (context).
1828   + highlight matched portions of lines.
1829   + add '-and', which can go between regexes to override
1830     the default logical or of the regexes.
1831   + add something like
1832       -below GLOB
1833     which will examine a tree and only consider files that
1834     lie in a directory deeper than one named by the pattern.
1835   + add 'warning' and 'error' directives.
1836   + add 'help' directive.
1837 .fi
1838 .SH BUGS
1839 If -xdev and multiple -dir arguments are given, any file in any of the
1840 target filesystems are allowed. It would be better to allow each filesystem
1841 for each separate tree.
1842
1843 Multiple -dir args might also cause some confusing effects. Doing
1844 .nf
1845    -dir some/dir -dir other
1846 .fi
1847 will search "some/dir" completely, then search "other" completely. This
1848 is good. However, something like
1849 .nf
1850    -dir some/dir -dir some/dir/more/specific
1851 .fi
1852 will search "some/dir" completely *except for* "some/dir/more/specific",
1853 after which it will return and be searched. Not really a bug, but just sort
1854 of odd.
1855
1856 File times (for -newer, etc.) of symbolic links are for the file, not the
1857 link. This could cause some misunderstandings.
1858
1859 Probably more. Please let me know.
1860 .SH AUTHOR
1861 Jeffrey Friedl, Omron Corp (jfriedl@omron.co.jp)
1862 .br
1863 http://www.wg.omron.co.jp/cgi-bin/j-e/jfriedl.html
1864
1865 .SH "LATEST SOURCE"
1866 See http://www.wg.omron.co.jp/~jfriedl/perl/index.html