| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161 |
- #!/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
- # https://github.com/petdance/ack2
- # and submit patches against the individual files
- # that build ack.
- #
- package main;
- use strict;
- use warnings;
- our $VERSION = '2.14'; # Check http://beyondgrep.com/ for updates
- use 5.008008;
- use Getopt::Long 2.35 ();
- use Carp 1.04 ();
- use File::Spec ();
- # XXX Don't make this so brute force
- # See also: https://github.com/petdance/ack2/issues/89
- # These are all our globals.
- MAIN: {
- $App::Ack::orig_program_name = $0;
- $0 = join(' ', 'ack', $0);
- 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 my $arg ( @ARGV ) {
- last if ( $arg eq '--' );
- # Get the --thpppt, --bar, --cathy checking out of the way.
- $arg =~ /^--th[pt]+t+$/ and App::Ack::_thpppt($arg);
- $arg eq '--bar' and App::Ack::_bar();
- $arg eq '--cathy' and App::Ack::_cathy();
- # See if we want to ignore the environment. (Don't tell Al Gore.)
- $arg eq '--env' and $env_is_usable = 1;
- $arg eq '--noenv' and $env_is_usable = 0;
- }
- if ( !$env_is_usable ) {
- my @keys = ( 'ACKRC', grep { /^ACK_/ } keys %ENV );
- delete @ENV{@keys};
- }
- load_colors();
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- Getopt::Long::Configure('pass_through', 'no_auto_abbrev');
- Getopt::Long::GetOptions(
- 'help' => sub { App::Ack::show_help(); exit; },
- 'version' => sub { App::Ack::print_version_statement(); exit; },
- 'man' => sub { App::Ack::show_man(); exit; },
- );
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- if ( !@ARGV ) {
- App::Ack::show_help();
- exit 1;
- }
- main();
- }
- sub _compile_descend_filter {
- my ( $opt ) = @_;
- my $idirs = $opt->{idirs};
- my $dont_ignore_dirs = $opt->{no_ignore_dirs};
- # if we have one or more --noignore-dir directives, we can't ignore
- # entire subdirectory hierarchies, so we return an "accept all"
- # filter and scrutinize the files more in _compile_file_filter
- return if $dont_ignore_dirs;
- return unless $idirs && @{$idirs};
- my %ignore_dirs;
- foreach my $idir (@{$idirs}) {
- if ( $idir =~ /^(\w+):(.*)/ ) {
- if ( $1 eq 'is') {
- $ignore_dirs{$2} = 1;
- }
- else {
- Carp::croak( 'Non-is filters are not yet supported for --ignore-dir' );
- }
- }
- else {
- Carp::croak( qq{Invalid filter specification "$idir"} );
- }
- }
- return sub {
- return !exists $ignore_dirs{$_} && !exists $ignore_dirs{$File::Next::dir};
- };
- }
- sub _compile_file_filter {
- my ( $opt, $start ) = @_;
- my $ifiles = $opt->{ifiles};
- $ifiles ||= [];
- my $ifiles_filters = App::Ack::Filter::Collection->new();
- foreach my $filter_spec (@{$ifiles}) {
- if ( $filter_spec =~ /^(\w+):(.+)/ ) {
- my ($how,$what) = ($1,$2);
- my $filter = App::Ack::Filter->create_filter($how, split(/,/, $what));
- $ifiles_filters->add($filter);
- }
- else {
- Carp::croak( qq{Invalid filter specification "$filter_spec"} );
- }
- }
- my $filters = $opt->{'filters'} || [];
- my $direct_filters = App::Ack::Filter::Collection->new();
- my $inverse_filters = App::Ack::Filter::Collection->new();
- foreach my $filter (@{$filters}) {
- if ($filter->is_inverted()) {
- # We want to check if files match the uninverted filters
- $inverse_filters->add($filter->invert());
- }
- else {
- $direct_filters->add($filter);
- }
- }
- my %is_member_of_starting_set = map { (get_file_id($_) => 1) } @{$start};
- my $ignore_dir_list = $opt->{idirs};
- my $dont_ignore_dir_list = $opt->{no_ignore_dirs};
- my %ignore_dir_set;
- my %dont_ignore_dir_set;
- foreach my $filter (@{ $ignore_dir_list }) {
- if ( $filter =~ /^(\w+):(.*)/ ) {
- if ( $1 eq 'is' ) {
- $ignore_dir_set{ $2 } = 1;
- } else {
- Carp::croak( 'Non-is filters are not yet supported for --ignore-dir' );
- }
- } else {
- Carp::croak( qq{Invalid filter specification "$filter"} );
- }
- }
- foreach my $filter (@{ $dont_ignore_dir_list }) {
- if ( $filter =~ /^(\w+):(.*)/ ) {
- if ( $1 eq 'is' ) {
- $dont_ignore_dir_set{ $2 } = 1;
- } else {
- Carp::croak( 'Non-is filters are not yet supported for --ignore-dir' );
- }
- } else {
- Carp::croak( qq{Invalid filter specification "$filter"} );
- }
- }
- my $match_filenames = $opt->{g};
- my $match_regex = $opt->{regex};
- my $is_inverted = $opt->{v};
- return sub {
- if ( $match_filenames ) {
- if ( $File::Next::name =~ /$match_regex/ && $is_inverted ) {
- return;
- }
- if ( $File::Next::name !~ /$match_regex/ && !$is_inverted ) {
- return;
- }
- }
- # ack always selects files that are specified on the command
- # line, regardless of filetype. If you want to ack a JPEG,
- # and say "ack foo whatever.jpg" it will do it for you.
- return 1 if $is_member_of_starting_set{ get_file_id($File::Next::name) };
- if ( $dont_ignore_dir_list ) {
- my ( undef, $dirname ) = File::Spec->splitpath($File::Next::name);
- my @dirs = File::Spec->splitdir($dirname);
- my $is_ignoring = 0;
- foreach my $dir ( @dirs ) {
- if ( $ignore_dir_set{ $dir } ) {
- $is_ignoring = 1;
- }
- elsif ( $dont_ignore_dir_set{ $dir } ) {
- $is_ignoring = 0;
- }
- }
- if ( $is_ignoring ) {
- return 0;
- }
- }
- # Ignore named pipes found in directory searching. Named
- # pipes created by subprocesses get specified on the command
- # line, so the rule of "always select whatever is on the
- # command line" wins.
- return 0 if -p $File::Next::name;
- # We can't handle unreadable filenames; report them.
- if ( not -r _ ) {
- use filetest 'access';
- if ( not -R $File::Next::name ) {
- if ( $App::Ack::report_bad_filenames ) {
- App::Ack::warn( "${File::Next::name}: cannot open file for reading" );
- }
- return 0;
- }
- }
- my $resource = App::Ack::Resource::Basic->new($File::Next::name);
- return 0 if !$resource || $ifiles_filters->filter($resource);
- my $match_found = $direct_filters->filter($resource);
- # Don't bother invoking inverse filters unless we consider the current resource a match
- if ( $match_found && $inverse_filters->filter( $resource ) ) {
- $match_found = 0;
- }
- return $match_found;
- };
- }
- sub show_types {
- my $resource = shift;
- my $ors = shift;
- my @types = filetypes( $resource );
- my $types = join( ',', @types );
- my $arrow = @types ? ' => ' : ' =>';
- App::Ack::print( $resource->name, $arrow, join( ',', @types ), $ors );
- return;
- }
- # Set default colors, load Term::ANSIColor
- sub load_colors {
- eval 'use Term::ANSIColor 1.10 ()';
- eval 'use Win32::Console::ANSI' if $App::Ack::is_windows;
- $ENV{ACK_COLOR_MATCH} ||= 'black on_yellow';
- $ENV{ACK_COLOR_FILENAME} ||= 'bold green';
- $ENV{ACK_COLOR_LINENO} ||= 'bold yellow';
- return;
- }
- sub filetypes {
- my ( $resource ) = @_;
- my @matches;
- foreach my $k (keys %App::Ack::mappings) {
- my $filters = $App::Ack::mappings{$k};
- foreach my $filter (@{$filters}) {
- # clone the resource
- my $clone = $resource->clone;
- if ( $filter->filter($clone) ) {
- push @matches, $k;
- last;
- }
- }
- }
- # http://search.cpan.org/dist/Perl-Critic/lib/Perl/Critic/Policy/Subroutines/ProhibitReturnSort.pm
- @matches = sort @matches;
- return @matches;
- }
- # Returns a (fairly) unique identifier for a file.
- # Use this function to compare two files to see if they're
- # equal (ie. the same file, but with a different path/links/etc).
- sub get_file_id {
- my ( $filename ) = @_;
- if ( $App::Ack::is_windows ) {
- return File::Next::reslash( $filename );
- }
- else {
- # XXX is this the best method? it always hits the FS
- if( my ( $dev, $inode ) = (stat($filename))[0, 1] ) {
- return join(':', $dev, $inode);
- }
- else {
- # XXX this could be better
- return $filename;
- }
- }
- }
- # Returns a regex object based on a string and command-line options.
- # Dies when the regex $str is undefined (i.e. not given on command line).
- 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} ) {
- my $pristine_str = $str;
- $str = "(?:$str)";
- $str = "\\b$str" if $pristine_str =~ /^\w/;
- $str = "$str\\b" if $pristine_str =~ /\w$/;
- }
- my $regex_is_lc = $str eq lc $str;
- if ( $opt->{i} || ($opt->{smart_case} && $regex_is_lc) ) {
- $str = "(?i)$str";
- }
- my $re = eval { qr/$str/ };
- if ( !$re ) {
- die "Invalid regex '$str':\n $@";
- }
- return $re;
- }
- {
- my @before_ctx_lines;
- my @after_ctx_lines;
- my $is_iterating;
- my $has_printed_something;
- BEGIN {
- $has_printed_something = 0;
- }
- sub print_matches_in_resource {
- my ( $resource, $opt ) = @_;
- my $passthru = $opt->{passthru};
- my $max_count = $opt->{m} || -1;
- my $nmatches = 0;
- my $filename = $resource->name;
- my $break = $opt->{break};
- my $heading = $opt->{heading};
- my $ors = $opt->{print0} ? "\0" : "\n";
- my $color = $opt->{color};
- my $print_filename = $opt->{show_filename};
- my $has_printed_for_this_resource = 0;
- $is_iterating = 1;
- local $opt->{before_context} = $opt->{output} ? 0 : $opt->{before_context};
- local $opt->{after_context} = $opt->{output} ? 0 : $opt->{after_context};
- my $n_before_ctx_lines = $opt->{before_context} || 0;
- my $n_after_ctx_lines = $opt->{after_context} || 0;
- @after_ctx_lines = @before_ctx_lines = ();
- my $fh = $resource->open();
- if ( !$fh ) {
- if ( $App::Ack::report_bad_filenames ) {
- App::Ack::warn( "$filename: $!" );
- }
- return 0;
- }
- my $display_filename = $filename;
- if ( $print_filename && $heading && $color ) {
- $display_filename = Term::ANSIColor::colored($display_filename, $ENV{ACK_COLOR_FILENAME});
- }
- # check for context before the main loop, so we don't
- # pay for it if we don't need it
- if ( $n_before_ctx_lines || $n_after_ctx_lines ) {
- my $current_line = <$fh>; # prime the first line of input
- while ( defined $current_line ) {
- while ( (@after_ctx_lines < $n_after_ctx_lines) && defined($_ = <$fh>) ) {
- push @after_ctx_lines, $_;
- }
- local $_ = $current_line;
- my $former_dot_period = $.;
- $. -= @after_ctx_lines;
- if ( does_match($opt, $_) ) {
- if ( !$has_printed_for_this_resource ) {
- if ( $break && $has_printed_something ) {
- App::Ack::print_blank_line();
- }
- if ( $print_filename && $heading ) {
- App::Ack::print_filename( $display_filename, $ors );
- }
- }
- print_line_with_context($opt, $filename, $_, $.);
- $has_printed_for_this_resource = 1;
- $nmatches++;
- $max_count--;
- }
- elsif ( $passthru ) {
- chomp; # XXX proper newline handling?
- # XXX inline this call?
- if ( $break && !$has_printed_for_this_resource && $has_printed_something ) {
- App::Ack::print_blank_line();
- }
- print_line_with_options($opt, $filename, $_, $., ':');
- $has_printed_for_this_resource = 1;
- }
- last unless $max_count != 0;
- # I tried doing this with local(), but for some reason,
- # $. continued to have its new value after the exit of the
- # enclosing block. I'm guessing that $. has some extra
- # magic associated with it or something. If someone can
- # tell me why this happened, I would love to know!
- $. = $former_dot_period; # XXX this won't happen on an exception
- if ( $n_before_ctx_lines ) {
- push @before_ctx_lines, $current_line;
- shift @before_ctx_lines while @before_ctx_lines > $n_before_ctx_lines;
- }
- if ( $n_after_ctx_lines ) {
- $current_line = shift @after_ctx_lines;
- }
- else {
- $current_line = <$fh>;
- }
- }
- }
- else {
- local $_;
- while ( <$fh> ) {
- if ( does_match($opt, $_) ) {
- if ( !$has_printed_for_this_resource ) {
- if ( $break && $has_printed_something ) {
- App::Ack::print_blank_line();
- }
- if ( $print_filename && $heading ) {
- App::Ack::print_filename( $display_filename, $ors );
- }
- }
- print_line_with_context($opt, $filename, $_, $.);
- $has_printed_for_this_resource = 1;
- $nmatches++;
- $max_count--;
- }
- elsif ( $passthru ) {
- chomp; # XXX proper newline handling?
- if ( $break && !$has_printed_for_this_resource && $has_printed_something ) {
- App::Ack::print_blank_line();
- }
- print_line_with_options($opt, $filename, $_, $., ':');
- $has_printed_for_this_resource = 1;
- }
- last unless $max_count != 0;
- }
- }
- $is_iterating = 0; # XXX this won't happen on an exception
- # then again, do we care? ack doesn't really
- # handle exceptions anyway.
- return $nmatches;
- }
- sub print_line_with_options {
- my ( $opt, $filename, $line, $line_no, $separator ) = @_;
- $has_printed_something = 1;
- my $print_filename = $opt->{show_filename};
- my $print_column = $opt->{column};
- my $ors = $opt->{print0} ? "\0" : "\n";
- my $heading = $opt->{heading};
- my $output_expr = $opt->{output};
- my $color = $opt->{color};
- my @line_parts;
- if( $color ) {
- $filename = Term::ANSIColor::colored($filename,
- $ENV{ACK_COLOR_FILENAME});
- $line_no = Term::ANSIColor::colored($line_no,
- $ENV{ACK_COLOR_LINENO});
- }
- if($print_filename) {
- if( $heading ) {
- push @line_parts, $line_no;
- }
- else {
- push @line_parts, $filename, $line_no;
- }
- if( $print_column ) {
- push @line_parts, get_match_column();
- }
- }
- if( $output_expr ) {
- while ( $line =~ /$opt->{regex}/og ) {
- # XXX We need to stop using eval() for --output. See https://github.com/petdance/ack2/issues/421
- my $output = eval $output_expr;
- App::Ack::print( join( $separator, @line_parts, $output ), $ors );
- }
- }
- else {
- if ( $color ) {
- $line =~ /$opt->{regex}/o; # this match is redundant, but we need
- # to perfom it in order to get if
- # capture groups are set
- if ( @+ > 1 ) { # if we have captures
- while ( $line =~ /$opt->{regex}/og ) {
- my $offset = 0; # additional offset for when we add stuff
- my $previous_match_end = 0;
- for ( my $i = 1; $i < @+; $i++ ) {
- my ( $match_start, $match_end ) = ( $-[$i], $+[$i] );
- next unless defined($match_start);
- next if $match_start < $previous_match_end;
- my $substring = substr( $line,
- $offset + $match_start, $match_end - $match_start );
- my $substitution = Term::ANSIColor::colored( $substring,
- $ENV{ACK_COLOR_MATCH} );
- substr( $line, $offset + $match_start,
- $match_end - $match_start, $substitution );
- $previous_match_end = $match_end; # offsets do not need to be applied
- $offset += length( $substitution ) - length( $substring );
- }
- pos($line) = $+[0] + $offset;
- }
- }
- else {
- my $matched = 0; # flag; if matched, need to escape afterwards
- while ( $line =~ /$opt->{regex}/og ) {
- $matched = 1;
- my ( $match_start, $match_end ) = ($-[0], $+[0]);
- next unless defined($match_start);
- my $substring = substr( $line, $match_start,
- $match_end - $match_start );
- my $substitution = Term::ANSIColor::colored( $substring,
- $ENV{ACK_COLOR_MATCH} );
- substr( $line, $match_start, $match_end - $match_start,
- $substitution );
- pos($line) = $match_end +
- (length( $substitution ) - length( $substring ));
- }
- # XXX why do we do this?
- $line .= "\033[0m\033[K" if $matched;
- }
- }
- push @line_parts, $line;
- App::Ack::print( join( $separator, @line_parts ), $ors );
- }
- return;
- }
- sub iterate {
- my ( $resource, $opt, $cb ) = @_;
- $is_iterating = 1;
- local $opt->{before_context} = $opt->{output} ? 0 : $opt->{before_context};
- local $opt->{after_context} = $opt->{output} ? 0 : $opt->{after_context};
- my $n_before_ctx_lines = $opt->{before_context} || 0;
- my $n_after_ctx_lines = $opt->{after_context} || 0;
- @after_ctx_lines = @before_ctx_lines = ();
- my $fh = $resource->open();
- if ( !$fh ) {
- if ( $App::Ack::report_bad_filenames ) {
- App::Ack::warn( $resource->name . ': ' . $! );
- }
- return;
- }
- # Check for context before the main loop, so we don't pay for it if we don't need it.
- if ( $n_before_ctx_lines || $n_after_ctx_lines ) {
- my $current_line = <$fh>; # prime the first line of input
- while ( defined $current_line ) {
- while ( (@after_ctx_lines < $n_after_ctx_lines) && defined($_ = <$fh>) ) {
- push @after_ctx_lines, $_;
- }
- local $_ = $current_line;
- my $former_dot_period = $.;
- $. -= @after_ctx_lines;
- last unless $cb->();
- # I tried doing this with local(), but for some reason,
- # $. continued to have its new value after the exit of the
- # enclosing block. I'm guessing that $. has some extra
- # magic associated with it or something. If someone can
- # tell me why this happened, I would love to know!
- $. = $former_dot_period; # XXX this won't happen on an exception
- if ( $n_before_ctx_lines ) {
- push @before_ctx_lines, $current_line;
- shift @before_ctx_lines while @before_ctx_lines > $n_before_ctx_lines;
- }
- if ( $n_after_ctx_lines ) {
- $current_line = shift @after_ctx_lines;
- }
- else {
- $current_line = <$fh>;
- }
- }
- }
- else {
- local $_;
- while ( <$fh> ) {
- last unless $cb->();
- }
- }
- $is_iterating = 0; # XXX this won't happen on an exception
- # then again, do we care? ack doesn't really
- # handle exceptions anyway.
- return;
- }
- sub get_context {
- if ( not $is_iterating ) {
- Carp::croak( 'get_context() called outside of iterate()' );
- }
- return (
- scalar(@before_ctx_lines) ? \@before_ctx_lines : undef,
- scalar(@after_ctx_lines) ? \@after_ctx_lines : undef,
- );
- }
- }
- {
- my $is_first_match;
- my $previous_file_processed;
- my $previous_line_printed;
- BEGIN {
- $is_first_match = 1;
- $previous_line_printed = -1;
- }
- sub print_line_with_context {
- my ( $opt, $filename, $matching_line, $line_no ) = @_;
- my $heading = $opt->{heading};
- if( !defined($previous_file_processed) ||
- $previous_file_processed ne $filename ) {
- $previous_file_processed = $filename;
- $previous_line_printed = -1;
- if( $heading ) {
- $is_first_match = 1;
- }
- }
- my $ors = $opt->{print0} ? "\0" : "\n";
- my $match_word = $opt->{w};
- my $is_tracking_context = $opt->{after_context} || $opt->{before_context};
- my $output_expr = $opt->{output};
- $matching_line =~ s/[\r\n]+$//g;
- my ( $before_context, $after_context ) = get_context();
- if ( $before_context ) {
- my $first_line = $. - @{$before_context};
- if ( $first_line <= $previous_line_printed ) {
- splice @{$before_context}, 0, $previous_line_printed - $first_line + 1;
- $first_line = $. - @{$before_context};
- }
- if ( @{$before_context} ) {
- my $offset = @{$before_context};
- if( !$is_first_match && $previous_line_printed != $first_line - 1 ) {
- App::Ack::print('--', $ors);
- }
- foreach my $line (@{$before_context}) {
- my $context_line_no = $. - $offset;
- if ( $context_line_no <= $previous_line_printed ) {
- next;
- }
- chomp $line;
- local $opt->{column};
- print_line_with_options($opt, $filename, $line, $context_line_no, '-');
- $previous_line_printed = $context_line_no;
- $offset--;
- }
- }
- }
- if ( $. > $previous_line_printed ) {
- if( $is_tracking_context && !$is_first_match && $previous_line_printed != $. - 1 ) {
- App::Ack::print('--', $ors);
- }
- print_line_with_options($opt, $filename, $matching_line, $line_no, ':');
- $previous_line_printed = $.;
- }
- if($after_context) {
- my $offset = 1;
- foreach my $line (@{$after_context}) {
- # XXX improve this!
- if ( $previous_line_printed >= $. + $offset ) {
- $offset++;
- next;
- }
- chomp $line;
- if ( $opt->{regex} && does_match( $opt, $line ) ) {
- print_line_with_options($opt, $filename, $line, $. + $offset, ':');
- }
- else {
- local $opt->{column};
- print_line_with_options($opt, $filename, $line, $. + $offset, '-');
- }
- $previous_line_printed = $. + $offset;
- $offset++;
- }
- }
- $is_first_match = 0;
- return;
- }
- }
- {
- my $match_column_number;
- # does_match() MUST have an $opt->{regex} set.
- sub does_match {
- my ( $opt, $line ) = @_;
- $match_column_number = undef;
- if ( $opt->{v} ) {
- return ( $line !~ /$opt->{regex}/o );
- }
- else {
- if ( $line =~ /$opt->{regex}/o ) {
- # @- = @LAST_MATCH_START
- # @+ = @LAST_MATCH_END
- $match_column_number = $-[0] + 1;
- return 1;
- }
- else {
- return;
- }
- }
- }
- sub get_match_column {
- return $match_column_number;
- }
- }
- sub resource_has_match {
- my ( $resource, $opt ) = @_;
- my $has_match = 0;
- my $fh = $resource->open();
- if ( !$fh ) {
- if ( $App::Ack::report_bad_filenames ) {
- App::Ack::warn( $resource->name . ': ' . $! );
- }
- }
- else {
- my $re = $opt->{regex};
- if ( $opt->{v} ) {
- while ( <$fh> ) {
- if (!/$re/o) {
- $has_match = 1;
- last;
- }
- }
- }
- else {
- # XXX read in chunks
- # XXX only do this for certain file sizes?
- my $content = do {
- local $/;
- <$fh>;
- };
- $has_match = $content =~ /$re/og;
- }
- close $fh;
- }
- return $has_match;
- }
- sub count_matches_in_resource {
- my ( $resource, $opt ) = @_;
- my $nmatches = 0;
- my $fh = $resource->open();
- if ( !$fh ) {
- if ( $App::Ack::report_bad_filenames ) {
- App::Ack::warn( $resource->name . ': ' . $! );
- }
- }
- else {
- my $re = $opt->{regex};
- if ( $opt->{v} ) {
- while ( <$fh> ) {
- ++$nmatches if (!/$re/o);
- }
- }
- else {
- my $content = do {
- local $/;
- <$fh>;
- };
- $nmatches =()= ($content =~ /$re/og);
- }
- close $fh;
- }
- return $nmatches;
- }
- sub main {
- my @arg_sources = App::Ack::ConfigLoader::retrieve_arg_sources();
- my $opt = App::Ack::ConfigLoader::process_args( @arg_sources );
- $App::Ack::report_bad_filenames = !$opt->{dont_report_bad_filenames};
- if ( $opt->{flush} ) {
- $| = 1;
- }
- if ( !defined($opt->{color}) && !$opt->{g} ) {
- my $windows_color = 1;
- if ( $App::Ack::is_windows ) {
- $windows_color = eval { require Win32::Console::ANSI; };
- }
- $opt->{color} = !App::Ack::output_to_pipe() && $windows_color;
- }
- if ( not defined $opt->{heading} and not defined $opt->{break} ) {
- $opt->{heading} = $opt->{break} = !App::Ack::output_to_pipe();
- }
- if ( defined($opt->{H}) || defined($opt->{h}) ) {
- $opt->{show_filename}= $opt->{H} && !$opt->{h};
- }
- if ( my $output = $opt->{output} ) {
- $output =~ s{\\}{\\\\}g;
- $output =~ s{"}{\\"}g;
- $opt->{output} = qq{"$output"};
- }
- my $resources;
- if ( $App::Ack::is_filter_mode && !$opt->{files_from} ) { # probably -x
- $resources = App::Ack::Resources->from_stdin( $opt );
- my $regex = $opt->{regex};
- $regex = shift @ARGV if not defined $regex;
- $opt->{regex} = build_regex( $regex, $opt );
- }
- else {
- if ( $opt->{f} || $opt->{lines} ) {
- if ( $opt->{regex} ) {
- App::Ack::warn( "regex ($opt->{regex}) specified with -f or --lines" );
- App::Ack::exit_from_ack( 0 ); # XXX the 0 is misleading
- }
- }
- else {
- my $regex = $opt->{regex};
- $regex = shift @ARGV if not defined $regex;
- $opt->{regex} = build_regex( $regex, $opt );
- }
- my @start;
- if ( not defined $opt->{files_from} ) {
- @start = @ARGV;
- }
- if ( !exists($opt->{show_filename}) ) {
- unless(@start == 1 && !(-d $start[0])) {
- $opt->{show_filename} = 1;
- }
- }
- if ( defined $opt->{files_from} ) {
- $resources = App::Ack::Resources->from_file( $opt, $opt->{files_from} );
- exit 1 unless $resources;
- }
- else {
- @start = ('.') unless @start;
- foreach my $target (@start) {
- if ( !-e $target && $App::Ack::report_bad_filenames) {
- App::Ack::warn( "$target: No such file or directory" );
- }
- }
- $opt->{file_filter} = _compile_file_filter($opt, \@start);
- $opt->{descend_filter} = _compile_descend_filter($opt);
- $resources = App::Ack::Resources->from_argv( $opt, \@start );
- }
- }
- App::Ack::set_up_pager( $opt->{pager} ) if defined $opt->{pager};
- my $print_filenames = $opt->{show_filename};
- my $max_count = $opt->{m};
- my $ors = $opt->{print0} ? "\0" : "\n";
- my $only_first = $opt->{1};
- my $nmatches = 0;
- my $total_count = 0;
- RESOURCES:
- while ( my $resource = $resources->next ) {
- # XXX Combine the -f and -g functions
- if ( $opt->{f} ) {
- # XXX printing should probably happen inside of App::Ack
- if ( $opt->{show_types} ) {
- show_types( $resource, $ors );
- }
- else {
- App::Ack::print( $resource->name, $ors );
- }
- ++$nmatches;
- last RESOURCES if defined($max_count) && $nmatches >= $max_count;
- }
- elsif ( $opt->{g} ) {
- if ( $opt->{show_types} ) {
- show_types( $resource, $ors );
- }
- else {
- local $opt->{show_filename} = 0; # XXX Why is this local?
- print_line_with_options($opt, '', $resource->name, 0, $ors);
- }
- ++$nmatches;
- last RESOURCES if defined($max_count) && $nmatches >= $max_count;
- }
- elsif ( $opt->{lines} ) {
- my $print_filename = $opt->{show_filename};
- my $passthru = $opt->{passthru};
- my %line_numbers;
- foreach my $line ( @{ $opt->{lines} } ) {
- my @lines = split /,/, $line;
- @lines = map {
- /^(\d+)-(\d+)$/
- ? ( $1 .. $2 )
- : $_
- } @lines;
- @line_numbers{@lines} = (1) x @lines;
- }
- my $filename = $resource->name;
- local $opt->{color} = 0;
- iterate($resource, $opt, sub {
- chomp;
- if ( $line_numbers{$.} ) {
- print_line_with_context($opt, $filename, $_, $.);
- }
- elsif ( $passthru ) {
- print_line_with_options($opt, $filename, $_, $., ':');
- }
- return 1;
- });
- }
- elsif ( $opt->{count} ) {
- my $matches_for_this_file = count_matches_in_resource( $resource, $opt );
- if ( not $opt->{show_filename} ) {
- $total_count += $matches_for_this_file;
- next RESOURCES;
- }
- if ( !$opt->{l} || $matches_for_this_file > 0) {
- if ( $print_filenames ) {
- App::Ack::print( $resource->name, ':', $matches_for_this_file, $ors );
- }
- else {
- App::Ack::print( $matches_for_this_file, $ors );
- }
- }
- }
- elsif ( $opt->{l} || $opt->{L} ) {
- my $is_match = resource_has_match( $resource, $opt );
- if ( $opt->{L} ? !$is_match : $is_match ) {
- App::Ack::print( $resource->name, $ors );
- ++$nmatches;
- last RESOURCES if $only_first;
- last RESOURCES if defined($max_count) && $nmatches >= $max_count;
- }
- }
- else {
- $nmatches += print_matches_in_resource( $resource, $opt );
- if ( $nmatches && $only_first ) {
- last RESOURCES;
- }
- }
- }
- if ( $opt->{count} && !$opt->{show_filename} ) {
- App::Ack::print( $total_count, "\n" );
- }
- 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 an alternative to F<grep> for programmers.
- 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.
- PATTERN is a Perl regular expression. Perl regular expressions
- are commonly found in other programming languages, but for the particulars
- of their behavior, please consult
- L<http://perldoc.perl.org/perlreref.html|perlreref>. If you don't know
- how to use regular expression but are interested in learning, you may
- consult L<http://perldoc.perl.org/perlretut.html|perlretut>. If you do not
- need or want ack to use regular expressions, please see the
- C<-Q>/C<--literal> option.
- 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
- If files are not specified for searching, either on the command
- line or piped in with the C<-x> option, I<ack> delves into
- subdirectories selecting files for searching.
- 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 selection, I<ack> searches through regular files that
- are not explicitly excluded by B<--ignore-dir> and B<--ignore-file>
- options, either present in F<ackrc> files or on the command line.
- The default options for I<ack> ignore certain files and directories. These
- include:
- =over 4
- =item * Backup files: Files matching F<#*#> or ending with F<~>.
- =item * Coredumps: Files matching F<core.\d+>
- =item * Version control directories like F<.svn> and F<.git>.
- =back
- Run I<ack> with the C<--dump> option to see what settings are set.
- However, I<ack> always searches the files given on the command line,
- no matter what type. If you tell I<ack> to search in a coredump,
- it will search in a coredump.
- =head1 DIRECTORY SELECTION
- I<ack> descends through the directory tree of the starting directories
- specified. If no directories are specified, the current working directory is
- used. 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
- C<ack --dump>.
- =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<--ackrc>
- Specifies an ackrc file to load after all others; see L</"ACKRC LOCATION SEMANTICS">.
- =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<--[no]break>
- Print a break between results from different files. On by default
- when used interactively.
- =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<--[no]color>, B<--[no]colour>
- B<--color> highlights the matching text. B<--nocolor> suppresses
- 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<--[no]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<--create-ackrc>
- Dumps the default ack options to standard output. This is useful for
- when you want to customize the defaults.
- =item B<--dump>
- Writes the list of options loaded and where they came from to standard
- output. Handy for debugging.
- =item B<--[no]env>
- 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<--files-from=I<FILE>>
- The list of files to be searched is specified in I<FILE>. The list of
- files are separated by newlines. If I<FILE> is C<->, the list is loaded
- from standard input.
- =item B<--[no]filter>
- Forces ack to act as if it were receiving input via a pipe.
- =item B<--[no]follow>
- 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<PATTERN>>
- Print files where the relative path + filename matches I<PATTERN>.
- This option can be combined with B<--color> to make it easier to spot
- the match.
- =item B<--[no]group>
- B<--group> groups matches by file name. 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. This is the default unless searching
- a single explicitly specified file.
- =item B<-h>, B<--no-filename>
- Suppress the prefixing of filenames on output when multiple files are
- searched.
- =item B<--[no]heading>
- Print a filename heading above each file's results. This is the default
- when used interactively.
- =item B<--help>, B<-?>
- Print a short help statement.
- =item B<--help-types>, B<--help=types>
- Print all known types.
- =item B<-i>, B<--ignore-case>
- Ignore case distinctions in PATTERN
- =item B<--ignore-ack-defaults>
- Tells ack to completely ignore the default definitions provided with ack.
- This is useful in combination with B<--create-ackrc> if you I<really> want
- to customize ack.
- =item B<--[no]ignore-dir=I<DIRNAME>>, B<--[no]ignore-directory=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<--ignore-file=I<FILTERTYPE:FILTERARGS>>
- Ignore files matching I<FILTERTYPE:FILTERARGS>. The filters are specified
- identically to file type filters as seen in L</"Defining your own types">.
- =item B<-k>, B<--known-types>
- Limit selected files to those with types that ack knows about. This is
- equivalent to the default behavior found in ack 1.
- =item B<--lines=I<NUM>>
- Only print line I<NUM> of each file. Multiple lines can be given with multiple
- B<--lines> options or as a comma separated list (B<--lines=3,5,7>). B<--lines=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.
- =item B<--match I<PATTERN>>
- Specify the I<PATTERN> 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)
- If PATTERN matches more than once then a line is output for each non-overlapping match.
- For more information please see the section L</"Examples of F<--output>">.
- =item B<--pager=I<program>>, B<--nopager>
- B<--pager> directs 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.
- B<--nopager> cancels any setting in ~/.ackrc, C<ACK_PAGER> or C<ACK_PAGER_COLOR>.
- No output will be sent through a pager.
- =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.
- =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<-s>
- Suppress error messages about nonexistent or unreadable files. This is taken
- from fgrep.
- =item B<--[no]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, and ignored if C<-i> is specified.
- B<-i> always overrides this option.
- =item B<--sort-files>
- Sorts the found files lexicographically. 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<--type=[no]TYPE>
- 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<FILTER>:I<FILTERARGS>>
- Files with the given FILTERARGS applied to the given FILTER
- are recognized as being of (the existing) type TYPE.
- See also L</"Defining your own types">.
- =item B<--type-set I<TYPE>:I<FILTER>:I<FILTERARGS>>
- Files with the given FILTERARGS applied to the given FILTER are recognized as
- being of type TYPE. This replaces an existing definition for type TYPE. See
- also L</"Defining your own types">.
- =item B<--type-del I<TYPE>>
- The filters associated with TYPE are removed from Ack, and are no longer considered
- for searches.
- =item B<-v>, B<--invert-match>
- Invert match: select non-matching lines
- =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.
- =item B<-x>
- An abbreviation for B<--files-from=->; the list of files to search are read
- from standard input, with one line per file.
- =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.
- =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<--bar>
- Check with the admiral for traps.
- =item B<--cathy>
- Chocolate, Chocolate, Chocolate!
- =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 several locations for F<.ackrc> files; the searching
- process is detailed in L</"ACKRC LOCATION SEMANTICS">. These
- files are not considered if B<--noenv> is specified on the command line.
- =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:ext: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:ext: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:ext: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:ext: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:ext:e,eiffel
- or writing on separate lines
- --type-set
- eiffel:ext:e,eiffel
- The following does B<NOT> work in the F<.ackrc> file:
- --type-set eiffel:ext:e,eiffel
- In order to see all currently defined types, use I<--help-types>, e.g.
- I<ack --type-set backup:ext:bak --type-add perl:ext:perl --help-types>
- In addition to filtering based on extension (like ack 1.x allowed), ack 2
- offers additional filter types. The generic syntax is
- I<--type-set TYPE:FILTER:FILTERARGS>; I<FILTERARGS> depends on the value
- of I<FILTER>.
- =over 4
- =item is:I<FILENAME>
- I<is> filters match the target filename exactly. It takes exactly one
- argument, which is the name of the file to match.
- Example:
- --type-set make:is:Makefile
- =item ext:I<EXTENSION>[,I<EXTENSION2>[,...]]
- I<ext> filters match the extension of the target file against a list
- of extensions. No leading dot is needed for the extensions.
- Example:
- --type-set perl:ext:pl,pm,t
- =item match:I<PATTERN>
- I<match> filters match the target filename against a regular expression.
- The regular expression is made case insensitive for the search.
- Example:
- --type-set make:match:/(gnu)?makefile/
- =item firstlinematch:I<PATTERN>
- I<firstlinematch> matches the first line of the target file against a
- regular expression. Like I<match>, the regular expression is made
- case insensitive.
- Example:
- --type-add perl:firstlinematch:/perl/
- =back
- More filter types may be made available in the future.
- =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 user's 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 AVAILABLE COLORS
- F<ack> uses the colors available in Perl's L<Term::ANSIColor> module, which
- provides the following listed values. Note that case does not matter when using
- these values.
- =head2 Foreground colors
- black red green yellow blue magenta cyan white
- bright_black bright_red bright_green bright_yellow
- bright_blue bright_magenta bright_cyan bright_white
- =head2 Background colors
- on_black on_red on_green on_yellow
- on_blue on_magenta on_cyan on_white
- on_bright_black on_bright_red on_bright_green on_bright_yellow
- on_bright_blue on_bright_magenta on_bright_cyan on_bright_white
- =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\ -k
- That example uses C<-k> to search through only files of the types ack
- knows about, 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
- Miles Sterrett has written a Vim plugin for F<ack> which allows you to use
- C<:Ack> instead of C<:grep>, as well as several other advanced features.
- L<https://github.com/mileszs/ack.vim>
- =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 have been selected
- Ack's B<-f> was originally added as a debugging tool. If ack is
- not finding matches you think it should find, run F<ack -f> to see
- what files have been selected. You can also add the C<--show-types>
- options to show the type of each file selected.
- =head2 Use B<--dump>
- This lists the ackrc files that are loaded and the options loaded
- from them.
- So for example you can find a list of directories that do not get searched or where filetypes are defined.
- =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/that/g' $(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 Examples of F<--output>
- Following variables are useful in the expansion string:
- =over 4
- =item C<$&>
- The whole string matched by PATTERN.
- =item C<$1>, C<$2>, ...
- The contents of the 1st, 2nd ... bracketed group in PATTERN.
- =item C<$`>
- The string before the match.
- =item C<$'>
- The string after the match.
- =back
- For more details and other variables see
- L<http://perldoc.perl.org/perlvar.html#Variables-related-to-regular-expressions|perlvar>.
- This example shows how to add text around a particular pattern
- (in this case adding _ around word with "e")
- ack2.pl "\w*e\w*" quick.txt --output="$`_$&_$'"
- _The_ quick brown fox jumps over the lazy dog
- The quick brown fox jumps _over_ the lazy dog
- The quick brown fox jumps over _the_ lazy dog
- This shows how to pick out particular parts of a match using ( ) within regular expression.
- ack '=head(\d+)\s+(.*)' --output=' $1 : $2'
- input file contains "=head1 NAME"
- output "1 : NAME"
- =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. You can use the C<--show-types> switch to show which type
- ack thinks each file is.
- =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 I make ack recognize F<.xyz> files?
- Yes! Please see L</"Defining your own types">. If you think
- that F<ack> should recognize a type by default, please see
- L</"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
- Alternatively, you could use a shell alias:
- # bash/zsh
- alias ack=ack-grep
- # csh
- alias ack ack-grep
- =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.
- =head2 Why is ack telling me I have an invalid option when searching for C<+foo>?
- ack treats command line options beginning with C<+> or C<-> as options; if you
- would like to search for these, you may prefix your search term with C<--> or
- use the C<--match> option. (However, don't forget that C<+> is a regular
- expression metacharacter!)
- =head2 Why does C<"ack '.{40000,}'"> fail? Isn't that a valid regex?
- The Perl language limits the repetition quanitifier to 32K. You
- can search for C<.{32767}> but not C<.{32768}>.
- =head1 ACKRC LOCATION SEMANTICS
- Ack can load its configuration from many sources. This list
- specifies the sources Ack looks for configuration; each one
- that is found is loaded in the order specified here, and
- each one overrides options set in any of the sources preceding
- it. (For example, if I set --sort-files in my user ackrc, and
- --nosort-files on the command line, the command line takes
- precedence)
- =over 4
- =item *
- Defaults are loaded from App::Ack::ConfigDefaults. This can be omitted
- using C<--ignore-ack-defaults>.
- =item * Global ackrc
- Options are then loaded from the global ackrc. This is located at
- C</etc/ackrc> on Unix-like systems.
- Under Windows XP and earlier, the ackrc is at
- C<C:\Documents and Settings\All Users\Application Data\ackrc>.
- Under Windows Vista/7, the global ackrc is at
- C<C:\ProgramData>
- The C<--noenv> option prevents all ackrc files from being loaded.
- =item * User ackrc
- Options are then loaded from the user's ackrc. This is located at
- C<$HOME/.ackrc> on Unix-like systems.
- Under Windows XP and earlier, the user's ackrc is at
- C<C:\Documents and Settings\$USER\Application Data\ackrc>.
- Under Windows Vista/7, the user's ackrc is at
- <C:\Users\$USER\AppData\Roaming>.
- If you want to load a different user-level ackrc, it may be specified
- with the C<$ACKRC> environment variable.
- The C<--noenv> option prevents all ackrc files from being loaded.
- =item * Project ackrc
- Options are then loaded from the project ackrc. The project ackrc is
- the first ackrc file with the name C<.ackrc> or C<_ackrc>, first searching
- in the current directory, then the parent directory, then the grandparent
- directory, etc. This can be omitted using C<--noenv>.
- =item * --ackrc
- The C<--ackrc> option may be included on the command line to specify an
- ackrc file that can override all others. It is consulted even if C<--noenv>
- is present.
- =item * ACK_OPTIONS
- Options are then loaded from the environment variable C<ACK_OPTIONS>. This can
- be omitted using C<--noenv>.
- =item * Command line
- Options are then loaded from the command line.
- =back
- =head1 DIFFERENCES BETWEEN ACK 1.X AND ACK 2.X
- A lot of changes were made for ack 2; here is a list of them.
- =head2 GENERAL CHANGES
- =over 4
- =item *
- When no selectors are specified, ack 1.x only searches through files that
- it can map to a file type. ack 2.x, by contrast, will search through
- every regular, non-binary file that is not explicitly ignored via
- B<--ignore-file> or B<--ignore-dir>. This is similar to the behavior of the
- B<-a/--all> option in ack 1.x.
- =item *
- A more flexible filter system has been added, so that more powerful file types
- may be created by the user. For details, please consult
- L</"Defining your own types">.
- =item *
- ack now loads multiple ackrc files; see L</"ACKRC LOCATION SEMANTICS"> for
- details.
- =item *
- ack's default filter definitions aren't special; you may tell ack to
- completely disregard them if you don't like them.
- =back
- =head2 REMOVED OPTIONS
- =over 4
- =item *
- Because of the change in default search behavior, the B<-a/--all> and
- B<-u/--unrestricted> options have been removed. In addition, the
- B<-k/--known-types> option was added to cause ack to behave with
- the default search behavior of ack 1.x.
- =item *
- The B<-G> option has been removed. Two regular expressions on the
- command line was considered too confusing; to simulate B<-G>'s functionality,
- you may use the new B<-x> option to pipe filenames from one invocation of
- ack into another.
- =item *
- The B<--binary> option has been removed.
- =item *
- The B<--skipped> option has been removed.
- =item *
- The B<--text> option has been removed.
- =item *
- The B<--invert-file-match> option has been removed. Instead, you may
- use B<-v> with B<-g>.
- =back
- =head2 CHANGED OPTIONS
- =over 4
- =item *
- The options that modify the regular expression's behavior (B<-i>, B<-w>,
- B<-Q>, and B<-v>) may now be used with B<-g>.
- =back
- =head2 ADDED OPTIONS
- =over 4
- =item *
- B<--files-from> was added so that a user may submit a list of filenames as
- a list of files to search.
- =item *
- B<-x> was added to tell ack to accept a list of filenames via standard input;
- this list is the list of filenames that will be used for the search.
- =item *
- B<-s> was added to tell ack to suppress error messages about non-existent or
- unreadable files.
- =item *
- B<--ignore-directory> and B<--noignore-directory> were added as aliases for
- B<--ignore-dir> and B<--noignore-dir> respectively.
- =item *
- B<--ignore-file> was added so that users may specify patterns of files to
- ignore (ex. /.*~$/).
- =item *
- B<--dump> was added to allow users to easily find out which options are
- set where.
- =item *
- B<--create-ackrc> was added so that users may create custom ackrc files based
- on the default settings loaded by ack, and so that users may easily view those
- defaults.
- =item *
- B<--type-del> was added to selectively remove file type definitions.
- =item *
- B<--ignore-ack-defaults> was added so that users may ignore ack's default
- options in favor of their own.
- =item *
- B<--bar> was added so ack users may consult Admiral Ackbar.
- =back
- =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<https://github.com/petdance/ack2/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<https://github.com/petdance/ack2/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://beyondgrep.com/>
- =item * The ack-users mailing list
- L<http://groups.google.com/group/ack-users>
- =item * The ack issues list at Github
- L<https://github.com/petdance/ack2/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<https://github.com/petdance/ack2>
- =back
- =head1 ACKNOWLEDGEMENTS
- How appropriate to have I<ack>nowledgements!
- Thanks to everyone who has contributed to ack in any way, including
- Stephen Thirlwall,
- Jonah Bishop,
- Chris Rebert,
- Denis Howe,
- RaE<uacute>l GundE<iacute>n,
- James McCoy,
- Daniel Perrett,
- Steven Lee,
- Jonathan Perret,
- Fraser Tweedale,
- RaE<aacute>l GundE<aacute>n,
- Steffen Jaeckel,
- Stephan Hohe,
- Michael Beijen,
- Alexandr Ciornii,
- Christian Walde,
- Charles Lee,
- Joe McMahon,
- John Warwick,
- David Steinbrunner,
- Kara Martens,
- Volodymyr Medvid,
- Ron Savage,
- Konrad Borowski,
- Dale Sedivic,
- Michael McClimon,
- Andrew Black,
- Ralph Bodenner,
- Shaun Patterson,
- Ryan Olson,
- Shlomi Fish,
- Karen Etheridge,
- Olivier Mengue,
- Matthew Wild,
- Scott Kyle,
- 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,
- Pete Krawczyk and
- Rob Hoelz.
- =head1 COPYRIGHT & LICENSE
- Copyright 2005-2014 Andy Lester.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the Artistic License v2.0.
- See http://www.perlfoundation.org/artistic_license_2_0 or the LICENSE.md
- file that comes with the ack distribution.
- =cut
- package App::Ack;
- use warnings;
- use strict;
- our $VERSION;
- our $COPYRIGHT;
- BEGIN {
- $VERSION = '2.14';
- $COPYRIGHT = 'Copyright 2005-2014 Andy Lester.';
- }
- our $fh;
- BEGIN {
- $fh = *STDOUT;
- }
- our %types;
- our %type_wanted;
- our %mappings;
- our %ignore_dirs;
- our $is_filter_mode;
- our $output_to_pipe;
- our $dir_sep_chars;
- our $is_cygwin;
- our $is_windows;
- use File::Spec 1.00015 ();
- BEGIN {
- # These have to be checked before any filehandle diddling.
- $output_to_pipe = not -t *STDOUT;
- $is_filter_mode = -p STDIN;
- $is_cygwin = ($^O eq 'cygwin');
- $is_windows = ($^O eq 'MSWin32');
- $dir_sep_chars = $is_windows ? quotemeta( '\\/' ) : quotemeta( File::Spec->catfile( '', '' ) );
- }
- sub remove_dir_sep {
- my $path = shift;
- $path =~ s/[$dir_sep_chars]$//;
- return $path;
- }
- 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 _bar {
- my $x;
- $x = <<'_BAR';
- 6?!I'7!I"?%+!
- 3~!I#7#I"7#I!?!+!="+"="+!:!
- 2?#I!7!I!?#I!7!I"+"=%+"=#
- 1?"+!?*+!=#~"=!+#?"="+!
- 0?"+!?"I"?&+!="~!=!~"=!+%="+"
- /I!+!?)+!?!+!=$~!=!~!="+!="+"?!="?!
- .?%I"?%+%='?!=#~$="
- ,,!?%I"?(+$=$~!=#:"~$:!~!
- ,I!?!I!?"I"?!+#?"+!?!+#="~$:!~!:!~!:!,!:!,":#~!
- +I!?&+!="+!?#+$=!~":!~!:!~!:!,!:#,!:!,%:"
- *+!I!?!+$=!+!=!+!?$+#=!~":!~":#,$:",#:!,!:!
- *I!?"+!?!+!=$+!?#+#=#~":$,!:",!:!,&:"
- )I!?$=!~!=#+"?!+!=!+!=!~!="~!:!~":!,'.!,%:!~!
- (=!?"+!?!=!~$?"+!?!+!=#~"=",!="~$,$.",#.!:!=!
- (I"+"="~"=!+&=!~"=!~!,!~!+!=!?!+!?!=!I!?!+"=!.",!.!,":!
- %I$?!+!?!=%+!~!+#~!=!~#:#=!~!+!~!=#:!,%.!,!.!:"
- $I!?!=!?!I!+!?"+!=!~!=!~!?!I!?!=!+!=!~#:",!~"=!~!:"~!=!:",&:" '-/
- $?!+!I!?"+"=!+"~!,!:"+#~#:#,"=!~"=!,!~!,!.",!:".!:! */! !I!t!'!s! !a! !g!r!e!p!!! !/!
- $+"=!+!?!+"~!=!:!~!:"I!+!,!~!=!:!~!,!:!,$:!~".&:"~!,# (-/
- %~!=!~!=!:!.!+"~!:!,!.!,!~!=!:$.!,":!,!.!:!~!,!:!=!.#="~!,!:" ./!
- %=!~!?!+"?"+!=!~",!.!:!?!~!.!:!,!:!,#.!,!:","~!:!=!~!=!:",!~! ./!
- %+"~":!~!=#~!:!~!,!.!~!:",!~!=!~!.!:!,!.",!:!,":!=":!.!,!:!7! -/!
- %~",!:".#:!=!:!,!:"+!:!~!:!.!,!~!,!.#,!.!,$:"~!,":"~!=! */!
- &=!~!=#+!=!~",!.!:",#:#,!.",+:!,!.",!=!+!?!
- &~!=!~!=!~!:"~#:",!.!,#~!:!.!+!,!.",$.",$.#,!+!I!?!
- &~!="~!:!~":!~",!~!=!~":!,!:!~!,!:!,&.$,#."+!?!I!?!I!
- &~!=!~!=!+!,!:!~!:!=!,!:!~&:$,!.!,".!,".!,#."~!+!?$I!
- &~!=!~!="~!=!:!~":!,!~%:#,!:",!.!,#.",#I!7"I!?!+!?"I"
- &+!I!7!:#~"=!~!:!,!:"~$.!=!.!,!~!,$.#,!~!7!I#?!+!?"I"7!
- %7#?!+!~!:!=!~!=!~":!,!:"~":#.!,)7#I"?"I!7&
- %7#I!=":!=!~!:"~$:"~!:#,!:!,!:!~!:#,!7#I!?#7)
- $7$+!,!~!=#~!:!~!:!~$:#,!.!~!:!=!,":!7#I"?#7+=!?!
- $7#I!~!,!~#=!~!:"~!:!,!:!,#:!=!~",":!7$I!?#I!7*+!=!+"
- "I!7$I!,":!,!.!=":$,!:!,$:$7$I!+!?"I!7+?"I!7!I!7!,!
- !,!7%I!:",!."~":!,&.!,!:!~!I!7$I!+!?"I!7,?!I!7',!
- !7(,!.#~":!,%.!,!7%I!7!?#I"7,+!?!7*
- 7+:!,!~#,"=!7'I!?#I"7/+!7+
- 77I!+!7!?!7!I"71+!7,
- _BAR
- return App::Ack::__pic($x);
- }
- sub _cathy {
- my $x = <<'CATHY';
- 0+!--+!
- 0|! "C!H!O!C!O!L!A!T!E!!! !|!
- 0|! "C!H!O!C!O!L!A!T!E!!! !|!
- 0|! "C!H!O!C!O!L!A!T!E!!! !|!
- 0|! $A"C!K!!! $|!
- 0+!--+!
- 6\! 1:!,!.! !
- 7\! /.!M!~!Z!M!~!
- 8\! /~!D! "M! !
- 4.! $\! /M!~!.!8! +.!M# 4
- 0,!.! (\! .~!M!N! ,+!I!.!M!.! 3
- /?!O!.!M!:! '\! .O!.! +~!Z!=!N!.! 4
- ..! !D!Z!.!Z!.! '\! 9=!M".! 6
- /.! !.!~!M".! '\! 8~! 9
- 4M!.! /.!7!N!M!.! F
- 4.! &:!M! !N"M# !M"N!M! #D!M&=! =
- :M!7!M#:! !~!M!7!,!$!M!:! #.! !O!N!.!M!:!M# ;
- 8Z!M"~!N!$!D!.!N!?! !I!N!.! (?!M! !M!,!D!M".! 9
- (?!Z!M!N!:! )=!M!O!8!.!M!+!M! !M!,! !O!M! +,!M!.!M!~!Z!N!M!:! &:!~! 0
- &8!7!.!~!M"D!M!,! &M!?!=!8! !M!,!O! !M!+! !+!O!.!M! $M#~! !.!8!M!Z!.!M! !O!M"Z! %:!~!M!Z!M!Z!.! +
- &:!M!7!,! *M!.!Z!M! !8"M!.!M!~! !.!M!.!=! #~!8!.!M! !7!M! "N!Z#I! !D!M!,!M!.! $."M!,! !M!.! *
- 2$!O! "N! !.!M!I! !7" "M! "+!O! !~!M! !d!O!.!7!I!M!.! !.!O!=!M!.! !M",!M!.! %.!$!O!D! +
- 1~!O! "M!+! !8!$! "M! "?!O! %Z!8!D!M!?!8!I!O!7!M! #M!.!M! "M",!M! 4
- 07!~! ".!8! !.!M! "I!+! !.!M! &Z!D!.!7!=!M! !:!.!M! #:!8"+! !.!+!8! !8! 3
- /~!M! #N! !~!M!$! !.!M! !.!M" &~!M! "~!M!O! "D! $M! !8! "M!,!M!+!D!.! 1
- #.! #?!M!N!.! #~!O! $M!.!7!$! "?" !?!~!M! '7!8!?!M!.!+!M"O! $?"$!D! !.!O! !$!7!I!.! 0
- $,!M!:!O!?! ".! !?!=! $=!:!O! !M! "M! !M! !+!$! (.! +.!M! !M!.! !8! !+"Z!~! $:!M!$! !.! '
- #.!8!.!I!$! $7!I! %M" !=!M! !~!M!D! "7!I! .I!O! %?!=!,!D! !,!M! !D!~!8!~! %D!M! (
- #.!M"?! $=!O! %=!N! "8!.! !Z!M! #M!~! (M!:! #.!M" &O! !M!.! !?!,! !8!.!N!~! $8!N!M!,!.! %
- *$!O! &M!,! "O! !.!M!.! #M! (~!M( &O!.! !7! "M! !.!M!.!M!,! #.!M! !M! &
- )=!8!.! $.!M!O!.! "$!.!I!N! !I!M# (7!M(I! %D"Z!M! "=!I! "M! !M!:! #~!D! '
- )D! &8!N!:! ".!O! !M!="M! "M! (7!M) %." !M!D!."M!.! !$!=! !M!,! +
- (M! &+!.!M! #Z!7!O!M!.!~!8! +,!M#D!?!M#D! #.!Z!M#,!Z!?! !~!N! "N!.! !M! +
- 'D!:! %$!D! !?! #M!Z! !8!.! !M"?!7!?!7! '+!I!D! !?!O!:!M!:! ":!M!:! !M!7".!M! "8!+! !:!D! !.!M! *
- %.!O!:! $.!O!+! !D!.! #M! "M!.!+!N!I!Z! "7!M!N!M!N!?!I!7!Z!=!M'D"~! #M!.!8!$! !:! !.!M! "N!?! !,!O! )
- !.!?!M!:!M!I! %8!,! "M!.! #M! "N! !M!.! !M!.! !+!~! !.!M!.! ':!M! $M! $M!Z!$! !M!.! "D! "M! "?!M! (
- !7!8! !+!I! ".! "$!=! ":!$! "+! !M!.! !O! !M!I!M".! !=!~! ",!O! '=!M! $$!,! #N!:! ":!8!.! !D!~! !,!M!.! !:!M!.! &
- !:!,!.! &Z" #D! !.!8!."M!.! !8!?!Z!M!.!M! #Z!~! !?!M!Z!.! %~!O!.!8!$!N!8!O!I!:!~! !+! #M!.! !.!M!.! !+!M! ".!~!M!+! $
- !.! 'D!I! #?!M!.!M!,! !.!Z! !.!8! #M&O!I!?! (~!I!M"." !M!Z!.! !M!N!.! "+!$!.! "M!.! !M!?!.! "8!M! $
- (O!8! $M! !M!.! ".!:! !+!=! #M! #.!M! !+" *$!M":!.! !M!~! "M!7! #M! #7!Z! "M"$!M!.! !.! #
- '$!Z! #.!7!+!M! $.!,! !+!:! #N! #.!M!.!+!M! +D!M! #=!N! ":!O! #=!M! #Z!D! $M!I! %
- $,! ".! $.!M" %$!.! !?!~! "+!7!." !.!M!,! !M! *,!N!M!.$M!?! "D!,! #M!.! #N! +
- ,M!Z! &M! "I!,! "M! %I!M! !?!=!.! (Z!8!M! $:!M!.! !,!M! $D! #.!M!.! )
- +8!O! &.!8! "I!,! !~!M! &N!M! !M!D! '?!N!O!." $?!7! "?!~! #M!.! #I!D!.! (
- 3M!,! "N!.! !D" &.!+!M!.! !M":!.":!M!7!M!D! 'M!.! "M!.! "M!,! $I! )
- 3I! #M! "M!,! !:! &.!M" ".!,! !.!$!M!I! #.! !:! !.!M!?! "N!+! ".! /
- 1M!,! #.!M!8!M!=!.! +~!N"O!Z"~! *+!M!.! "M! 2
- 0.!M! &M!.! 8:! %.!M!Z! "M!=! *O!,! %
- 0?!$! &N! )." .,! %."M! ":!M!.! 0
- 0N!:! %?!O! #.! ..! &,! &.!D!,! "N!I! 0
- CATHY
- return App::Ack::__pic($x);
- }
- sub __pic {
- my($compressed) = @_;
- $compressed =~ s/(.)(.)/$1x(ord($2)-32)/eg;
- App::Ack::print( $compressed );
- exit 0;
- }
- sub show_help {
- my $help_arg = shift || 0;
- return show_help_types() if $help_arg =~ /^types?/;
- App::Ack::print( <<"END_OF_HELP" );
- Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
- Search for PATTERN in each source file in the tree from the current
- directory on down. If any files or directories are specified, then
- only those files and directories are checked. ack may also search
- STDIN, but only if no file or directory arguments are specified,
- or if one of them 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:
- --lines=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
- --output=expr Output the evaluation of expr for each line
- (turns off text highlighting)
- -o Show only the part of a line matching PATTERN
- Same as --output='\$&'
- --passthru Print all lines, whether matching or not
- --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 (default:
- on unless explicitly searching a single file)
- -h, --no-filename Suppress the prefixing filename on output
- -c, --count Show number of lines matching per file
- --[no]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.
- -s Suppress error messages about nonexistent or
- unreadable files.
- 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 selected, without
- searching. The PATTERN must not be specified.
- -g Same as -f, but only select files matching
- PATTERN.
- --sort-files Sort the found files lexically.
- --show-types Show which types each file has.
- --files-from=FILE Read the list of files to search from FILE.
- -x Read the list of files to search from STDIN.
- File inclusion/exclusion:
- --[no]ignore-dir=name Add/remove directory from list of ignored dirs
- --[no]ignore-directory=name Synonym for ignore-dir
- --ignore-file=filter Add filter for ignoring files
- -r, -R, --recurse Recurse into subdirectories (default: on)
- -n, --no-recurse No descending into subdirectories
- --[no]follow Follow symlinks. Default is off.
- -k, --known-types Include only files of types that ack recognizes.
- --type=X Include only X files, where X is a recognized
- filetype.
- --type=noX Exclude X files.
- See "ack --help-types" for supported filetypes.
- File type specification:
- --type-set TYPE:FILTER:FILTERARGS
- Files with the given FILTERARGS applied to the
- given FILTER are recognized as being of type
- TYPE. This replaces an existing definition for
- type TYPE.
- --type-add TYPE:FILTER:FILTERARGS
- Files with the given FILTERARGS applied to the
- given FILTER are recognized as being type TYPE.
- --type-del TYPE Removes all filters associated with TYPE.
- Miscellaneous:
- --[no]env Ignore environment variables and global ackrc
- files. --env is legal but redundant.
- --ackrc=filename Specify an ackrc file to use
- --ignore-ack-defaults Ignore default definitions included with ack.
- --create-ackrc Outputs a default ackrc for your customization
- to standard output.
- --help, -? This help
- --help-types Display all known types
- --dump Dump information on which options are loaded
- from which RC files
- --[no]filter Force ack to treat standard input as a pipe
- (--filter) or tty (--nofilter)
- --man Man page
- --version Display version & copyright
- --thpppt Bill the Cat
- --bar The warning admiral
- --cathy Chocolate! Chocolate! Chocolate!
- Exit status is 0 if match, 1 if no match.
- ack's home page is at http://beyondgrep.com/
- The full ack manual is available by running "ack --man".
- This is version $VERSION of ack. Run "ack --version" for full version info.
- END_OF_HELP
- return;
- }
- sub show_help_types {
- App::Ack::print( <<'END_OF_HELP' );
- Usage: ack [OPTION]... PATTERN [FILES OR DIRECTORIES]
- 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 { $_->to_string } @{$ext_list} );
- }
- App::Ack::print( sprintf( " --[no]%-*.*s %s\n", $maxlen, $maxlen, $type, $ext_list ) );
- }
- return;
- }
- sub show_man {
- require Pod::Usage;
- Pod::Usage::pod2usage({
- -input => $App::Ack::orig_program_name,
- -verbose => 2,
- -exitval => 0,
- });
- return;
- }
- 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;
- }
- # 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} @_; return; }
- sub print_first_filename { App::Ack::print( $_[0], "\n" ); return; }
- sub print_blank_line { App::Ack::print( "\n" ); return; }
- sub print_separator { App::Ack::print( "--\n" ); return; }
- sub print_filename { App::Ack::print( $_[0], $_[1] ); return; }
- sub print_line_no { App::Ack::print( $_[0], $_[1] ); return; }
- sub print_column_no { App::Ack::print( $_[0], $_[1] ); return; }
- 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 );
- return;
- }
- 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 );
- }
- return;
- }
- 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 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::Resource;
- use warnings;
- use strict;
- use overload
- '""' => 'name';
- sub FAIL {
- require Carp;
- Carp::confess( 'Must be overloaded' );
- }
- sub new {
- return FAIL();
- }
- sub name {
- return FAIL();
- }
- sub is_binary {
- return FAIL();
- }
- sub open {
- return FAIL();
- }
- sub needs_line_scan {
- return FAIL();
- }
- sub reset {
- return FAIL();
- }
- sub close {
- return FAIL();
- }
- sub clone {
- return FAIL();
- }
- sub firstliney {
- return FAIL();
- }
- 1;
- package App::Ack::Resources;
- use warnings;
- use strict;
- sub _generate_error_handler {
- my $opt = shift;
- if ( $opt->{dont_report_bad_filenames} ) {
- return sub {
- my $msg = shift;
- # XXX restricting to specific error messages for now; I would
- # prefer a different way of doing this
- if ( $msg =~ /Permission denied/ ) {
- return;
- }
- App::Ack::warn( $msg );
- };
- }
- else {
- return sub {
- my $msg = shift;
- App::Ack::warn( $msg );
- };
- }
- }
- sub from_argv {
- my $class = shift;
- my $opt = shift;
- my $start = shift;
- my $self = bless {}, $class;
- my $file_filter = undef;
- my $descend_filter = $opt->{descend_filter};
- if( $opt->{n} ) {
- $descend_filter = sub {
- return 0;
- };
- }
- $self->{iter} =
- File::Next::files( {
- file_filter => $opt->{file_filter},
- descend_filter => $descend_filter,
- error_handler => _generate_error_handler($opt),
- warning_handler => sub {},
- sort_files => $opt->{sort_files},
- follow_symlinks => $opt->{follow},
- }, @{$start} );
- return $self;
- }
- sub from_file {
- my $class = shift;
- my $opt = shift;
- my $file = shift;
- my $iter =
- File::Next::from_file( {
- error_handler => _generate_error_handler($opt),
- warning_handler => _generate_error_handler($opt),
- sort_files => $opt->{sort_files},
- }, $file ) or return undef;
- return bless {
- iter => $iter,
- }, $class;
- }
- # This is for reading input lines from STDIN, not the list of files from STDIN
- sub from_stdin {
- my $class = shift;
- my $opt = shift;
- my $self = bless {}, $class;
- my $has_been_called = 0;
- $self->{iter} = sub {
- if ( !$has_been_called ) {
- $has_been_called = 1;
- return '-';
- }
- return;
- };
- return $self;
- }
- sub next {
- my $self = shift;
- my $file = $self->{iter}->() or return;
- return App::Ack::Resource::Basic->new( $file );
- }
- 1;
- package App::Ack::Resource::Basic;
- use warnings;
- use strict;
- use Fcntl ();
- BEGIN {
- our @ISA = 'App::Ack::Resource';
- }
- sub new {
- my $class = shift;
- my $filename = shift;
- my $self = bless {
- filename => $filename,
- fh => undef,
- opened => 0,
- }, $class;
- if ( $self->{filename} eq '-' ) {
- $self->{fh} = *STDIN;
- $self->{opened} = 1;
- }
- return $self;
- }
- sub name {
- return $_[0]->{filename};
- }
- 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 ( !defined($rc) && $App::Ack::report_bad_filenames ) {
- 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;
- # return if we haven't opened the file yet
- if ( !defined($self->{fh}) ) {
- return;
- }
- if( !seek( $self->{fh}, 0, 0 ) && $App::Ack::report_bad_filenames ) {
- App::Ack::warn( "$self->{filename}: $!" );
- }
- return;
- }
- sub close {
- my $self = shift;
- # return if we haven't opened the file yet
- if ( !defined($self->{fh}) ) {
- return;
- }
- if ( !close($self->{fh}) && $App::Ack::report_bad_filenames ) {
- App::Ack::warn( $self->name() . ": $!" );
- }
- $self->{opened} = 0;
- return;
- }
- sub clone {
- my ( $self ) = @_;
- return __PACKAGE__->new($self->name);
- }
- sub firstliney {
- my ( $self ) = @_;
- my $fh = $self->open();
- if ( !exists $self->{firstliney} ) {
- my $buffer = '';
- my $rc = sysread( $fh, $buffer, 250 );
- unless($rc) { # XXX handle this better?
- $buffer = '';
- }
- $buffer =~ s/[\r\n].*//s;
- $self->{firstliney} = $buffer;
- $self->reset;
- }
- $self->close;
- return $self->{firstliney};
- }
- sub open {
- my ( $self ) = @_;
- return $self->{fh} if $self->{opened};
- if ( ! open $self->{fh}, '<', $self->{filename} ) {
- return;
- }
- $self->{opened} = 1;
- return $self->{fh};
- }
- 1;
- package App::Ack::ConfigDefault;
- use warnings;
- use strict;
- sub options {
- return split( /\n/, _options_block() );
- }
- sub options_clean {
- return grep { /./ && !/^#/ } options();
- }
- sub _options_block {
- my $lines = <<'HERE';
- # This is the default ackrc for ack version ==VERSION==.
- # There are four different ways to match
- #
- # is: Match the filename exactly
- #
- # ext: Match the extension of the filename exactly
- #
- # match: Match the filename against a Perl regular expression
- #
- # firstlinematch: Match the first 250 characters of the first line
- # of text against a Perl regular expression. This is only for
- # the --type-add option.
- ### Directories to ignore
- # Bazaar
- # http://bazaar.canonical.com/
- --ignore-directory=is:.bzr
- # Codeville
- # http://freecode.com/projects/codeville
- --ignore-directory=is:.cdv
- # Interface Builder (Xcode)
- # http://en.wikipedia.org/wiki/Interface_Builder
- --ignore-directory=is:~.dep
- --ignore-directory=is:~.dot
- --ignore-directory=is:~.nib
- --ignore-directory=is:~.plst
- # Git
- # http://git-scm.com/
- --ignore-directory=is:.git
- # Mercurial
- # http://mercurial.selenic.com/
- --ignore-directory=is:.hg
- # quilt
- # http://directory.fsf.org/wiki/Quilt
- --ignore-directory=is:.pc
- # Subversion
- # http://subversion.tigris.org/
- --ignore-directory=is:.svn
- # Monotone
- # http://www.monotone.ca/
- --ignore-directory=is:_MTN
- # CVS
- # http://savannah.nongnu.org/projects/cvs
- --ignore-directory=is:CVS
- # RCS
- # http://www.gnu.org/software/rcs/
- --ignore-directory=is:RCS
- # SCCS
- # http://en.wikipedia.org/wiki/Source_Code_Control_System
- --ignore-directory=is:SCCS
- # darcs
- # http://darcs.net/
- --ignore-directory=is:_darcs
- # Vault/Fortress
- --ignore-directory=is:_sgbak
- # autoconf
- # http://www.gnu.org/software/autoconf/
- --ignore-directory=is:autom4te.cache
- # Perl module building
- --ignore-directory=is:blib
- --ignore-directory=is:_build
- # Perl Devel::Cover module's output directory
- # https://metacpan.org/release/Devel-Cover
- --ignore-directory=is:cover_db
- # Node modules created by npm
- --ignore-directory=is:node_modules
- # CMake cache
- # http://www.cmake.org/
- --ignore-directory=is:CMakeFiles
- # Eclipse workspace folder
- # http://eclipse.org/
- --ignore-directory=is:.metadata
- # Cabal (Haskell) sandboxes
- # http://www.haskell.org/cabal/users-guide/installing-packages.html
- --ignore-directory=is:.cabal-sandbox
- ### Files to ignore
- # Backup files
- --ignore-file=ext:bak
- --ignore-file=match:/~$/
- # Emacs swap files
- --ignore-file=match:/^#.+#$/
- # vi/vim swap files http://vim.org/
- --ignore-file=match:/[._].*\.swp$/
- # core dumps
- --ignore-file=match:/core\.\d+$/
- # minified Javascript
- --ignore-file=match:/[.-]min[.]js$/
- --ignore-file=match:/[.]js[.]min$/
- # minified CSS
- --ignore-file=match:/[.]min[.]css$/
- --ignore-file=match:/[.]css[.]min$/
- # JS and CSS source maps
- --ignore-file=match:/[.]js[.]map$/
- --ignore-file=match:/[.]css[.]map$/
- # PDFs, because they pass Perl's -T detection
- --ignore-file=ext:pdf
- # Common graphics, just as an optimization
- --ignore-file=ext:gif,jpg,jpeg,png
- ### Filetypes defined
- # Perl
- # http://perl.org/
- --type-add=perl:ext:pl,pm,pod,t,psgi
- --type-add=perl:firstlinematch:/^#!.*\bperl/
- # Perl tests
- --type-add=perltest:ext:t
- # Makefiles
- # http://www.gnu.org/s/make/
- --type-add=make:ext:mk
- --type-add=make:ext:mak
- --type-add=make:is:makefile
- --type-add=make:is:Makefile
- --type-add=make:is:Makefile.Debug
- --type-add=make:is:Makefile.Release
- # Rakefiles
- # http://rake.rubyforge.org/
- --type-add=rake:is:Rakefile
- # CMake
- # http://www.cmake.org/
- --type-add=cmake:is:CMakeLists.txt
- --type-add=cmake:ext:cmake
- # Actionscript
- --type-add=actionscript:ext:as,mxml
- # Ada
- # http://www.adaic.org/
- --type-add=ada:ext:ada,adb,ads
- # ASP
- # http://msdn.microsoft.com/en-us/library/aa286483.aspx
- --type-add=asp:ext:asp
- # ASP.Net
- # http://www.asp.net/
- --type-add=aspx:ext:master,ascx,asmx,aspx,svc
- # Assembly
- --type-add=asm:ext:asm,s
- # Batch
- --type-add=batch:ext:bat,cmd
- # ColdFusion
- # http://en.wikipedia.org/wiki/ColdFusion
- --type-add=cfmx:ext:cfc,cfm,cfml
- # Clojure
- # http://clojure.org/
- --type-add=clojure:ext:clj
- # C
- # .xs are Perl C files
- --type-add=cc:ext:c,h,xs
- # C header files
- --type-add=hh:ext:h
- # CoffeeScript
- # http://coffeescript.org/
- --type-add=coffeescript:ext:coffee
- # C++
- --type-add=cpp:ext:cpp,cc,cxx,m,hpp,hh,h,hxx
- # C#
- --type-add=csharp:ext:cs
- # CSS
- # http://www.w3.org/Style/CSS/
- --type-add=css:ext:css
- # Dart
- # http://www.dartlang.org/
- --type-add=dart:ext:dart
- # Delphi
- # http://en.wikipedia.org/wiki/Embarcadero_Delphi
- --type-add=delphi:ext:pas,int,dfm,nfm,dof,dpk,dproj,groupproj,bdsgroup,bdsproj
- # Elixir
- # http://elixir-lang.org/
- --type-add=elixir:ext:ex,exs
- # Emacs Lisp
- # http://www.gnu.org/software/emacs
- --type-add=elisp:ext:el
- # Erlang
- # http://www.erlang.org/
- --type-add=erlang:ext:erl,hrl
- # Fortran
- # http://en.wikipedia.org/wiki/Fortran
- --type-add=fortran:ext:f,f77,f90,f95,f03,for,ftn,fpp
- # Go
- # http://golang.org/
- --type-add=go:ext:go
- # Groovy
- # http://groovy.codehaus.org/
- --type-add=groovy:ext:groovy,gtmpl,gpp,grunit,gradle
- # Haskell
- # http://www.haskell.org/
- --type-add=haskell:ext:hs,lhs
- # HTML
- --type-add=html:ext:htm,html
- # Jade
- # http://jade-lang.com/
- --type-add=jade:ext:jade
- # Java
- # http://www.oracle.com/technetwork/java/index.html
- --type-add=java:ext:java,properties
- # JavaScript
- --type-add=js:ext:js
- # JSP
- # http://www.oracle.com/technetwork/java/javaee/jsp/index.html
- --type-add=jsp:ext:jsp,jspx,jhtm,jhtml
- # JSON
- # http://www.json.org/
- --type-add=json:ext:json
- # Less
- # http://www.lesscss.org/
- --type-add=less:ext:less
- # Common Lisp
- # http://common-lisp.net/
- --type-add=lisp:ext:lisp,lsp
- # Lua
- # http://www.lua.org/
- --type-add=lua:ext:lua
- --type-add=lua:firstlinematch:/^#!.*\blua(jit)?/
- # Objective-C
- --type-add=objc:ext:m,h
- # Objective-C++
- --type-add=objcpp:ext:mm,h
- # OCaml
- # http://caml.inria.fr/
- --type-add=ocaml:ext:ml,mli
- # Matlab
- # http://en.wikipedia.org/wiki/MATLAB
- --type-add=matlab:ext:m
- # Parrot
- # http://www.parrot.org/
- --type-add=parrot:ext:pir,pasm,pmc,ops,pod,pg,tg
- # PHP
- # http://www.php.net/
- --type-add=php:ext:php,phpt,php3,php4,php5,phtml
- --type-add=php:firstlinematch:/^#!.*\bphp/
- # Plone
- # http://plone.org/
- --type-add=plone:ext:pt,cpt,metadata,cpy,py
- # Python
- # http://www.python.org/
- --type-add=python:ext:py
- --type-add=python:firstlinematch:/^#!.*\bpython/
- # R
- # http://www.r-project.org/
- --type-add=rr:ext:R
- # reStructured Text
- # http://docutils.sourceforge.net/rst.html
- --type-add=rst:ext:rst
- # Ruby
- # http://www.ruby-lang.org/
- --type-add=ruby:ext:rb,rhtml,rjs,rxml,erb,rake,spec
- --type-add=ruby:is:Rakefile
- --type-add=ruby:firstlinematch:/^#!.*\bruby/
- # Rust
- # http://www.rust-lang.org/
- --type-add=rust:ext:rs
- # Sass
- # http://sass-lang.com
- --type-add=sass:ext:sass,scss
- # Scala
- # http://www.scala-lang.org/
- --type-add=scala:ext:scala
- # Scheme
- # http://groups.csail.mit.edu/mac/projects/scheme/
- --type-add=scheme:ext:scm,ss
- # Shell
- --type-add=shell:ext:sh,bash,csh,tcsh,ksh,zsh,fish
- --type-add=shell:firstlinematch:/^#!.*\b(?:ba|t?c|k|z|fi)?sh\b/
- # Smalltalk
- # http://www.smalltalk.org/
- --type-add=smalltalk:ext:st
- # Smarty
- # http://www.smarty.net/
- --type-add=smarty:ext:tpl
- # SQL
- # http://www.iso.org/iso/catalogue_detail.htm?csnumber=45498
- --type-add=sql:ext:sql,ctl
- # Stylus
- # http://learnboost.github.io/stylus/
- --type-add=stylus:ext:styl
- # Tcl
- # http://www.tcl.tk/
- --type-add=tcl:ext:tcl,itcl,itk
- # LaTeX
- # http://www.latex-project.org/
- --type-add=tex:ext:tex,cls,sty
- # Template Toolkit (Perl)
- # http://template-toolkit.org/
- --type-add=tt:ext:tt,tt2,ttml
- # Visual Basic
- --type-add=vb:ext:bas,cls,frm,ctl,vb,resx
- # Verilog
- --type-add=verilog:ext:v,vh,sv
- # VHDL
- # http://www.eda.org/twiki/bin/view.cgi/P1076/WebHome
- --type-add=vhdl:ext:vhd,vhdl
- # Vim
- # http://www.vim.org/
- --type-add=vim:ext:vim
- # XML
- # http://www.w3.org/TR/REC-xml/
- --type-add=xml:ext:xml,dtd,xsl,xslt,ent
- --type-add=xml:firstlinematch:/<[?]xml/
- # YAML
- # http://yaml.org/
- --type-add=yaml:ext:yaml,yml
- HERE
- $lines =~ s/==VERSION==/$App::Ack::VERSION/sm;
- return $lines;
- }
- 1;
- package App::Ack::ConfigFinder;
- use strict;
- use warnings;
- use Cwd 3.00 ();
- use File::Spec 3.00;
- use if ($^O eq 'MSWin32'), 'Win32';
- sub new {
- my ( $class ) = @_;
- return bless {}, $class;
- }
- sub _remove_redundancies {
- my @configs = @_;
- my %seen;
- foreach my $config (@configs) {
- my $key = $config->{path};
- if ( not $App::Ack::is_windows ) {
- # On Unix, uniquify on inode.
- my ($dev, $inode) = (stat $key)[0, 1];
- $key = "$dev:$inode" if defined $dev;
- }
- undef $config if $seen{$key}++;
- }
- return grep { defined } @configs;
- }
- sub _check_for_ackrc {
- return unless defined $_[0];
- my @files = grep { -f }
- map { File::Spec->catfile(@_, $_) }
- qw(.ackrc _ackrc);
- die File::Spec->catdir(@_) . " contains both .ackrc and _ackrc.\n" .
- "Please remove one of those files.\n"
- if @files > 1;
- return wantarray ? @files : $files[0];
- } # end _check_for_ackrc
- sub find_config_files {
- my @config_files;
- if ( $App::Ack::is_windows ) {
- push @config_files, map { +{ path => File::Spec->catfile($_, 'ackrc') } } (
- Win32::GetFolderPath(Win32::CSIDL_COMMON_APPDATA()),
- Win32::GetFolderPath(Win32::CSIDL_APPDATA()),
- );
- }
- else {
- push @config_files, { path => '/etc/ackrc' };
- }
- if ( $ENV{'ACKRC'} && -f $ENV{'ACKRC'} ) {
- push @config_files, { path => $ENV{'ACKRC'} };
- }
- else {
- push @config_files, map { +{ path => $_ } } _check_for_ackrc($ENV{'HOME'});
- }
- # XXX This should go through some untainted cwd-fetching function, and not get untainted inline like this.
- my $cwd = Cwd::getcwd();
- $cwd =~ /(.+)/;
- $cwd = $1;
- my @dirs = File::Spec->splitdir( $cwd );
- while(@dirs) {
- my $ackrc = _check_for_ackrc(@dirs);
- if(defined $ackrc) {
- push @config_files, { project => 1, path => $ackrc };
- last;
- }
- pop @dirs;
- }
- # We only test for existence here, so if the file is deleted out from under us, this will fail later.
- return _remove_redundancies( @config_files );
- }
- sub read_rcfile {
- my $file = shift;
- return unless defined $file && -e $file;
- my @lines;
- open( my $fh, '<', $file ) or App::Ack::die( "Unable to read $file: $!" );
- while ( my $line = <$fh> ) {
- chomp $line;
- $line =~ s/^\s+//;
- $line =~ s/\s+$//;
- next if $line eq '';
- next if $line =~ /^\s*#/;
- push( @lines, $line );
- }
- close $fh or App::Ack::die( "Unable to close $file: $!" );
- return @lines;
- }
- 1;
- package App::Ack::ConfigLoader;
- use strict;
- use warnings;
- use Carp 1.04 ();
- use Getopt::Long 2.35 ();
- use Text::ParseWords 3.1 ();
- my @INVALID_COMBINATIONS;
- BEGIN {
- my @context = qw( -A -B -C --after-context --before-context --context );
- my @pretty = qw( --heading --group --break );
- my @filename = qw( -h -H --with-filename --no-filename );
- @INVALID_COMBINATIONS = (
- # XXX normalize
- [qw(-l)] => [@context, @pretty, @filename, qw(-L -o --passthru --output --max-count --column -f -g --show-types)],
- [qw(-L)] => [@context, @pretty, @filename, qw(-l -o --passthru --output --max-count --column -f -g --show-types -c --count)],
- [qw(--line)] => [@context, @pretty, @filename, qw(-l --files-with-matches --files-without-matches -L -o --passthru --match -m --max-count -1 -c --count --column --print0 -f -g --show-types)],
- [qw(-o)] => [@context, qw(--output -c --count --column --column -f --show-types)],
- [qw(--passthru)] => [@context, qw(--output --column -m --max-count -1 -c --count -f -g)],
- [qw(--output)] => [@context, qw(-c --count -f -g)],
- [qw(--match)] => [qw(-f -g)],
- [qw(-m --max-count)] => [qw(-1 -f -g -c --count)],
- [qw(-h --no-filename)] => [qw(-H --with-filename -f -g --group --heading)],
- [qw(-H --with-filename)] => [qw(-h --no-filename -f -g)],
- [qw(-c --count)] => [@context, @pretty, qw(--column -f -g)],
- [qw(--column)] => [qw(-f -g)],
- [@context] => [qw(-f -g)],
- [qw(-f)] => [qw(-g), @pretty],
- [qw(-g)] => [qw(-f), @pretty],
- );
- }
- sub process_filter_spec {
- my ( $spec ) = @_;
- if ( $spec =~ /^(\w+):(\w+):(.*)/ ) {
- my ( $type_name, $ext_type, $arguments ) = ( $1, $2, $3 );
- return ( $type_name,
- App::Ack::Filter->create_filter($ext_type, split(/,/, $arguments)) );
- }
- elsif ( $spec =~ /^(\w+)=(.*)/ ) { # Check to see if we have ack1-style argument specification.
- my ( $type_name, $extensions ) = ( $1, $2 );
- my @extensions = split(/,/, $extensions);
- foreach my $extension ( @extensions ) {
- $extension =~ s/^[.]//;
- }
- return ( $type_name, App::Ack::Filter->create_filter('ext', @extensions) );
- }
- else {
- Carp::croak "invalid filter specification '$spec'";
- }
- }
- sub uninvert_filter {
- my ( $opt, @filters ) = @_;
- return unless defined $opt->{filters} && @filters;
- # Loop through all the registered filters. If we hit one that
- # matches this extension and it's inverted, we need to delete it from
- # the options.
- for ( my $i = 0; $i < @{ $opt->{filters} }; $i++ ) {
- my $opt_filter = @{ $opt->{filters} }[$i];
- # XXX Do a real list comparison? This just checks string equivalence.
- if ( $opt_filter->is_inverted() && "$opt_filter->{filter}" eq "@filters" ) {
- splice @{ $opt->{filters} }, $i, 1;
- $i--;
- }
- }
- }
- sub process_filetypes {
- my ( $opt, $arg_sources ) = @_;
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version'); # start with default options, minus some annoying ones
- Getopt::Long::Configure(
- 'no_ignore_case',
- 'no_auto_abbrev',
- 'pass_through',
- );
- my %additional_specs;
- my $add_spec = sub {
- my ( undef, $spec ) = @_;
- my ( $name, $filter ) = process_filter_spec($spec);
- push @{ $App::Ack::mappings{$name} }, $filter;
- $additional_specs{$name . '!'} = sub {
- my ( undef, $value ) = @_;
- my @filters = @{ $App::Ack::mappings{$name} };
- if ( not $value ) {
- @filters = map { $_->invert() } @filters;
- }
- else {
- uninvert_filter( $opt, @filters );
- }
- push @{ $opt->{'filters'} }, @filters;
- };
- };
- my $set_spec = sub {
- my ( undef, $spec ) = @_;
- my ( $name, $filter ) = process_filter_spec($spec);
- $App::Ack::mappings{$name} = [ $filter ];
- $additional_specs{$name . '!'} = sub {
- my ( undef, $value ) = @_;
- my @filters = @{ $App::Ack::mappings{$name} };
- if ( not $value ) {
- @filters = map { $_->invert() } @filters;
- }
- push @{ $opt->{'filters'} }, @filters;
- };
- };
- my $delete_spec = sub {
- my ( undef, $name ) = @_;
- delete $App::Ack::mappings{$name};
- delete $additional_specs{$name . '!'};
- };
- my %type_arg_specs = (
- 'type-add=s' => $add_spec,
- 'type-set=s' => $set_spec,
- 'type-del=s' => $delete_spec,
- );
- foreach my $source (@{$arg_sources}) {
- my ( $source_name, $args ) = @{$source}{qw/name contents/};
- if ( ref($args) ) {
- # $args are modified in place, so no need to munge $arg_sources
- local @ARGV = @{$args};
- Getopt::Long::GetOptions(%type_arg_specs);
- @{$args} = @ARGV;
- }
- else {
- ( undef, $source->{contents} ) =
- Getopt::Long::GetOptionsFromString($args, %type_arg_specs);
- }
- }
- $additional_specs{'k|known-types'} = sub {
- my ( undef, $value ) = @_;
- my @filters = map { @{$_} } values(%App::Ack::mappings);
- push @{ $opt->{'filters'} }, @filters;
- };
- return \%additional_specs;
- }
- sub removed_option {
- my ( $option, $explanation ) = @_;
- $explanation ||= '';
- return sub {
- warn "Option '$option' is not valid in ack 2\n$explanation";
- exit 1;
- };
- }
- sub get_arg_spec {
- my ( $opt, $extra_specs ) = @_;
- my $dash_a_explanation = <<'EOT';
- This is because we now have -k/--known-types which makes it only select files
- of known types, rather than any text file (which is the behavior of ack 1.x).
- You may have options in a .ackrc, or in the ACKRC_OPTIONS environment variable.
- Try using the --dump flag.
- EOT
- return {
- 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' => removed_option('-a', $dash_a_explanation),
- 'all' => removed_option('--all', $dash_a_explanation),
- '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},
- 'create-ackrc' => sub { print "$_\n" for ( '--ignore-ack-defaults', App::Ack::ConfigDefault::options() ); exit; },
- 'env!' => sub {
- my ( undef, $value ) = @_;
- if ( !$value ) {
- $opt->{noenv_seen} = 1;
- }
- },
- f => \$opt->{f},
- 'files-from=s' => \$opt->{files_from},
- 'filter!' => \$App::Ack::is_filter_mode,
- flush => \$opt->{flush},
- 'follow!' => \$opt->{follow},
- g => \$opt->{g},
- G => removed_option('-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},
- 'ignore-directory|ignore-dir=s' => sub {
- my ( undef, $dir ) = @_;
- $dir = App::Ack::remove_dir_sep( $dir );
- if ( $dir !~ /^(?:is|match):/ ) {
- $dir = 'is:' . $dir;
- }
- push @{ $opt->{idirs} }, $dir;
- },
- 'ignore-file=s' => sub {
- my ( undef, $file ) = @_;
- push @{ $opt->{ifiles} }, $file;
- },
- 'lines=s' => sub { shift; my $val = shift; push @{$opt->{lines}}, $val },
- 'l|files-with-matches'
- => \$opt->{l},
- 'L|files-without-matches'
- => \$opt->{L},
- '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' => sub {
- my ( undef, $value ) = @_;
- $opt->{pager} = $value || $ENV{PAGER};
- },
- 'noignore-directory|noignore-dir=s'
- => sub {
- my ( undef, $dir ) = @_;
- # XXX can you do --noignore-dir=match,...?
- $dir = App::Ack::remove_dir_sep( $dir );
- if ( $dir !~ /^(?:is|match):/ ) {
- $dir = 'is:' . $dir;
- }
- if ( $dir !~ /^(?:is|match):/ ) {
- Carp::croak("invalid noignore-directory argument: '$dir'");
- }
- @{ $opt->{idirs} } = grep {
- $_ ne $dir
- } @{ $opt->{idirs} };
- push @{ $opt->{no_ignore_dirs} }, $dir;
- },
- 'nopager' => sub { $opt->{pager} = undef },
- 'passthru' => \$opt->{passthru},
- 'print0' => \$opt->{print0},
- 'Q|literal' => \$opt->{Q},
- 'r|R|recurse' => sub { $opt->{n} = 0 },
- 's' => \$opt->{dont_report_bad_filenames},
- 'show-types' => \$opt->{show_types},
- 'smart-case!' => \$opt->{smart_case},
- 'sort-files' => \$opt->{sort_files},
- 'type=s' => sub {
- my ( $getopt, $value ) = @_;
- my $cb_value = 1;
- if ( $value =~ s/^no// ) {
- $cb_value = 0;
- }
- my $callback = $extra_specs->{ $value . '!' };
- if ( $callback ) {
- $callback->( $getopt, $cb_value );
- }
- else {
- Carp::croak( "Unknown type '$value'" );
- }
- },
- 'u' => removed_option('-u'),
- 'unrestricted' => removed_option('--unrestricted'),
- 'v|invert-match' => \$opt->{v},
- 'w|word-regexp' => \$opt->{w},
- 'x' => sub { $opt->{files_from} = '-' },
- 'version' => sub { App::Ack::print_version_statement(); exit; },
- 'help|?:s' => sub { shift; App::Ack::show_help(@_); exit; },
- 'help-types' => sub { App::Ack::show_help_types(); exit; },
- 'man' => sub { App::Ack::show_man(); exit; },
- $extra_specs ? %{$extra_specs} : (),
- }; # arg_specs
- }
- sub process_other {
- my ( $opt, $extra_specs, $arg_sources ) = @_;
- # Start with default options, minus some annoying ones.
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- Getopt::Long::Configure(
- 'bundling',
- 'no_ignore_case',
- );
- my $argv_source;
- my $is_help_types_active;
- foreach my $source (@{$arg_sources}) {
- my ( $source_name, $args ) = @{$source}{qw/name contents/};
- if ( $source_name eq 'ARGV' ) {
- $argv_source = $args;
- last;
- }
- }
- if ( $argv_source ) { # This *should* always be true, but you never know...
- my @copy = @{$argv_source};
- local @ARGV = @copy;
- Getopt::Long::Configure('pass_through');
- Getopt::Long::GetOptions(
- 'help-types' => \$is_help_types_active,
- );
- Getopt::Long::Configure('no_pass_through');
- }
- my $arg_specs = get_arg_spec($opt, $extra_specs);
- foreach my $source (@{$arg_sources}) {
- my ( $source_name, $args ) = @{$source}{qw/name contents/};
- my $args_for_source = $arg_specs;
- if ( $source->{project} ) {
- my $illegal = sub {
- die "Options --output, --pager and --match are forbidden in project .ackrc files.\n";
- };
- $args_for_source = { %$args_for_source,
- 'output=s' => $illegal,
- 'pager:s' => $illegal,
- 'match=s' => $illegal,
- };
- }
- my $ret;
- if ( ref($args) ) {
- local @ARGV = @{$args};
- $ret = Getopt::Long::GetOptions( %{$args_for_source} );
- @{$args} = @ARGV;
- }
- else {
- ( $ret, $source->{contents} ) =
- Getopt::Long::GetOptionsFromString( $args, %{$args_for_source} );
- }
- if ( !$ret ) {
- if ( !$is_help_types_active ) {
- my $where = $source_name eq 'ARGV' ? 'on command line' : "in $source_name";
- App::Ack::die( "Invalid option $where" );
- }
- }
- if ( $opt->{noenv_seen} ) {
- App::Ack::die( "--noenv found in $source_name" );
- }
- }
- # XXX We need to check on a -- in the middle of a non-ARGV source
- return;
- }
- sub should_dump_options {
- my ( $sources ) = @_;
- foreach my $source (@{$sources}) {
- my ( $name, $options ) = @{$source}{qw/name contents/};
- if($name eq 'ARGV') {
- my $dump;
- local @ARGV = @{$options};
- Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');
- Getopt::Long::GetOptions(
- 'dump' => \$dump,
- );
- @{$options} = @ARGV;
- return $dump;
- }
- }
- return;
- }
- sub explode_sources {
- my ( $sources ) = @_;
- my @new_sources;
- Getopt::Long::Configure('default', 'pass_through', 'no_auto_help', 'no_auto_version');
- my %opt;
- my $arg_spec = get_arg_spec(\%opt);
- my $add_type = sub {
- my ( undef, $arg ) = @_;
- # XXX refactor?
- if ( $arg =~ /(\w+)=/) {
- $arg_spec->{$1} = sub {};
- }
- else {
- ( $arg ) = split /:/, $arg;
- $arg_spec->{$arg} = sub {};
- }
- };
- my $del_type = sub {
- my ( undef, $arg ) = @_;
- delete $arg_spec->{$arg};
- };
- foreach my $source (@{$sources}) {
- my ( $name, $options ) = @{$source}{qw/name contents/};
- if ( ref($options) ne 'ARRAY' ) {
- $source->{contents} = $options =
- [ Text::ParseWords::shellwords($options) ];
- }
- for my $j ( 0 .. @{$options}-1 ) {
- next unless $options->[$j] =~ /^-/;
- my @chunk = ( $options->[$j] );
- push @chunk, $options->[$j] while ++$j < @{$options} && $options->[$j] !~ /^-/;
- $j--;
- my @copy = @chunk;
- local @ARGV = @chunk;
- Getopt::Long::GetOptions(
- 'type-add=s' => $add_type,
- 'type-set=s' => $add_type,
- 'type-del=s' => $del_type,
- );
- Getopt::Long::GetOptions( %{$arg_spec} );
- push @new_sources, {
- name => $name,
- contents => \@copy,
- };
- }
- }
- return \@new_sources;
- }
- sub compare_opts {
- my ( $a, $b ) = @_;
- my $first_a = $a->[0];
- my $first_b = $b->[0];
- $first_a =~ s/^--?//;
- $first_b =~ s/^--?//;
- return $first_a cmp $first_b;
- }
- sub dump_options {
- my ( $sources ) = @_;
- $sources = explode_sources($sources);
- my %opts_by_source;
- my @source_names;
- foreach my $source (@{$sources}) {
- my ( $name, $contents ) = @{$source}{qw/name contents/};
- if ( not $opts_by_source{$name} ) {
- $opts_by_source{$name} = [];
- push @source_names, $name;
- }
- push @{$opts_by_source{$name}}, $contents;
- }
- foreach my $name (@source_names) {
- my $contents = $opts_by_source{$name};
- print $name, "\n";
- print '=' x length($name), "\n";
- print ' ', join(' ', @{$_}), "\n" foreach sort { compare_opts($a, $b) } @{$contents};
- }
- return;
- }
- sub remove_default_options_if_needed {
- my ( $sources ) = @_;
- my $default_index;
- foreach my $index ( 0 .. $#{$sources} ) {
- if ( $sources->[$index]{'name'} eq 'Defaults' ) {
- $default_index = $index;
- last;
- }
- }
- return $sources unless defined $default_index;
- my $should_remove = 0;
- # Start with default options, minus some annoying ones.
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- Getopt::Long::Configure(
- 'no_ignore_case',
- 'no_auto_abbrev',
- 'pass_through',
- );
- foreach my $index ( $default_index + 1 .. $#{$sources} ) {
- my ( $name, $args ) = @{$sources->[$index]}{qw/name contents/};
- if (ref($args)) {
- local @ARGV = @{$args};
- Getopt::Long::GetOptions(
- 'ignore-ack-defaults' => \$should_remove,
- );
- @{$args} = @ARGV;
- }
- else {
- ( undef, $sources->[$index]{contents} ) = Getopt::Long::GetOptionsFromString($args,
- 'ignore-ack-defaults' => \$should_remove,
- );
- }
- }
- Getopt::Long::Configure('default');
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- return $sources unless $should_remove;
- my @copy = @{$sources};
- splice @copy, $default_index, 1;
- return \@copy;
- }
- sub check_for_mutually_exclusive_options {
- my ( $arg_sources ) = @_;
- my %mutually_exclusive_with;
- my @copy = @{$arg_sources};
- for(my $i = 0; $i < @INVALID_COMBINATIONS; $i += 2) {
- my ( $lhs, $rhs ) = @INVALID_COMBINATIONS[ $i, $i + 1 ];
- foreach my $l_opt ( @{$lhs} ) {
- foreach my $r_opt ( @{$rhs} ) {
- push @{ $mutually_exclusive_with{ $l_opt } }, $r_opt;
- push @{ $mutually_exclusive_with{ $r_opt } }, $l_opt;
- }
- }
- }
- while( @copy ) {
- my %set_opts;
- my $source = shift @copy;
- my ( $source_name, $args ) = @{$source}{qw/name contents/};
- $args = ref($args) ? [ @{$args} ] : [ Text::ParseWords::shellwords($args) ];
- foreach my $opt ( @{$args} ) {
- next unless $opt =~ /^[-+]/;
- last if $opt eq '--';
- if( $opt =~ /^(.*)=/ ) {
- $opt = $1;
- }
- elsif ( $opt =~ /^(-[^-]).+/ ) {
- $opt = $1;
- }
- $set_opts{ $opt } = 1;
- my $mutex_opts = $mutually_exclusive_with{ $opt };
- next unless $mutex_opts;
- foreach my $mutex_opt ( @{$mutex_opts} ) {
- if($set_opts{ $mutex_opt }) {
- die "Options '$mutex_opt' and '$opt' are mutually exclusive\n";
- }
- }
- }
- }
- }
- sub process_args {
- my $arg_sources = \@_;
- my %opt = (
- pager => $ENV{ACK_PAGER_COLOR} || $ENV{ACK_PAGER},
- );
- check_for_mutually_exclusive_options($arg_sources);
- $arg_sources = remove_default_options_if_needed($arg_sources);
- if ( should_dump_options($arg_sources) ) {
- dump_options($arg_sources);
- exit(0);
- }
- my $type_specs = process_filetypes(\%opt, $arg_sources);
- process_other(\%opt, $type_specs, $arg_sources);
- while ( @{$arg_sources} ) {
- my $source = shift @{$arg_sources};
- my ( $source_name, $args ) = @{$source}{qw/name contents/};
- # All of our sources should be transformed into an array ref
- if ( ref($args) ) {
- if ( $source_name eq 'ARGV' ) {
- @ARGV = @{$args};
- }
- elsif (@{$args}) {
- Carp::croak "source '$source_name' has extra arguments!";
- }
- }
- else {
- Carp::croak 'The impossible has occurred!';
- }
- }
- my $filters = ($opt{filters} ||= []);
- # Throw the default filter in if no others are selected.
- if ( not grep { !$_->is_inverted() } @{$filters} ) {
- push @{$filters}, App::Ack::Filter::Default->new();
- }
- return \%opt;
- }
- sub retrieve_arg_sources {
- my @arg_sources;
- my $noenv;
- my $ackrc;
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- Getopt::Long::Configure('pass_through');
- Getopt::Long::Configure('no_auto_abbrev');
- Getopt::Long::GetOptions(
- 'noenv' => \$noenv,
- 'ackrc=s' => \$ackrc,
- );
- Getopt::Long::Configure('default', 'no_auto_help', 'no_auto_version');
- my @files;
- if ( !$noenv ) {
- my $finder = App::Ack::ConfigFinder->new;
- @files = $finder->find_config_files;
- }
- if ( $ackrc ) {
- # We explicitly use open so we get a nice error message.
- # XXX This is a potential race condition!.
- if(open my $fh, '<', $ackrc) {
- close $fh;
- }
- else {
- die "Unable to load ackrc '$ackrc': $!"
- }
- push( @files, { path => $ackrc } );
- }
- push @arg_sources, {
- name => 'Defaults',
- contents => [ App::Ack::ConfigDefault::options_clean() ],
- };
- foreach my $file ( @files) {
- my @lines = App::Ack::ConfigFinder::read_rcfile($file->{path});
- if(@lines) {
- push @arg_sources, {
- name => $file->{path},
- contents => \@lines,
- project => $file->{project},
- };
- }
- }
- if ( $ENV{ACK_OPTIONS} && !$noenv ) {
- push @arg_sources, {
- name => 'ACK_OPTIONS',
- contents => $ENV{ACK_OPTIONS},
- };
- }
- push @arg_sources, {
- name => 'ARGV',
- contents => [ @ARGV ],
- };
- return @arg_sources;
- }
- 1; # End of App::Ack::ConfigLoader
- package App::Ack::Filter;
- use strict;
- use warnings;
- use overload
- '""' => 'to_string';
- use Carp 1.04 ();
- my %filter_types;
- sub create_filter {
- my ( undef, $type, @args ) = @_;
- if ( my $package = $filter_types{$type} ) {
- return $package->new(@args);
- }
- Carp::croak "Unknown filter type '$type'";
- }
- sub register_filter {
- my ( undef, $type, $package ) = @_;
- $filter_types{$type} = $package;
- return;
- }
- sub invert {
- my ( $self ) = @_;
- return App::Ack::Filter::Inverse->new( $self );
- }
- sub is_inverted {
- return 0;
- }
- sub to_string {
- my ( $self ) = @_;
- return '(unimplemented to_string)';
- }
- sub inspect {
- my ( $self ) = @_;
- return ref($self);
- }
- 1;
- package App::Ack::Filter::Extension;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- sub new {
- my ( $class, @extensions ) = @_;
- my $exts = join('|', map { "\Q$_\E"} @extensions);
- my $re = qr/[.](?:$exts)$/i;
- return bless {
- extensions => \@extensions,
- regex => $re,
- groupname => 'ExtensionGroup',
- }, $class;
- }
- sub create_group {
- return App::Ack::Filter::ExtensionGroup->new();
- }
- sub filter {
- my ( $self, $resource ) = @_;
- my $re = $self->{'regex'};
- return $resource->name =~ /$re/;
- }
- sub inspect {
- my ( $self ) = @_;
- my $re = $self->{'regex'};
- return ref($self) . " - $re";
- }
- sub to_string {
- my ( $self ) = @_;
- my $exts = $self->{'extensions'};
- return join(' ', map { ".$_" } @{$exts});
- }
- BEGIN {
- App::Ack::Filter->register_filter(ext => __PACKAGE__);
- }
- 1;
- package App::Ack::Filter::FirstLineMatch;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- sub new {
- my ( $class, $re ) = @_;
- $re =~ s{^/|/$}{}g; # XXX validate?
- $re = qr{$re}i;
- return bless {
- regex => $re,
- }, $class;
- }
- # This test reads the first 250 characters of a file, then just uses the
- # first line found in that. This prevents reading something like an entire
- # .min.js file (which might be only one "line" long) into memory.
- sub filter {
- my ( $self, $resource ) = @_;
- my $re = $self->{'regex'};
- my $line = $resource->firstliney;
- return $line =~ /$re/;
- }
- sub inspect {
- my ( $self ) = @_;
- my $re = $self->{'regex'};
- return ref($self) . " - $re";
- }
- sub to_string {
- my ( $self ) = @_;
- (my $re = $self->{regex}) =~ s{\([^:]*:(.*)\)$}{$1};
- return "first line matches /$re/";
- }
- BEGIN {
- App::Ack::Filter->register_filter(firstlinematch => __PACKAGE__);
- }
- 1;
- package App::Ack::Filter::Is;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- use File::Spec 3.00 ();
- sub new {
- my ( $class, $filename ) = @_;
- return bless {
- filename => $filename,
- groupname => 'IsGroup',
- }, $class;
- }
- sub create_group {
- return App::Ack::Filter::IsGroup->new();
- }
- sub filter {
- my ( $self, $resource ) = @_;
- my $filename = $self->{'filename'};
- my $base = (File::Spec->splitpath($resource->name))[2];
- return $base eq $filename;
- }
- sub inspect {
- my ( $self ) = @_;
- my $filename = $self->{'filename'};
- return ref($self) . " - $filename";
- }
- sub to_string {
- my ( $self ) = @_;
- my $filename = $self->{'filename'};
- return $filename;
- }
- BEGIN {
- App::Ack::Filter->register_filter(is => __PACKAGE__);
- }
- 1;
- package App::Ack::Filter::Match;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- use File::Spec 3.00;
- sub new {
- my ( $class, $re ) = @_;
- $re =~ s{^/|/$}{}g; # XXX validate?
- $re = qr/$re/i;
- return bless {
- regex => $re,
- }, $class;
- }
- sub filter {
- my ( $self, $resource ) = @_;
- my $re = $self->{'regex'};
- my $base = (File::Spec->splitpath($resource->name))[2];
- return $base =~ /$re/;
- }
- sub inspect {
- my ( $self ) = @_;
- my $re = $self->{'regex'};
- print ref($self) . " - $re";
- return;
- }
- sub to_string {
- my ( $self ) = @_;
- my $re = $self->{'regex'};
- return "filename matches $re";
- }
- BEGIN {
- App::Ack::Filter->register_filter(match => __PACKAGE__);
- }
- 1;
- package App::Ack::Filter::Default;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- sub new {
- my ( $class ) = @_;
- return bless {}, $class;
- }
- sub filter {
- my ( $self, $resource ) = @_;
- return -T $resource->name;
- }
- 1;
- package App::Ack::Filter::Inverse;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- sub new {
- my ( $class, $filter ) = @_;
- return bless {
- filter => $filter,
- }, $class;
- }
- sub filter {
- my ( $self, $resource ) = @_;
- my $filter = $self->{'filter'};
- return !$filter->filter( $resource );
- }
- sub invert {
- my $self = shift;
- return $self->{'filter'};
- }
- sub is_inverted {
- return 1;
- }
- sub inspect {
- my ( $self ) = @_;
- my $filter = $self->{'filter'};
- return "!$filter";
- }
- 1;
- package App::Ack::Filter::Collection;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- sub new {
- my ( $class ) = @_;
- return bless {
- groups => {},
- ungrouped => [],
- }, $class;
- }
- sub filter {
- my ( $self, $resource ) = @_;
- for my $group (values %{$self->{'groups'}}) {
- if ($group->filter($resource)) {
- return 1;
- }
- }
- for my $filter (@{$self->{'ungrouped'}}) {
- if ($filter->filter($resource)) {
- return 1;
- }
- }
- return 0;
- }
- sub add {
- my ( $self, $filter ) = @_;
- if (exists $filter->{'groupname'}) {
- my $group = ($self->{groups}->{$filter->{groupname}} ||= $filter->create_group());
- $group->add($filter);
- }
- else {
- push @{$self->{'ungrouped'}}, $filter;
- }
- return;
- }
- sub inspect {
- my ( $self ) = @_;
- return ref($self) . " - $self";
- }
- sub to_string {
- my ( $self ) = @_;
- my $ungrouped = $self->{'ungrouped'};
- return join(', ', map { "($_)" } @{$ungrouped});
- }
- 1;
- package App::Ack::Filter::IsGroup;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- use File::Spec 3.00 ();
- sub new {
- my ( $class ) = @_;
- return bless {
- data => {},
- }, $class;
- }
- sub add {
- my ( $self, $filter ) = @_;
- $self->{data}->{ $filter->{filename} } = 1;
- return;
- }
- sub filter {
- my ( $self, $resource ) = @_;
- my $data = $self->{'data'};
- my $base = (File::Spec->splitpath($resource->name))[2];
- return exists $data->{$base};
- }
- sub inspect {
- my ( $self ) = @_;
- return ref($self) . " - $self";
- }
- sub to_string {
- my ( $self ) = @_;
- return join(' ', keys %{$self->{data}});
- }
- 1;
- package App::Ack::Filter::ExtensionGroup;
- use strict;
- use warnings;
- BEGIN {
- our @ISA = 'App::Ack::Filter';
- }
- sub new {
- my ( $class ) = @_;
- return bless {
- data => {},
- }, $class;
- }
- sub add {
- my ( $self, $filter ) = @_;
- foreach my $ext (@{$filter->{extensions}}) {
- $self->{data}->{lc $ext} = 1;
- }
- return;
- }
- sub filter {
- my ( $self, $resource ) = @_;
- if ($resource->name =~ /[.]([^.]*)$/) {
- return exists $self->{'data'}->{lc $1};
- }
- return 0;
- }
- sub inspect {
- my ( $self ) = @_;
- return ref($self) . " - $self";
- }
- sub to_string {
- my ( $self ) = @_;
- return join(' ', map { ".$_" } sort keys %{$self->{data}});
- }
- 1;
- package File::Next;
- use strict;
- use warnings;
- our $VERSION = '1.12';
- 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 @_ },
- warning_handler => sub { CORE::warn @_ },
- sort_files => undef,
- follow_symlinks => 1,
- nul_separated => 0,
- );
- %skip_dirs = map {($_,1)} (File::Spec->curdir, File::Spec->updir);
- }
- sub files {
- die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
- my ($parms,@queue) = _setup( \%files_defaults, @_ );
- my $filter = $parms->{file_filter};
- return sub {
- while (@queue) {
- my ($dirname,$file,$fullpath) = splice( @queue, 0, 3 );
- if ( -f $fullpath || -p $fullpath || $fullpath =~ m{^/dev/fd} ) {
- if ( $filter ) {
- local $_ = $file;
- local $File::Next::dir = $dirname;
- local $File::Next::name = $fullpath;
- next if not $filter->();
- }
- return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
- }
- elsif ( -d _ ) {
- unshift( @queue, _candidate_files( $parms, $fullpath ) );
- }
- } # while
- return;
- }; # iterator
- }
- sub from_file {
- die _bad_invocation() if @_ && defined($_[0]) && ($_[0] eq __PACKAGE__);
- my ($parms,@queue) = _setup( \%files_defaults, @_ );
- my $err = $parms->{error_handler};
- my $warn = $parms->{error_handler};
- my $filename = $queue[1];
- if ( !defined($filename) ) {
- $err->( 'Must pass a filename to from_file()' );
- return undef;
- }
- my $fh;
- if ( $filename eq '-' ) {
- $fh = \*STDIN;
- }
- else {
- if ( !open( $fh, '<', $filename ) ) {
- $err->( "Unable to open $filename: $!" );
- return undef;
- }
- }
- my $filter = $parms->{file_filter};
- return sub {
- local $/ = $parms->{nul_separated} ? "\x00" : $/;
- while ( my $fullpath = <$fh> ) {
- chomp $fullpath;
- next unless $fullpath =~ /./;
- if ( not ( -f $fullpath || -p _ ) ) {
- $warn->( "$fullpath: No such file" );
- next;
- }
- my ($volume,$dirname,$file) = File::Spec->splitpath( $fullpath );
- if ( $filter ) {
- local $_ = $file;
- local $File::Next::dir = $dirname;
- local $File::Next::name = $fullpath;
- next if not $filter->();
- }
- return wantarray ? ($dirname,$file,$fullpath) : $fullpath;
- } # while
- close $fh;
- return;
- }; # iterator
- }
- sub _bad_invocation {
- my $good = (caller(1))[3];
- my $bad = $good;
- $bad =~ s/(.+)::/$1->/;
- return "$good must not be invoked as $bad";
- }
- 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 $dirname = shift;
- my $dh;
- if ( !opendir $dh, $dirname ) {
- $parms->{error_handler}->( "$dirname: $!" );
- 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( $dirname, $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, [ $dirname, $file, $fullpath ] );
- }
- else {
- push( @newfiles, $dirname, $file, $fullpath );
- }
- }
- closedir $dh;
- if ( $sort_sub ) {
- return map { @{$_} } sort $sort_sub @newfiles;
- }
- return @newfiles;
- }
- 1; # End of File::Next
|