1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779 |
- #!/usr/bin/env perl
- #
- # This file, ack, is generated code.
- # Please DO NOT EDIT or send patches for it.
- #
- # Please take a look at the source from
- # http://github.com/petdance/ack
- # and submit patches against the individual files
- # that build ack.
- #
- use warnings;
- use strict;
- our $VERSION = '1.94';
- # Check http://betterthangrep.com/ for updates
- # These are all our globals.
- MAIN: {
- if ( $App::Ack::VERSION ne $main::VERSION ) {
- App::Ack::die( "Program/library version mismatch\n\t$0 is $main::VERSION\n\t$INC{'App/Ack.pm'} is $App::Ack::VERSION" );
- }
- # Do preliminary arg checking;
- my $env_is_usable = 1;
- for ( @ARGV ) {
- last if ( $_ eq '--' );
- # Priorities! Get the --thpppt checking out of the way.
- /^--th[pt]+t+$/ && App::Ack::_thpppt($_);
- # See if we want to ignore the environment. (Don't tell Al Gore.)
- if ( /^--(no)?env$/ ) {
- $env_is_usable = defined $1 ? 0 : 1;
- }
- }
- if ( $env_is_usable ) {
- unshift( @ARGV, App::Ack::read_ackrc() );
- }
- else {
- my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );
- delete @ENV{@keys};
- }
- App::Ack::load_colors();
- if ( exists $ENV{ACK_SWITCHES} ) {
- App::Ack::warn( 'ACK_SWITCHES is no longer supported. Use ACK_OPTIONS.' );
- }
- if ( !@ARGV ) {
- App::Ack::show_help();
- exit 1;
- }
- main();
- }
- sub main {
- my $opt = App::Ack::get_command_line_options();
- $| = 1 if $opt->{flush}; # Unbuffer the output if flush mode
- if ( App::Ack::input_from_pipe() ) {
- # We're going into filter mode
- for ( qw( f g l ) ) {
- $opt->{$_} and App::Ack::die( "Can't use -$_ when acting as a filter." );
- }
- $opt->{show_filename} = 0;
- $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt );
- if ( my $nargs = @ARGV ) {
- my $s = $nargs == 1 ? '' : 's';
- App::Ack::warn( "Ignoring $nargs argument$s on the command-line while acting as a filter." );
- }
- my $res = App::Ack::Resource::Basic->new( '-' );
- my $nmatches;
- if ( $opt->{count} ) {
- $nmatches = App::Ack::search_and_list( $res, $opt );
- }
- else {
- # normal searching
- $nmatches = App::Ack::search_resource( $res, $opt );
- }
- $res->close();
- App::Ack::exit_from_ack( $nmatches );
- }
- my $file_matching = $opt->{f} || $opt->{lines};
- if ( $file_matching ) {
- App::Ack::die( "Can't specify both a regex ($opt->{regex}) and use one of --line, -f or -g." ) if $opt->{regex};
- }
- else {
- $opt->{regex} = App::Ack::build_regex( defined $opt->{regex} ? $opt->{regex} : shift @ARGV, $opt );
- }
- # check that all regexes do compile fine
- App::Ack::check_regex( $_ ) for ( $opt->{regex}, $opt->{G} );
- my $what = App::Ack::get_starting_points( \@ARGV, $opt );
- my $iter = App::Ack::get_iterator( $what, $opt );
- App::Ack::filetype_setup();
- my $nmatches = 0;
- App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};
- if ( $opt->{f} ) {
- $nmatches = App::Ack::print_files( $iter, $opt );
- }
- elsif ( $opt->{l} || $opt->{count} ) {
- $nmatches = App::Ack::print_files_with_matches( $iter, $opt );
- }
- else {
- $nmatches = App::Ack::print_matches( $iter, $opt );
- }
- close $App::Ack::fh;
- App::Ack::exit_from_ack( $nmatches );
- }
- =head1 NAME
- ack - grep-like text finder
- =head1 SYNOPSIS
- ack [options] PATTERN [FILE...]
- ack -f [options] [DIRECTORY...]
- =head1 DESCRIPTION
- Ack is designed as a replacement for 99% of the uses of F<grep>.
- Ack searches the named input FILEs (or standard input if no files are
- named, or the file name - is given) for lines containing a match to the
- given PATTERN. By default, ack prints the matching lines.
- Ack can also list files that would be searched, without actually searching
- them, to let you take advantage of ack's file-type filtering capabilities.
- =head1 FILE SELECTION
- I<ack> is intelligent about the files it searches. It knows about
- certain file types, based on both the extension on the file and,
- in some cases, the contents of the file. These selections can be
- made with the B<--type> option.
- With no file selections, I<ack> only searches files of types that
- it recognizes. If you have a file called F<foo.wango>, and I<ack>
- doesn't know what a .wango file is, I<ack> won't search it.
- The B<-a> option tells I<ack> to select all files, regardless of
- type.
- Some files will never be selected by I<ack>, even with B<-a>,
- including:
- =over 4
- =item * Backup files: Files matching F<#*#> or ending with F<~>.
- =item * Coredumps: Files matching F<core.\d+>
- =back
- However, I<ack> always searches the files given on the command line,
- no matter what type. Furthermore, by specifying the B<-u> option all
- files will be searched.
- =head1 DIRECTORY SELECTION
- I<ack> descends through the directory tree of the starting directories
- specified. However, it will ignore the shadow directories used by
- many version control systems, and the build directories used by the
- Perl MakeMaker system. You may add or remove a directory from this
- list with the B<--[no]ignore-dir> option. The option may be repeated
- to add/remove multiple directories from the ignore list.
- For a complete list of directories that do not get searched, run
- F<ack --help>.
- =head1 WHEN TO USE GREP
- I<ack> trumps I<grep> as an everyday tool 99% of the time, but don't
- throw I<grep> away, because there are times you'll still need it.
- E.g., searching through huge files looking for regexes that can be
- expressed with I<grep> syntax should be quicker with I<grep>.
- If your script or parent program uses I<grep> C<--quiet> or
- C<--silent> or needs exit 2 on IO error, use I<grep>.
- =head1 OPTIONS
- =over 4
- =item B<-a>, B<--all>
- Operate on all files, regardless of type (but still skip directories
- like F<blib>, F<CVS>, etc.)
- =item B<-A I<NUM>>, B<--after-context=I<NUM>>
- Print I<NUM> lines of trailing context after matching lines.
- =item B<-B I<NUM>>, B<--before-context=I<NUM>>
- Print I<NUM> lines of leading context before matching lines.
- =item B<-C [I<NUM>]>, B<--context[=I<NUM>]>
- Print I<NUM> lines (default 2) of context around matching lines.
- =item B<-c>, B<--count>
- Suppress normal output; instead print a count of matching lines for
- each input file. If B<-l> is in effect, it will only show the
- number of lines for each file that has lines matching. Without
- B<-l>, some line counts may be zeroes.
- If combined with B<-h> (B<--no-filename>) ack outputs only one total count.
- =item B<--color>, B<--nocolor>
- B<--color> highlights the matching text. B<--nocolor> supresses
- the color. This is on by default unless the output is redirected.
- On Windows, this option is off by default unless the
- L<Win32::Console::ANSI> module is installed or the C<ACK_PAGER_COLOR>
- environment variable is used.
- =item B<--color-filename=I<color>>
- Sets the color to be used for filenames.
- =item B<--color-match=I<color>>
- Sets the color to be used for matches.
- =item B<--color-lineno=I<color>>
- Sets the color to be used for line numbers.
- =item B<--column>
- Show the column number of the first match. This is helpful for editors
- that can place your cursor at a given position.
- =item B<--env>, B<--noenv>
- B<--noenv> disables all environment processing. No F<.ackrc> is read
- and all environment variables are ignored. By default, F<ack> considers
- F<.ackrc> and settings in the environment.
- =item B<--flush>
- B<--flush> flushes output immediately. This is off by default
- unless ack is running interactively (when output goes to a pipe
- or file).
- =item B<-f>
- Only print the files that would be searched, without actually doing
- any searching. PATTERN must not be specified, or it will be taken as
- a path to search.
- =item B<--follow>, B<--nofollow>
- Follow or don't follow symlinks, other than whatever starting files
- or directories were specified on the command line.
- This is off by default.
- =item B<-G I<REGEX>>
- Only paths matching I<REGEX> are included in the search. The entire
- path and filename are matched against I<REGEX>, and I<REGEX> is a
- Perl regular expression, not a shell glob.
- The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.
- =item B<-g I<REGEX>>
- Print files where the relative path + filename matches I<REGEX>. This option is
- a convenience shortcut for B<-f> B<-G I<REGEX>>.
- The options B<-i>, B<-w>, B<-v>, and B<-Q> do not apply to this I<REGEX>.
- =item B<--group>, B<--nogroup>
- B<--group> groups matches by file name with. This is the default when
- used interactively.
- B<--nogroup> prints one result per line, like grep. This is the default
- when output is redirected.
- =item B<-H>, B<--with-filename>
- Print the filename for each match.
- =item B<-h>, B<--no-filename>
- Suppress the prefixing of filenames on output when multiple files are
- searched.
- =item B<--help>
- Print a short help statement.
- =item B<-i>, B<--ignore-case>
- Ignore case in the search strings.
- This applies only to the PATTERN, not to the regexes given for the B<-g>
- and B<-G> options.
- =item B<--[no]ignore-dir=I<DIRNAME>>
- Ignore directory (as CVS, .svn, etc are ignored). May be used multiple times
- to ignore multiple directories. For example, mason users may wish to include
- B<--ignore-dir=data>. The B<--noignore-dir> option allows users to search
- directories which would normally be ignored (perhaps to research the contents
- of F<.svn/props> directories).
- The I<DIRNAME> must always be a simple directory name. Nested directories like
- F<foo/bar> are NOT supported. You would need to specify B<--ignore-dir=foo> and
- then no files from any foo directory are taken into account by ack unless given
- explicitly on the command line.
- =item B<--line=I<NUM>>
- Only print line I<NUM> of each file. Multiple lines can be given with multiple
- B<--line> options or as a comma separated list (B<--line=3,5,7>). B<--line=4-7>
- also works. The lines are always output in ascending order, no matter the
- order given on the command line.
- =item B<-l>, B<--files-with-matches>
- Only print the filenames of matching files, instead of the matching text.
- =item B<-L>, B<--files-without-matches>
- Only print the filenames of files that do I<NOT> match. This is equivalent
- to specifying B<-l> and B<-v>.
- =item B<--match I<REGEX>>
- Specify the I<REGEX> explicitly. This is helpful if you don't want to put the
- regex as your first argument, e.g. when executing multiple searches over the
- same set of files.
- # search for foo and bar in given files
- ack file1 t/file* --match foo
- ack file1 t/file* --match bar
- =item B<-m=I<NUM>>, B<--max-count=I<NUM>>
- Stop reading a file after I<NUM> matches.
- =item B<--man>
- Print this manual page.
- =item B<-n>, B<--no-recurse>
- No descending into subdirectories.
- =item B<-o>
- Show only the part of each line matching PATTERN (turns off text
- highlighting)
- =item B<--output=I<expr>>
- Output the evaluation of I<expr> for each line (turns off text
- highlighting)
- =item B<--pager=I<program>>
- Direct ack's output through I<program>. This can also be specified
- via the C<ACK_PAGER> and C<ACK_PAGER_COLOR> environment variables.
- Using --pager does not suppress grouping and coloring like piping
- output on the command-line does.
- =item B<--passthru>
- Prints all lines, whether or not they match the expression. Highlighting
- will still work, though, so it can be used to highlight matches while
- still seeing the entire file, as in:
- # Watch a log file, and highlight a certain IP address
- $ tail -f ~/access.log | ack --passthru 123.45.67.89
- =item B<--print0>
- Only works in conjunction with -f, -g, -l or -c (filename output). The filenames
- are output separated with a null byte instead of the usual newline. This is
- helpful when dealing with filenames that contain whitespace, e.g.
- # remove all files of type html
- ack -f --html --print0 | xargs -0 rm -f
- =item B<-Q>, B<--literal>
- Quote all metacharacters in PATTERN, it is treated as a literal.
- This applies only to the PATTERN, not to the regexes given for the B<-g>
- and B<-G> options.
- =item B<-r>, B<-R>, B<--recurse>
- Recurse into sub-directories. This is the default and just here for
- compatibility with grep. You can also use it for turning B<--no-recurse> off.
- =item B<--smart-case>, B<--no-smart-case>
- Ignore case in the search strings if PATTERN contains no uppercase
- characters. This is similar to C<smartcase> in vim. This option is
- off by default.
- B<-i> always overrides this option.
- This applies only to the PATTERN, not to the regexes given for the
- B<-g> and B<-G> options.
- =item B<--sort-files>
- Sorts the found files lexically. Use this if you want your file
- listings to be deterministic between runs of I<ack>.
- =item B<--show-types>
- Outputs the filetypes that ack associates with each file.
- Works with B<-f> and B<-g> options.
- =item B<--thpppt>
- Display the all-important Bill The Cat logo. Note that the exact
- spelling of B<--thpppppt> is not important. It's checked against
- a regular expression.
- =item B<--type=TYPE>, B<--type=noTYPE>
- Specify the types of files to include or exclude from a search.
- TYPE is a filetype, like I<perl> or I<xml>. B<--type=perl> can
- also be specified as B<--perl>, and B<--type=noperl> can be done
- as B<--noperl>.
- If a file is of both type "foo" and "bar", specifying --foo and
- --nobar will exclude the file, because an exclusion takes precedence
- over an inclusion.
- Type specifications can be repeated and are ORed together.
- See I<ack --help=types> for a list of valid types.
- =item B<--type-add I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>
- Files with the given EXTENSION(s) are recognized as being of (the
- existing) type TYPE. See also L</"Defining your own types">.
- =item B<--type-set I<TYPE>=I<.EXTENSION>[,I<.EXT2>[,...]]>
- Files with the given EXTENSION(s) are recognized as being of type
- TYPE. This replaces an existing definition for type TYPE. See also
- L</"Defining your own types">.
- =item B<-u>, B<--unrestricted>
- All files and directories (including blib/, core.*, ...) are searched,
- nothing is skipped. When both B<-u> and B<--ignore-dir> are used, the
- B<--ignore-dir> option has no effect.
- =item B<-v>, B<--invert-match>
- Invert match: select non-matching lines
- This applies only to the PATTERN, not to the regexes given for the B<-g>
- and B<-G> options.
- =item B<--version>
- Display version and copyright information.
- =item B<-w>, B<--word-regexp>
- Force PATTERN to match only whole words. The PATTERN is wrapped with
- C<\b> metacharacters.
- This applies only to the PATTERN, not to the regexes given for the B<-g>
- and B<-G> options.
- =item B<-1>
- Stops after reporting first match of any kind. This is different
- from B<--max-count=1> or B<-m1>, where only one match per file is
- shown. Also, B<-1> works with B<-f> and B<-g>, where B<-m> does
- not.
- =back
- =head1 THE .ackrc FILE
- The F<.ackrc> file contains command-line options that are prepended
- to the command line before processing. Multiple options may live
- on multiple lines. Lines beginning with a # are ignored. A F<.ackrc>
- might look like this:
- # Always sort the files
- --sort-files
- # Always color, even if piping to a another program
- --color
- # Use "less -r" as my pager
- --pager=less -r
- Note that arguments with spaces in them do not need to be quoted,
- as they are not interpreted by the shell. Basically, each I<line>
- in the F<.ackrc> file is interpreted as one element of C<@ARGV>.
- F<ack> looks in your home directory for the F<.ackrc>. You can
- specify another location with the F<ACKRC> variable, below.
- If B<--noenv> is specified on the command line, the F<.ackrc> file
- is ignored.
- =head1 Defining your own types
- ack allows you to define your own types in addition to the predefined
- types. This is done with command line options that are best put into
- an F<.ackrc> file - then you do not have to define your types over and
- over again. In the following examples the options will always be shown
- on one command line so that they can be easily copy & pasted.
- I<ack --perl foo> searches for foo in all perl files. I<ack --help=types>
- tells you, that perl files are files ending
- in .pl, .pm, .pod or .t. So what if you would like to include .xs
- files as well when searching for --perl files? I<ack --type-add perl=.xs --perl foo>
- does this for you. B<--type-add> appends
- additional extensions to an existing type.
- If you want to define a new type, or completely redefine an existing
- type, then use B<--type-set>. I<ack --type-set
- eiffel=.e,.eiffel> defines the type I<eiffel> to include files with
- the extensions .e or .eiffel. So to search for all eiffel files
- containing the word Bertrand use I<ack --type-set eiffel=.e,.eiffel --eiffel Bertrand>.
- As usual, you can also write B<--type=eiffel>
- instead of B<--eiffel>. Negation also works, so B<--noeiffel> excludes
- all eiffel files from a search. Redefining also works: I<ack --type-set cc=.c,.h>
- and I<.xs> files no longer belong to the type I<cc>.
- When defining your own types in the F<.ackrc> file you have to use
- the following:
- --type-set=eiffel=.e,.eiffel
- or writing on separate lines
- --type-set
- eiffel=.e,.eiffel
- The following does B<NOT> work in the F<.ackrc> file:
- --type-set eiffel=.e,.eiffel
- In order to see all currently defined types, use I<--help types>, e.g.
- I<ack --type-set backup=.bak --type-add perl=.perl --help types>
- Restrictions:
- =over 4
- =item
- The types 'skipped', 'make', 'binary' and 'text' are considered "builtin" and
- cannot be altered.
- =item
- The shebang line recognition of the types 'perl', 'ruby', 'php', 'python',
- 'shell' and 'xml' cannot be redefined by I<--type-set>, it is always
- active. However, the shebang line is only examined for files where the
- extension is not recognised. Therefore it is possible to say
- I<ack --type-set perl=.perl --type-set foo=.pl,.pm,.pod,.t --perl --nofoo> and
- only find your shiny new I<.perl> files (and all files with unrecognized extension
- and perl on the shebang line).
- =back
- =head1 ENVIRONMENT VARIABLES
- For commonly-used ack options, environment variables can make life much easier.
- These variables are ignored if B<--noenv> is specified on the command line.
- =over 4
- =item ACKRC
- Specifies the location of the F<.ackrc> file. If this file doesn't
- exist, F<ack> looks in the default location.
- =item ACK_OPTIONS
- This variable specifies default options to be placed in front of
- any explicit options on the command line.
- =item ACK_COLOR_FILENAME
- Specifies the color of the filename when it's printed in B<--group>
- mode. By default, it's "bold green".
- The recognized attributes are clear, reset, dark, bold, underline,
- underscore, blink, reverse, concealed black, red, green, yellow,
- blue, magenta, on_black, on_red, on_green, on_yellow, on_blue,
- on_magenta, on_cyan, and on_white. Case is not significant.
- Underline and underscore are equivalent, as are clear and reset.
- The color alone sets the foreground color, and on_color sets the
- background color.
- This option can also be set with B<--color-filename>.
- =item ACK_COLOR_MATCH
- Specifies the color of the matching text when printed in B<--color>
- mode. By default, it's "black on_yellow".
- This option can also be set with B<--color-match>.
- See B<ACK_COLOR_FILENAME> for the color specifications.
- =item ACK_COLOR_LINENO
- Specifies the color of the line number when printed in B<--color>
- mode. By default, it's "bold yellow".
- This option can also be set with B<--color-lineno>.
- See B<ACK_COLOR_FILENAME> for the color specifications.
- =item ACK_PAGER
- Specifies a pager program, such as C<more>, C<less> or C<most>, to which
- ack will send its output.
- Using C<ACK_PAGER> does not suppress grouping and coloring like
- piping output on the command-line does, except that on Windows
- ack will assume that C<ACK_PAGER> does not support color.
- C<ACK_PAGER_COLOR> overrides C<ACK_PAGER> if both are specified.
- =item ACK_PAGER_COLOR
- Specifies a pager program that understands ANSI color sequences.
- Using C<ACK_PAGER_COLOR> does not suppress grouping and coloring
- like piping output on the command-line does.
- If you are not on Windows, you never need to use C<ACK_PAGER_COLOR>.
- =back
- =head1 ACK & OTHER TOOLS
- =head2 Vim integration
- F<ack> integrates easily with the Vim text editor. Set this in your
- F<.vimrc> to use F<ack> instead of F<grep>:
- set grepprg=ack\ -a
- That examples uses C<-a> to search through all files, but you may
- use other default flags. Now you can search with F<ack> and easily
- step through the results in Vim:
- :grep Dumper perllib
- =head2 Emacs integration
- Phil Jackson put together an F<ack.el> extension that "provides a
- simple compilation mode ... has the ability to guess what files you
- want to search for based on the major-mode."
- L<http://www.shellarchive.co.uk/content/emacs.html>
- =head2 TextMate integration
- Pedro Melo is a TextMate user who writes "I spend my day mostly
- inside TextMate, and the built-in find-in-project sucks with large
- projects. So I hacked a TextMate command that was using find +
- grep to use ack. The result is the Search in Project with ack, and
- you can find it here:
- L<http://www.simplicidade.org/notes/archives/2008/03/search_in_proje.html>"
- =head2 Shell and Return Code
- For greater compatibility with I<grep>, I<ack> in normal use returns
- shell return or exit code of 0 only if something is found and 1 if
- no match is found.
- (Shell exit code 1 is C<$?=256> in perl with C<system> or backticks.)
- The I<grep> code 2 for errors is not used.
- If C<-f> or C<-g> are specified, then 0 is returned if at least one
- file is found. If no files are found, then 1 is returned.
- =cut
- =head1 DEBUGGING ACK PROBLEMS
- If ack gives you output you're not expecting, start with a few simple steps.
- =head2 Use B<--noenv>
- Your environment variables and F<.ackrc> may be doing things you're
- not expecting, or forgotten you specified. Use B<--noenv> to ignore
- your environment and F<.ackrc>.
- =head2 Use B<-f> to see what files you're scanning
- The reason I created B<-f> in the first place was as a debugging
- tool. If ack is not finding matches you think it should find, run
- F<ack -f> to see what files are being checked.
- =head1 TIPS
- =head2 Use the F<.ackrc> file.
- The F<.ackrc> is the place to put all your options you use most of
- the time but don't want to remember. Put all your --type-add and
- --type-set definitions in it. If you like --smart-case, set it
- there, too. I also set --sort-files there.
- =head2 Use F<-f> for working with big codesets
- Ack does more than search files. C<ack -f --perl> will create a
- list of all the Perl files in a tree, ideal for sending into F<xargs>.
- For example:
- # Change all "this" to "that" in all Perl files in a tree.
- ack -f --perl | xargs perl -p -i -e's/this/that/g'
- or if you prefer:
- perl -p -i -e's/this/thatg/' $(ack -f --perl)
- =head2 Use F<-Q> when in doubt about metacharacters
- If you're searching for something with a regular expression
- metacharacter, most often a period in a filename or IP address, add
- the -Q to avoid false positives without all the backslashing. See
- the following example for more...
- =head2 Use ack to watch log files
- Here's one I used the other day to find trouble spots for a website
- visitor. The user had a problem loading F<troublesome.gif>, so I
- took the access log and scanned it with ack twice.
- ack -Q aa.bb.cc.dd /path/to/access.log | ack -Q -B5 troublesome.gif
- The first ack finds only the lines in the Apache log for the given
- IP. The second finds the match on my troublesome GIF, and shows
- the previous five lines from the log in each case.
- =head2 Share your knowledge
- Join the ack-users mailing list. Send me your tips and I may add
- them here.
- =head1 FAQ
- =head2 Why isn't ack finding a match in (some file)?
- Probably because it's of a type that ack doesn't recognize. ack's
- searching behavior is driven by filetype. B<If ack doesn't know
- what kind of file it is, ack ignores the file.>
- Use the C<-f> switch to see a list of files that ack will search
- for you.
- If you want ack to search files that it doesn't recognize, use the
- C<-a> switch.
- If you want ack to search every file, even ones that it always
- ignores like coredumps and backup files, use the C<-u> switch.
- =head2 Why does ack ignore unknown files by default?
- ack is designed by a programmer, for programmers, for searching
- large trees of code. Most codebases have a lot files in them which
- aren't source files (like compiled object files, source control
- metadata, etc), and grep wastes a lot of time searching through all
- of those as well and returning matches from those files.
- That's why ack's behavior of not searching things it doesn't recognize
- is one of its greatest strengths: the speed you get from only
- searching the things that you want to be looking at.
- =head2 Wouldn't it be great if F<ack> did search & replace?
- No, ack will always be read-only. Perl has a perfectly good way
- to do search & replace in files, using the C<-i>, C<-p> and C<-n>
- switches.
- You can certainly use ack to select your files to update. For
- example, to change all "foo" to "bar" in all PHP files, you can do
- this from the Unix shell:
- $ perl -i -p -e's/foo/bar/g' $(ack -f --php)
- =head2 Can you make ack recognize F<.xyz> files?
- That's an enhancement. Please see the section in the manual about
- enhancements.
- =head2 There's already a program/package called ack.
- Yes, I know.
- =head2 Why is it called ack if it's called ack-grep?
- The name of the program is "ack". Some packagers have called it
- "ack-grep" when creating packages because there's already a package
- out there called "ack" that has nothing to do with this ack.
- I suggest you make a symlink named F<ack> that points to F<ack-grep>
- because one of the crucial benefits of ack is having a name that's
- so short and simple to type.
- To do that, run this with F<sudo> or as root:
- ln -s /usr/bin/ack-grep /usr/bin/ack
- =head2 What does F<ack> mean?
- Nothing. I wanted a name that was easy to type and that you could
- pronounce as a single syllable.
- =head2 Can I do multi-line regexes?
- No, ack does not support regexes that match multiple lines. Doing
- so would require reading in the entire file at a time.
- If you want to see lines near your match, use the C<--A>, C<--B>
- and C<--C> switches for displaying context.
- =head1 AUTHOR
- Andy Lester, C<< <andy at petdance.com> >>
- =head1 BUGS
- Please report any bugs or feature requests to the issues list at
- Github: L<http://github.com/petdance/ack/issues>
- =head1 ENHANCEMENTS
- All enhancement requests MUST first be posted to the ack-users
- mailing list at L<http://groups.google.com/group/ack-users>. I
- will not consider a request without it first getting seen by other
- ack users. This includes requests for new filetypes.
- There is a list of enhancements I want to make to F<ack> in the ack
- issues list at Github: L<http://github.com/petdance/ack/issues>
- Patches are always welcome, but patches with tests get the most
- attention.
- =head1 SUPPORT
- Support for and information about F<ack> can be found at:
- =over 4
- =item * The ack homepage
- L<http://betterthangrep.com/>
- =item * The ack issues list at Github
- L<http://github.com/petdance/ack/issues>
- =item * AnnoCPAN: Annotated CPAN documentation
- L<http://annocpan.org/dist/ack>
- =item * CPAN Ratings
- L<http://cpanratings.perl.org/d/ack>
- =item * Search CPAN
- L<http://search.cpan.org/dist/ack>
- =item * Git source repository
- L<http://github.com/petdance/ack>
- =back
- =head1 ACKNOWLEDGEMENTS
- How appropriate to have I<ack>nowledgements!
- Thanks to everyone who has contributed to ack in any way, including
- Nick Hooey,
- Bo Borgerson,
- Mark Szymanski,
- Marq Schneider,
- Packy Anderson,
- JR Boyens,
- Dan Sully,
- Ryan Niebur,
- Kent Fredric,
- Mike Morearty,
- Ingmar Vanhassel,
- Eric Van Dewoestine,
- Sitaram Chamarty,
- Adam James,
- Richard Carlsson,
- Pedro Melo,
- AJ Schuster,
- Phil Jackson,
- Michael Schwern,
- Jan Dubois,
- Christopher J. Madsen,
- Matthew Wickline,
- David Dyck,
- Jason Porritt,
- Jjgod Jiang,
- Thomas Klausner,
- Uri Guttman,
- Peter Lewis,
- Kevin Riggle,
- Ori Avtalion,
- Torsten Blix,
- Nigel Metheringham,
- GE<aacute>bor SzabE<oacute>,
- Tod Hagan,
- Michael Hendricks,
- E<AElig>var ArnfjE<ouml>rE<eth> Bjarmason,
- Piers Cawley,
- Stephen Steneker,
- Elias Lutfallah,
- Mark Leighton Fisher,
- Matt Diephouse,
- Christian Jaeger,
- Bill Sully,
- Bill Ricker,
- David Golden,
- Nilson Santos F. Jr,
- Elliot Shank,
- Merijn Broeren,
- Uwe Voelker,
- Rick Scott,
- Ask BjE<oslash>rn Hansen,
- Jerry Gay,
- Will Coleda,
- Mike O'Regan,
- Slaven ReziE<0x107>,
- Mark Stosberg,
- David Alan Pisoni,
- Adriano Ferreira,
- James Keenan,
- Leland Johnson,
- Ricardo Signes
- and Pete Krawczyk.
- =head1 COPYRIGHT & LICENSE
- Copyright 2005-2010 Andy Lester.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the Artistic License v2.0.
- =cut
- package File::Next;
- use strict;
- use warnings;
- our $VERSION = '1.06';
- use File::Spec ();
- our $name; # name of the current file
- our $dir; # dir of the current file
- our %files_defaults;
- our %skip_dirs;
- BEGIN {
- %files_defaults = (
- file_filter => undef,
- descend_filter => undef,
- error_handler => sub { CORE::die @_ },
- sort_files => undef,
- follow_symlinks => 1,
- );
- %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
- }
- sub files {
- ($_[0] eq __PACKAGE__) && die 'File::Next::files must not be invoked as File::Next->files';
- my ($parms,@queue) = _setup( \%files_defaults, @_ );
- my $filter = $parms->{file_filter};
- return sub {
- while (@queue) {
- my ($dir,$file,$fullpath) = splice( @queue, 0, 3 );
- if ( -f $fullpath ) {
- if ( $filter ) {
- local $_ = $file;
- local $File::Next::dir = $dir;
- local $File::Next::name = $fullpath;
- next if not $filter->();
- }
- return wantarray ? ($dir,$file,$fullpath) : $fullpath;
- }
- elsif ( -d _ ) {
- unshift( @queue, _candidate_files( $parms, $fullpath ) );
- }
- } # while
- return;
- }; # iterator
- }
- sub sort_standard($$) { return $_[0]->[1] cmp $_[1]->[1] }
- sub sort_reverse($$) { return $_[1]->[1] cmp $_[0]->[1] }
- sub reslash {
- my $path = shift;
- my @parts = split( /\//, $path );
- return $path if @parts < 2;
- return File::Spec->catfile( @parts );
- }
- sub _setup {
- my $defaults = shift;
- my $passed_parms = ref $_[0] eq 'HASH' ? {%{+shift}} : {}; # copy parm hash
- my %passed_parms = %{$passed_parms};
- my $parms = {};
- for my $key ( keys %{$defaults} ) {
- $parms->{$key} =
- exists $passed_parms{$key}
- ? delete $passed_parms{$key}
- : $defaults->{$key};
- }
- # Any leftover keys are bogus
- for my $badkey ( keys %passed_parms ) {
- my $sub = (caller(1))[3];
- $parms->{error_handler}->( "Invalid option passed to $sub(): $badkey" );
- }
- # If it's not a code ref, assume standard sort
- if ( $parms->{sort_files} && ( ref($parms->{sort_files}) ne 'CODE' ) ) {
- $parms->{sort_files} = \&sort_standard;
- }
- my @queue;
- for ( @_ ) {
- my $start = reslash( $_ );
- if (-d $start) {
- push @queue, ($start,undef,$start);
- }
- else {
- push @queue, (undef,$start,$start);
- }
- }
- return ($parms,@queue);
- }
- sub _candidate_files {
- my $parms = shift;
- my $dir = shift;
- my $dh;
- if ( !opendir $dh, $dir ) {
- $parms->{error_handler}->( "$dir: $!" );
- return;
- }
- my @newfiles;
- my $descend_filter = $parms->{descend_filter};
- my $follow_symlinks = $parms->{follow_symlinks};
- my $sort_sub = $parms->{sort_files};
- for my $file ( grep { !exists $skip_dirs{$_} } readdir $dh ) {
- my $has_stat;
- # Only do directory checking if we have a descend_filter
- my $fullpath = File::Spec->catdir( $dir, $file );
- if ( !$follow_symlinks ) {
- next if -l $fullpath;
- $has_stat = 1;
- }
- if ( $descend_filter ) {
- if ( $has_stat ? (-d _) : (-d $fullpath) ) {
- local $File::Next::dir = $fullpath;
- local $_ = $file;
- next if not $descend_filter->();
- }
- }
- if ( $sort_sub ) {
- push( @newfiles, [ $dir, $file, $fullpath ] );
- }
- else {
- push( @newfiles, $dir, $file, $fullpath );
- }
- }
- closedir $dh;
- if ( $sort_sub ) {
- return map { @{$_} } sort $sort_sub @newfiles;
- }
- return @newfiles;
- }
- 1; # End of File::Next
- package App::Ack;
- use warnings;
- use strict;
- our $VERSION;
- our $COPYRIGHT;
- BEGIN {
- $VERSION = '1.94';
- $COPYRIGHT = 'Copyright 2005-2010 Andy Lester.';
- }
- our $fh;
- BEGIN {
- $fh = *STDOUT;
- }
- our %types;
- our %type_wanted;
- our %mappings;
- our %ignore_dirs;
- our $input_from_pipe;
- our $output_to_pipe;
- our $dir_sep_chars;
- our $is_cygwin;
- our $is_windows;
- use File::Spec ();
- use File::Glob ':glob';
- use Getopt::Long ();
- BEGIN {
- %ignore_dirs = (
- '.bzr' => 'Bazaar',
- '.cdv' => 'Codeville',
- '~.dep' => 'Interface Builder',
- '~.dot' => 'Interface Builder',
- '~.nib' => 'Interface Builder',
- '~.plst' => 'Interface Builder',
- '.git' => 'Git',
- '.hg' => 'Mercurial',
- '.pc' => 'quilt',
- '.svn' => 'Subversion',
- _MTN => 'Monotone',
- blib => 'Perl module building',
- CVS => 'CVS',
- RCS => 'RCS',
- SCCS => 'SCCS',
- _darcs => 'darcs',
- _sgbak => 'Vault/Fortress',
- 'autom4te.cache' => 'autoconf',
- 'cover_db' => 'Devel::Cover',
- _build => 'Module::Build',
- );
- %mappings = (
- actionscript => [qw( as mxml )],
- ada => [qw( ada adb ads )],
- asm => [qw( asm s )],
- batch => [qw( bat cmd )],
- binary => q{Binary files, as defined by Perl's -B op (default: off)},
- cc => [qw( c h xs )],
- cfmx => [qw( cfc cfm cfml )],
- clojure => [qw( clj )],
- cpp => [qw( cpp cc cxx m hpp hh h hxx )],
- csharp => [qw( cs )],
- css => [qw( css )],
- delphi => [qw( pas int dfm nfm dof dpk dproj groupproj bdsgroup bdsproj )],
- elisp => [qw( el )],
- erlang => [qw( erl hrl )],
- fortran => [qw( f f77 f90 f95 f03 for ftn fpp )],
- go => [qw( go )],
- haskell => [qw( hs lhs )],
- hh => [qw( h )],
- html => [qw( htm html shtml xhtml )],
- java => [qw( java properties )],
- js => [qw( js )],
- jsp => [qw( jsp jspx jhtm jhtml )],
- lisp => [qw( lisp lsp )],
- lua => [qw( lua )],
- make => q{Makefiles (including *.mk and *.mak)},
- mason => [qw( mas mhtml mpl mtxt )],
- objc => [qw( m h )],
- objcpp => [qw( mm h )],
- ocaml => [qw( ml mli )],
- parrot => [qw( pir pasm pmc ops pod pg tg )],
- perl => [qw( pl pm pod t )],
- php => [qw( php phpt php3 php4 php5 phtml)],
- plone => [qw( pt cpt metadata cpy py )],
- python => [qw( py )],
- rake => q{Rakefiles},
- ruby => [qw( rb rhtml rjs rxml erb rake spec )],
- scala => [qw( scala )],
- scheme => [qw( scm ss )],
- shell => [qw( sh bash csh tcsh ksh zsh )],
- skipped => q{Files, but not directories, normally skipped by ack (default: off)},
- smalltalk => [qw( st )],
- sql => [qw( sql ctl )],
- tcl => [qw( tcl itcl itk )],
- tex => [qw( tex cls sty )],
- text => q{Text files, as defined by Perl's -T op (default: off)},
- tt => [qw( tt tt2 ttml )],
- vb => [qw( bas cls frm ctl vb resx )],
- verilog => [qw( v vh sv )],
- vhdl => [qw( vhd vhdl )],
- vim => [qw( vim )],
- yaml => [qw( yaml yml )],
- xml => [qw( xml dtd xsl xslt ent )],
- );
- while ( my ($type,$exts) = each %mappings ) {
- if ( ref $exts ) {
- for my $ext ( @{$exts} ) {
- push( @{$types{$ext}}, $type );
- }
- }
- }
- # add manually Makefile extensions
- push @{$types{$_}}, 'make' for qw{ mk mak };
- # These have to be checked before any filehandle diddling.
- $output_to_pipe = not -t *STDOUT;
- $input_from_pipe = -p STDIN;
- $is_cygwin = ($^O eq 'cygwin');
- $is_windows = ($^O =~ /MSWin32/);
- $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
- }
- sub read_ackrc {
- my @files = ( $ENV{ACKRC} );
- my @dirs =
- $is_windows
- ? ( $ENV{HOME}, $ENV{USERPROFILE} )
- : ( '~', $ENV{HOME} );
- for my $dir ( grep { defined } @dirs ) {
- for my $file ( '.ackrc', '_ackrc' ) {
- push( @files, bsd_glob( "$dir/$file", GLOB_TILDE ) );
- }
- }
- for my $filename ( @files ) {
- if ( defined $filename && -e $filename ) {
- open( my $fh, '<', $filename ) or App::Ack::die( "$filename: $!\n" );
- my @lines = grep { /./ && !/^\s*#/ } <$fh>;
- chomp @lines;
- close $fh or App::Ack::die( "$filename: $!\n" );
- # get rid of leading and trailing whitespaces
- for ( @lines ) {
- s/^\s+//;
- s/\s+$//;
- }
- return @lines;
- }
- }
- return;
- }
- sub get_command_line_options {
- my %opt = (
- pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
- );
- my $getopt_specs = {
- 1 => sub { $opt{1} = $opt{m} = 1 },
- 'A|after-context=i' => \$opt{after_context},
- 'B|before-context=i' => \$opt{before_context},
- 'C|context:i' => sub { shift; my $val = shift; $opt{before_context} = $opt{after_context} = ($val || 2) },
- 'a|all-types' => \$opt{all},
- 'break!' => \$opt{break},
- c => \$opt{count},
- 'color|colour!' => \$opt{color},
- 'color-match=s' => \$ENV{ACK_COLOR_MATCH},
- 'color-filename=s' => \$ENV{ACK_COLOR_FILENAME},
- 'color-lineno=s' => \$ENV{ACK_COLOR_LINENO},
- 'column!' => \$opt{column},
- count => \$opt{count},
- 'env!' => sub { }, # ignore this option, it is handled beforehand
- f => \$opt{f},
- flush => \$opt{flush},
- 'follow!' => \$opt{follow},
- 'g=s' => sub { shift; $opt{G} = shift; $opt{f} = 1 },
- 'G=s' => \$opt{G},
- 'group!' => sub { shift; $opt{heading} = $opt{break} = shift },
- 'heading!' => \$opt{heading},
- 'h|no-filename' => \$opt{h},
- 'H|with-filename' => \$opt{H},
- 'i|ignore-case' => \$opt{i},
- 'invert-file-match' => \$opt{invert_file_match},
- 'lines=s' => sub { shift; my $val = shift; push @{$opt{lines}}, $val },
- 'l|files-with-matches' => \$opt{l},
- 'L|files-without-matches' => sub { $opt{l} = $opt{v} = 1 },
- 'm|max-count=i' => \$opt{m},
- 'match=s' => \$opt{regex},
- 'n|no-recurse' => \$opt{n},
- o => sub { $opt{output} = '$&' },
- 'output=s' => \$opt{output},
- 'pager=s' => \$opt{pager},
- 'nopager' => sub { $opt{pager} = undef },
- 'passthru' => \$opt{passthru},
- 'print0' => \$opt{print0},
- 'Q|literal' => \$opt{Q},
- 'r|R|recurse' => sub { $opt{n} = 0 },
- 'show-types' => \$opt{show_types},
- 'smart-case!' => \$opt{smart_case},
- 'sort-files' => \$opt{sort_files},
- 'u|unrestricted' => \$opt{u},
- 'v|invert-match' => \$opt{v},
- 'w|word-regexp' => \$opt{w},
- 'ignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); $ignore_dirs{$dir} = '--ignore-dirs' },
- 'noignore-dirs=s' => sub { shift; my $dir = remove_dir_sep( shift ); delete $ignore_dirs{$dir} },
- 'version' => sub { print_version_statement(); exit; },
- 'help|?:s' => sub { shift; show_help(@_); exit; },
- 'help-types'=> sub { show_help_types(); exit; },
- 'man' => sub {
- require Pod::Usage;
- Pod::Usage::pod2usage({
- -verbose => 2,
- -exitval => 0,
- });
- },
- 'type=s' => sub {
- # Whatever --type=xxx they specify, set it manually in the hash
- my $dummy = shift;
- my $type = shift;
- my $wanted = ($type =~ s/^no//) ? 0 : 1; # must not be undef later
- if ( exists $type_wanted{ $type } ) {
- $type_wanted{ $type } = $wanted;
- }
- else {
- App::Ack::die( qq{Unknown --type "$type"} );
- }
- }, # type sub
- };
- # Stick any default switches at the beginning, so they can be overridden
- # by the command line switches.
- unshift @ARGV, split( ' ', $ENV{ACK_OPTIONS} ) if defined $ENV{ACK_OPTIONS};
- # first pass through options, looking for type definitions
- def_types_from_ARGV();
- for my $i ( filetypes_supported() ) {
- $getopt_specs->{ "$i!" } = \$type_wanted{ $i };
- }
- my $parser = Getopt::Long::Parser->new();
- $parser->configure( 'bundling', 'no_ignore_case', );
- $parser->getoptions( %{$getopt_specs} ) or
- App::Ack::die( 'See ack --help, ack --help-types or ack --man for options.' );
- my $to_screen = not output_to_pipe();
- my %defaults = (
- all => 0,
- color => $to_screen,
- follow => 0,
- break => $to_screen,
- heading => $to_screen,
- before_context => 0,
- after_context => 0,
- );
- if ( $is_windows && $defaults{color} && not $ENV{ACK_PAGER_COLOR} ) {
- if ( $ENV{ACK_PAGER} || not eval { require Win32::Console::ANSI } ) {
- $defaults{color} = 0;
- }
- }
- if ( $to_screen && $ENV{ACK_PAGER_COLOR} ) {
- $defaults{color} = 1;
- }
- while ( my ($key,$value) = each %defaults ) {
- if ( not defined $opt{$key} ) {
- $opt{$key} = $value;
- }
- }
- if ( defined $opt{m} && $opt{m} <= 0 ) {
- App::Ack::die( '-m must be greater than zero' );
- }
- for ( qw( before_context after_context ) ) {
- if ( defined $opt{$_} && $opt{$_} < 0 ) {
- App::Ack::die( "--$_ may not be negative" );
- }
- }
- if ( defined( my $val = $opt{output} ) ) {
- $opt{output} = eval qq[ sub { "$val" } ];
- }
- if ( defined( my $l = $opt{lines} ) ) {
- # --line=1 --line=5 is equivalent to --line=1,5
- my @lines = split( /,/, join( ',', @{$l} ) );
- # --line=1-3 is equivalent to --line=1,2,3
- @lines = map {
- my @ret;
- if ( /-/ ) {
- my ($from, $to) = split /-/, $_;
- if ( $from > $to ) {
- App::Ack::warn( "ignoring --line=$from-$to" );
- @ret = ();
- }
- else {
- @ret = ( $from .. $to );
- }
- }
- else {
- @ret = ( $_ );
- };
- @ret
- } @lines;
- if ( @lines ) {
- my %uniq;
- @uniq{ @lines } = ();
- $opt{lines} = [ sort { $a <=> $b } keys %uniq ]; # numerical sort and each line occurs only once!
- }
- else {
- # happens if there are only ignored --line directives
- App::Ack::die( 'All --line options are invalid.' );
- }
- }
- return \%opt;
- }
- sub def_types_from_ARGV {
- my @typedef;
- my $parser = Getopt::Long::Parser->new();
- # pass_through => leave unrecognized command line arguments alone
- # no_auto_abbrev => otherwise -c is expanded and not left alone
- $parser->configure( 'no_ignore_case', 'pass_through', 'no_auto_abbrev' );
- $parser->getoptions(
- 'type-set=s' => sub { shift; push @typedef, ['c', shift] },
- 'type-add=s' => sub { shift; push @typedef, ['a', shift] },
- ) or App::Ack::die( 'See ack --help or ack --man for options.' );
- for my $td (@typedef) {
- my ($type, $ext) = split /=/, $td->[1];
- if ( $td->[0] eq 'c' ) {
- # type-set
- if ( exists $mappings{$type} ) {
- # can't redefine types 'make', 'skipped', 'text' and 'binary'
- App::Ack::die( qq{--type-set: Builtin type "$type" cannot be changed.} )
- if ref $mappings{$type} ne 'ARRAY';
- delete_type($type);
- }
- }
- else {
- # type-add
- # can't append to types 'make', 'skipped', 'text' and 'binary'
- App::Ack::die( qq{--type-add: Builtin type "$type" cannot be changed.} )
- if exists $mappings{$type} && ref $mappings{$type} ne 'ARRAY';
- App::Ack::warn( qq{--type-add: Type "$type" does not exist, creating with "$ext" ...} )
- unless exists $mappings{$type};
- }
- my @exts = split /,/, $ext;
- s/^\.// for @exts;
- if ( !exists $mappings{$type} || ref($mappings{$type}) eq 'ARRAY' ) {
- push @{$mappings{$type}}, @exts;
- for my $e ( @exts ) {
- push @{$types{$e}}, $type;
- }
- }
- else {
- App::Ack::die( qq{Cannot append to type "$type".} );
- }
- }
- return;
- }
- sub delete_type {
- my $type = shift;
- App::Ack::die( qq{Internal error: Cannot delete builtin type "$type".} )
- unless ref $mappings{$type} eq 'ARRAY';
- delete $mappings{$type};
- delete $type_wanted{$type};
- for my $ext ( keys %types ) {
- $types{$ext} = [ grep { $_ ne $type } @{$types{$ext}} ];
- }
- }
- sub ignoredir_filter {
- return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};
- }
- sub remove_dir_sep {
- my $path = shift;
- $path =~ s/[$dir_sep_chars]$//;
- return $path;
- }
- use constant TEXT => 'text';
- sub filetypes {
- my $filename = shift;
- my $basename = $filename;
- $basename =~ s{.*[$dir_sep_chars]}{};
- return 'skipped' unless is_searchable( $basename );
- my $lc_basename = lc $basename;
- return ('make',TEXT) if $lc_basename eq 'makefile' || $lc_basename eq 'gnumakefile';
- return ('rake','ruby',TEXT) if $lc_basename eq 'rakefile';
- # If there's an extension, look it up
- if ( $filename =~ m{\.([^\.$dir_sep_chars]+)$}o ) {
- my $ref = $types{lc $1};
- return (@{$ref},TEXT) if $ref;
- }
- # At this point, we can't tell from just the name. Now we have to
- # open it and look inside.
- return unless -e $filename;
- # From Elliot Shank:
- # I can't see any reason that -r would fail on these-- the ACLs look
- # fine, and no program has any of them open, so the busted Windows
- # file locking model isn't getting in there. If I comment the if
- # statement out, everything works fine
- # So, for cygwin, don't bother trying to check for readability.
- if ( !$is_cygwin ) {
- if ( !-r $filename ) {
- App::Ack::warn( "$filename: Permission denied" );
- return;
- }
- }
- return 'binary' if -B $filename;
- # If there's no extension, or we don't recognize it, check the shebang line
- my $fh;
- if ( !open( $fh, '<', $filename ) ) {
- App::Ack::warn( "$filename: $!" );
- return;
- }
- my $header = <$fh>;
- close $fh;
- if ( $header =~ /^#!/ ) {
- return ($1,TEXT) if $header =~ /\b(ruby|p(?:erl|hp|ython))\b/;
- return ('shell',TEXT) if $header =~ /\b(?:ba|t?c|k|z)?sh\b/;
- }
- else {
- return ('xml',TEXT) if $header =~ /\Q<?xml /i;
- }
- return (TEXT);
- }
- sub is_searchable {
- my $filename = shift;
- # If these are updated, update the --help message
- return if $filename =~ /[.]bak$/;
- return if $filename =~ /~$/;
- return if $filename =~ m{^#.*#$}o;
- return if $filename =~ m{^core\.\d+$}o;
- return if $filename =~ m{[._].*\.swp$}o;
- return 1;
- }
- sub build_regex {
- my $str = shift;
- my $opt = shift;
- defined $str or App::Ack::die( 'No regular expression found.' );
- $str = quotemeta( $str ) if $opt->{Q};
- if ( $opt->{w} ) {
- $str = "\\b$str" if $str =~ /^\w/;
- $str = "$str\\b" if $str =~ /\w$/;
- }
- my $regex_is_lc = $str eq lc $str;
- if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {
- $str = "(?i)$str";
- }
- return $str;
- }
- sub check_regex {
- my $regex = shift;
- return unless defined $regex;
- eval { qr/$regex/ };
- if ($@) {
- (my $error = $@) =~ s/ at \S+ line \d+.*//;
- chomp($error);
- App::Ack::die( "Invalid regex '$regex':\n $error" );
- }
- return;
- }
- sub warn {
- return CORE::warn( _my_program(), ': ', @_, "\n" );
- }
- sub die {
- return CORE::die( _my_program(), ': ', @_, "\n" );
- }
- sub _my_program {
- require File::Basename;
- return File::Basename::basename( $0 );
- }
- sub filetypes_supported {
- return keys %mappings;
- }
- sub _get_thpppt {
- my $y = q{_ /|,\\'!.x',=(www)=, U };
- $y =~ tr/,x!w/\nOo_/;
- return $y;
- }
- sub _thpppt {
- my $y = _get_thpppt();
- App::Ack::print( "$y ack $_[0]!\n" );
- exit 0;
- }
- sub _key {
- my $str = lc shift;
- $str =~ s/[^a-z]//g;
- return $str;
- }
- sub show_help {
- my $help_arg = shift || 0;
- return show_help_types() if $help_arg =~ /^types?/;
- my $ignore_dirs = _listify( sort { _key($a) cmp _key($b) } keys %ignore_dirs );
- App::Ack::print( <<"END_OF_HELP" );
- Usage: ack [OPTION]... PATTERN [FILE]
- Search for PATTERN in each source file in the tree from cwd on down.
- If [FILES] is specified, then only those files/directories are checked.
- ack may also search STDIN, but only if no FILE are specified, or if
- one of FILES is "-".
- Default switches may be specified in ACK_OPTIONS environment variable or
- an .ackrc file. If you want no dependency on the environment, turn it
- off with --noenv.
- Example: ack -i select
- Searching:
- -i, --ignore-case Ignore case distinctions in PATTERN
- --[no]smart-case Ignore case distinctions in PATTERN,
- only if PATTERN contains no upper case
- Ignored if -i is specified
- -v, --invert-match Invert match: select non-matching lines
- -w, --word-regexp Force PATTERN to match only whole words
- -Q, --literal Quote all metacharacters; PATTERN is literal
- Search output:
- --line=NUM Only print line(s) NUM of each file
- -l, --files-with-matches
- Only print filenames containing matches
- -L, --files-without-matches
- Only print filenames with no matches
- -o Show only the part of a line matching PATTERN
- (turns off text highlighting)
- --passthru Print all lines, whether matching or not
- --output=expr Output the evaluation of expr for each line
- (turns off text highlighting)
- --match PATTERN Specify PATTERN explicitly.
- -m, --max-count=NUM Stop searching in each file after NUM matches
- -1 Stop searching after one match of any kind
- -H, --with-filename Print the filename for each match
- -h, --no-filename Suppress the prefixing filename on output
- -c, --count Show number of lines matching per file
- --column Show the column number of the first match
- -A NUM, --after-context=NUM
- Print NUM lines of trailing context after matching
- lines.
- -B NUM, --before-context=NUM
- Print NUM lines of leading context before matching
- lines.
- -C [NUM], --context[=NUM]
- Print NUM lines (default 2) of output context.
- --print0 Print null byte as separator between filenames,
- only works with -f, -g, -l, -L or -c.
- File presentation:
- --pager=COMMAND Pipes all ack output through COMMAND. For example,
- --pager="less -R". Ignored if output is redirected.
- --nopager Do not send output through a pager. Cancels any
- setting in ~/.ackrc, ACK_PAGER or ACK_PAGER_COLOR.
- --[no]heading Print a filename heading above each file's results.
- (default: on when used interactively)
- --[no]break Print a break between results from different files.
- (default: on when used interactively)
- --group Same as --heading --break
- --nogroup Same as --noheading --nobreak
- --[no]color Highlight the matching text (default: on unless
- output is redirected, or on Windows)
- --[no]colour Same as --[no]color
- --color-filename=COLOR
- --color-match=COLOR
- --color-lineno=COLOR Set the color for filenames, matches, and line numbers.
- --flush Flush output immediately, even when ack is used
- non-interactively (when output goes to a pipe or
- file).
- File finding:
- -f Only print the files found, without searching.
- The PATTERN must not be specified.
- -g REGEX Same as -f, but only print files matching REGEX.
- --sort-files Sort the found files lexically.
- --invert-file-match Print/search handle files that do not match -g/-G.
- --show-types Show which types each file has.
- File inclusion/exclusion:
- -a, --all-types All file types searched;
- Ignores CVS, .svn and other ignored directories
- -u, --unrestricted All files and directories searched
- --[no]ignore-dir=name Add/Remove directory from the list of ignored dirs
- -r, -R, --recurse Recurse into subdirectories (ack's default behavior)
- -n, --no-recurse No descending into subdirectories
- -G REGEX Only search files that match REGEX
- --perl Include only Perl files.
- --type=perl Include only Perl files.
- --noperl Exclude Perl files.
- --type=noperl Exclude Perl files.
- See "ack --help type" for supported filetypes.
- --type-set TYPE=.EXTENSION[,.EXT2[,...]]
- Files with the given EXTENSION(s) are recognized as
- being of type TYPE. This replaces an existing
- definition for type TYPE.
- --type-add TYPE=.EXTENSION[,.EXT2[,...]]
- Files with the given EXTENSION(s) are recognized as
- being of (the existing) type TYPE
- --[no]follow Follow symlinks. Default is off.
- Directories ignored by default:
- $ignore_dirs
- Files not checked for type:
- /~\$/ - Unix backup files
- /#.+#\$/ - Emacs swap files
- /[._].*\\.swp\$/ - Vi(m) swap files
- /core\\.\\d+\$/ - core dumps
- Miscellaneous:
- --noenv Ignore environment variables and ~/.ackrc
- --help This help
- --man Man page
- --version Display version & copyright
- --thpppt Bill the Cat
- Exit status is 0 if match, 1 if no match.
- This is version $VERSION of ack.
- END_OF_HELP
- return;
- }
- sub show_help_types {
- App::Ack::print( <<'END_OF_HELP' );
- Usage: ack [OPTION]... PATTERN [FILES]
- The following is the list of filetypes supported by ack. You can
- specify a file type with the --type=TYPE format, or the --TYPE
- format. For example, both --type=perl and --perl work.
- Note that some extensions may appear in multiple types. For example,
- .pod files are both Perl and Parrot.
- END_OF_HELP
- my @types = filetypes_supported();
- my $maxlen = 0;
- for ( @types ) {
- $maxlen = length if $maxlen < length;
- }
- for my $type ( sort @types ) {
- next if $type =~ /^-/; # Stuff to not show
- my $ext_list = $mappings{$type};
- if ( ref $ext_list ) {
- $ext_list = join( ' ', map { ".$_" } @{$ext_list} );
- }
- App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
- }
- return;
- }
- sub _listify {
- my @whats = @_;
- return '' if !@whats;
- my $end = pop @whats;
- my $str = @whats ? join( ', ', @whats ) . " and $end" : $end;
- no warnings 'once';
- require Text::Wrap;
- $Text::Wrap::columns = 75;
- return Text::Wrap::wrap( '', ' ', $str );
- }
- sub get_version_statement {
- require Config;
- my $copyright = get_copyright();
- my $this_perl = $Config::Config{perlpath};
- if ($^O ne 'VMS') {
- my $ext = $Config::Config{_exe};
- $this_perl .= $ext unless $this_perl =~ m/$ext$/i;
- }
- my $ver = sprintf( '%vd', $^V );
- return <<"END_OF_VERSION";
- ack $VERSION
- Running under Perl $ver at $this_perl
- $copyright
- This program is free software. You may modify or distribute it
- under the terms of the Artistic License v2.0.
- END_OF_VERSION
- }
- sub print_version_statement {
- App::Ack::print( get_version_statement() );
- return;
- }
- sub get_copyright {
- return $COPYRIGHT;
- }
- sub load_colors {
- eval 'use Term::ANSIColor ()';
- $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';
- $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
- $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';
- return;
- }
- sub is_interesting {
- return if /^\./;
- my $include;
- for my $type ( filetypes( $File::Next::name ) ) {
- if ( defined $type_wanted{$type} ) {
- if ( $type_wanted{$type} ) {
- $include = 1;
- }
- else {
- return;
- }
- }
- }
- return $include;
- }
- # print subs added in order to make it easy for a third party
- # module (such as App::Wack) to redefine the display methods
- # and show the results in a different way.
- sub print { print {$fh} @_ }
- sub print_first_filename { App::Ack::print( $_[0], "\n" ) }
- sub print_blank_line { App::Ack::print( "\n" ) }
- sub print_separator { App::Ack::print( "--\n" ) }
- sub print_filename { App::Ack::print( $_[0], $_[1] ) }
- sub print_line_no { App::Ack::print( $_[0], $_[1] ) }
- sub print_column_no { App::Ack::print( $_[0], $_[1] ) }
- sub print_count {
- my $filename = shift;
- my $nmatches = shift;
- my $ors = shift;
- my $count = shift;
- my $show_filename = shift;
- if ($show_filename) {
- App::Ack::print( $filename );
- App::Ack::print( ':', $nmatches ) if $count;
- }
- else {
- App::Ack::print( $nmatches ) if $count;
- }
- App::Ack::print( $ors );
- }
- sub print_count0 {
- my $filename = shift;
- my $ors = shift;
- my $show_filename = shift;
- if ($show_filename) {
- App::Ack::print( $filename, ':0', $ors );
- }
- else {
- App::Ack::print( '0', $ors );
- }
- }
- {
- my $filename;
- my $regex;
- my $display_filename;
- my $keep_context;
- my $last_output_line; # number of the last line that has been output
- my $any_output; # has there been any output for the current file yet
- my $context_overall_output_count; # has there been any output at all
- sub search_resource {
- my $res = shift;
- my $opt = shift;
- $filename = $res->name();
- my $v = $opt->{v};
- my $passthru = $opt->{passthru};
- my $max = $opt->{m};
- my $nmatches = 0;
- $display_filename = undef;
- # for --line processing
- my $has_lines = 0;
- my @lines;
- if ( defined $opt->{lines} ) {
- $has_lines = 1;
- @lines = ( @{$opt->{lines}}, -1 );
- undef $regex; # Don't match when printing matching line
- }
- else {
- $regex = qr/$opt->{regex}/;
- }
- # for context processing
- $last_output_line = -1;
- $any_output = 0;
- my $before_context = $opt->{before_context};
- my $after_context = $opt->{after_context};
- $keep_context = ($before_context || $after_context) && !$passthru;
- my @before;
- my $before_starts_at_line;
- my $after = 0; # number of lines still to print after a match
- while ( $res->next_text ) {
- # XXX Optimize away the case when there are no more @lines to find.
- # XXX $has_lines, $passthru and $v never change. Optimize.
- if ( $has_lines
- ? $. != $lines[0] # $lines[0] should be a scalar
- : $v ? m/$regex/ : !m/$regex/ ) {
- if ( $passthru ) {
- App::Ack::print( $_ );
- next;
- }
- if ( $keep_context ) {
- if ( $after ) {
- print_match_or_context( $opt, 0, $., $-[0], $+[0], $_ );
- $after--;
- }
- elsif ( $before_context ) {
- if ( @before ) {
- if ( @before >= $before_context ) {
- shift @before;
- ++$before_starts_at_line;
- }
- }
- else {
- $before_starts_at_line = $.;
- }
- push @before, $_;
- }
- last if $max && ( $nmatches >= $max ) && !$after;
- }
- next;
- } # not a match
- ++$nmatches;
- # print an empty line as a divider before first line in each file (not before the first file)
- if ( !$any_output && $opt->{show_filename} && $opt->{break} && defined( $context_overall_output_count ) ) {
- App::Ack::print_blank_line();
- }
- shift @lines if $has_lines;
- if ( $res->is_binary ) {
- App::Ack::print( "Binary file $filename matches\n" );
- last;
- }
- if ( $keep_context ) {
- if ( @before ) {
- print_match_or_context( $opt, 0, $before_starts_at_line, $-[0], $+[0], @before );
- @before = ();
- $before_starts_at_line = 0;
- }
- if ( $max && $nmatches > $max ) {
- --$after;
- }
- else {
- $after = $after_context;
- }
- }
- print_match_or_context( $opt, 1, $., $-[0], $+[0], $_ );
- last if $max && ( $nmatches >= $max ) && !$after;
- } # while
- return $nmatches;
- } # search_resource()
- sub print_match_or_context {
- my $opt = shift; # opts array
- my $is_match = shift; # is there a match on the line?
- my $line_no = shift;
- my $match_start = shift;
- my $match_end = shift;
- my $color = $opt->{color};
- my $heading = $opt->{heading};
- my $show_filename = $opt->{show_filename};
- my $show_column = $opt->{column};
- if ( $show_filename ) {
- if ( not defined $display_filename ) {
- $display_filename =
- $color
- ? Term::ANSIColor::colored( $filename, $ENV{ACK_COLOR_FILENAME} )
- : $filename;
- if ( $heading && !$any_output ) {
- App::Ack::print_first_filename($display_filename);
- }
- }
- }
- my $sep = $is_match ? ':' : '-';
- my $output_func = $opt->{output};
- for ( @_ ) {
- if ( $keep_context && !$output_func ) {
- if ( ( $last_output_line != $line_no - 1 ) &&
- ( $any_output || ( !$heading && defined( $context_overall_output_count ) ) ) ) {
- App::Ack::print_separator();
- }
- # to ensure separators between different files when --noheading
- $last_output_line = $line_no;
- }
- if ( $show_filename ) {
- App::Ack::print_filename($display_filename, $sep) if not $heading;
- my $display_line_no =
- $color
- ? Term::ANSIColor::colored( $line_no, $ENV{ACK_COLOR_LINENO} )
- : $line_no;
- App::Ack::print_line_no($display_line_no, $sep);
- }
- if ( $output_func ) {
- while ( /$regex/go ) {
- App::Ack::print( $output_func->() . "\n" );
- }
- }
- else {
- if ( $color && $is_match && $regex &&
- s/$regex/Term::ANSIColor::colored( substr($_, $-[0], $+[0] - $-[0]), $ENV{ACK_COLOR_MATCH} )/eg ) {
- # At the end of the line reset the color and remove newline
- s/[\r\n]*\z/\e[0m\e[K/;
- }
- else {
- # remove any kind of newline at the end of the line
- s/[\r\n]*\z//;
- }
- if ( $show_column ) {
- App::Ack::print_column_no( $match_start+1, $sep );
- }
- App::Ack::print($_ . "\n");
- }
- $any_output = 1;
- ++$context_overall_output_count;
- ++$line_no;
- }
- return;
- } # print_match_or_context()
- } # scope around search_resource() and print_match_or_context()
- TOTAL_COUNT_SCOPE: {
- my $total_count;
- sub get_total_count {
- return $total_count;
- }
- sub reset_total_count {
- $total_count = 0;
- }
- sub search_and_list {
- my $res = shift;
- my $opt = shift;
- my $nmatches = 0;
- my $count = $opt->{count};
- my $ors = $opt->{print0} ? "\0" : "\n"; # output record separator
- my $show_filename = $opt->{show_filename};
- my $regex = qr/$opt->{regex}/;
- if ( $opt->{v} ) {
- while ( $res->next_text ) {
- if ( /$regex/ ) {
- return 0 unless $count;
- }
- else {
- ++$nmatches;
- }
- }
- }
- else {
- while ( $res->next_text ) {
- if ( /$regex/ ) {
- ++$nmatches;
- last unless $count;
- }
- }
- }
- if ( $opt->{show_total} ) {
- $total_count += $nmatches;
- }
- else {
- if ( $nmatches ) {
- App::Ack::print_count( $res->name, $nmatches, $ors, $count, $show_filename );
- }
- elsif ( $count && !$opt->{l} ) {
- App::Ack::print_count0( $res->name, $ors, $show_filename );
- }
- }
- return $nmatches ? 1 : 0;
- } # search_and_list()
- } # scope around $total_count
- sub filetypes_supported_set {
- return grep { defined $type_wanted{$_} && ($type_wanted{$_} == 1) } filetypes_supported();
- }
- sub print_files {
- my $iter = shift;
- my $opt = shift;
- my $ors = $opt->{print0} ? "\0" : "\n";
- my $nmatches = 0;
- while ( defined ( my $file = $iter->() ) ) {
- App::Ack::print $file, $opt->{show_types} ? " => " . join( ',', filetypes( $file ) ) : (), $ors;
- $nmatches++;
- last if $opt->{1};
- }
- return $nmatches;
- }
- sub print_files_with_matches {
- my $iter = shift;
- my $opt = shift;
- # if we have -l and only 1 file given on command line (this means
- # show_filename is set to 0), we want to see the filename nevertheless
- $opt->{show_filename} = 1 if $opt->{l};
- $opt->{show_filename} = 0 if $opt->{h};
- $opt->{show_filename} = 1 if $opt->{H};
- # abuse options to hand in the show_total parameter to search_and_list
- $opt->{show_total} = $opt->{count} && !$opt->{show_filename};
- reset_total_count();
- my $nmatches = 0;
- while ( defined ( my $filename = $iter->() ) ) {
- my $repo = App::Ack::Repository::Basic->new( $filename );
- my $res;
- while ( $res = $repo->next_resource() ) {
- $nmatches += search_and_list( $res, $opt );
- $res->close();
- last if $nmatches && $opt->{1};
- }
- $repo->close();
- }
- if ( $nmatches && $opt->{show_total} ) {
- App::Ack::print_count('', get_total_count(), "\n", 1, 0 )
- }
- return $nmatches;
- }
- sub print_matches {
- my $iter = shift;
- my $opt = shift;
- $opt->{show_filename} = 0 if $opt->{h};
- $opt->{show_filename} = 1 if $opt->{H};
- my $nmatches = 0;
- while ( defined ( my $filename = $iter->() ) ) {
- my $repo;
- my $tarballs_work = 0;
- if ( $tarballs_work && $filename =~ /\.tar\.gz$/ ) {
- App::Ack::die( 'Not working here yet' );
- require App::Ack::Repository::Tar; # XXX Error checking
- $repo = App::Ack::Repository::Tar->new( $filename );
- }
- else {
- $repo = App::Ack::Repository::Basic->new( $filename );
- }
- $repo or next;
- while ( my $res = $repo->next_resource() ) {
- my $needs_line_scan;
- if ( $opt->{regex} && !$opt->{passthru} ) {
- $needs_line_scan = $res->needs_line_scan( $opt );
- if ( $needs_line_scan ) {
- $res->reset();
- }
- }
- else {
- $needs_line_scan = 1;
- }
- if ( $needs_line_scan ) {
- $nmatches += search_resource( $res, $opt );
- }
- $res->close();
- }
- last if $nmatches && $opt->{1};
- $repo->close();
- }
- return $nmatches;
- }
- sub filetype_setup {
- my $filetypes_supported_set = filetypes_supported_set();
- # If anyone says --no-whatever, we assume all other types must be on.
- if ( !$filetypes_supported_set ) {
- for my $i ( keys %type_wanted ) {
- $type_wanted{$i} = 1 unless ( defined( $type_wanted{$i} ) || $i eq 'binary' || $i eq 'text' || $i eq 'skipped' );
- }
- }
- return;
- }
- EXPAND_FILENAMES_SCOPE: {
- my $filter;
- sub expand_filenames {
- my $argv = shift;
- my $attr;
- my @files;
- foreach my $pattern ( @{$argv} ) {
- my @results = bsd_glob( $pattern );
- if (@results == 0) {
- @results = $pattern; # Glob didn't match, pass it thru unchanged
- }
- elsif ( (@results > 1) or ($results[0] ne $pattern) ) {
- if (not defined $filter) {
- eval 'require Win32::File;';
- if ($@) {
- $filter = 0;
- }
- else {
- $filter = Win32::File::HIDDEN()|Win32::File::SYSTEM();
- }
- } # end unless we've tried to load Win32::File
- if ( $filter ) {
- # Filter out hidden and system files:
- @results = grep { not(Win32::File::GetAttributes($_, $attr) and $attr & $filter) } @results;
- App::Ack::warn( "$pattern: Matched only hidden files" ) unless @results;
- } # end if we can filter by file attributes
- } # end elsif this pattern got expanded
- push @files, @results;
- } # end foreach pattern
- return \@files;
- } # end expand_filenames
- } # EXPAND_FILENAMES_SCOPE
- sub get_starting_points {
- my $argv = shift;
- my $opt = shift;
- my @what;
- if ( @{$argv} ) {
- @what = @{ $is_windows ? expand_filenames($argv) : $argv };
- $_ = File::Next::reslash( $_ ) for @what;
- # Show filenames unless we've specified one single file
- $opt->{show_filename} = (@what > 1) || (!-f $what[0]);
- }
- else {
- @what = '.'; # Assume current directory
- $opt->{show_filename} = 1;
- }
- for my $start_point (@what) {
- App::Ack::warn( "$start_point: No such file or directory" ) unless -e $start_point;
- }
- return \@what;
- }
- sub _match {
- my ( $target, $expression, $invert_flag ) = @_;
- if ( $invert_flag ) {
- return $target !~ $expression;
- }
- else {
- return $target =~ $expression;
- }
- }
- sub get_iterator {
- my $what = shift;
- my $opt = shift;
- # Starting points are always searched, no matter what
- my %starting_point = map { ($_ => 1) } @{$what};
- my $g_regex = defined $opt->{G} ? qr/$opt->{G}/ : undef;
- my $file_filter;
- if ( $g_regex ) {
- $file_filter
- = $opt->{u} ? sub { _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) } # XXX Maybe this should be a 1, no?
- : $opt->{all} ? sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_searchable( $_ ) ) }
- : sub { $starting_point{ $File::Next::name } || ( _match( $File::Next::name, qr/$g_regex/, $opt->{invert_file_match} ) && is_interesting( @ _) ) }
- ;
- }
- else {
- $file_filter
- = $opt->{u} ? sub {1}
- : $opt->{all} ? sub { $starting_point{ $File::Next::name } || is_searchable( $_ ) }
- : sub { $starting_point{ $File::Next::name } || is_interesting( @_ ) }
- ;
- }
- my $descend_filter
- = $opt->{n} ? sub {0}
- : $opt->{u} ? sub {1}
- : \&ignoredir_filter;
- my $iter =
- File::Next::files( {
- file_filter => $file_filter,
- descend_filter => $descend_filter,
- error_handler => sub { my $msg = shift; App::Ack::warn( $msg ) },
- sort_files => $opt->{sort_files},
- follow_symlinks => $opt->{follow},
- }, @{$what} );
- return $iter;
- }
- sub set_up_pager {
- my $command = shift;
- return if App::Ack::output_to_pipe();
- my $pager;
- if ( not open( $pager, '|-', $command ) ) {
- App::Ack::die( qq{Unable to pipe to pager "$command": $!} );
- }
- $fh = $pager;
- return;
- }
- sub input_from_pipe {
- return $input_from_pipe;
- }
- sub output_to_pipe {
- return $output_to_pipe;
- }
- sub exit_from_ack {
- my $nmatches = shift;
- my $rc = $nmatches ? 0 : 1;
- exit $rc;
- }
- 1; # End of App::Ack
- package App::Ack::Repository;
- use warnings;
- use strict;
- sub FAIL {
- require Carp;
- Carp::confess( 'Must be overloaded' );
- }
- sub new {
- FAIL();
- }
- sub next_resource {
- FAIL();
- }
- sub close {
- FAIL();
- }
- 1;
- package App::Ack::Resource;
- use warnings;
- use strict;
- sub FAIL {
- require Carp;
- Carp::confess( 'Must be overloaded' );
- }
- sub new {
- FAIL();
- }
- sub name {
- FAIL();
- }
- sub is_binary {
- FAIL();
- }
- sub needs_line_scan {
- FAIL();
- }
- sub reset {
- FAIL();
- }
- sub next_text {
- FAIL();
- }
- sub close {
- FAIL();
- }
- 1;
- package App::Ack::Plugin::Basic;
- package App::Ack::Resource::Basic;
- use warnings;
- use strict;
- our @ISA = qw( App::Ack::Resource );
- sub new {
- my $class = shift;
- my $filename = shift;
- my $self = bless {
- filename => $filename,
- fh => undef,
- could_be_binary => undef,
- opened => undef,
- id => undef,
- }, $class;
- if ( $self->{filename} eq '-' ) {
- $self->{fh} = *STDIN;
- $self->{could_be_binary} = 0;
- }
- else {
- if ( !open( $self->{fh}, '<', $self->{filename} ) ) {
- App::Ack::warn( "$self->{filename}: $!" );
- return;
- }
- $self->{could_be_binary} = 1;
- }
- return $self;
- }
- sub name {
- my $self = shift;
- return $self->{filename};
- }
- sub is_binary {
- my $self = shift;
- if ( $self->{could_be_binary} ) {
- return -B $self->{filename};
- }
- return 0;
- }
- sub needs_line_scan {
- my $self = shift;
- my $opt = shift;
- return 1 if $opt->{v};
- my $size = -s $self->{fh};
- if ( $size == 0 ) {
- return 0;
- }
- elsif ( $size > 100_000 ) {
- return 1;
- }
- my $buffer;
- my $rc = sysread( $self->{fh}, $buffer, $size );
- if ( not defined $rc ) {
- App::Ack::warn( "$self->{filename}: $!" );
- return 1;
- }
- return 0 unless $rc && ( $rc == $size );
- my $regex = $opt->{regex};
- return $buffer =~ /$regex/m;
- }
- sub reset {
- my $self = shift;
- seek( $self->{fh}, 0, 0 )
- or App::Ack::warn( "$self->{filename}: $!" );
- return;
- }
- sub next_text {
- if ( defined ($_ = readline $_[0]->{fh}) ) {
- $. = ++$_[0]->{line};
- return 1;
- }
- return;
- }
- sub close {
- my $self = shift;
- if ( not close $self->{fh} ) {
- App::Ack::warn( $self->name() . ": $!" );
- }
- return;
- }
- package App::Ack::Repository::Basic;
- our @ISA = qw( App::Ack::Repository );
- use warnings;
- use strict;
- sub new {
- my $class = shift;
- my $filename = shift;
- my $self = bless {
- filename => $filename,
- nexted => 0,
- }, $class;
- return $self;
- }
- sub next_resource {
- my $self = shift;
- return if $self->{nexted};
- $self->{nexted} = 1;
- return App::Ack::Resource::Basic->new( $self->{filename} );
- }
- sub close {
- }
- 1;
|