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