Compare commits

...

438 commits

Author SHA1 Message Date
Geoff Greer
a61f1780b6
Merge pull request #1424 from sanjaymsh/ppc64le
Travis-ci: added support for ppc64le
2020-12-16 17:09:07 -08:00
sanjay-cpu
0da08b76af Travis-ci: added support for ppc64le 2020-12-11 07:18:23 +00:00
Geoff Greer
5a1c8d83ba Fix test. 2020-07-04 12:27:44 -07:00
Geoff Greer
cb9941b4bb
Merge pull request #1393 from JFLarvoire/winget
Document how to install ag.exe for Windows using winget
2020-07-04 12:22:31 -07:00
JFLarvoire
ed0fe56e79 Document how to install ag.exe for Windows using winget 2020-06-29 18:17:37 +02:00
Geoff Greer
5600c1b6b1
Merge pull request #1389 from aswild/getenv-segfault
options: fix segfault when TERM or HOME isn't set
2020-06-26 11:49:34 -07:00
Geoff Greer
6169c2d608
Merge pull request #1392 from corelight-chris/master
Change bro filetype support to zeek.
2020-06-26 11:48:57 -07:00
Geoff Greer
9b23390076
Merge pull request #1390 from matthewhughes934/fix-list_file_types-test
Add missing whitespace to test
2020-06-26 11:48:44 -07:00
Christopher M. Hobbs
01f2a97ab6 Change bro filetype support to zeek. 2020-06-25 17:19:16 -05:00
Allen Wild
0da483f0a5 tests: add empty_environment test, fix whitespace in list_file_types
Verify that ag works with no environment variables set.
2020-06-22 11:17:40 -04:00
Matthew Hughes
d949e71d9d Add missing whitespace to test
This fixes the failing test.

* Add missing indentation (missed in c95d72f)
* Add missing newline at end of file (removed with 328afb2)
2020-06-22 15:55:24 +01:00
Allen Wild
1dae7a25a3 options: fix segfault when TERM or HOME isn't set
Regression from 3289ab8fba, if TERM isn't
set in the environment, getenv returns NULL which we shouldn't strcmp.
Similarly guard against using home_dir if it's null when looking for
global gitignore files.

This can be reproduced and tested with `env -i ./ag foo`.
2020-06-22 00:14:30 -04:00
Geoff Greer
a9917e4490
Merge pull request #1326 from Redfoxymoon/master
midipix support
2020-06-20 17:09:43 -07:00
Geoff Greer
cd0705f12a
Merge pull request #1350 from sergeyklay/feature/zephir-support
Add Zephir support
2020-06-20 17:09:22 -07:00
Geoff Greer
8a7032e89b
Merge pull request #1387 from yymm/master
Add support for Vue SFC
2020-06-20 17:08:02 -07:00
Geoff Greer
f7b15a0b03
Merge pull request #1384 from felipefiali/master
Add support for Bazel files
2020-06-20 17:07:50 -07:00
Geoff Greer
3f6ecb6c02
Merge pull request #1377 from shlomif/fix-multi-defs-of-globals
Fix multiple global symbols definitions.
2020-06-20 17:07:07 -07:00
Geoff Greer
092d73b190
Merge pull request #1330 from MicahElliott/clojure-add-edn-file-type
Add .edn file type for clojure files
2020-06-19 18:40:58 -07:00
Geoff Greer
56e73c9f0a
Merge pull request #1332 from wgrr/nodefault-color
Default to no color when $TERM is dumb
2020-06-19 18:40:29 -07:00
Geoff Greer
0aa76d6bfe
Merge pull request #1383 from Alexis-D/ad/add-gradle
Add --gradle
2020-06-19 18:40:07 -07:00
Geoff Greer
6342a8ed28
Merge pull request #1374 from equwal/master
Update link to ack3
2020-06-19 17:31:59 -07:00
Geoff Greer
e55cc9baa6
Merge pull request #1372 from dzmitry-lahoda/patch-1
added fzf
2020-06-19 17:31:45 -07:00
Geoff Greer
4fc15ecd70
Merge pull request #1359 from nbarrios1337/fix_bash_completion
Update bash completion script to reflect v2.0 changes
2020-06-19 17:28:13 -07:00
Geoff Greer
594c447d28
Merge pull request #1362 from veprbl/patch-1
README: add NixOS/Nix/Nixpkgs instructions
2020-06-19 17:27:55 -07:00
yuya yano
cbebe3b77e Add support for Vue SFC 2020-06-09 14:57:14 +09:00
Felipe Fiali
c95d72f3e7 Add support for Bazel files 2020-05-26 10:55:25 +02:00
Alexis Daboville
31376c9c5c Add --gradle 2020-05-22 16:24:54 +01:00
Shlomi Fish
21eaa1c416 Fix multiple global symbols definitions.
See the use of extern here:

* https://www.geeksforgeeks.org/understanding-extern-keyword-in-c/

* https://en.wikipedia.org/wiki/External_variable

*
https://stackoverflow.com/questions/496448/how-to-correctly-use-the-extern-keyword-in-c
2020-04-15 20:26:10 +03:00
Spenser Truex
293697938c Update link to ack3
New version has a new repo.
2020-03-28 15:41:46 -07:00
Dzmitry Lahoda
cba17bc01f
added fzf 2020-03-25 15:01:24 +03:00
Dmitry Kalinkin
232b85bed5
README: add NixOS/Nix/Nixpkgs instructions 2020-02-27 11:10:26 -05:00
Geoff Greer
b93c271993
Merge pull request #1358 from gliptak/patch-1
Bring Ubuntu to 16.04 in Travis
2020-02-24 15:18:49 -08:00
Nicolas Barrios
860046891c
Updated bash completion script to reflect v2.0 changes 2020-02-21 14:01:01 -05:00
Gábor Lipták
9fc018f0f1
Bring Ubuntu to 16.04 in Travis 2020-02-18 10:26:31 -05:00
Geoff Greer
a509a8172b Fix formatting. 2020-01-10 11:25:39 -08:00
Geoff Greer
de3e3c7cac
Merge pull request #1354 from emoses/master
Fix ignore patterns in subdirectories with leading slashes
2020-01-10 11:23:05 -08:00
Evan Moses
755c6e5c06 Fix ignore patterns in subdirectories with leading slashes
Currenetly if you have an ignore file in a subdirectory "sub" with a pattern
like
    /ignorethis
The directory sub/ignorethis will be ignored if you run ag from
within sub, but it won't be ignored if you run it from sub's parent.
that is

    $ ag needle

will search files in sub/ignorethis, but

    $ cd sub
    $ ag needle

Will not.  This is a bug
2020-01-09 17:34:44 -08:00
Serghei Iakovlev
328afb27d3
Add Zephir support 2019-12-20 12:58:32 +02:00
Wagner Riffel
3289ab8fba default to no color when terminal is dumb
Signed-off-by: Wagner Riffel <wgrriffel@gmail.com>
2019-08-18 22:49:50 -03:00
Micah Elliott
c1514aeee0 Add .edn file type for clojure files
It's common that you'd want edn files to show up in your clojure
searching.

https://github.com/edn-format/edn
2019-08-13 10:47:17 -07:00
Ørjan Malde
b34f3e385e
midipix support
midipix uses the musl libc, which provides the same bits as on linux.
2019-06-29 23:13:49 +02:00
Geoff Greer
965f71dcbc
Merge pull request #1245 from kpengboy/fix_-L
Fix -L again and decouple from -v
2019-03-22 09:11:11 -07:00
Geoff Greer
55eaaddab5
Merge pull request #1306 from brianary/feature-windows-extensions
Add various Windows extensions
2019-03-18 19:30:42 -07:00
Geoff Greer
20941e2bfd
Merge pull request #1310 from DeeeeLAN/verilog/work
added .svh for verilog
2019-03-18 19:30:18 -07:00
Dillan Mills
41fd43f48c added .svh for verilog 2019-03-05 22:56:28 -07:00
Brian Lalonde
09a7c68921 Fix trailing whitespace 2019-02-18 20:18:56 -08:00
Brian Lalonde
c951fc2c40 Add various Windows extensions 2019-02-18 20:12:07 -08:00
Geoff Greer
4f7aca7b01
Merge pull request #1270 from zealoussnow/fix-rpmbuild-error
fix rpmbuild error
2019-02-16 11:41:03 -08:00
Geoff Greer
7c70fe3b9b
Merge pull request #1287 from jacwah/pipe-link
Skip symlinks to named pipes
2019-01-17 17:20:17 -08:00
Geoff Greer
fa8cd32008
Merge pull request #1290 from aitzkora/master
add some extensions in uppercase for fortran files : now, the list has a length < 12
2019-01-17 17:16:51 -08:00
aitzkora
23e5b507c5 suppress some extension in fortran to be less than 12 2018-12-03 13:54:58 +01:00
aitzkora
14e5fe6f91 add support in fortran types for preprocessed files with uppercases 2018-12-03 12:38:35 +01:00
Jacob Wahlgren
095c3f091e Skip symlinks to named pipes
The d_type field contains the type as lstat would put it, but when
checking for a named pipe we need the stat behavior.

Fixes https://github.com/ggreer/the_silver_searcher/issues/1272
2018-11-28 23:42:24 +01:00
Jeff Horelick
1b06a9fd8d
Merge pull request #1283 from zawataki/add-dependencies-for-CentOS-onto-README
Add necessary dependencies in case build on CentOS
2018-11-26 11:11:39 -05:00
Yuki Takizawa
7a8143a41d Add necessary dependencies in case build on CentOS 2018-11-23 17:56:40 +09:00
Leo Zhang
295ab1072e fix rpmbuild error 2018-09-18 14:52:33 +08:00
Geoff Greer
bd82cd3d2f Add Travis IRC notifications. 2018-08-07 00:07:25 -07:00
Geoff Greer
97f33796a1 Update LLVM version for travis. 2018-08-06 23:59:21 -07:00
Geoff Greer
520ff29c52 Bump version to v2.2.0 2018-08-06 23:43:51 -07:00
Geoff Greer
93bd01ae94
Merge pull request #1209 from travisjeffery/master
Support terraform file type
2018-08-06 23:41:04 -07:00
Geoff Greer
ea9080d373
Merge pull request #1249 from alexbiehl/patch-1
Also search Backpack signatures for Haskell
2018-08-06 23:40:31 -07:00
Geoff Greer
5afe087759
Merge pull request #1253 from bapfeld/master
Adds stata files as a filetype option
2018-08-06 23:40:18 -07:00
Geoff Greer
838c03ad27 Improve performance when not using mmap()ed I/O (mostly macOS)
If ignoring binary files (the default behavior), read the first 512 bytes and call is_binary(). Previously, ag read the entire file (possibly gigabytes), checked the first 512 bytes, then didn't search the file. That could be comically wasteful.
2018-08-06 23:36:18 -07:00
Geoff Greer
7ada8f665c clang-format 2018-08-06 23:20:37 -07:00
Geoff Greer
bd320d43a2 On some platforms, read() will only read 2GB-4GB maximum. read() again until the whole file is in memory. 2018-08-04 23:46:08 -07:00
Kevin Peng
1c2a64d58b Fix -L again and decouple from -v 2018-08-02 03:09:57 -07:00
Kevin Peng
5c2c18ca17 Refactor search_buf and search_stream to return # matches 2018-08-02 03:09:57 -07:00
Brendan Apfeld
70841b4892 Fix white space between lines 2018-07-06 08:06:04 -05:00
Brendan Apfeld
c442e39efb Adds stata files as a language option 2018-07-04 14:56:52 -05:00
Alexander Biehl
ae53f271d4
Update list_file_types.t 2018-06-18 16:47:30 +02:00
Alexander Biehl
1a556b587b
Add hsig files for Haskell 2018-06-18 16:32:19 +02:00
Travis Jeffery
9191a7b197 add tfvars 2018-06-07 09:08:18 -04:00
Geoff Greer
9163065a5e
Merge pull request #1208 from jschneier/master
Add pug as a supported file type
2018-06-07 00:15:29 -07:00
Geoff Greer
160ea7bb1e
Merge pull request #1239 from aswild/buf-readline
util: buf_getline: validate index of the next byte before reading it
2018-06-02 22:41:40 -07:00
Allen Wild
17072d8849 util: buf_getline: validate index of the next byte before reading it
buf_getline dereferences cur[i] before checking that i is a valid index
in the buffer. Under the right circumstances, this can cause
a segmentation fault.

The right circumstances are:
  * multiline search disabled (the only way buf_getline is called)
  * file size is an exact multiple of the page size (4096 bytes)
  * end of file has no trailing newline
  * searching a directory (not just a single file on the command line)
  * using several worker threads

(I don't think the last two points are directly related to this bug, but
I couldn't reproduce it otherwise)

Address Sanitizer catches this consistently as a heap overflow if run
when mmap is disabled.

Swap the order of the for loop's condition checks so that i is validated
before dereferencing cur[i].

Signed-off-by: Allen Wild <allenwild93@gmail.com>
2018-05-10 21:52:49 -04:00
Geoff Greer
5b10c68e0f
Merge pull request #1194 from bazzargh/patch-1
Add -W/--width option to man page
2018-04-22 17:57:30 -07:00
Geoff Greer
eebcd60784
Merge pull request #1224 from Liambeguin/master
add filter for devicetree source files
2018-04-22 17:54:02 -07:00
Geoff Greer
be53a330d9
Merge pull request #1231 from nbeernink/patch-1
fix ack.vim link in README.md
2018-04-22 17:53:08 -07:00
Niek Beernink
de495b00eb
Update README.md
fixed ack.vim link
2018-03-28 14:16:17 +02:00
Liam Beguin
7051a784aa add filter for devicetree source files
Signed-off-by: Liam Beguin <lvb@xiphos.com>
2018-03-03 18:55:59 -05:00
Travis Jeffery
261b60a57e support terraform file type 2018-01-27 21:31:24 -05:00
Josh Schneier
f56e91391d Add pug as a supported file type 2018-01-27 16:02:35 +07:00
Geoff Greer
4aa9f05ec3
Merge pull request #1203 from felipefiali/master
Adding support for .thrift files search by using --thrift
2018-01-24 03:12:51 +00:00
Geoff Greer
f54d31f650
Merge pull request #1202 from Nelyah/master
adding support for PDB (Protein Data Bank) with --pdb and Naccess format with --naccess
2018-01-24 03:12:37 +00:00
Felipe Fiali
e8b6b3e292 Adding support for .thrift files 2018-01-23 17:24:54 -02:00
Chloé Dequeker
3b20a68907 tests really pass for list_file_types.t 2018-01-22 20:49:48 +01:00
Chloé Dequeker
b6fc5e83ac fixing format list_file_types.t 2018-01-22 20:41:17 +01:00
Chloé Dequeker
2e9a22fb1a adding support for PDB (Protein Data Bank) and Naccess format 2018-01-22 20:19:54 +01:00
bazzargh
f4d22405ac
Add -W/--width option to man page
The option added in #720 never made it onto the man page.
2018-01-08 19:02:00 +00:00
Geoff Greer
5516568a30 Replace longs in ag_stats struct with size_t. Should help with #1158. 2017-12-26 22:25:16 -08:00
Geoff Greer
a539c8e788
Merge pull request #1169 from Ssamm45/dlang
Added searching for the D Programming Language
2017-12-24 20:21:42 -08:00
Geoff Greer
4ce403ba07
Merge pull request #1170 from johnyf/tla
ENH: add file extensions for TLA+, Isabelle, Coq
2017-12-24 20:21:27 -08:00
Geoff Greer
502c017f9c
Merge pull request #1173 from Phyllostachys/master
Fix pgo.sh so that it actually does the thing.
2017-12-24 20:21:13 -08:00
Geoff Greer
6810deb255
Merge pull request #1187 from kevinkirkup/master
Add support for search Jinja2 templates
2017-12-24 20:19:41 -08:00
Kevin S Kirkup
8b62b3eb4b Support jinja2 template searching 2017-12-20 10:04:08 -05:00
Geoff Greer
e2bb8d4997 Oops. Formatting error. 2017-12-17 19:26:40 -08:00
Geoff Greer
6cec45f271 Add fall through comments to hide compiler warnings. 2017-12-16 22:13:10 -08:00
Geoff Greer
3ad49be72e
Merge pull request #1176 from toolboxen/patch-1
README: add windows installation via Chocolatey
2017-11-30 23:13:25 -08:00
Geoff Greer
c394ab1413
Merge pull request #1179 from murmour/fix-r-extension
Add .r to R file type
2017-11-30 23:13:03 -08:00
Max Mouratov
a2f619a599 Add .r to R file type 2017-11-19 19:51:03 +05:00
toolboxen
f7fb7a5673
README: add windows installation via Chocolatey
ag can be installed as a package from Chocolatey: https://chocolatey.org/packages/ag
2017-11-08 21:53:07 -08:00
Jacob Shaffer
bd9409e2ab Fix pgo.sh so that it actually does the thing. 2017-11-01 10:38:03 -04:00
Ioannis Filippidis
e8c9e0dd7c ENH: add file extensions for TLA+, Isabelle, Coq 2017-10-26 20:56:24 -07:00
Samuel
c2973fa7f8
Added whitespace preventing travice-cli test from passing 2017-10-26 20:19:33 -06:00
Samuel
a2e2f17d1d
Removed whitespace preventing test from passing 2017-10-26 20:15:41 -06:00
Samuel
3a97d548d5
Added searching for the D Programming Language 2017-10-26 19:58:25 -06:00
Geoff Greer
1791664604 Merge pull request #1163 from Synthetica9/patch-1
Added support for Idris, APL and J
2017-10-22 16:56:28 -07:00
Patrick Hilhorst
24e017157f So are spaces. 2017-10-05 13:11:46 +02:00
Patrick Hilhorst
5e3d3ff3c4 The alphabet is hard 2017-10-05 13:04:55 +02:00
Patrick Hilhorst
2390ec9360 Update list_file_types.t 2017-10-05 12:59:07 +02:00
Patrick Hilhorst
927fe60fd3 Added support for Idris, APL and J
Added support for the filetypes used by:

- [Idris](https://www.idris-lang.org) (`.idr`, `.lidr` and `.ipkg`)
- [APL](https://en.wikipedia.org/wiki/APL_(programming_language\)) (`.apl`)
- [J](http://jsoftware.com) (`.ijs`)
2017-10-05 12:50:42 +02:00
Geoff Greer
1a5e259b25 Bump version to v2.1.0 2017-08-24 23:17:11 -07:00
Geoff Greer
02ed769a05 Merge branch 'stedolan-master' 2017-08-24 22:51:57 -07:00
Geoff Greer
808c7da52d Merge branch 'master' of https://github.com/stedolan/the_silver_searcher into stedolan-master 2017-08-24 22:51:43 -07:00
Geoff Greer
0d1d5fb4fc Merge branch 'master' of github.com:ggreer/the_silver_searcher 2017-08-24 22:47:19 -07:00
Geoff Greer
982ed05cfb Fix test 2017-08-24 22:47:13 -07:00
Geoff Greer
17b2fd58cc Merge pull request #1137 from mhickman/add_handlebars
Adding Handlebars filetype
2017-08-24 22:45:41 -07:00
Geoff Greer
8d9c23863a Merge pull request #1130 from k-takata/update-windows-doc
Update windows doc, add a link to daily builds
2017-08-24 22:43:56 -07:00
Geoff Greer
ebfaa1e514 Merge pull request #1127 from aswild/issue-1126
zfile: define off64_t to fix cygwin build (Fix #1126)
2017-08-24 22:43:41 -07:00
Geoff Greer
5684fdb8c2 Merge pull request #1119 from ojn/master
languages added: asciidoc, org-mode and ipython-notebook
2017-08-24 22:43:11 -07:00
Geoff Greer
1030d76ada Merge pull request #1071 from osa1/nix_support
Add nix support
2017-08-24 22:41:54 -07:00
Geoff Greer
20b7fa49f8 Merge pull request #1123 from k-takata/fix-mingw
Fix compile error on MinGW
2017-08-24 20:57:56 -07:00
Ömer Sinan Ağacan
b94dc794a9 Add nix support 2017-08-23 15:58:02 +03:00
Matt Hickman
229d1c1769 Adding Handlebars filetype 2017-08-10 10:18:00 -07:00
ojn
f01f269294 Update list_file_types.t
added .asc for asciidoc
2017-08-01 01:45:17 +02:00
ojn
2406ad0d01 Update lang.c 2017-08-01 01:43:54 +02:00
K.Takata
c58d047913 doc: Describe how to install on MSYS2 2017-07-22 08:13:37 +09:00
K.Takata
6eeba6fbce doc: Add a link to Win32 unofficial daily builds 2017-07-22 08:09:34 +09:00
Allen Wild
fdf98dba41 zfile: define off64_t to fix cygwin build (Fix #1126)
Cygwin defines _off64_t, so typedef off64_t to that.
2017-07-16 19:18:24 -04:00
K.Takata
13367b0ac2 Fix compile error on MinGW
MinGW doesn't have err.h.
2017-07-14 20:49:54 +09:00
ojn
357d58e07e added: graphviz dot lang 2017-07-07 13:55:29 +02:00
ojn
33ed59c123 added: graphviz dot lang 2017-07-07 13:53:41 +02:00
ojn
ff8ff04028 nix lang added 2017-07-06 01:17:28 +02:00
ojn
635310538d nix lang added 2017-07-06 01:15:41 +02:00
ojn
a38d7fe638 asciidoc, org-mode and ipython-notebook 2017-07-06 01:08:36 +02:00
ojn
e5554dff79 updated 2017-07-06 01:02:25 +02:00
ojn
5d9f6fa9cf asciidoc, org-mode and ipython-notebook 2017-07-06 00:48:01 +02:00
Geoff Greer
9b00f5f3f7 Merge pull request #1116 from sobolevn/patch-1
Updates README.md with svg badge
2017-07-04 00:51:10 -07:00
Nikita Sobolev
62c61f44a7 Updates README.md with svg badge 2017-07-04 09:58:06 +03:00
Geoff Greer
d10085cbcc Merge pull request #1106 from cemeyer/896-zip-streaming
Fix #896 - Stream decompress zipped files
2017-06-26 23:46:54 -07:00
Geoff Greer
81c544a196 Merge pull request #1111 from cemeyer/add-freebsd-cpu-affinity-support
FreeBSD: Add support for setting CPU affinity
2017-06-24 22:22:02 -07:00
Conrad Meyer
90b7d10e95 FreeBSD: Add support for setting CPU affinity 2017-06-23 16:24:36 -07:00
Geoff Greer
ddfef43dde Merge pull request #1110 from ZidHuss/md-arg
Added md alias for markdown files
2017-06-23 10:01:28 -07:00
Hussein Mohammed
f331c30889 Added md alias for markdown files 2017-06-23 10:19:04 +01:00
Geoff Greer
a22c2ef6c5 Merge pull request #1104 from k-takata/topic/fix-1040
Fix -O detection in $CFLAGS
2017-06-22 23:13:57 -07:00
Geoff Greer
0472b8e99f Merge pull request #1108 from jonasstein/patch-1
complete gentoo emerge command
2017-06-22 23:01:04 -07:00
Geoff Greer
ae6043c6b2 Merge pull request #1109 from mshibanami/plist-support
Add plist support
2017-06-22 23:00:44 -07:00
Manabu Nakazawa
8d6dc102a3 Add plist support 2017-06-20 08:08:35 +09:00
Jonas Stein
9f9ead6f8b complete gentoo emerge command
-a -> ask the user y/n after calculating dependencies.
2017-06-18 21:44:34 +02:00
Conrad Meyer
2ec37825e5 Fix #896 - Stream decompress zipped files
Use the POSIX fopencookie(3) mechanism to produce a FILE object, and
then treat them the same as other non-mmapable streams (i.e., FIFOs).

Since some supported platforms do not support fopencookie(3) (Mac OS X,
maybe Cygwin, older BSDs), retain non-streaming zip file support.
2017-06-17 10:47:11 -07:00
K.Takata
c6c610e512 Fix -O detection in $CFLAGS
The commit 815d6975ab (PR #1040) doesn't
work as expected.
`./configure` hangs up in some situations because of this.
2017-06-10 12:14:39 +09:00
Geoff Greer
99cf1834ce Bump to v2.0. Removal of .agignore (in favor of .ignore) broke backwards compatibility. 2017-06-04 14:27:07 -07:00
Geoff Greer
e5230abd98 Merge pull request #1091 from uhlissuh/add-all-files-option
Add --print-all-files option
2017-06-04 14:06:09 -07:00
Alissa Sobo
bc9255a474 Rename --all-files to --print-all-files
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2017-06-04 13:56:51 -07:00
Geoff Greer
055e54327d Re-add PCRE_CFLAGS removed by #1040 2017-06-03 22:03:19 -07:00
Geoff Greer
e081d080d1 Merge pull request #1040 from aswild/sanitize
Fix Heap Buffer Flow (fixes #1039)
2017-06-03 21:59:17 -07:00
Allen Wild
581335d101 Add script to automatically run tests with the LLVM sanitizers or valgrind
hash_strnstr uses intentional unaligned access, so add an __attribute__
to tell clang to ignore that.
2017-06-03 21:57:55 -07:00
Geoff Greer
c8f8389dbb Merge pull request #1101 from ggreer/invert-ignores
Add support for invert ignore rules (eg: !blah.txt)
2017-06-03 17:03:09 -07:00
Geoff Greer
33d9d71176 Add test for invert ignore rules. 2017-06-03 17:01:26 -07:00
Geoff Greer
36cb44d7d3 Add support for invert ignore rules (eg: !blah.txt) 2017-06-03 17:00:02 -07:00
Geoff Greer
abd982483e Merge pull request #1095 from fkotsian/master
Docs: Add new CentOS yum command
2017-06-02 21:25:18 -07:00
Geoff Greer
93f61fda3e Merge pull request #1093 from ggreer/makefile
Run make with -r option (remove default rules).
2017-06-02 21:25:05 -07:00
Geoff Greer
61167f6e84 Merge pull request #1081 from Gama11/haxe
Add Haxe support
2017-06-02 21:24:53 -07:00
Geoff Greer
3b5eff52f7 Merge pull request #1076 from lucascaton/patch-1
Rename 'MacOS' to 'macOS' in README
2017-06-02 21:24:34 -07:00
Frank Kotsianas
7e399fda55 Document CentOS yum install
New!
2017-05-30 12:54:25 -07:00
Geoff Greer
cd03629a52 Merge pull request #1072 from jschpp/patch-2
Adding filetypes
2017-05-29 13:54:15 -07:00
Geoff Greer
bfaec904ed Merge pull request #1094 from k-takata/fix-mingw
Fix compile error on MinGW
2017-05-29 13:53:20 -07:00
K.Takata
947f2894d9 Fix compile error on MinGW
MinGW doesn't have S_ISSOCK.
2017-05-29 23:09:37 +09:00
Geoff Greer
744e3c6c72 Run make with -r option (remove default rules). 2017-05-27 23:32:27 -07:00
Geoff Greer
9fdc38e41a Make other test rules phony. 2017-05-27 23:29:00 -07:00
Geoff Greer
13225fc8aa Merge pull request #1092 from uhlissuh/fix-ackmate-column-numbers
Fix ackmate column numbers for multi-line matches
2017-05-27 23:07:09 -07:00
Geoff Greer
ed0ece929b Merge pull request #1089 from ggreer/remove-agignore
Remove .agignore
2017-05-27 22:52:54 -07:00
Alissa Sobo
05bea43f55 Fix ackmate column numbers for multi-line matches
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2017-05-26 19:55:14 -07:00
Alissa Sobo
2ef780b3dd Sort output in --all-files test
Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2017-05-26 19:51:32 -07:00
Alissa Sobo
060d19bc7c Add --all-files option
This option causes ag to print headings for all files that it searches,
not just those that contain matches. This will be useful in the case
where ag is being called by another program that needs to show the user
a progress indicator of how many paths have been searched.

Signed-off-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
2017-05-26 19:24:51 -07:00
Geoff Greer
c8cef90777 Remove .agignore. .ignore is the canonical file to read patterns from. 2017-05-25 19:40:16 -07:00
Geoff Greer
2dddcd223d Merge pull request #1083 from ackl/master
add support for jsp tag and fragment files
2017-05-14 21:49:02 -07:00
Andrew Low
ba9b069e8a fix list file types test for jsps 2017-05-10 23:40:47 +01:00
ackl
e598fcad32 add support for jsp tag and fragment files 2017-05-09 16:26:03 +01:00
Geoff Greer
69a6ebd35a Merge pull request #932 from mattscamp/accept_empty_query
Accept empty query
2017-05-07 20:17:39 -07:00
Jens Fischer
8c74a91aba Add Haxe support 2017-05-03 09:21:20 +02:00
Geoff Greer
2be9f9003d Bump version to v1.0.3. 2017-05-02 23:35:21 -07:00
Geoff Greer
b72a669d7a Formatting. 2017-05-02 23:29:03 -07:00
Geoff Greer
04a373bafa Fix error in order of operations. 2017-05-02 23:28:54 -07:00
Geoff Greer
eeacac0e90 Merge branch 'master' of https://github.com/PRDeving/the_silver_searcher into PRDeving-master 2017-05-02 23:24:39 -07:00
Geoff Greer
d69d8cf4a2 Merge pull request #1051 from jefffederman/add-es6-to-js-file-type
Add es6 to js file type
2017-05-02 23:20:48 -07:00
Geoff Greer
7d8883c975 Merge pull request #1061 from jdeniau/patch-1
Add "twig" to languages
2017-05-02 23:20:25 -07:00
Geoff Greer
49ed6a4661 Merge pull request #1063 from bigardone/feature/elm-support
Added Elm filetype support
2017-05-02 23:20:04 -07:00
Geoff Greer
4b3f8310f4 Merge pull request #1068 from jschpp/Fix#1030
Added long option for filename pattern
2017-05-02 23:17:36 -07:00
Lucas Caton
7b89111eed Rename 'MacOS' to 'macOS' in README 2017-04-12 10:48:31 +10:00
Johannes Schöpp
fe36783d5e Adding filetypes
Added explicit instructions on how to add filetypes
2017-04-02 12:56:51 +02:00
PRDeving
2355b1c9fa Boyer moore algorithm ignore case refactor 2017-03-30 11:41:32 +02:00
Johannes Schoepp
99fa19f33e Added long option for filename pattern 2017-03-20 17:59:40 +01:00
Julien Deniau
27b360fa6e Add "twig" to languages 2017-03-20 16:21:22 +01:00
Ricardo García Vega
2b69965980 removed whitespace 2017-03-20 15:06:22 +01:00
Ricardo García Vega
b44355d403 added missing whitespace 2017-03-20 07:12:02 +01:00
Geoff Greer
88a5c0c2fe Merge pull request #1052 from jan-ruzicka/patch-1
stat test reduced to regular files and FIFOs
2017-03-11 22:02:28 -08:00
Ricardo García Vega
0e43b1722c added elm filetype support 2017-03-03 13:41:08 +01:00
Geoff Greer
da79caccf8 Merge pull request #1059 from realdeadfish/gradle-files-support
Support for gradle files
2017-02-23 23:58:55 -08:00
Thomas Keller
e15c6e112f Add .gradle files to the list of known files to search for when filtering for --groovy 2017-02-15 14:07:58 +01:00
Jan Ruzicka
902efac863 removed space characters at empty line 2017-02-02 07:52:22 -05:00
Jan Ruzicka
99a0169969 stat test reduced to regular files and FIFOs
Stat checks now allow searching only in regular files and FIFOs.
The stat test were duplicated to prevent TOCTOU.
related to issue #745
2017-02-01 23:47:40 -05:00
Jeff Federman
be6d07f51c Add .es6 to js file type 2017-02-01 13:34:21 -05:00
Jeff Federman
5b9dd1d532 Add .es6 to js file type 2017-02-01 13:32:33 -05:00
Geoff Greer
ee56182f61 Add script for performance-guided optimization. Ignore pgo files.
Thanks to @gwern & @FeepingCreature for showing this to me.
2017-01-28 12:56:06 -08:00
Geoff Greer
8639866dcc stat() files before open()ing them. This should help with issues such as opening /dev/watchdog (#745) 2017-01-26 21:04:46 -08:00
Geoff Greer
7110453f8c Only munmap() if mmap() failed. Thanks to @lpenz for noticing this bug. 2017-01-25 23:02:01 -08:00
Geoff Greer
221e4c2f07 Ignore fix: *.js now correctly ignores *.min.js. 2017-01-16 13:11:59 -08:00
Geoff Greer
4a0c638ea1 Revert "Fix for ignoring *.min.js"
This reverts commit fc94b00046.
2017-01-16 13:08:46 -08:00
Geoff Greer
0cbe479bd9 Merge branch 'brianpeiris-ignore-multiple-ext' 2017-01-16 13:02:07 -08:00
Geoff Greer
2f1700cbfa Better descriptions for these tests. 2017-01-16 13:01:48 -08:00
Geoff Greer
4640af7bcd Merge branch 'ignore-multiple-ext' of https://github.com/brianpeiris/the_silver_searcher into brianpeiris-ignore-multiple-ext 2017-01-16 12:42:08 -08:00
Geoff Greer
feca4775d7 Merge pull request #1048 from denilsonsa/patch-1
Fix for ignoring *.min.js
2017-01-16 12:33:54 -08:00
Denilson Sá Maia
fc94b00046 Fix for ignoring *.min.js
Problem:

* If `*.` is found at the beginning of the expression, the rest of the expression is considered an *extension*, and gets added to `ig->extensions`.
* When a file `jquery.min.js` is being processed, the silver searcher tries matching the extension `js` with the items from `ig->extensions`.
* Thus `min.js` will never match any extension.

Solution:

* Don't add a string to `ig->extensions` if that string still contains another `.`.

Performance impact:

* Unknown. I haven't measured.

Related issues:

* https://github.com/ggreer/the_silver_searcher/issues/710
* https://github.com/ggreer/the_silver_searcher/issues/755
* https://github.com/ggreer/the_silver_searcher/pull/834 (the failing test from that PR should hopefully be fixed with this PR)
2017-01-16 17:59:35 +01:00
Allen Wild
18df57f04c Fix incorrect path_start which can lead to heap buffer overflow
Fixes #1039
path_start is supposed to be the part of path which isn't in base_path,
but an incorrect assumption about a trailing '/' in base_path combined
with an off-by-one issue led to a buffer overflow of path.

This patch fixes the above problems by adding a trailing '/' to
base_path when necessary during parse_options() as well as strengthening
the logic for figuring out path_start.
Since path and base_path don't change in the middle of search_dir,
I further optimized by calculating path_start in search_dir once rather
than for every file in the directory in filename_filter.
2016-12-21 23:43:43 -05:00
Allen Wild
815d6975ab configure: don't add -O2 if -O is in CFLAGS 2016-12-21 20:16:02 -05:00
Geoff Greer
5444df9612 Sort results in empty match test so that it passes on weird filesystems like XFS.
Fixes #1029.
2016-12-18 23:54:40 -08:00
Geoff Greer
0ff06435a4 Merge pull request #1026 from andreasstieger/openSUSE
Add install and source instructions for openSUSE and SLES
2016-12-17 23:55:50 -08:00
Geoff Greer
850e2b3887 Merge pull request #1002 from spencercrissman/master
Add support for log file type.
2016-12-17 23:55:17 -08:00
Geoff Greer
96fc48595c Merge pull request #1032 from aswild/pull/autogen-sh
Create autogen.sh script
2016-12-11 21:10:00 -08:00
Allen Wild
64c58a33a4 Create autogen.sh script
Many autotools-based projects have an autogen.sh script which handles
creating the configure script properly. The existing build.sh does this
but doesn't offer a way to just run autotools without configure/make.

Here I split the first part of build.sh into autogen.sh and then call
autogen.sh from build.sh. Using "set -e" cleans up the script a bit to
avoid a long chain of "&&"s
2016-12-11 17:42:47 -05:00
Geoff Greer
682ab865e1 Tag new release: v1.0.2. 2016-12-03 14:42:05 -08:00
Geoff Greer
4eb06b24db Update docs to explain mmap defaults. Regenerate manpage. 2016-12-03 13:40:44 -08:00
Geoff Greer
6eeb19512e mmap() is slower than read() on macos. Default to using read() on mac. 2016-12-03 13:39:27 -08:00
Geoff Greer
d3976d562e s/OS X/MacOS/g 2016-12-03 13:38:45 -08:00
Andreas Stieger
52b90fa43e Add install and source instructions for openSUSE and SUSE Linux Enterprise 2016-11-28 23:24:04 +01:00
Geoff Greer
2fb5554124 Fix language now that 1.0 is out. 2016-11-27 14:19:42 -08:00
Geoff Greer
87b2086ef3 Tag new release v1.0.1. Remove old code and cleanup. 2016-11-27 14:04:23 -08:00
Geoff Greer
750108a79e Regenerate manpage 2016-11-27 14:03:13 -08:00
Geoff Greer
844c200157 Add author name (me) and link to authoritative website for ag releases. I should have done this a long time ago. 2016-11-27 14:02:53 -08:00
Geoff Greer
420c32710f Hard-code ~/.agignore instead of referencing the first item in ignore_pattern_files[] 2016-11-27 13:46:56 -08:00
Geoff Greer
3371e1c035 Fix wasteful re-loading of --path-to-ignore files in search_dir(). Also, this now allows for multiple --path-to-ignore options to be passed. 2016-11-27 13:46:10 -08:00
Geoff Greer
3f9b85e908 Merge pull request #1024 from ggreer/remove_old_svn_ignore
Remove svn ignore code.
2016-11-27 13:31:08 -08:00
Geoff Greer
3e7ffac51d svn ignore code only parsed old format (svn v1.6 and earlier). Nobody uses ancient subversion anymore, so remove this code. 2016-11-27 13:28:43 -08:00
Geoff Greer
a32df33964 Only flush output if stdout is not a tty. 2016-11-27 12:11:50 -08:00
Geoff Greer
84fccb7385 It's happening: Tag ag release v1.0. Thank you so much to everyone who has contributed over the years! 2016-11-27 12:04:56 -08:00
Geoff Greer
f21ce3a06e Styling cleanup. opts.search_stream doesn't matter in conditional, as we break the loop a few lines above if search_stream is true. 2016-11-27 12:02:39 -08:00
Geoff Greer
1988fdc0a4 Reset last_printed_match when searching stream. Fixes an error where matches after the first line weren't highlighted when searching a stream. 2016-11-27 12:01:59 -08:00
Geoff Greer
1f990aa634 Merge branch 'erantapaa-master' 2016-11-27 11:25:48 -08:00
Geoff Greer
6146669703 Merge branch 'master' of https://github.com/erantapaa/the_silver_searcher into erantapaa-master 2016-11-27 11:25:28 -08:00
Geoff Greer
deb3e96f17 Merge pull request #939 from bogdanvlviv/patch-1
Deleted links to ag.vim [ci skip]
2016-11-27 10:59:29 -08:00
Geoff Greer
fc78c0d5f7 Merge pull request #824 from y-yamagata/master
Fix literal word-regexp.
2016-11-26 19:58:44 -08:00
Geoff Greer
b5e750a42c Merge pull request #900 from ptroja/master
ignore local-domain sockets just like named pipes
2016-11-26 19:35:00 -08:00
Geoff Greer
3c2087609b Merge pull request #907 from ilmari/fix-l-with-one-filename
Fix --files-with(out)-matches with single filename
2016-11-26 19:33:07 -08:00
Geoff Greer
1b0efde468 Merge pull request #947 from jschpp/fix946
Added tpp as c++ extension
2016-11-26 19:20:16 -08:00
Geoff Greer
cfcc87fc28 Remove extraneous typedefs. 2016-11-26 01:17:41 -08:00
Geoff Greer
71aa080c6c Fix spelling error in manpage found by @sgtbigman. 2016-11-25 18:30:32 -08:00
Geoff Greer
a32165f016 Merge pull request #1005 from Phyllostachys/master
Added GLSL file types
2016-11-25 17:52:35 -08:00
Geoff Greer
f8919a3f37 Merge pull request #897 from rf232/patch-1
Add dpr to delphi source files
2016-11-25 15:38:24 -08:00
Geoff Greer
91456b3208 Merge branch 'acconsta-hash' 2016-11-25 15:34:36 -08:00
Geoff Greer
1ad491b5df Fix formatting. 2016-11-25 15:31:36 -08:00
Geoff Greer
1a02a8d608 Merge branch 'hash' of https://github.com/acconsta/the_silver_searcher into acconsta-hash 2016-11-25 15:24:18 -08:00
Geoff Greer
6c1041fc7e clang-format reformat. Fix build. 2016-11-25 14:35:59 -08:00
Geoff Greer
05be1eddca Merge pull request #895 from cemeyer/347-context
Fix #347 - Keep enough context around to print in stream mode
2016-11-25 14:08:04 -08:00
Geoff Greer
93e92028a5 Merge pull request #933 from mattscamp/tld_support
Add tld file type support
2016-11-25 14:05:53 -08:00
Geoff Greer
da2c746f73 Merge pull request #1008 from yellow-beard/add-proto-file-type
add protobuf file type
2016-11-25 13:54:56 -08:00
Geoff Greer
f46e4e20a1 Merge pull request #957 from HaleTom/fix-no-query-with-options-lu
Print empty files with empty query and -ul
2016-11-25 13:53:34 -08:00
Lee Cunliffe
434a1e93c5 add protobuf file type 2016-11-11 11:14:51 -08:00
Jacob Shaffer
d86492b4e8 Added GLSL file types 2016-11-06 09:08:13 -05:00
Spencer Crissman
2f9a6ea989 Adjust whitespace between file types to match others. 2016-10-30 10:20:48 -04:00
Spencer Crissman
f81a4cd4dd Add support for log file type. 2016-10-28 23:27:17 -04:00
Spencer Crissman
67ae130a1f Added support for log file type. 2016-10-28 23:23:14 -04:00
Stephen Dolan
41757634ad Make '-A n -B n' act like '-C n' by printing blank lines. 2016-10-18 11:45:12 +01:00
Geoff Greer
b4215ac105 Remove Ackmate from related tools. Add ripgrep. 2016-10-09 23:42:20 -07:00
Geoff Greer
ee8ef44654 Merge pull request #985 from okdana/982/double-dash
Document use of '--' to end option parsing (addresses #982 &al.)
2016-10-08 16:00:38 -07:00
dana
3d35cabe44 Re-generate man page 2016-10-08 02:42:59 -05:00
dana
ccf877407a Document example of using '--' to 'escape' patterns with a leading hyphen 2016-10-08 02:35:26 -05:00
Geoff Greer
068e6f6fca Merge pull request #984 from okdana/980/no-option
Support both --no-<option> and --no<option> (addresses #980)
2016-10-08 00:06:40 -07:00
dana
579fc08cbe Address feedback re: redundant conditions 2016-10-08 00:25:24 -05:00
dana
0d21a7ce2c Add cram test to ensure support for both --no-<option> and --no<option> forms 2016-10-07 16:36:48 -05:00
dana
ddd9d17b42 Support both --no-<option> and --no<option> forms 2016-10-07 16:36:22 -05:00
Geoff Greer
6fdab47561 Regenerate manpage. 2016-10-06 22:59:23 -07:00
Geoff Greer
052cb33cdc Better usage synopsis. Looks good in markdown-land and is generated correctly. Thanks to @jbodah for bringing this to my attention. 2016-10-06 22:59:08 -07:00
Geoff Greer
e13864f30f Merge pull request #971 from okapia/zshcomp
add zsh completion function
2016-09-28 00:12:44 -07:00
Oliver Kiddle
3ac3a6affb add zsh completion function 2016-09-27 22:35:39 +02:00
Geoff Greer
944e0111e2 Tag new release: v0.33.0. 2016-09-24 17:21:49 -07:00
Geoff Greer
d2e56fba90 Fix formatting error and regenerate manpage. 2016-09-24 17:18:33 -07:00
Geoff Greer
d88eee3499 Regenerate manpage. 2016-09-24 17:16:41 -07:00
Geoff Greer
36b6223707 Update man page with new options. Clean up descriptions of other options. 2016-09-24 17:16:25 -07:00
Geoff Greer
4977de87fc Remove a few unnoticed bits of agignore stuff. Add a TODO to fix what is almost certainly a performance issue. 2016-09-24 16:59:46 -07:00
Geoff Greer
69b2ee0470 Extraneous \n 2016-09-24 12:38:52 -07:00
Geoff Greer
4f6e5dca4c Default to a maximum of 8 worker threads. Unless you have really weird hardware or are doing a really weird search, there's just not enough memory bandwidth to feed more than 8 cores. 2016-09-24 12:38:38 -07:00
Geoff Greer
34c98cb614 Clean up formatting. Only do filename_len comparisons if platform lacks dirent->d_namlen. 2016-09-24 12:36:32 -07:00
Geoff Greer
625ef06203 Merge pull request #949 from bennet321/master
Fixed filename_filter function so that strlen is not called everytime
2016-09-24 12:00:31 -07:00
Geoff Greer
b995d3b826 Merge pull request #973 from erogers6264/master
Correct spelling error in readme
2016-09-23 18:23:56 -07:00
Geoff Greer
c41ce6c633 Merge pull request #974 from ggreer/ignore
Prefer .ignore to .agignore.
2016-09-23 18:23:34 -07:00
Geoff Greer
dd8e8396ef Update docs and help to refer to .ignore instead of .agignore. agignore will still work for the forseeable future, but .ignore is preferred. 2016-09-23 18:17:26 -07:00
Geoff Greer
283b689c53 Change a few test cases to use .ignore instead of .gitignore, just to make sure the functionality works. 2016-09-23 18:12:09 -07:00
Geoff Greer
18caee48a7 Add .ignore to ignore_pattern_files[]. Mention agignore being deprecated. Add test case to ensure ag -U works. 2016-09-23 18:11:27 -07:00
Geoff Greer
b733aaa5a4 Add a philosophical note. 2016-09-23 17:30:42 -07:00
Geoff Greer
bd65e2691d Add (currently undocumented) --nommap option to ag. This is a naive implementation, but it's useful for testing or for systems on which mmap is really slow. 2016-09-23 11:41:32 -07:00
Ethan Rogers
0b691838b5 Correct spelling error in readme 2016-09-22 13:02:38 -07:00
Geoff Greer
cda635000a Merge pull request #960 from ggreer/ungarbled_logging
Use the print mutex when logging.
2016-09-02 12:15:09 -07:00
Geoff Greer
d0071bb31d Use the print mutex when logging. Fixes occasional glitches in log messages. 2016-09-02 11:42:06 -07:00
Tom Hale
022828a2ed Print empty files with empty query and -ul
Fix issue #956:

`ag -ul` with an empty query should list all filenames, as the manual
says:

  -u --unrestricted
	  Search *all* files. ...

  -l --files-with-matches
	  ...An empty query will print all files that would be searched.
2016-08-25 14:10:17 +07:00
Luke Bennett
7f0a8c7f96 fixed filename_filter function so the strlen is not called everytime 2016-08-02 16:56:38 +10:00
Luke Bennett
d6c0910fda modified file_filter function so that strlen() is not called everytime filename_filter is called. 2016-08-02 03:23:59 +10:00
Jjschpp
49279e4a68 Added tpp as c++ extension 2016-07-28 09:32:49 +00:00
Bogdan
787d9e3261 Deleted links to ag.vim [ci skip] 2016-07-14 18:27:30 +03:00
Matt Camp
eff132af7e Add tld test 2016-07-05 14:13:20 +07:00
Matt Camp
770967a5f2 Add tld support 2016-07-05 14:11:44 +07:00
Matt Camp
c6301b0fcf Move comment 2016-07-05 12:45:48 +07:00
Matt Camp
5af055d180 Accept an empty query when no query is needed 2016-07-05 12:44:08 +07:00
Geoff Greer
7d52078cb6 Merge pull request #929 from davidcpell/update-rhel-install-steps
modify README with up-to-date installation instructions for redhat systems
2016-07-04 16:04:33 -07:00
David Pell
0c65cbc68d modify README with up-to-date installation instructions for RHEL 2016-07-03 22:16:19 -04:00
Geoff Greer
31e1f8cb85 Merge pull request #924 from ryangreenberg/rake_search_rakefile
Search files named Rakefile with --rake flag
2016-06-25 23:08:12 -07:00
Ryan Greenberg
bdb85389bc Search files named Rakefile with --rake flag 2016-06-24 08:31:33 -07:00
Geoff Greer
a790a06583 Update date in NOTICE 2016-06-23 01:23:10 -07:00
Geoff Greer
c2a8761afd Travis CI doesn't seem to obey this directive, so remove it. Looks like we're stuck on 12.04 :( 2016-06-23 01:21:06 -07:00
Geoff Greer
9ada47b125 Merge pull request #922 from ggreer/travis_ci
Get Travis-CI tests working again
2016-06-23 01:17:04 -07:00
Geoff Greer
23b9d8ecfd Clean up steps to download and extract llvm+clang. 2016-06-23 01:13:39 -07:00
Geoff Greer
52b8d5b361 format.sh reformat using clang-format 3.8 2016-06-23 00:41:04 -07:00
Geoff Greer
239234d2a7 Force Travis-CI to use the downloaded version of clang-format, not the one that ships with the OS. 2016-06-23 00:40:30 -07:00
Geoff Greer
8e40f20b1f Use llvm 3.8 2016-06-23 00:34:28 -07:00
Geoff Greer
0c58fb4f66 When detecting clang-format, try v3.8. 2016-06-23 00:15:57 -07:00
Geoff Greer
d4f736d8b0 Use LLVM 3.6 2016-06-23 00:15:32 -07:00
Geoff Greer
51121a8f5e Add --noaffinity to tests so they don't fail in Travic CI containers. 2016-06-23 00:10:46 -07:00
Geoff Greer
18f9833d26 Get Travis-CI working again. LLVM took down their apt repo. :( 2016-06-22 23:43:22 -07:00
Geoff Greer
1a39e7d2fd Merge pull request #913 from mizuno-as/remove_shebang
remove shebang from bash completion script
2016-06-22 12:48:04 -07:00
Geoff Greer
17a683d320 Merge pull request #781 from danielshahaf/word-regexp-alternates
Fix --word-regexp not applying to alternates
2016-06-06 12:01:03 -07:00
Erik Rantapaa
5ce4215761 Add fflush call to print_file_matches 2016-06-04 09:51:16 -05:00
Hajime MIZUNO
ea4f372dcf remove shebang from bash completion script 2016-05-29 13:19:12 +09:00
Dagfinn Ilmari Mannsåker
210a8763e0 Fix --files-with(out)-matches with single filename
print_path() by default doesn't print file names if there was only one
specified on the command line, but when listing files with or without
matches we always want to print it.

Also add a test for --files-with(out)-matches, of which there were none.

Fixes #769
2016-05-19 14:00:14 +01:00
Geoff Greer
630125a924 Fix another Coverity error: key_len + 1 could overflow. So just bail if someone has an absurdly long key in their svn dir-prop-base. 2016-05-16 15:15:27 -07:00
Geoff Greer
81e22e2756 Check return value of fseek() and bail if it fails. 2016-05-16 14:48:49 -07:00
Geoff Greer
9b623bc968 Fix minor issue found by Coverity: ag -v . leaked 16 bytes of memory. Thanks goes to Conrad Meyer from FreeBSD for the Coverity invite. 2016-05-16 14:26:48 -07:00
Geoff Greer
878751003c Tag new release: v0.32.0. 2016-05-15 22:15:26 -07:00
Piotr Trojanek
582ab32c51 ignore local-domain sockets just like named pipes 2016-05-13 10:02:52 +01:00
Rob Franken
37f3d9960a Also update the test output for the new filetype 2016-05-09 11:00:50 +02:00
Rob Franken
04a6bb1d4f Add dpr to delphi source files
dpr files are the main source files for delphi projects (like dpk are the main files for delphi package projects.) Add them to the list of the delphi language extensions.
2016-05-09 10:52:20 +02:00
Geoff Greer
0c55a13514 Merge pull request #894 from cemeyer/fbsd-filetype.t
Fix #893 - Use [ \t] instead of \s (BSD grep)
2016-05-08 23:31:47 -07:00
Conrad Meyer
08b7b0aa3e Fix #347 - Keep enough context around to print in stream mode 2016-05-07 19:24:57 -07:00
Conrad Meyer
e2d0f49dc2 #347 - Move print context to a per-file/stream, per-thread structure 2016-05-07 19:24:03 -07:00
Conrad Meyer
b8f9c68c22 Fix #893 - Use [ \t] instead of \s (BSD grep) 2016-05-07 15:12:56 -07:00
Geoff Greer
9fc8924c0b Merge pull request #819 from martinlindhe/master
--js: recognize .vue components from vue.js
2016-05-01 23:46:37 -07:00
Geoff Greer
00ffeb36a2 Merge pull request #878 from eksperimental/elixir_extensions
File Types: Add eex under Elixir
2016-04-28 20:15:56 -07:00
Geoff Greer
2260fe0103 Merge pull request #886 from lgarron/patch-1
Add `.fish` to shell extensions.
2016-04-28 20:15:32 -07:00
Geoff Greer
c7869c21e5 Merge pull request #887 from sach1t/master
Fix potential integer overflow in binary search
2016-04-28 20:14:48 -07:00
Lucas Garron
80a7d1a0da Add .fish to shell extensions.
`.fish` is the file extension for the Fish shell: https://fishshell.com/
2016-04-26 11:20:34 -07:00
sach1t
7d3e02f452 Fix potential overflow 2016-04-26 01:47:03 -04:00
Geoff Greer
de7ee68c66 Merge pull request #867 from me-and/bad-echo
Use printf in place of echo in tests
2016-04-13 21:43:24 -07:00
Geoff Greer
c6a84e2b38 Merge pull request #877 from me-and/cygwin-install
Add install instructions for Cygwin
2016-04-12 12:57:06 -07:00
eksperimental
c5890a42aa File Types: Add eex under Elixir 2016-04-07 23:46:29 +07:00
Geoff Greer
ed3026cb43 Merge pull request #865 from me-and/not-one-device
Skip one_device.t if /dev/shm isn't a mountpoint
2016-04-04 23:50:17 -07:00
Geoff Greer
f05870c95d Merge pull request #846 from jschpp/fix_845
Fix #845
2016-03-27 23:54:53 -07:00
Adam Dinwoodie
be8dd4ee4a Make one_device.t strictly POSIX compliant 2016-03-17 13:41:05 +00:00
Adam Dinwoodie
2bf6ee0ab7 Skip one_device.t if /dev/shm isn't a mountpoint
If /dev/shm isn't a separate device to its parent directory, skip using
it to test limiting a search to a single device.  If /dev/shm isn't a
mountpoint there's no obvious alternative for checking Ag doesn't
descend into one, so just skip the test entirely.

Fixes #864.
2016-03-15 13:19:55 +00:00
Adam Dinwoodie
26088a18ab Add install instructions for Cygwin 2016-03-15 11:56:20 +00:00
Adam Dinwoodie
1f2d79ebee Use printf in place of echo in tests
The behaviour of echo is inconsistent between implementations, in
particular regarding whether it expands escapes like `\n`.  Use printf
consistently in test scripts to avoid test failures from inconsistent
echo behaviour.

Fixes #866.
2016-03-15 03:42:19 +00:00
Geoff Greer
8cc74e04ea Merge branch 'master' of github.com:ggreer/the_silver_searcher 2016-03-13 23:40:21 -07:00
Ben Albrecht
835fb80a98 Add Chapel Language 2016-03-13 23:39:33 -07:00
Geoff Greer
9815ae5066 Merge pull request #860 from erg/factor-support
Add support for .factor files.
2016-03-12 23:23:50 -08:00
Geoff Greer
7aa189456f Move variable declarations closer to their first use in filename_filter(). Compilers should be more than smart enough to optimize this, but the code is a little easier to follow as well. Fixes #856. 2016-03-11 13:57:54 -08:00
Geoff Greer
9adb7d1b9c Fall back to default core exclude location (~/.config/git/ignore) if git config's core.excludesfile isn't set. Fixes #796. 2016-03-11 13:45:26 -08:00
Doug Coleman
7466509320 Add support for .factor files.
https://factorcode.org
2016-03-05 13:10:17 -08:00
Geoff Greer
edae22e91d Merge pull request #809 from ban-dana/word-regexp-filenames
Fix -g ignores -w #803
2016-02-28 22:18:25 -08:00
Geoff Greer
28065df37d Merge pull request #841 from sebgod/remove-grep-perl-dep
Makefile.w32: Remove Perl-regex grep dependency
2016-02-28 22:16:19 -08:00
Geoff Greer
d29d5b5a98 Merge pull request #842 from n0la/master
On OpenBSD use pledge to drop privileges.
2016-02-25 23:38:03 -08:00
Geoff Greer
8be18f2ded Merge pull request #847 from jschpp/pr/777
Fix issue #771 Add a cython filetype
2016-02-25 23:37:19 -08:00
Geoff Greer
f266fa92aa Merge pull request #849 from FredDeschenes/add-kotlin-language
Add Kotlin language
2016-02-25 23:37:01 -08:00
Fred Deschenes
f557f84ddc Add Kotlin language 2016-02-22 10:08:54 -05:00
jschpp
2c38236a27 fixed strange whitespacing and ran ./format reformat 2016-02-20 16:49:28 +01:00
jschpp
cea4894deb Fix #845 2016-02-20 16:02:43 +01:00
Geoff Greer
f4bb889c98 Remove travis email notifications. They're useless. 2016-02-17 01:04:15 -08:00
Sebastian Godelet
120b63064c Makefile.w32: Remove Perl-regex grep dependency
msys grep does not support the -Po option, so use sed instead
README.md: better describe that building requires an msys/MinGW
environment
2016-02-17 10:43:35 +08:00
Geoff Greer
8c3d307a59 Fix shellcheck warnings/errors in shell scripts. 2016-02-15 18:35:05 -08:00
Geoff Greer
c0f73f8b71 Merge pull request #839 from jschpp/pr/786
Added filetypes to test case
2016-02-14 17:57:32 -08:00
jschpp
6618482d7e Added filetypes to test case 2016-02-14 12:40:53 +01:00
Geoff Greer
b53b72fe9d Merge pull request #828 from sebgod/add-wix-ft
Support WiX files with --wix
2016-02-11 21:07:43 -08:00
Geoff Greer
e775a27f40 Merge pull request #833 from srstevenson/julia
Add support for Julia with `--julia`
2016-02-11 21:07:24 -08:00
Florian Stinglmayr
af5a9550bc On OpenBSD use pledge to drop privileges.
With suggestions by Doug Hogan and the initial work by Michael
McConville.
2016-02-04 17:16:29 +01:00
Brian Peiris
f3c1d86e20 Add test for ignoring files with multiple extensions 2016-02-01 08:39:05 -05:00
Scott Stevenson
59ba02d128 Add support for Julia with --julia 2016-02-01 08:54:24 +00:00
Sebastian Godelet
76c9c08b01 Support WiX files with --wix
Add the XML source [file extensions](http://wixtoolset.org/documentation/manual/v3/overview/files.html)
that are commonly used by WiX installer projects.
2016-01-26 09:08:50 +08:00
Geoff Greer
36754027ac Merge pull request #826 from k-takata/fix-mingw-w64-compile-error
Fix compilation error when using MinGW-w64
2016-01-20 20:46:47 -08:00
K.Takata
a22c9f5a53 Fix compilation error when using MinGW-w64 2016-01-20 00:17:07 +09:00
Geoff Greer
aa5cfb5b91 Merge pull request #788 from ban-dana/win32_symlinks
Fix symlink support on Win32
2016-01-16 23:29:23 -08:00
yamagata
aedd3ef37a Fix literal word-regexp. 2016-01-14 01:17:52 +09:00
Geoff Greer
6d35745e6d Merge pull request #822 from y-yamagata/master
Fix filetype option
2016-01-12 23:40:12 -08:00
yamagata
0622224f26 Fix filetype option 2016-01-12 23:07:12 +09:00
Martin Lindhe
601a2b0dba --js: recognize .vue components from vue.js 2015-12-25 23:53:27 +01:00
Geoff Greer
0e577cc7a1 Merge pull request #814 from bpierre/add-qml
Support QML files with --qml
2015-12-22 23:12:30 -08:00
Pierre Bertet
01ad534127 Support QML files with --qml 2015-12-18 15:41:34 +00:00
Geoff Greer
ddba55542d Fix potential out-of-bounds error if buffer only contains '%PDF'. Thanks to @btrask for finding this. 2015-12-17 17:16:31 -08:00
Michael Butvinnik
0b0b02bfef Fix clang-format (line-endings) 2015-12-16 11:54:18 +02:00
Michael Butvinnik
887bcf699a Fix clag-format 2015-12-16 11:45:12 +02:00
Michael Butvinnik
0925213096 Fix -g ignores -w #803 2015-12-16 11:31:29 +02:00
Geoff Greer
e84db55a36 Merge pull request #798 from chuckd/add-typescript
Support TypeScript with --ts (.ts, .tsx)
2015-12-12 18:15:56 -08:00
Charles Dale
9c9a7c1181 Support TypeScript with --ts (.ts, .tsx) 2015-11-27 17:11:34 +11:00
Geoff Greer
9e11263acf Merge branch 'shabbyrobe-truncate-long-lines' 2015-11-25 09:13:37 -08:00
Geoff Greer
4218d63f8d Merge master. Fix conflicts. 2015-11-25 09:13:26 -08:00
Geoff Greer
ed4182b675 Merge pull request #758 from Arkanosis/bug#744
Don't alter casing if the file pattern is lowercase
2015-11-15 23:47:50 -08:00
Jérémie Roquet
fab27a44f5 Don't alter casing if the file pattern is lowercase
Closes ggreer/the_silver_searcher#744.
2015-11-15 20:24:15 +01:00
Geoff Greer
9999eb90b3 Merge branch 'master' of github.com:ggreer/the_silver_searcher 2015-11-14 23:45:54 -08:00
Geoff Greer
70206f5c9f Slightly better case sensitivity tests. Add file regex case sensitivity test. 2015-11-14 23:45:23 -08:00
Geoff Greer
d1061e5e46 Merge pull request #775 from joelpurra/velocity-extensions
Add .vtl and .vsl to Velocity file types
2015-11-14 23:36:08 -08:00
Geoff Greer
89a7d46ca0 Merge pull request #782 from jwworth/add-rdoc-support
Add support for RDoc files
2015-11-11 18:23:51 -08:00
Michael Butvinnik
fd64e34c00 Fix mb/wc issue in win32 symlinks support 2015-11-06 12:59:23 +02:00
Michael Butvinnik
1f8dceac58 Fix code formatting 2015-11-06 12:39:38 +02:00
Michael Butvinnik
f942eac4a0 Fix symlink support on Win32 2015-11-06 12:31:54 +02:00
Rodrigo Ferreira de Souza
5fb2374984 Add zcml and xml extensions for plone 2015-11-03 13:46:53 -02:00
Jake Worth
c3b6c6a2c1 Add support for RDoc files 2015-10-30 19:26:57 -05:00
Daniel Shahaf
1903dca026 Fix --word-regexp not applying to alternates 2015-10-30 10:36:26 +00:00
Jamie Smith
b47f7772df Fix issue #771 Add a cython filetype 2015-10-28 11:21:06 +00:00
Joel Purra
dfd03c62ea Add .vtl and .vsl to Velocity file types
Apart from `.vm`, which already exists, [Velocity](https://velocity.apache.org/) uses `.vtl` and `.vsl` file types/extensions.

From velocity-user mailing list archives, [RE: What does vm stand for?](https://mail-archives.apache.org/mod_mbox/velocity-user/200904.mbox/<9127AA4A72BF0D40AFECC2BECB6FEB940184D36D@PSPCOREXCH02.SYNTELORG.COM>):

> Velocity used a file extension of "*.vm", standing for "Velocity Macro". Other extensions
are also being used for Velocity templates: ".vtl" and ".vsl" (Velocity Stylesheet).

These are now in ag.

> The declarative Velocity engine DVSL additionally uses either ".dvsl" or ".vslt" extensions
(analogous to ".xslt").

Haven't added these two at this time. Can be done later, if someone feels the need.
2015-10-26 11:26:55 +01:00
Geoff Greer
7fb7a9a9e3 Merge pull request #772 from moreati/rst
Adds restructuredText (.rst) language type
2015-10-24 23:45:31 -07:00
Alex Willmer
bacdf068a3 Adds restructuredText (.rst) language type
restructuredText is a lightweight markup format used widely for e.g.

 - Python documentation
 - Static site generators
 - Academic papers
2015-10-21 12:00:05 +01:00
Geoff Greer
0a224428ef Merge pull request #770 from JuhaniImberg/master
Fix depending on PATH_MAX being defined
2015-10-14 15:00:58 -07:00
Juhani Imberg
12cab3e938 Fix depending on PATH_MAX being defined
As POSIX limits.h defines, PATH_MAX may be omitted from limits.h if the limit
depends on the file type the limit is applied to.

Passing NULL to realpath allocates the appropriate amount of memory itself.

This fixes compilation on GNU/Hurd where PATH_MAX is not defined.

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html
http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html
2015-10-14 22:54:01 +03:00
Geoff Greer
b0eb6d7336 Merge pull request #764 from bheesham/fedora-fix
Updated Fedora install command.
2015-10-10 23:56:13 -07:00
Bheesham Persaud
abc554b991 Updated Fedora install command. 2015-10-05 21:19:45 -04:00
Geoff Greer
4914996e46 Merge branch 'natemara-patch-1' 2015-10-04 23:57:21 -07:00
Nate Mara
411c7900ee Add TOML filetype support
TOML is a markup language used for the configuration of Rust's main build tool, [Cargo](https://crates.io). The spec is online [here](https://github.com/toml-lang/toml). This would allow Ag to search through TOML files via its regex file rules.
2015-10-04 23:56:19 -07:00
Geoff Greer
a99b010f41 Merge pull request #726 from siadat/add-crystal-filetype
Add support for crystal file types
2015-09-21 23:55:25 -07:00
Alex Constantino
e96eb424b0 Add faster substring search
This commit implements the fast substring search algorithm described by
Leonid Volnitsky (see http://volnitsky.com/project/str_search/).

The idea is we insert each consecutive pair of characters in the needle
into a hash table. We then step through pairs of characters in the
haystack. If the pair is in the hash table (and also probably the needle)
we do a character-by-character comparison.

Performance is 30-40% faster than Boyer-Moore on 2GB of text:

> ./ag_hash --stats blahblahbla ~/Downloads/wiki/
0 matches
0 files contained matches
4 files searched
1956034523 bytes searched
0.800452 seconds

> ./ag_master --stats blahblahbla ~/Downloads/wiki/
0 matches
0 files contained matches
4 files searched
1956034523 bytes searched
1.310972 seconds
2015-09-19 18:12:19 -07:00
Geoff Greer
9cd0d78652 Merge pull request #733 from SnoringFrog/master
Add --stats-only option
2015-09-13 23:45:58 -07:00
Geoff Greer
39d9ee2930 Merge pull request #741 from acconsta/segfault
Fix invalid memory access
2015-09-13 23:22:03 -07:00
acconsta
e78e3610d8 Fix invalid memory access
I've noticed occasional segfaults from accessing beyond the end of
the file buffer. This adds the correct bound to index variable i.
2015-09-13 20:54:03 -07:00
Geoff Greer
ba47f77d25 Merge branch 'jzipfler-master' 2015-09-12 23:35:53 -07:00
Geoff Greer
c3688910c2 Merge master. Fix conflicts. 2015-09-12 23:35:37 -07:00
Geoff Greer
347d33690e Default to smart-case when using file match (-g and -G). Fixes #724. 2015-09-11 23:41:38 -07:00
Geoff Greer
789f9bba7e Add a place for failing test cases. 2015-09-11 23:08:08 -07:00
Geoff Greer
f0e5ba5646 Merge pull request #728 from cardoe/master
add support for BitBake source files
2015-09-11 12:54:10 -07:00
Doug Goldstein
ebd91970c8 add support for BitBake source files
Add a --bitbake option which will search through the following files:

- .bb
- .bbappend
- .bbclass
- .inc

Signed-off-by: Doug Goldstein <cardoe@cardoe.com>
2015-09-11 13:56:50 -05:00
Geoff Greer
95b7d35469 Add test to check for a match at the end of a file. 2015-09-07 22:20:12 -07:00
Geoff Greer
d7193e13a7 Bump version to 0.31.0 2015-09-06 22:30:13 -07:00
Geoff Greer
07898b601c Fix gcc warning about shadowed var named "pipe" 2015-09-06 22:25:31 -07:00
Geoff Greer
b3ad70628e Const-ify some things that can be const-ified. 2015-09-06 22:24:29 -07:00
Geoff Greer
c8f45810d4 Fix compiler warnings about const. 2015-09-06 22:23:54 -07:00
Geoff Greer
bb203f50f5 Merge pull request #740 from ggreer/pcre_multiline
Add support for --nomultiline
2015-09-05 23:20:07 -07:00
Ethan Piekarski
1dfa7f8201 Reformatting 2015-08-19 14:12:50 -04:00
Ethan Piekarski
fbea8e3640 Added --stats-only documentation 2015-08-19 13:28:34 -04:00
Ethan Piekarski
ea3802d598 Add --stats-only option 2015-08-19 13:09:17 -04:00
jan
de1a600e8c Added the output of the bro filetype. 2015-08-11 16:02:07 +02:00
jan
c561dada5c Add filetypes for bro. 2015-08-11 15:51:07 +02:00
Sina Siadat
9fbf371634 Add support for crystal file types 2015-08-06 03:14:38 +04:30
shabbyrobe
72b162aa6f Added missing --width longopt handling 2015-07-30 12:18:59 +10:00
shabbyrobe
c975ff8847 Added line width truncation option -W 2015-07-30 12:13:12 +10:00
82 changed files with 2524 additions and 649 deletions

1
.gitignore vendored
View file

@ -1,4 +1,5 @@
*.dSYM
*.gcda
*.o
*.plist
.deps

View file

@ -1,27 +1,50 @@
language: c
dist: xenial
sudo: false
branches:
only:
- master
notifications:
email:
- geoff@greer.fm
- ppc64le
arch:
- amd64
- ppc64le
compiler:
- clang
- gcc
before_install:
- sudo add-apt-repository -y 'ppa:ubuntu-toolchain-r/test'
- sudo add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.6 main'
- curl http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
- sudo apt-get update -q
- sudo apt-get install -q -y automake pkg-config libpcre3-dev zlib1g-dev liblzma-dev
- sudo apt-get install -y clang-format-3.6
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- automake
- liblzma-dev
- libpcre3-dev
- pkg-config
- zlib1g-dev
before_script:
- sudo pip install cram
env:
global:
- LLVM_VERSION=6.0.1
- LLVM_PATH=$HOME/clang+llvm
- CLANG_FORMAT=$LLVM_PATH/bin/clang-format
before_install:
- wget http://llvm.org/releases/$LLVM_VERSION/clang+llvm-$LLVM_VERSION-x86_64-linux-gnu-ubuntu-16.04.tar.xz -O $LLVM_PATH.tar.xz
- mkdir $LLVM_PATH
- tar xf $LLVM_PATH.tar.xz -C $LLVM_PATH --strip-components=1
- export PATH=$HOME/.local/bin:$PATH
install:
- pip install --user cram
script:
- ./build.sh && make test
notifications:
irc: 'chat.freenode.net#ag'
on_success: change
on_failure: always
use_notice: true

View file

@ -14,3 +14,13 @@ The test suite uses [Cram](https://bitheap.org/cram/). You'll need to build ag
first, and then you can run the suite from the root of the repository :
make test
### Adding filetypes
Ag can search files which belong to a certain class for example `ag --html test`
searches all files with the extension defined in [lang.c](src/lang.c).
If you want to add a new file 'class' to ag please modify [lang.c](src/lang.c) and [list_file_types.t](tests/list_file_types.t).
`lang.c` adds the functionality and `list_file_types.t` adds the test case.
Without adding a test case the test __will__ fail.

View file

@ -1,16 +1,21 @@
ACLOCAL_AMFLAGS = ${ACLOCAL_FLAGS}
bin_PROGRAMS = ag
ag_SOURCES = src/ignore.c src/ignore.h src/log.c src/log.h src/options.c src/options.h src/print.c src/print_w32.c src/print.h src/scandir.c src/scandir.h src/search.c src/search.h src/lang.c src/lang.h src/util.c src/util.h src/decompress.c src/decompress.h src/uthash.h src/main.c
ag_SOURCES = src/ignore.c src/ignore.h src/log.c src/log.h src/options.c src/options.h src/print.c src/print_w32.c src/print.h src/scandir.c src/scandir.h src/search.c src/search.h src/lang.c src/lang.h src/util.c src/util.h src/decompress.c src/decompress.h src/uthash.h src/main.c src/zfile.c
ag_LDADD = ${PCRE_LIBS} ${LZMA_LIBS} ${ZLIB_LIBS} $(PTHREAD_LIBS)
dist_man_MANS = doc/ag.1
bashcompdir = $(pkgdatadir)/completions
dist_bashcomp_DATA = ag.bashcomp.sh
zshcompdir = $(datadir)/zsh/site-functions
dist_zshcomp_DATA = _the_silver_searcher
EXTRA_DIST = Makefile.w32 LICENSE NOTICE the_silver_searcher.spec README.md
all:
@$(MAKE) ag -r
test: ag
cram -v tests/*.t
if HAS_CLANG_FORMAT
@ -22,4 +27,7 @@ endif
test_big: ag
cram -v tests/big/*.t
.PHONY : all test clean
test_fail: ag
cram -v tests/fail/*.t
.PHONY : all clean test test_big test_fail

View file

@ -1,7 +1,8 @@
GREP=grep
VERSION:=$(shell $(GREP) -Po "(?<=\[)([0-9.]+.[0-9]+.[0-9]+)(?=\])" configure.ac)
SED=sed
VERSION:=$(shell "$(SED)" -n "s/[^[]*\[\([0-9]\+\.[0-9]\+\.[0-9]\+\)\],/\1/p" configure.ac)
CC=gcc
RM=/bin/rm
SRCS = \
src/decompress.c \
@ -23,11 +24,12 @@ TARGET = ag.exe
all : $(TARGET)
$(TARGET) : $(OBJS)
# depend on configure.ac to account for version changes
$(TARGET) : $(OBJS) configure.ac
$(CC) -o $@ $(OBJS) $(LIBS)
.c.o :
$(CC) -c $(CFLAGS) -Isrc $< -o $@
clean :
rm -f src/*.o $(TARGET)
$(RM) -f src/*.o $(TARGET)

2
NOTICE
View file

@ -1,2 +1,2 @@
The Silver Searcher
Copyright 2011-2014 Geoff Greer
Copyright 2011-2016 Geoff Greer

View file

@ -6,16 +6,16 @@ A code searching tool similar to `ack`, with a focus on speed.
[![Floobits Status](https://floobits.com/ggreer/ag.svg)](https://floobits.com/ggreer/ag/redirect)
[![#ag on Freenode](http://img.shields.io/Freenode/%23ag.png)](https://webchat.freenode.net/?channels=ag)
[![#ag on Freenode](https://img.shields.io/badge/Freenode-%23ag-brightgreen.svg)](https://webchat.freenode.net/?channels=ag)
Do you know C? I invite you to pair with me to [help me get to Ag 1.0](http://geoff.greer.fm/2014/10/13/help-me-get-to-ag-10/).
Do you know C? Want to improve ag? [I invite you to pair with me](http://geoff.greer.fm/2014/10/13/help-me-get-to-ag-10/).
## What's so great about Ag?
* It is an order of magnitude faster than `ack`.
* It ignores file patterns from your `.gitignore` and `.hgignore`.
* If there are files in your source repo you don't want to search, just add their patterns to a `.agignore` file. (\*cough\* extern \*cough\*)
* If there are files in your source repo you don't want to search, just add their patterns to a `.ignore` file. (\*cough\* `*.min.js` \*cough\*)
* The command name is 33% shorter than `ack`, and all keys are on the home row!
Ag is quite stable now. Most changes are new features, minor bug fixes, or performance improvements. It's much faster than Ack in my benchmarks:
@ -24,7 +24,7 @@ Ag is quite stable now. Most changes are new features, minor bug fixes, or perfo
ag test_blah ~/code/ 4.67s user 4.58s system 286% cpu 3.227 total
Ack and Ag found the same results, but Ag was 34x faster (3.2 seconds vs 110 seconds). My `~/code` directory is about 8GB. Thanks to git/hg/svn-ignore, Ag only searched 700MB of that.
Ack and Ag found the same results, but Ag was 34x faster (3.2 seconds vs 110 seconds). My `~/code` directory is about 8GB. Thanks to git/hg/ignore, Ag only searched 700MB of that.
There are also [graphs of performance across releases](http://geoff.greer.fm/ag/speed/).
@ -42,7 +42,7 @@ I've written several blog posts showing how I've improved performance. These inc
## Installing
### OS X
### macOS
brew install the_silver_searcher
@ -56,16 +56,18 @@ or
* Ubuntu >= 13.10 (Saucy) or Debian >= 8 (Jessie)
apt-get install silversearcher-ag
* Fedora 19+
* Fedora 21 and lower
yum install the_silver_searcher
* Fedora 22+
dnf install the_silver_searcher
* RHEL7+
rpm -Uvh http://download.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-5.noarch.rpm
yum install the_silver_searcher
yum install epel-release.noarch the_silver_searcher
* Gentoo
emerge the_silver_searcher
emerge -a sys-apps/the_silver_searcher
* Arch
pacman -S the_silver_searcher
@ -74,6 +76,20 @@ or
sbopkg -i the_silver_searcher
* openSUSE
zypper install the_silver_searcher
* CentOS
yum install the_silver_searcher
* NixOS/Nix/Nixpkgs
nix-env -iA silver-searcher
* SUSE Linux Enterprise: Follow [these simple instructions](https://software.opensuse.org/download.html?project=utilities&package=the_silver_searcher).
### BSD
@ -84,14 +100,42 @@ or
pkg_add the_silver_searcher
### Windows
* Win32/64
Unofficial daily builds are [available](https://github.com/k-takata/the_silver_searcher-win32).
* winget
winget install "The Silver Searcher"
Notes:
- This installs a [release](https://github.com/JFLarvoire/the_silver_searcher/releases) of ag.exe optimized for Windows.
- winget is intended to become the default package manager client for Windows.
As of June 2020, it's still in beta, and can be installed using instructions [there](https://github.com/microsoft/winget-cli).
- The setup script in the Ag's winget package installs ag.exe in the first directory that matches one of these criteria:
1. Over a previous instance of ag.exe *from the same [origin](https://github.com/JFLarvoire/the_silver_searcher)* found in the PATH
2. In the directory defined in environment variable bindir_%PROCESSOR_ARCHITECTURE%
3. In the directory defined in environment variable bindir
4. In the directory defined in environment variable windir
* Chocolatey
choco install ag
* MSYS2
pacman -S mingw-w64-{i686,x86_64}-ag
* Cygwin
Run the relevant [`setup-*.exe`](https://cygwin.com/install.html), and select "the\_silver\_searcher" in the "Utils" category.
## Building from source
### Building master
1. Install dependencies (Automake, pkg-config, PCRE, LZMA):
* OS X:
* macOS:
brew install automake pkg-config pcre xz
or
@ -106,15 +150,19 @@ or
* CentOS:
yum -y groupinstall "Development Tools"
yum -y install pcre-devel xz-devel
yum -y install pcre-devel xz-devel zlib-devel
* openSUSE:
zypper source-install --build-deps-only the_silver_searcher
* Windows: It's complicated. See [this wiki page](https://github.com/ggreer/the_silver_searcher/wiki/Windows).
2. Run the build script (which just runs aclocal, automake, etc):
./build.sh
On Windows:
On Windows (inside an msys/MinGW shell):
mingw32-make -f Makefile.w32
make -f Makefile.w32
3. Make install:
sudo make install
@ -137,7 +185,7 @@ You may need to use `sudo` or run as root for the make install.
### Vim
You can use Ag with [ack.vim][] by adding the following line to your `.vimrc`:
You can use Ag with [ack.vim](https://github.com/mileszs/ack.vim) by adding the following line to your `.vimrc`:
let g:ackprg = 'ag --nogroup --nocolor --column'
@ -147,13 +195,9 @@ or:
Which has the same effect but will report every match on the line.
There's also a fork of ack.vim tailored for use with Ag: [ag.vim][]
[ack.vim]: https://github.com/mileszs/ack.vim
[ag.vim]: https://github.com/rking/ag.vim
### Emacs
You can use [ag.el][] as an Emacs fronted to Ag. See also: [helm-ag].
You can use [ag.el][] as an Emacs front-end to Ag. See also: [helm-ag].
[ag.el]: https://github.com/Wilfred/ag.el
[helm-ag]: https://github.com/syohex/emacs-helm-ag
@ -164,10 +208,10 @@ TextMate users can use Ag with [my fork](https://github.com/ggreer/AckMate) of t
## Other stuff you might like
* [Ack](https://github.com/petdance/ack2) - Better than grep. Without Ack, Ag would not exist.
* [AckMate](https://github.com/protocool/AckMate) - An ack-powered replacement for TextMate's slow built-in search.
* [Ack](https://github.com/petdance/ack3) - Better than grep. Without Ack, Ag would not exist.
* [ack.vim](https://github.com/mileszs/ack.vim)
* [ag.vim]( https://github.com/rking/ag.vim)
* [Exuberant Ctags](http://ctags.sourceforge.net/) - Faster than Ag, but it builds an index beforehand. Good for *really* big codebases.
* [Git-grep](http://git-scm.com/docs/git-grep) - As fast as Ag but only works on git repos.
* [fzf](https://github.com/junegunn/fzf) - A command-line fuzzy finder
* [ripgrep](https://github.com/BurntSushi/ripgrep)
* [Sack](https://github.com/sampson-chen/sack) - A utility that wraps Ack and Ag. It removes a lot of repetition from searching and opening matching files.

86
_the_silver_searcher Normal file
View file

@ -0,0 +1,86 @@
#compdef ag
# Completion function for zsh
local ret=1
local -a args expl
# Intentionally avoided many possible mutual exlusions because it is
# likely that earlier options come from an alias. In line with this
# the following conditionally adds options that assert defaults.
[[ -n $words[(r)(-[is]|--ignore-case|--case-sensitive)] ]] && args+=(
'(-S --smart-case -s -s --ignore-case --case-sensitive)'{-S,--smart-case}'[insensitive match unless pattern includes uppercase]'
)
[[ -n $words[(r)--nobreak] ]] && args+=(
"(--nobreak)--break[print newlines between matches in different files]"
)
[[ -n $words[(r)--nogroup] ]] && args+=(
"(--nogroup)--group[don't repeat filename for each match line]"
)
_tags normal-options file-types
while _tags; do
_requested normal-options && _arguments -S -s $args \
'--ackmate[print results in AckMate-parseable format]' \
'(--after -A)'{--after=-,-A+}'[specify lines of trailing context]::lines [2]' \
'(--before -B)'{--before=-,-B+}'[specify lines of leading context]::lines [2]' \
"--nobreak[don't print newlines between matches in different files]" \
'(--count -c)'{--count,-c}'[only print a count of matching lines]' \
'--color[enable color highlighting of output]' \
'(--color-line-number --color-match --color-path)--nocolor[disable color highlighting of output]' \
'--color-line-number=[specify color for line numbers]:color [1;33]' \
'--color-match=[specify color for result match numbers]:color [30;43]' \
'--color-path=[specify color for path names]:color [1;32]' \
'--column[print column numbers in results]' \
'(--context -C)'{--context=-,-C+}'[specify lines of context]::lines' \
'(--debug -D)'{--debug,-D}'[output debug information]' \
'--depth=[specify directory levels to descend when searching]:levels [25]' \
'(--noheading)--nofilename[suppress printing of filenames]' \
'(-f --follow)'{-f,--follow}'[follow symlinks]' \
'(-F --fixed-strings --literal -Q)'{--fixed-strings,-F,--literal,-Q}'[use literal strings]' \
'--nogroup[repeat filename for each match line]' \
'(1 -G --file-search-regex)-g+[print filenames matching a pattern]:regex' \
'(-G --file-search-regex)'{-G+,--file-search-regex=}'[limit search to filenames matching pattern]:regex' \
'(-H --heading --noheading)'{-H,--heading}'[print filename with each match]' \
'(-H --heading --noheading --nofilename)--noheading[suppress printing of filenames]' \
'--hidden[search hidden files (obeying .*ignore files)]' \
{--ignore=,--ignore-dir=}'[ignore files/directories matching pattern]:regex' \
'(-i --ignore-case)'{-i,--ignore-case}'[match case-insensitively]' \
'(-l --files-with-matches)'{-l,--files-with-matches}"[output matching files' names only]" \
'(-L --files-without-matches)'{-L,--files-without-matches}"[output non-matching files' names only]" \
'(--max-count -m)'{--max-count=,-m+}'[stop after specified no of matches in each file]:max number of matches' \
'--numbers[prefix output with line numbers, even for streams]' \
'--nonumbers[suppress printing of line numbers]' \
'(--only-matching -o)'{--only-matching,-o}'[show only matching part of line]' \
'(-p --path-to-ignore)'{-p+,--path-to-ignore=}'[use specified .ignore file]:file:_files' \
'--print-long-lines[print matches on very long lines]' \
"--passthrough[when searching a stream, print all lines even if they don't match]" \
'(-s --case-sensitive)'{-s,--case-sensitive}'[match case]' \
'--silent[suppress all log messages, including errors]' \
'(--stats-only)--stats[print stats (files scanned, time taken, etc.)]' \
'(--stats)--stats-only[print stats and nothing else]' \
'(-U --skip-vcs-ignores)'{-U,--skip-vcs-ignores}'[ignore VCS files (stil obey .ignore)]' \
'(-v --invert-match)'{-v,--invert-match}'[select non-matching lines]' \
'--vimgrep[output results like vim :vimgrep /pattern/g would]' \
'(-w --word-regexp)'{-w,--word-regexp}'[force pattern to match only whole words]' \
'(-z --search-zip)'{-z,--search-zip}'[search contents of compressed files]' \
'(-0 --null)'{-0,--null}'[separate filenames with null]' \
': :_guard "^-*" pattern' \
'*:file:_files' \
'(- :)--list-file-types[list supported file types]' \
'(- :)'{-h,--help}'[display help information]' \
'(- :)'{-V,--version}'[display version information]' \
- '(ignores)' \
'(-a --all-types)'{-a,--all-types}'[search all files]' \
'--search-binary[search binary files for matches]' \
{-t,--all-text}'[search all text files (not including hidden files)]' \
{-u,--unrestricted}'[search all files]' && ret=0
_requested file-types && { ! zstyle -T ":completion:${curcontext}:options" prefix-needed ||
[[ -prefix - ]] } && _all_labels file-types expl 'file type' \
compadd - ${(M)$(_call_program file-types $words[1] --list-file-types):#--*} && ret=0
(( ret )) || break
done
return ret

View file

@ -67,7 +67,7 @@ _ag() {
--parallel
--passthrough
--passthru
--path-to-agignore
--path-to-ignore
--print-long-lines
--print0
--recurse
@ -106,7 +106,7 @@ _ag() {
--ignore-dir) # directory completion
_filedir -d
return 0;;
--path-to-agignore) # file completion
--path-to-ignore) # file completion
_filedir
return 0;;
--pager) # command completion
@ -129,4 +129,7 @@ _ag() {
return 0;;
esac
} &&
# shellcheck disable=SC2086
# shellcheck disable=SC2154,SC2086
complete -F _ag ${nospace} ag

21
autogen.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/sh
set -e
cd "$(dirname "$0")"
AC_SEARCH_OPTS=""
# For those of us with pkg-config and other tools in /usr/local
PATH=$PATH:/usr/local/bin
# This is to make life easier for people who installed pkg-config in /usr/local
# but have autoconf/make/etc in /usr/. AKA most mac users
if [ -d "/usr/local/share/aclocal" ]
then
AC_SEARCH_OPTS="-I /usr/local/share/aclocal"
fi
# shellcheck disable=2086
aclocal $AC_SEARCH_OPTS
autoconf
autoheader
automake --add-missing

View file

@ -1,21 +1,8 @@
#!/bin/sh
cd `dirname $0`
set -e
cd "$(dirname "$0")"
AC_SEARCH_OPTS=""
# For those of us with pkg-config and other tools in /usr/local
PATH=$PATH:/usr/local/bin
# This is to make life easier for people who installed pkg-config in /usr/local
# but have autoconf/make/etc in /usr/. AKA most mac users
if [ -d "/usr/local/share/aclocal" ]
then
AC_SEARCH_OPTS="-I /usr/local/share/aclocal"
fi
aclocal $AC_SEARCH_OPTS && \
autoconf && \
autoheader && \
automake --add-missing && \
./configure "$@" && \
./autogen.sh
./configure "$@"
make -j4

View file

@ -1,6 +1,6 @@
AC_INIT(
[the_silver_searcher],
[0.30.0],
[2.2.0],
[https://github.com/ggreer/the_silver_searcher/issues],
[the_silver_searcher],
[https://github.com/ggreer/the_silver_searcher])
@ -10,6 +10,7 @@ AM_INIT_AUTOMAKE([no-define foreign subdir-objects])
AC_PROG_CC
AM_PROG_CC_C_O
AC_PREREQ([2.59])
AC_PROG_GREP
m4_ifdef(
[AM_SILENT_RULES],
@ -24,7 +25,12 @@ AX_PTHREAD(
)
# Run CFLAGS="-pg" ./configure if you want debug symbols
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $PCRE_CFLAGS -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -Wshadow -Wpointer-arith -Wcast-qual -Wmissing-prototypes -Wno-missing-braces -std=gnu89 -D_GNU_SOURCE -O2"
if ! echo "$CFLAGS" | "$GREP" '\(^\|[[[:space:]]]\)-O' > /dev/null; then
CFLAGS="$CFLAGS -O2"
fi
CFLAGS="$CFLAGS $PTHREAD_CFLAGS $PCRE_CFLAGS -Wall -Wextra -Wformat=2 -Wno-format-nonliteral -Wshadow"
CFLAGS="$CFLAGS -Wpointer-arith -Wcast-qual -Wmissing-prototypes -Wno-missing-braces -std=gnu89 -D_GNU_SOURCE"
LDFLAGS="$LDFLAGS"
case $host in
@ -53,18 +59,19 @@ AS_IF([test "x$enable_lzma" != "xno"], [
AC_CHECK_DECL([PCRE_CONFIG_JIT], [AC_DEFINE([USE_PCRE_JIT], [], [Use PCRE JIT])], [], [#include <pcre.h>])
AC_CHECK_DECL([CPU_ZERO, CPU_SET], [AC_DEFINE([USE_CPU_SET], [], [Use CPU_SET macros])] , [], [#include <sched.h>])
AC_CHECK_HEADERS([sys/cpuset.h err.h])
AC_CHECK_MEMBER([struct dirent.d_type], [AC_DEFINE([HAVE_DIRENT_DTYPE], [], [Have dirent struct member d_type])], [], [[#include <dirent.h>]])
AC_CHECK_MEMBER([struct dirent.d_namlen], [AC_DEFINE([HAVE_DIRENT_DNAMLEN], [], [Have dirent struct member d_namlen])], [], [[#include <dirent.h>]])
AC_CHECK_FUNCS(fgetln getline realpath strlcpy strndup vasprintf madvise posix_fadvise pthread_setaffinity_np)
AC_CHECK_FUNCS(fgetln fopencookie getline realpath strlcpy strndup vasprintf madvise posix_fadvise pthread_setaffinity_np pledge)
AC_CONFIG_FILES([Makefile the_silver_searcher.spec])
AC_CONFIG_HEADERS([src/config.h])
AC_CHECK_PROGS(
[CLANG_FORMAT],
[clang-format-3.7 clang-format-3.6 clang-format],
[clang-format-3.8 clang-format-3.7 clang-format-3.6 clang-format],
[no]
)
AM_CONDITIONAL([HAS_CLANG_FORMAT], [test x$CLANG_FORMAT != xno])

104
doc/ag.1
View file

@ -1,13 +1,13 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "AG" "1" "February 2015" "" ""
.TH "AG" "1" "December 2016" "" ""
.
.SH "NAME"
\fBag\fR \- The Silver Searcher\. Like ack, but faster\.
.
.SH "SYNOPSIS"
\fBag\fR [\fIfile\-type\fR] [\fIoptions\fR] PATTERN [PATH]
\fBag\fR [\fIoptions\fR] \fIpattern\fR [\fIpath \.\.\.\fR]
.
.SH "DESCRIPTION"
Recursively search for PATTERN in PATH\. Like grep or ack, but faster\.
@ -19,16 +19,20 @@ Recursively search for PATTERN in PATH\. Like grep or ack, but faster\.
Output results in a format parseable by AckMate \fIhttps://github\.com/protocool/AckMate\fR\.
.
.TP
\fB\-\-[no]affinity\fR
Set thread affinity (if platform supports it)\. Default is true\.
.
.TP
\fB\-a \-\-all\-types\fR
Search all files\. This doesn\'t include hidden files, and doesn\'t respect any ignore files\.
.
.TP
\fB\-A \-\-after [LINES]\fR
Print lines after match\. Defaults to 2\.
Print lines after match\. If not provided, LINES defaults to 2\.
.
.TP
\fB\-B \-\-before [LINES]\fR
Print lines before match\. Defaults to 2\.
Print lines before match\. If not provided, LINES defaults to 2\.
.
.TP
\fB\-\-[no]break\fR
@ -44,15 +48,15 @@ Print color codes in results\. Enabled by default\.
.
.TP
\fB\-\-color\-line\-number\fR
Color codes for line numbers\. Defaults to 1;33\.
Color codes for line numbers\. Default is 1;33\.
.
.TP
\fB\-\-color\-match\fR
Color codes for result match numbers\. Defaults to 30;43\.
Color codes for result match numbers\. Default is 30;43\.
.
.TP
\fB\-\-color\-path\fR
Color codes for path names\. Defaults to 1;32\.
Color codes for path names\. Default is 1;32\.
.
.TP
\fB\-\-column\fR
@ -60,11 +64,11 @@ Print column numbers in results\.
.
.TP
\fB\-C \-\-context [LINES]\fR
Print lines before and after matches\. Defaults to 2\.
Print lines before and after matches\. Default is 2\.
.
.TP
\fB\-D \-\-debug\fR
Output ridiculous amounts of debugging info\. Probably not useful\.
Output ridiculous amounts of debugging info\. Not useful unless you\'re actually debugging\.
.
.TP
\fB\-\-depth NUM\fR
@ -75,8 +79,8 @@ Search up to NUM directories deep, \-1 for unlimited\. Default is 25\.
Print file names\. Enabled by default, except when searching a single file\.
.
.TP
\fB\-f \-\-follow\fR
Follow symlinks\.
\fB\-f \-\-[no]follow\fR
Follow symlinks\. Default is false\.
.
.TP
\fB\-F \-\-fixed\-strings\fR
@ -131,6 +135,18 @@ See \fBFILE TYPES\fR below\.
Skip the rest of a file after NUM matches\. Default is 0, which never skips\.
.
.TP
\fB\-\-[no]mmap\fR
Toggle use of memory\-mapped I/O\. Defaults to true on platforms where \fBmmap()\fR is faster than \fBread()\fR\. (All but macOS\.)
.
.TP
\fB\-\-[no]multiline\fR
Match regexes across newlines\. Enabled by default\.
.
.TP
\fB\-n \-\-norecurse\fR
Don\'t recurse into directories\.
.
.TP
\fB\-\-[no]numbers\fR
Print line numbers\. Default is to omit line numbers when searching streams\.
.
@ -139,19 +155,27 @@ Print line numbers\. Default is to omit line numbers when searching streams\.
Print only the matching part of the lines\.
.
.TP
\fB\-p \-\-path\-to\-agignore STRING\fR
Provide a path to a specific \.agignore file\.
\fB\-\-one\-device\fR
When recursing directories, don\'t scan dirs that reside on other storage devices\. This lets you avoid scanning slow network mounts\. This feature is not supported on all platforms\.
.
.TP
\fB\-p \-\-path\-to\-ignore STRING\fR
Provide a path to a specific \.ignore file\.
.
.TP
\fB\-\-pager COMMAND\fR
Use a pager such as less\. Use \fB\-\-nopager\fR to override\. This option is also ignored if output is piped to another program\.
Use a pager such as \fBless\fR\. Use \fB\-\-nopager\fR to override\. This option is also ignored if output is piped to another program\.
.
.TP
\fB\-\-parallel\fR
Parse the input stream as a search term, not data to search\. This is meant to be used with tools such as GNU parallel\. For example: \fBecho "foo\enbar\enbaz" | parallel "ag {} \."\fR will run 3 instances of ag, searching the current directory for "foo", "bar", and "baz"\.
.
.TP
\fB\-\-print\-long\-lines\fR
Print matches on very long lines (> 2k characters by default)\.
.
.TP
\fB\-\-passthrough\fR
\fB\-\-passthrough \-\-passthru\fR
When searching a stream, print all lines even if they don\'t match\.
.
.TP
@ -159,6 +183,10 @@ When searching a stream, print all lines even if they don\'t match\.
Do not parse PATTERN as a regular expression\. Try to match it literally\.
.
.TP
\fB\-r \-\-recurse\fR
Recurse into directories when searching\. Default is true\.
.
.TP
\fB\-s \-\-case\-sensitive\fR
Match case\-sensitively\.
.
@ -179,60 +207,66 @@ Suppress all log messages, including errors\.
Print stats (files scanned, time taken, etc)\.
.
.TP
\fB\-\-stats\-only\fR
Print stats (files scanned, time taken, etc) and nothing else\.
.
.TP
\fB\-t \-\-all\-text\fR
Search all text files\. This doesn\'t include hidden files\.
.
.TP
\fB\-u \-\-unrestricted\fR
Search \fIall\fR files\. This ignores \.agignore, \.gitignore, etc\. It searches binary and hidden files as well\.
Search \fIall\fR files\. This ignores \.ignore, \.gitignore, etc\. It searches binary and hidden files as well\.
.
.TP
\fB\-U \-\-skip\-vcs\-ignores\fR
Ignore VCS ignore files (\.gitignore, \.hgignore, svn:ignore), but still use \.agignore\.
Ignore VCS ignore files (\.gitignore, \.hgignore), but still use \.ignore\.
.
.TP
\fB\-v \-\-invert\-match\fR
Match every line \fInot\fR containing the specified pattern\.
.
.TP
\fB\-V \-\-version\fR
Print version info\.
.
.TP
\fB\-\-vimgrep\fR
Output results like vim\'s \fB:vimgrep /pattern/g\fR would (it reports every match on the line)\.
.
.br
Here\'s a ~/\.vimrc configuration example:
Output results in the same form as Vim\'s \fB:vimgrep /pattern/g\fR
.
.IP
\fBset grepprg=ag\e \-\-vimgrep\e $*\fR
.
.br
\fBset grepformat=%f:%l:%c:%m\fR
Here is a ~/\.vimrc configuration example:
.
.IP
Then use \fB:grep\fR to grep for something\.
\fBset grepprg=ag\e \-\-vimgrep\e $*\fR \fBset grepformat=%f:%l:%c:%m\fR
.
.br
Then use \fB:copen\fR, \fB:cn\fR, \fB:cp\fR, etc\. to navigate through the matches\.
.IP
Then use \fB:grep\fR to grep for something\. Then use \fB:copen\fR, \fB:cn\fR, \fB:cp\fR, etc\. to navigate through the matches\.
.
.TP
\fB\-w \-\-word\-regexp\fR
Only match whole words\.
.
.TP
\fB\-\-workers NUM\fR
Use NUM worker threads\. Default is the number of CPU cores, with a max of 8\.
.
.TP
\fB\-z \-\-search\-zip\fR
Search contents of compressed files\.
Search contents of compressed files\. Currently, gz and xz are supported\. This option requires that ag is built with lzma and zlib\.
.
.TP
\fB\-0 \-\-null \-\-print0\fR
Separate the filenames with \fB\e0\fR, rather than \fB\en\fR: this allows \fBxargs \-0 <command>\fR to correctly process filenames containing spaces or newlines\.
.
.SH "FILE TYPES"
It is possible to restrict the types of files searched\. For example, passing \fB\-\-html\fR as the \fBfile\-types\fR parameter will search only files with the extensions \fBhtm\fR, \fBhtml\fR, \fBshtml\fR or \fBxhtml\fR\. For a list of supported \fBfile\-types\fR run \fBag \-\-list\-file\-types\fR\.
It is possible to restrict the types of files searched\. For example, passing \fB\-\-html\fR will search only files with the extensions \fBhtm\fR, \fBhtml\fR, \fBshtml\fR or \fBxhtml\fR\. For a list of supported types, run \fBag \-\-list\-file\-types\fR\.
.
.SH "IGNORING FILES"
By default, ag will ignore files whose names match patterns in \.gitignore, \.hgignore, or \.agignore\. These files can be anywhere in the directories being searched\. Ag also ignores files matched by the svn:ignore property if \fBsvn \-\-version\fR is 1\.6 or older\. Finally, ag looks in $HOME/\.agignore for ignore patterns\. Binary files are ignored by default as well\.
By default, ag will ignore files whose names match patterns in \.gitignore, \.hgignore, or \.ignore\. These files can be anywhere in the directories being searched\. Binary files are ignored by default as well\. Finally, ag looks in $HOME/\.agignore for ignore patterns\.
.
.P
If you want to ignore \.gitignore, \.hgignore, and svn:ignore, but still take \.agignore into account, use \fB\-U\fR\.
If you want to ignore \.gitignore and \.hgignore, but still take \.ignore into account, use \fB\-U\fR\.
.
.P
Use the \fB\-t\fR option to search all text files; \fB\-a\fR to search all files; and \fB\-u\fR to search all, including hidden files\.
@ -243,5 +277,11 @@ Use the \fB\-t\fR option to search all text files; \fB\-a\fR to search all files
.P
\fBag foo /bar/\fR: Find matches for "foo" in path /bar/\.
.
.P
\fBag \-\- \-\-foo\fR: Find matches for "\-\-foo" in the current directory\. (As with most UNIX command line utilities, "\-\-" is used to signify that the remaining arguments should not be treated as options\.)
.
.SH "ABOUT"
ag was originally created by Geoff Greer\. More information (and the latest release) can be found at http://geoff\.greer\.fm/ag
.
.SH "SEE ALSO"
grep(1)

View file

@ -3,7 +3,7 @@ ag(1) -- The Silver Searcher. Like ack, but faster.
## SYNOPSIS
`ag` [<file-type>] [<options>] PATTERN [PATH]
`ag` [_options_] _pattern_ [_path ..._]
## DESCRIPTION
@ -14,14 +14,17 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
* `--ackmate`:
Output results in a format parseable by [AckMate](https://github.com/protocool/AckMate).
* `--[no]affinity`:
Set thread affinity (if platform supports it). Default is true.
* `-a --all-types`:
Search all files. This doesn't include hidden files, and doesn't respect any ignore files.
* `-A --after [LINES]`:
Print lines after match. Defaults to 2.
Print lines after match. If not provided, LINES defaults to 2.
* `-B --before [LINES]`:
Print lines before match. Defaults to 2.
Print lines before match. If not provided, LINES defaults to 2.
* `--[no]break`:
Print a newline between matches in different files. Enabled by default.
@ -35,22 +38,22 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
Print color codes in results. Enabled by default.
* `--color-line-number`:
Color codes for line numbers. Defaults to 1;33.
Color codes for line numbers. Default is 1;33.
* `--color-match`:
Color codes for result match numbers. Defaults to 30;43.
Color codes for result match numbers. Default is 30;43.
* `--color-path`:
Color codes for path names. Defaults to 1;32.
Color codes for path names. Default is 1;32.
* `--column`:
Print column numbers in results.
* `-C --context [LINES]`:
Print lines before and after matches. Defaults to 2.
Print lines before and after matches. Default is 2.
* `-D --debug`:
Output ridiculous amounts of debugging info. Probably not useful.
Output ridiculous amounts of debugging info. Not useful unless you're actually debugging.
* `--depth NUM`:
Search up to NUM directories deep, -1 for unlimited. Default is 25.
@ -58,8 +61,8 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
* `--[no]filename`:
Print file names. Enabled by default, except when searching a single file.
* `-f --follow`:
Follow symlinks.
* `-f --[no]follow`:
Follow symlinks. Default is false.
* `-F --fixed-strings`:
Alias for --literal for compatibility with grep.
@ -105,31 +108,52 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
* `-m --max-count NUM`:
Skip the rest of a file after NUM matches. Default is 0, which never skips.
* `--[no]mmap`:
Toggle use of memory-mapped I/O. Defaults to true on platforms where
`mmap()` is faster than `read()`. (All but macOS.)
* `--[no]multiline`:
Match regexes across newlines. Enabled by default.
* `-n --norecurse`:
Don't recurse into directories.
* `--[no]numbers`:
Print line numbers. Default is to omit line numbers when searching streams.
* `-o --only-matching`:
Print only the matching part of the lines.
* `-p --path-to-agignore STRING`:
Provide a path to a specific .agignore file.
* `--one-device`:
When recursing directories, don't scan dirs that reside on other storage
devices. This lets you avoid scanning slow network mounts.
This feature is not supported on all platforms.
* `-p --path-to-ignore STRING`:
Provide a path to a specific .ignore file.
* `--pager COMMAND`:
Use a pager such as less. Use `--nopager` to override. This option
Use a pager such as `less`. Use `--nopager` to override. This option
is also ignored if output is piped to another program.
* `--parallel`:
Parse the input stream as a search term, not data to search. This is meant
to be used with tools such as GNU parallel. For example:
`echo "foo\nbar\nbaz" | parallel "ag {} ."` will run 3 instances of ag,
searching the current directory for "foo", "bar", and "baz".
* `--print-long-lines`:
Print matches on very long lines (> 2k characters by default).
* `--passthrough`:
* `--passthrough --passthru`:
When searching a stream, print all lines even if they don't match.
* `-Q --literal`:
Do not parse PATTERN as a regular expression. Try to match it literally.
* `-r --recurse`:
Recurse into directories when searching. Default is true.
* `-s --case-sensitive`:
Match case-sensitively.
@ -146,23 +170,30 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
* `--stats`:
Print stats (files scanned, time taken, etc).
* `--stats-only`:
Print stats (files scanned, time taken, etc) and nothing else.
* `-t --all-text`:
Search all text files. This doesn't include hidden files.
* `-u --unrestricted`:
Search *all* files. This ignores .agignore, .gitignore, etc. It searches
Search *all* files. This ignores .ignore, .gitignore, etc. It searches
binary and hidden files as well.
* `-U --skip-vcs-ignores`:
Ignore VCS ignore files (.gitignore, .hgignore, svn:ignore), but still
use .agignore.
Ignore VCS ignore files (.gitignore, .hgignore), but still
use .ignore.
* `-v --invert-match`:
Match every line *not* containing the specified pattern.
* `-V --version`:
Print version info.
* `--vimgrep`:
Output results like vim's `:vimgrep /pattern/g` would (it reports every match on the line).
Here's a ~/.vimrc configuration example:
Output results in the same form as Vim's `:vimgrep /pattern/g`
Here is a ~/.vimrc configuration example:
`set grepprg=ag\ --vimgrep\ $*`
`set grepformat=%f:%l:%c:%m`
@ -173,8 +204,15 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
* `-w --word-regexp`:
Only match whole words.
* `--workers NUM`:
Use NUM worker threads. Default is the number of CPU cores, with a max of 8.
* `-W --width NUM`:
Truncate match lines after NUM characters.
* `-z --search-zip`:
Search contents of compressed files.
Search contents of compressed files. Currently, gz and xz are supported.
This option requires that ag is built with lzma and zlib.
* `-0 --null --print0`:
Separate the filenames with `\0`, rather than `\n`:
@ -185,20 +223,18 @@ Recursively search for PATTERN in PATH. Like grep or ack, but faster.
## FILE TYPES
It is possible to restrict the types of files searched. For example, passing
`--html` as the `file-types` parameter will search only files with the
extensions `htm`, `html`, `shtml` or `xhtml`. For a list of supported `file-types`
run `ag --list-file-types`.
`--html` will search only files with the extensions `htm`, `html`, `shtml`
or `xhtml`. For a list of supported types, run `ag --list-file-types`.
## IGNORING FILES
By default, ag will ignore files whose names match patterns in .gitignore,
.hgignore, or .agignore. These files can be anywhere in the directories being
searched. Ag also ignores files matched by the svn:ignore property if `svn
--version` is 1.6 or older. Finally, ag looks in $HOME/.agignore for ignore
patterns. Binary files are ignored by default as well.
.hgignore, or .ignore. These files can be anywhere in the directories being
searched. Binary files are ignored by default as well. Finally, ag looks in
$HOME/.agignore for ignore patterns.
If you want to ignore .gitignore, .hgignore, and svn:ignore, but still take
.agignore into account, use `-U`.
If you want to ignore .gitignore and .hgignore, but still take .ignore into
account, use `-U`.
Use the `-t` option to search all text files; `-a` to search all files; and `-u`
to search all, including hidden files.
@ -211,6 +247,16 @@ to search all, including hidden files.
`ag foo /bar/`:
Find matches for "foo" in path /bar/.
`ag -- --foo`:
Find matches for "--foo" in the current directory. (As with most UNIX command
line utilities, "--" is used to signify that the remaining arguments should
not be treated as options.)
## ABOUT
ag was originally created by Geoff Greer. More information (and the latest
release) can be found at http://geoff.greer.fm/ag
## SEE ALSO
grep(1)

View file

@ -22,18 +22,21 @@ then
exit 1
fi
SOURCE_FILES=`git ls-files src/`
SOURCE_FILES=$(git ls-files src/)
if [ "$1" == "reformat" ]
then
echo "Reformatting source files"
# shellcheck disable=2086
echo $CLANG_FORMAT -style=file -i $SOURCE_FILES
# shellcheck disable=2086
$CLANG_FORMAT -style=file -i $SOURCE_FILES
exit 0
elif [ "$1" == "test" ]
then
RESULT=`$CLANG_FORMAT -style=file -output-replacements-xml $SOURCE_FILES | grep -c '<replacement '`
if [ $RESULT -eq 0 ]
# shellcheck disable=2086
RESULT=$($CLANG_FORMAT -style=file -output-replacements-xml $SOURCE_FILES | grep -c '<replacement ')
if [ "$RESULT" -eq 0 ]
then
echo "code is formatted correctly :)"
exit 0

10
pgo.sh Executable file
View file

@ -0,0 +1,10 @@
#!/bin/sh
set -e
cd "$(dirname "$0")"
make clean
./build.sh CFLAGS="$CFLAGS -fprofile-generate"
./ag example ..
make clean
./build.sh CFLAGS="$CFLAGS -fprofile-correction -fprofile-use"

196
sanitize.sh Executable file
View file

@ -0,0 +1,196 @@
#!/bin/bash
# Copyright 2016 Allen Wild
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
AVAILABLE_SANITIZERS=(
address
thread
undefined
valgrind
)
DEFAULT_SANITIZERS=(
address
thread
undefined
)
usage() {
cat <<EOF
Usage: $0 [-h] [valgrind | [SANITIZERS ...]]
This script recompiles ag using -fsanitize=<SANITIZER> and then runs the test suite.
Memory leaks or other errors will be printed in ag's output, thus failing the test.
Available LLVM sanitizers are: ${AVAILABLE_SANITIZERS[*]}
The compile-time sanitizers are supported in clang/llvm >= 3.1 and gcc >= 4.8
for x86_64 Linux only. clang is preferred and will be used, if available.
For function names and line numbers in error output traces, llvm-symbolizer needs
to be available in PATH or set through ASAN_SYMBOLIZER_PATH.
If 'valgrind' is passed as the sanitizer, then ag will be run through valgrind
without recompiling. If $(dirname $0)/ag doesn't exist, then it will be built.
WARNING: This script will run "make distclean" and "./configure" to recompile ag
once per sanitizer (except for valgrind). If you need to pass additional
options to ./configure, put them in the CONFIGOPTS environment variable.
EOF
}
vrun() {
echo "Running: $*"
"$@"
}
die() {
echo "Fatal: $*"
exit 1
}
valid_sanitizer() {
for san in "${AVAILABLE_SANITIZERS[@]}"; do
if [[ "$1" == "$san" ]]; then
return 0
fi
done
return 1
}
run_sanitizer() {
sanitizer=$1
if [[ "$sanitizer" == "valgrind" ]]; then
run_valgrind
return $?
fi
echo -e "\nCompiling for sanitizer '$sanitizer'"
[[ -f Makefile ]] && vrun make distclean
vrun ./configure $CONFIGOPTS CC=$SANITIZE_CC \
CFLAGS="-g -O0 -fsanitize=$sanitizer $EXTRA_CFLAGS"
if [[ $? != 0 ]]; then
echo "ERROR: Failed to configure. Try setting CONFIGOPTS?"
return 1
fi
vrun make
if [[ $? != 0 ]]; then
echo "ERROR: failed to build"
return 1
fi
echo "Testing with sanitizer '$sanitizer'"
vrun make test
if [[ $? != 0 ]]; then
echo "Tests for sanitizer '$sanitizer' FAIL!"
echo "Check the above output for failure information"
return 2
else
echo "Tests for sanitizer '$sanitizer' PASS!"
return 0
fi
}
run_valgrind() {
echo "Compiling ag normally for use with valgrind"
[[ -f Makefile ]] && vrun make distclean
vrun ./configure $CONFIGOPTS
if [[ $? != 0 ]]; then
echo "ERROR: Failed to configure. Try setting CONFIGOPTS?"
return 1
fi
vrun make
if [[ $? != 0 ]]; then
echo "ERROR: failed to build"
return 1
fi
echo "Running: AGPROG=\"valgrind -q $PWD/ag\" make test"
AGPROG="valgrind -q $PWD/ag" make test
if [[ $? != 0 ]]; then
echo "Valgrind tests FAIL!"
return 1
else
echo "Valgrind tests PASS!"
return 0
fi
}
#### MAIN ####
run_sanitizers=()
for opt in "$@"; do
if [[ "$opt" == -* ]]; then
case opt in
-h|--help)
usage
exit 0
;;
*)
echo "Unknown option: '$opt'"
usage
exit 1
;;
esac
else
if valid_sanitizer "$opt"; then
run_sanitizers+=("$opt")
else
echo "Invalid Sanitizer: '$opt'"
usage
exit 1
fi
fi
done
if [[ ${#run_sanitizers[@]} == 0 ]]; then
run_sanitizers=(${DEFAULT_SANITIZERS[@]})
fi
if [[ -n $CC ]]; then
echo "Using CC=$CC"
SANITIZE_CC="$CC"
elif which clang &>/dev/null; then
SANITIZE_CC="clang"
else
echo "Warning: CC unset and clang not found"
fi
if [[ -n $CFLAGS ]]; then
EXTRA_CFLAGS="$CFLAGS"
unset CFLAGS
fi
if [[ ! -e ./configure ]]; then
echo "Warning: ./configure not found. Running autogen"
vrun ./autogen.sh || die "autogen.sh failed"
fi
echo "Running sanitizers: ${run_sanitizers[*]}"
failedsan=()
for san in "${run_sanitizers[@]}"; do
run_sanitizer $san
if [[ $? != 0 ]]; then
failedsan+=($san)
fi
done
if [[ ${#failedsan[@]} == 0 ]]; then
echo "All sanitizers PASSED"
exit 0
else
echo "The following sanitizers FAILED: ${failedsan[*]}"
exit ${#failedsan[@]}
fi

View file

@ -1,6 +1,8 @@
#ifndef DECOMPRESS_H
#define DECOMPRESS_H
#include <stdio.h>
#include "config.h"
#include "log.h"
#include "options.h"
@ -16,4 +18,9 @@ typedef enum {
ag_compression_type is_zipped(const void *buf, const int buf_len);
void *decompress(const ag_compression_type zip_type, const void *buf, const int buf_len, const char *dir_full_path, int *new_buf_len);
#if HAVE_FOPENCOOKIE
FILE *decompress_open(int fd, const char *mode, ag_compression_type ctype);
#endif
#endif

View file

@ -1,5 +1,6 @@
#include <ctype.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -19,6 +20,8 @@
const int fnmatch_flags = FNM_PATHNAME;
#endif
ignores *root_ignores;
/* TODO: build a huge-ass list of files we want to ignore by default (build cache stuff, pyc files, etc) */
const char *evil_hardcoded_ignore_files[] = {
@ -27,13 +30,12 @@ const char *evil_hardcoded_ignore_files[] = {
NULL
};
/* Warning: changing the first string will break skip_vcs_ignores. */
/* Warning: changing the first two strings will break skip_vcs_ignores. */
const char *ignore_pattern_files[] = {
".agignore",
".ignore",
".gitignore",
".git/info/exclude",
".hgignore",
".svn",
NULL
};
@ -51,6 +53,8 @@ ignores *init_ignore(ignores *parent, const char *dirname, const size_t dirname_
ig->slash_names_len = 0;
ig->regexes = NULL;
ig->regexes_len = 0;
ig->invert_regexes = NULL;
ig->invert_regexes_len = 0;
ig->slash_regexes = NULL;
ig->slash_regexes_len = 0;
ig->dirname = dirname;
@ -84,6 +88,7 @@ void cleanup_ignore(ignores *ig) {
free_strings(ig->names, ig->names_len);
free_strings(ig->slash_names, ig->slash_names_len);
free_strings(ig->regexes, ig->regexes_len);
free_strings(ig->invert_regexes, ig->invert_regexes_len);
free_strings(ig->slash_regexes, ig->slash_regexes_len);
if (ig->abs_path) {
free(ig->abs_path);
@ -115,15 +120,21 @@ void add_ignore_pattern(ignores *ig, const char *pattern) {
char ***patterns_p;
size_t *patterns_len;
if (is_fnmatch(pattern)) {
if (pattern[0] == '*' && pattern[1] == '.' && !(is_fnmatch(pattern + 2))) {
if (pattern[0] == '*' && pattern[1] == '.' && strchr(pattern + 2, '.') && !is_fnmatch(pattern + 2)) {
patterns_p = &(ig->extensions);
patterns_len = &(ig->extensions_len);
pattern += 2;
pattern_len -= 2;
} else if (pattern[0] == '/') {
patterns_p = &(ig->slash_regexes);
patterns_len = &(ig->slash_regexes_len);
pattern++;
pattern_len--;
} else if (pattern[0] == '!') {
patterns_p = &(ig->invert_regexes);
patterns_len = &(ig->invert_regexes_len);
pattern++;
pattern_len--;
} else {
patterns_p = &(ig->regexes);
patterns_len = &(ig->regexes_len);
@ -186,76 +197,6 @@ void load_ignore_patterns(ignores *ig, const char *path) {
fclose(fp);
}
void load_svn_ignore_patterns(ignores *ig, const char *path) {
FILE *fp = NULL;
char *dir_prop_base;
ag_asprintf(&dir_prop_base, "%s/%s", path, SVN_DIR_PROP_BASE);
fp = fopen(dir_prop_base, "r");
if (fp == NULL) {
log_debug("Skipping svn ignore file %s", dir_prop_base);
free(dir_prop_base);
return;
}
char *entry = NULL;
size_t entry_len = 0;
char *key = ag_malloc(32); /* Sane start for max key length. */
size_t key_len = 0;
size_t bytes_read = 0;
char *entry_line;
size_t line_len;
int matches;
while (fscanf(fp, "K %zu\n", &key_len) == 1) {
key = ag_realloc(key, key_len + 1);
bytes_read = fread(key, 1, key_len, fp);
key[key_len] = '\0';
matches = fscanf(fp, "\nV %zu\n", &entry_len);
if (matches != 1) {
log_debug("Unable to parse svnignore file %s: fscanf() got %i matches, expected 1.", dir_prop_base, matches);
goto cleanup;
}
if (strncmp(SVN_PROP_IGNORE, key, bytes_read) != 0) {
log_debug("key is %s, not %s. skipping %u bytes", key, SVN_PROP_IGNORE, entry_len);
/* Not the key we care about. fseek and repeat */
fseek(fp, entry_len + 1, SEEK_CUR); /* +1 to account for newline. yes I know this is hacky */
continue;
}
/* Aww yeah. Time to ignore stuff */
entry = ag_malloc(entry_len + 1);
bytes_read = fread(entry, 1, entry_len, fp);
entry[bytes_read] = '\0';
log_debug("entry: %s", entry);
break;
}
if (entry == NULL) {
goto cleanup;
}
char *patterns = entry;
size_t patterns_len = strlen(patterns);
while (*patterns != '\0' && patterns < (entry + bytes_read)) {
for (line_len = 0; line_len < patterns_len; line_len++) {
if (patterns[line_len] == '\n') {
break;
}
}
if (line_len > 0) {
entry_line = ag_strndup(patterns, line_len);
add_ignore_pattern(ig, entry_line);
free(entry_line);
}
patterns += line_len + 1;
patterns_len -= line_len + 1;
}
free(entry);
cleanup:
free(dir_prop_base);
free(key);
fclose(fp);
}
static int ackmate_dir_match(const char *dir_name) {
if (opts.ackmate_dir_filter == NULL) {
return 0;
@ -267,6 +208,7 @@ static int ackmate_dir_match(const char *dir_name) {
/* This is the hottest code in Ag. 10-15% of all execution time is spent here */
static int path_ignore_search(const ignores *ig, const char *path, const char *filename) {
char *temp;
int temp_start_pos;
size_t i;
int match_pos;
@ -277,9 +219,12 @@ static int path_ignore_search(const ignores *ig, const char *path, const char *f
}
ag_asprintf(&temp, "%s/%s", path[0] == '.' ? path + 1 : path, filename);
//ig->abs_path has its leading slash stripped, so we have to strip the leading slash
//of temp as well
temp_start_pos = (temp[0] == '/') ? 1 : 0;
if (strncmp(temp, ig->abs_path, ig->abs_path_len) == 0) {
char *slash_filename = temp + ig->abs_path_len;
if (strncmp(temp + temp_start_pos, ig->abs_path, ig->abs_path_len) == 0) {
char *slash_filename = temp + temp_start_pos + ig->abs_path_len;
if (slash_filename[0] == '/') {
slash_filename++;
}
@ -320,6 +265,15 @@ static int path_ignore_search(const ignores *ig, const char *path, const char *f
}
}
for (i = 0; i < ig->invert_regexes_len; i++) {
if (fnmatch(ig->invert_regexes[i], filename, fnmatch_flags) == 0) {
log_debug("file %s not ignored because name matches regex pattern !%s", filename, ig->invert_regexes[i]);
free(temp);
return 0;
}
log_debug("pattern !%s doesn't match file %s", ig->invert_regexes[i], filename);
}
for (i = 0; i < ig->regexes_len; i++) {
if (fnmatch(ig->regexes[i], filename, fnmatch_flags) == 0) {
log_debug("file %s ignored because name matches regex pattern %s", filename, ig->regexes[i]);
@ -337,24 +291,11 @@ static int path_ignore_search(const ignores *ig, const char *path, const char *f
/* This function is REALLY HOT. It gets called for every file */
int filename_filter(const char *path, const struct dirent *dir, void *baton) {
const char *filename = dir->d_name;
/* TODO: don't call strlen on filename every time we call filename_filter() */
#ifdef HAVE_DIRENT_DNAMLEN
size_t filename_len = dir->d_namlen;
#else
size_t filename_len = strlen(filename);
#endif
size_t i;
scandir_baton_t *scandir_baton = (scandir_baton_t *)baton;
const ignores *ig = scandir_baton->ig;
const char *base_path = scandir_baton->base_path;
const size_t base_path_len = scandir_baton->base_path_len;
const char *path_start = path;
char *temp;
if (!opts.search_hidden_files && filename[0] == '.') {
return 0;
}
size_t i;
for (i = 0; evil_hardcoded_ignore_files[i] != NULL; i++) {
if (strcmp(filename, evil_hardcoded_ignore_files[i]) == 0) {
return 0;
@ -367,19 +308,16 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
}
if (is_named_pipe(path, dir)) {
log_debug("%s ignored because it's a named pipe", path);
log_debug("%s ignored because it's a named pipe or socket", path);
return 0;
}
if (opts.search_all_files && !opts.path_to_agignore) {
if (opts.search_all_files && !opts.path_to_ignore) {
return 1;
}
for (i = 0; base_path[i] == path[i] && i < base_path_len; i++) {
/* base_path always ends with "/\0" while path doesn't, so this is safe */
path_start = path + i + 2;
}
log_debug("path_start %s filename %s", path_start, filename);
scandir_baton_t *scandir_baton = (scandir_baton_t *)baton;
const char *path_start = scandir_baton->path_start;
const char *extension = strchr(filename, '.');
if (extension) {
@ -392,12 +330,23 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
}
}
while (ig != NULL) {
if (strncmp(filename, "./", 2) == 0) {
filename++;
filename_len--;
}
#ifdef HAVE_DIRENT_DNAMLEN
size_t filename_len = dir->d_namlen;
#else
size_t filename_len = 0;
#endif
if (strncmp(filename, "./", 2) == 0) {
#ifndef HAVE_DIRENT_DNAMLEN
filename_len = strlen(filename);
#endif
filename++;
filename_len--;
}
const ignores *ig = scandir_baton->ig;
while (ig != NULL) {
if (extension) {
int match_pos = binary_search(extension, ig->extensions, 0, ig->extensions_len);
if (match_pos >= 0) {
@ -410,12 +359,20 @@ int filename_filter(const char *path, const struct dirent *dir, void *baton) {
return 0;
}
if (is_directory(path, dir) && filename[filename_len - 1] != '/') {
ag_asprintf(&temp, "%s/", filename);
int rv = path_ignore_search(ig, path_start, temp);
free(temp);
if (rv) {
return 0;
if (is_directory(path, dir)) {
#ifndef HAVE_DIRENT_DNAMLEN
if (!filename_len) {
filename_len = strlen(filename);
}
#endif
if (filename[filename_len - 1] != '/') {
char *temp;
ag_asprintf(&temp, "%s/", filename);
int rv = path_ignore_search(ig, path_start, temp);
free(temp);
if (rv) {
return 0;
}
}
}
ig = ig->parent;

View file

@ -4,10 +4,6 @@
#include <dirent.h>
#include <sys/types.h>
#define SVN_DIR_PROP_BASE "dir-prop-base"
#define SVN_DIR ".svn"
#define SVN_PROP_IGNORE "svn:ignore"
struct ignores {
char **extensions; /* File extensions to ignore */
size_t extensions_len;
@ -19,6 +15,8 @@ struct ignores {
char **regexes; /* For patterns that need fnmatch */
size_t regexes_len;
char **invert_regexes; /* For "!" patterns */
size_t invert_regexes_len;
char **slash_regexes;
size_t slash_regexes_len;
@ -31,7 +29,7 @@ struct ignores {
};
typedef struct ignores ignores;
ignores *root_ignores;
extern ignores *root_ignores;
extern const char *evil_hardcoded_ignore_files[];
extern const char *ignore_pattern_files[];
@ -42,7 +40,6 @@ void cleanup_ignore(ignores *ig);
void add_ignore_pattern(ignores *ig, const char *pattern);
void load_ignore_patterns(ignores *ig, const char *path);
void load_svn_ignore_patterns(ignores *ig, const char *path);
int filename_filter(const char *path, const struct dirent *dir, void *baton);

View file

@ -7,38 +7,67 @@
lang_spec_t langs[] = {
{ "actionscript", { "as", "mxml" } },
{ "ada", { "ada", "adb", "ads" } },
{ "asciidoc", { "adoc", "ad", "asc", "asciidoc" } },
{ "apl", { "apl" } },
{ "asm", { "asm", "s" } },
{ "asp", { "asp", "asa", "aspx", "asax", "ashx", "ascx", "asmx" } },
{ "aspx", { "asp", "asa", "aspx", "asax", "ashx", "ascx", "asmx" } },
{ "batch", { "bat", "cmd" } },
{ "bazel", { "bazel" } },
{ "bitbake", { "bb", "bbappend", "bbclass", "inc" } },
{ "cc", { "c", "h", "xs" } },
{ "cfmx", { "cfc", "cfm", "cfml" } },
{ "clojure", { "clj", "cljs", "cljc", "cljx" } },
{ "chpl", { "chpl" } },
{ "clojure", { "clj", "cljs", "cljc", "cljx", "edn" } },
{ "coffee", { "coffee", "cjsx" } },
{ "cpp", { "cpp", "cc", "C", "cxx", "m", "hpp", "hh", "h", "H", "hxx" } },
{ "config", { "config" } },
{ "coq", { "coq", "g", "v" } },
{ "cpp", { "cpp", "cc", "C", "cxx", "m", "hpp", "hh", "h", "H", "hxx", "tpp" } },
{ "crystal", { "cr", "ecr" } },
{ "csharp", { "cs" } },
{ "cshtml", { "cshtml" } },
{ "css", { "css" } },
{ "delphi", { "pas", "int", "dfm", "nfm", "dof", "dpk", "dproj", "groupproj", "bdsgroup", "bdsproj" } },
{ "cython", { "pyx", "pxd", "pxi" } },
{ "delphi", { "pas", "int", "dfm", "nfm", "dof", "dpk", "dpr", "dproj", "groupproj", "bdsgroup", "bdsproj" } },
{ "dlang", { "d", "di" } },
{ "dot", { "dot", "gv" } },
{ "dts", { "dts", "dtsi" } },
{ "ebuild", { "ebuild", "eclass" } },
{ "elisp", { "el" } },
{ "elixir", { "ex", "exs" } },
{ "elixir", { "ex", "eex", "exs" } },
{ "elm", { "elm" } },
{ "erlang", { "erl", "hrl" } },
{ "fortran", { "f", "f77", "f90", "f95", "f03", "for", "ftn", "fpp" } },
{ "factor", { "factor" } },
{ "fortran", { "f", "F", "f77", "f90", "F90", "f95", "f03", "for", "ftn", "fpp", "FPP" } },
{ "fsharp", { "fs", "fsi", "fsx" } },
{ "gettext", { "po", "pot", "mo" } },
{ "glsl", { "vert", "tesc", "tese", "geom", "frag", "comp" } },
{ "go", { "go" } },
{ "groovy", { "groovy", "gtmpl", "gpp", "grunit" } },
{ "gradle", { "gradle" } },
{ "groovy", { "groovy", "gtmpl", "gpp", "grunit", "gradle" } },
{ "haml", { "haml" } },
{ "haskell", { "hs", "lhs" } },
{ "handlebars", { "hbs" } },
{ "haskell", { "hs", "hsig", "lhs" } },
{ "haxe", { "hx" } },
{ "hh", { "h" } },
{ "html", { "htm", "html", "shtml", "xhtml" } },
{ "idris", { "idr", "ipkg", "lidr" } },
{ "ini", { "ini" } },
{ "ipython", { "ipynb" } },
{ "isabelle", { "thy" } },
{ "j", { "ijs" } },
{ "jade", { "jade" } },
{ "java", { "java", "properties" } },
{ "js", { "js", "jsx" } },
{ "jinja2", { "j2" } },
{ "js", { "es6", "js", "jsx", "vue" } },
{ "json", { "json" } },
{ "jsp", { "jsp", "jspx", "jhtm", "jhtml" } },
{ "jsp", { "jsp", "jspx", "jhtm", "jhtml", "jspf", "tag", "tagf" } },
{ "julia", { "jl" } },
{ "kotlin", { "kt" } },
{ "less", { "less" } },
{ "liquid", { "liquid" } },
{ "lisp", { "lisp", "lsp" } },
{ "log", { "log" } },
{ "lua", { "lua" } },
{ "m4", { "m4" } },
{ "make", { "Makefiles", "mk", "mak" } },
@ -47,48 +76,73 @@ lang_spec_t langs[] = {
{ "mason", { "mas", "mhtml", "mpl", "mtxt" } },
{ "matlab", { "m" } },
{ "mathematica", { "m", "wl" } },
{ "md", { "markdown", "mdown", "mdwn", "mkdn", "mkd", "md" } },
{ "mercury", { "m", "moo" } },
{ "naccess", { "asa", "rsa" } },
{ "nim", { "nim" } },
{ "nix", { "nix" } },
{ "objc", { "m", "h" } },
{ "objcpp", { "mm", "h" } },
{ "ocaml", { "ml", "mli", "mll", "mly" } },
{ "octave", { "m" } },
{ "org", { "org" } },
{ "parrot", { "pir", "pasm", "pmc", "ops", "pod", "pg", "tg" } },
{ "pdb", { "pdb" } },
{ "perl", { "pl", "pm", "pm6", "pod", "t" } },
{ "php", { "php", "phpt", "php3", "php4", "php5", "phtml" } },
{ "pike", { "pike", "pmod" } },
{ "plone", { "pt", "cpt", "metadata", "cpy", "py" } },
{ "plist", { "plist" } },
{ "plone", { "pt", "cpt", "metadata", "cpy", "py", "xml", "zcml" } },
{ "powershell", { "ps1" } },
{ "proto", { "proto" } },
{ "ps1", { "ps1" } },
{ "pug", { "pug" } },
{ "puppet", { "pp" } },
{ "python", { "py" } },
{ "qml", { "qml" } },
{ "racket", { "rkt", "ss", "scm" } },
{ "rake", { "Rakefiles" } },
{ "rake", { "Rakefile" } },
{ "razor", { "cshtml" } },
{ "restructuredtext", { "rst" } },
{ "rs", { "rs" } },
{ "r", { "R", "Rmd", "Rnw", "Rtex", "Rrst" } },
{ "r", { "r", "R", "Rmd", "Rnw", "Rtex", "Rrst" } },
{ "rdoc", { "rdoc" } },
{ "ruby", { "rb", "rhtml", "rjs", "rxml", "erb", "rake", "spec" } },
{ "rust", { "rs" } },
{ "salt", { "sls" } },
{ "sass", { "sass", "scss" } },
{ "scala", { "scala" } },
{ "scheme", { "scm", "ss" } },
{ "shell", { "sh", "bash", "csh", "tcsh", "ksh", "zsh" } },
{ "shell", { "sh", "bash", "csh", "tcsh", "ksh", "zsh", "fish" } },
{ "smalltalk", { "st" } },
{ "sml", { "sml", "fun", "mlb", "sig" } },
{ "sql", { "sql", "ctl" } },
{ "stata", { "do", "ado" } },
{ "stylus", { "styl" } },
{ "swift", { "swift" } },
{ "tcl", { "tcl", "itcl", "itk" } },
{ "terraform", { "tf", "tfvars" } },
{ "tex", { "tex", "cls", "sty" } },
{ "thrift", { "thrift" } },
{ "tla", { "tla" } },
{ "tt", { "tt", "tt2", "ttml" } },
{ "toml", { "toml" } },
{ "ts", { "ts", "tsx" } },
{ "twig", { "twig" } },
{ "vala", { "vala", "vapi" } },
{ "vb", { "bas", "cls", "frm", "ctl", "vb", "resx" } },
{ "velocity", { "vm" } },
{ "verilog", { "v", "vh", "sv" } },
{ "velocity", { "vm", "vtl", "vsl" } },
{ "verilog", { "v", "vh", "sv", "svh" } },
{ "vhdl", { "vhd", "vhdl" } },
{ "vim", { "vim" } },
{ "vue", { "vue" } },
{ "wix", { "wxi", "wxs" } },
{ "wsdl", { "wsdl" } },
{ "wadl", { "wadl" } },
{ "xml", { "xml", "dtd", "xsl", "xslt", "ent" } },
{ "yaml", { "yaml", "yml" } }
{ "xml", { "xml", "dtd", "xsl", "xslt", "xsd", "ent", "tld", "plist", "wsdl" } },
{ "yaml", { "yaml", "yml" } },
{ "zeek", { "zeek", "bro", "bif" } },
{ "zephir", { "zep" } }
};
size_t get_lang_count() {

View file

@ -1,9 +1,10 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdio.h>
#include "log.h"
#include "util.h"
pthread_mutex_t print_mtx = PTHREAD_MUTEX_INITIALIZER;
static enum log_level log_threshold = LOG_LEVEL_ERR;
void set_log_level(enum log_level threshold) {
@ -43,6 +44,7 @@ void vplog(const unsigned int level, const char *fmt, va_list args) {
return;
}
pthread_mutex_lock(&print_mtx);
FILE *stream = out_fd;
switch (level) {
@ -63,6 +65,7 @@ void vplog(const unsigned int level, const char *fmt, va_list args) {
vfprintf(stream, fmt, args);
fprintf(stream, "\n");
pthread_mutex_unlock(&print_mtx);
}
void plog(const unsigned int level, const char *fmt, ...) {

View file

@ -3,6 +3,14 @@
#include <stdarg.h>
#include "config.h"
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
extern pthread_mutex_t print_mtx;
enum log_level {
LOG_LEVEL_DEBUG = 10,
LOG_LEVEL_MSG = 20,

View file

@ -1,8 +1,8 @@
#include <ctype.h>
#include <pcre.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/time.h>
#include <unistd.h>
#ifdef _WIN32
@ -11,10 +11,18 @@
#include "config.h"
#ifdef HAVE_SYS_CPUSET_H
#include <sys/cpuset.h>
#endif
#ifdef HAVE_PTHREAD_H
#include <pthread.h>
#endif
#if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(__FreeBSD__)
#include <pthread_np.h>
#endif
#include "log.h"
#include "options.h"
#include "search.h"
@ -35,6 +43,12 @@ int main(int argc, char **argv) {
int workers_len;
int num_cores;
#ifdef HAVE_PLEDGE
if (pledge("stdio rpath proc exec", NULL) == -1) {
die("pledge: %s", strerror(errno));
}
#endif
set_log_level(LOG_LEVEL_WARN);
work_queue = NULL;
@ -67,7 +81,7 @@ int main(int argc, char **argv) {
num_cores = (int)sysconf(_SC_NPROCESSORS_ONLN);
#endif
workers_len = num_cores;
workers_len = num_cores < 8 ? num_cores : 8;
if (opts.literal) {
workers_len--;
}
@ -109,6 +123,7 @@ int main(int argc, char **argv) {
generate_alpha_skip(opts.query, opts.query_len, alpha_skip_lookup, opts.casing == CASE_SENSITIVE);
find_skip_lookup = NULL;
generate_find_skip(opts.query, opts.query_len, &find_skip_lookup, opts.casing == CASE_SENSITIVE);
generate_hash(opts.query, opts.query_len, h_table, opts.casing == CASE_SENSITIVE);
if (opts.word_regexp) {
init_wordchar_table();
opts.literal_starts_wordchar = is_wordchar(opts.query[0]);
@ -120,7 +135,7 @@ int main(int argc, char **argv) {
}
if (opts.word_regexp) {
char *word_regexp_query;
ag_asprintf(&word_regexp_query, "\\b%s\\b", opts.query);
ag_asprintf(&word_regexp_query, "\\b(?:%s)\\b", opts.query);
free(opts.query);
opts.query = word_regexp_query;
opts.query_len = strlen(opts.query);
@ -137,9 +152,13 @@ int main(int argc, char **argv) {
if (rv != 0) {
die("Error in pthread_create(): %s", strerror(rv));
}
#if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(USE_CPU_SET)
#if defined(HAVE_PTHREAD_SETAFFINITY_NP) && (defined(USE_CPU_SET) || defined(HAVE_SYS_CPUSET_H))
if (opts.use_thread_affinity) {
#if defined(__linux__) || defined(__midipix__)
cpu_set_t cpu_set;
#elif __FreeBSD__
cpuset_t cpu_set;
#endif
CPU_ZERO(&cpu_set);
CPU_SET(i % num_cores, &cpu_set);
rv = pthread_setaffinity_np(workers[i].thread, sizeof(cpu_set), &cpu_set);
@ -156,11 +175,17 @@ int main(int argc, char **argv) {
log_debug("No CPU affinity support.");
#endif
}
#ifdef HAVE_PLEDGE
if (pledge("stdio rpath", NULL) == -1) {
die("pledge: %s", strerror(errno));
}
#endif
for (i = 0; paths[i] != NULL; i++) {
log_debug("searching path %s for %s", paths[i], opts.query);
symhash = NULL;
ignores *ig = init_ignore(root_ignores, "", 0);
struct stat s = {.st_dev = 0 };
struct stat s = { .st_dev = 0 };
#ifndef _WIN32
/* The device is ignored if opts.one_dev is false, so it's fine
* to leave it at the default 0
@ -188,7 +213,7 @@ int main(int argc, char **argv) {
double time_diff = ((long)stats.time_end.tv_sec * 1000000 + stats.time_end.tv_usec) -
((long)stats.time_start.tv_sec * 1000000 + stats.time_start.tv_usec);
time_diff /= 1000000;
printf("%ld matches\n%ld files contained matches\n%ld files searched\n%ld bytes searched\n%f seconds\n",
printf("%zu matches\n%zu files contained matches\n%zu files searched\n%zu bytes searched\n%f seconds\n",
stats.total_matches, stats.total_file_matches, stats.total_files, stats.total_bytes, time_diff);
pthread_mutex_destroy(&stats_mtx);
}

View file

@ -10,16 +10,18 @@
#include "config.h"
#include "ignore.h"
#include "options.h"
#include "lang.h"
#include "log.h"
#include "util.h"
#include "options.h"
#include "print.h"
#include "util.h"
const char *color_line_number = "\033[1;33m"; /* bold yellow */
const char *color_match = "\033[30;43m"; /* black with yellow background */
const char *color_path = "\033[1;32m"; /* bold green */
cli_options opts;
/* TODO: try to obey out_fd? */
void usage(void) {
printf("\n");
@ -57,11 +59,14 @@ Output Options:\n\
(Enabled by default)\n\
-C --context [LINES] Print lines before and after matches (Default: 2)\n\
--[no]group Same as --[no]break --[no]heading\n\
-g PATTERN Print filenames matching PATTERN\n\
-g --filename-pattern PATTERN\n\
Print filenames matching PATTERN\n\
-l --files-with-matches Only print filenames that contain matches\n\
(don't print the matching lines)\n\
-L --files-without-matches\n\
Only print filenames that don't contain matches\n\
--print-all-files Print headings for all files searched, even those that\n\
don't contain matches\n\
--[no]numbers Print line numbers. Default is to omit line numbers\n\
when searching streams\n\
-o --only-matching Prints only the matching part of the lines\n\
@ -70,6 +75,8 @@ Output Options:\n\
don't match\n\
--silent Suppress all log messages, including errors\n\
--stats Print stats (files scanned, time taken, etc.)\n\
--stats-only Print stats and nothing else.\n\
(Same as --count when searching a single file)\n\
--vimgrep Print results like vim's :vimgrep /pattern/g would\n\
(it reports every match on the line)\n\
-0 --null --print0 Separate filenames with null (for 'xargs -0')\n\
@ -89,20 +96,21 @@ Search Options:\n\
--ignore-dir NAME Alias for --ignore for compatibility with ack.\n\
-m --max-count NUM Skip the rest of a file after NUM matches (Default: 10,000)\n\
--one-device Don't follow links to other devices.\n\
-p --path-to-agignore STRING\n\
Use .agignore file at STRING\n\
-p --path-to-ignore STRING\n\
Use .ignore file at STRING\n\
-Q --literal Don't parse PATTERN as a regular expression\n\
-s --case-sensitive Match case sensitively\n\
-S --smart-case Match case insensitively unless PATTERN contains\n\
uppercase characters (Enabled by default)\n\
--search-binary Search binary files for matches\n\
-t --all-text Search all text files (doesn't include hidden files)\n\
-u --unrestricted Search all files (ignore .agignore, .gitignore, etc.;\n\
-u --unrestricted Search all files (ignore .ignore, .gitignore, etc.;\n\
searches binary and hidden files as well)\n\
-U --skip-vcs-ignores Ignore VCS ignore files\n\
(.gitignore, .hgignore, .svnignore; still obey .agignore)\n\
(.gitignore, .hgignore; still obey .ignore)\n\
-v --invert-match\n\
-w --word-regexp Only match whole words\n\
-W --width NUM Truncate match lines after NUM characters\n\
-z --search-zip Search contents of compressed (e.g., gzip) files\n\
\n");
printf("File Types:\n\
@ -111,7 +119,9 @@ The search can be restricted to certain types of files. Example:\n\
- Searches for 'needle' in files with suffix .htm, .html, .shtml or .xhtml.\n\
\n\
For a list of supported file types run:\n\
ag --list-file-types\n\n");
ag --list-file-types\n\n\
ag was originally created by Geoff Greer. More information (and the latest release)\n\
can be found at http://geoff.greer.fm/ag\n");
}
void print_version(void) {
@ -135,16 +145,29 @@ void print_version(void) {
}
void init_options(void) {
char *term = getenv("TERM");
memset(&opts, 0, sizeof(opts));
opts.casing = CASE_DEFAULT;
opts.color = TRUE;
if (term && !strcmp(term, "dumb")) {
opts.color = FALSE;
}
opts.color_win_ansi = FALSE;
opts.max_matches_per_file = 0;
opts.max_search_depth = DEFAULT_MAX_SEARCH_DEPTH;
#if defined(__APPLE__) || defined(__MACH__)
/* mamp() is slower than normal read() on macos. default to off */
opts.mmap = FALSE;
#else
opts.mmap = TRUE;
#endif
opts.multiline = TRUE;
opts.width = 0;
opts.path_sep = '\n';
opts.print_break = TRUE;
opts.print_path = PATH_PRINT_DEFAULT;
opts.print_all_paths = FALSE;
opts.print_line_numbers = TRUE;
opts.recurse_dirs = TRUE;
opts.color_path = ag_strdup(color_path);
@ -187,6 +210,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
int ch;
size_t i;
int path_len = 0;
int base_path_len = 0;
int useless = 0;
int group = 1;
int help = 0;
@ -202,6 +226,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
int rv;
size_t lang_count;
size_t lang_num = 0;
int has_filetype = 0;
size_t longopts_len, full_len;
option_t *longopts;
@ -233,6 +258,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
{ "debug", no_argument, NULL, 'D' },
{ "depth", required_argument, NULL, 0 },
{ "filename", no_argument, NULL, 0 },
{ "filename-pattern", required_argument, NULL, 'g' },
{ "file-search-regex", required_argument, NULL, 'G' },
{ "files-with-matches", no_argument, NULL, 'l' },
{ "files-without-matches", no_argument, NULL, 'L' },
@ -252,20 +278,32 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
{ "literal", no_argument, NULL, 'Q' },
{ "match", no_argument, &useless, 0 },
{ "max-count", required_argument, NULL, 'm' },
{ "mmap", no_argument, &opts.mmap, TRUE },
{ "multiline", no_argument, &opts.multiline, TRUE },
/* "no-" is deprecated. Remove these eventually. */
{ "no-numbers", no_argument, &opts.print_line_numbers, FALSE },
{ "no-recurse", no_argument, NULL, 'n' },
/* Accept both --no-* and --no* forms for convenience/BC */
{ "no-affinity", no_argument, &opts.use_thread_affinity, 0 },
{ "noaffinity", no_argument, &opts.use_thread_affinity, 0 },
{ "no-break", no_argument, &opts.print_break, 0 },
{ "nobreak", no_argument, &opts.print_break, 0 },
{ "no-color", no_argument, &opts.color, 0 },
{ "nocolor", no_argument, &opts.color, 0 },
{ "no-filename", no_argument, NULL, 0 },
{ "nofilename", no_argument, NULL, 0 },
{ "no-follow", no_argument, &opts.follow_symlinks, 0 },
{ "nofollow", no_argument, &opts.follow_symlinks, 0 },
{ "no-group", no_argument, &group, 0 },
{ "nogroup", no_argument, &group, 0 },
{ "no-heading", no_argument, &opts.print_path, PATH_PRINT_EACH_LINE },
{ "noheading", no_argument, &opts.print_path, PATH_PRINT_EACH_LINE },
{ "no-mmap", no_argument, &opts.mmap, FALSE },
{ "nommap", no_argument, &opts.mmap, FALSE },
{ "no-multiline", no_argument, &opts.multiline, FALSE },
{ "nomultiline", no_argument, &opts.multiline, FALSE },
{ "no-numbers", no_argument, &opts.print_line_numbers, FALSE },
{ "nonumbers", no_argument, &opts.print_line_numbers, FALSE },
{ "no-pager", no_argument, NULL, 0 },
{ "nopager", no_argument, NULL, 0 },
{ "no-recurse", no_argument, NULL, 'n' },
{ "norecurse", no_argument, NULL, 'n' },
{ "null", no_argument, NULL, '0' },
{ "numbers", no_argument, &opts.print_line_numbers, 2 },
@ -275,8 +313,9 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
{ "parallel", no_argument, &opts.parallel, 1 },
{ "passthrough", no_argument, &opts.passthrough, 1 },
{ "passthru", no_argument, &opts.passthrough, 1 },
{ "path-to-agignore", required_argument, NULL, 'p' },
{ "path-to-ignore", required_argument, NULL, 'p' },
{ "print0", no_argument, NULL, '0' },
{ "print-all-files", no_argument, NULL, 0 },
{ "print-long-lines", no_argument, &opts.print_long_lines, 1 },
{ "recurse", no_argument, NULL, 'r' },
{ "search-binary", no_argument, &opts.search_binary_files, 1 },
@ -286,9 +325,11 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
{ "skip-vcs-ignores", no_argument, NULL, 'U' },
{ "smart-case", no_argument, NULL, 'S' },
{ "stats", no_argument, &opts.stats, 1 },
{ "stats-only", no_argument, NULL, 0 },
{ "unrestricted", no_argument, NULL, 'u' },
{ "version", no_argument, &version, 1 },
{ "vimgrep", no_argument, &opts.vimgrep, 1 },
{ "width", required_argument, NULL, 'W' },
{ "word-regexp", no_argument, NULL, 'w' },
{ "workers", required_argument, NULL, 0 },
};
@ -337,8 +378,8 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
opts.stdout_inode = statbuf.st_ino;
}
int pcre_opts = 0;
while ((ch = getopt_long(argc, argv, "A:aB:C:cDG:g:FfHhiLlm:nop:QRrSsvVtuUwz0", longopts, &opt_index)) != -1) {
char *file_search_regex = NULL;
while ((ch = getopt_long(argc, argv, "A:aB:C:cDG:g:FfHhiLlm:nop:QRrSsvVtuUwW:z0", longopts, &opt_index)) != -1) {
switch (ch) {
case 'A':
if (optarg) {
@ -393,15 +434,14 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
case 'g':
needs_query = accepts_query = 0;
opts.match_files = 1;
/* Fall through and build regex */
/* fall through */
case 'G':
if (opts.casing == CASE_DEFAULT) {
opts.casing = CASE_SENSITIVE;
if (file_search_regex) {
log_err("File search regex (-g or -G) already specified.");
usage();
exit(1);
}
if (opts.casing != CASE_SENSITIVE) {
pcre_opts |= PCRE_CASELESS;
}
compile_study(&opts.file_search_regex, &opts.file_search_regex_extra, optarg, pcre_opts, 0);
file_search_regex = ag_strdup(optarg);
break;
case 'H':
opts.print_path = PATH_PRINT_TOP;
@ -413,11 +453,13 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
opts.casing = CASE_INSENSITIVE;
break;
case 'L':
opts.invert_match = 1;
/* fall through */
opts.print_nonmatching_files = 1;
opts.print_path = PATH_PRINT_TOP;
break;
case 'l':
needs_query = 0;
opts.print_filename_only = 1;
opts.print_path = PATH_PRINT_TOP;
break;
case 'm':
opts.max_matches_per_file = atoi(optarg);
@ -426,7 +468,8 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
opts.recurse_dirs = 0;
break;
case 'p':
opts.path_to_agignore = optarg;
opts.path_to_ignore = TRUE;
load_ignore_patterns(root_ignores, optarg);
break;
case 'o':
opts.only_matching = 1;
@ -467,6 +510,12 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
case 'w':
opts.word_regexp = 1;
break;
case 'W':
opts.width = strtol(optarg, &num_end, 10);
if (num_end == optarg || *num_end != '\0' || errno == ERANGE) {
die("Invalid width\n");
}
break;
case 'z':
opts.search_zip_files = 1;
break;
@ -490,17 +539,22 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
} else if (strcmp(longopts[opt_index].name, "ignore") == 0) {
add_ignore_pattern(root_ignores, optarg);
break;
} else if (strcmp(longopts[opt_index].name, "nofilename") == 0) {
} else if (strcmp(longopts[opt_index].name, "no-filename") == 0 ||
strcmp(longopts[opt_index].name, "nofilename") == 0) {
opts.print_path = PATH_PRINT_NOTHING;
opts.print_line_numbers = FALSE;
break;
} else if (strcmp(longopts[opt_index].name, "nopager") == 0) {
} else if (strcmp(longopts[opt_index].name, "no-pager") == 0 ||
strcmp(longopts[opt_index].name, "nopager") == 0) {
out_fd = stdout;
opts.pager = NULL;
break;
} else if (strcmp(longopts[opt_index].name, "pager") == 0) {
opts.pager = optarg;
break;
} else if (strcmp(longopts[opt_index].name, "print-all-files") == 0) {
opts.print_all_paths = TRUE;
break;
} else if (strcmp(longopts[opt_index].name, "workers") == 0) {
opts.workers = atoi(optarg);
break;
@ -519,6 +573,11 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
} else if (strcmp(longopts[opt_index].name, "silent") == 0) {
set_log_level(LOG_LEVEL_NONE);
break;
} else if (strcmp(longopts[opt_index].name, "stats-only") == 0) {
opts.print_filename_only = 1;
opts.print_path = PATH_PRINT_NOTHING;
opts.stats = 1;
break;
}
/* Continue to usage if we don't recognize the option */
@ -528,6 +587,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
for (i = 0; i < lang_count; i++) {
if (strcmp(longopts[opt_index].name, langs[i].name) == 0) {
has_filetype = 1;
ext_index[lang_num++] = i;
break;
}
@ -537,14 +597,32 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
}
log_err("option %s does not take a value", longopts[opt_index].name);
/* fall through */
default:
usage();
exit(1);
}
}
if (opts.casing == CASE_DEFAULT) {
opts.casing = CASE_SMART;
}
if (ext_index[0]) {
if (file_search_regex) {
int pcre_opts = 0;
if (opts.casing == CASE_INSENSITIVE || (opts.casing == CASE_SMART && is_lowercase(file_search_regex))) {
pcre_opts |= PCRE_CASELESS;
}
if (opts.word_regexp) {
char *old_file_search_regex = file_search_regex;
ag_asprintf(&file_search_regex, "\\b%s\\b", file_search_regex);
free(old_file_search_regex);
}
compile_study(&opts.file_search_regex, &opts.file_search_regex_extra, file_search_regex, pcre_opts, 0);
free(file_search_regex);
}
if (has_filetype) {
num_exts = combine_file_extensions(ext_index, lang_num, &extensions);
lang_regex = make_lang_regex(extensions, num_exts);
compile_study(&opts.file_search_regex, &opts.file_search_regex_extra, lang_regex, 0, 0);
@ -562,10 +640,6 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
argc -= optind;
argv += optind;
if (opts.casing == CASE_DEFAULT) {
opts.casing = CASE_SMART;
}
if (opts.pager) {
out_fd = popen(opts.pager, "w");
if (!out_fd) {
@ -574,6 +648,14 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
}
}
#ifdef HAVE_PLEDGE
if (opts.skip_vcs_ignores) {
if (pledge("stdio rpath proc", NULL) == -1) {
die("pledge: %s", strerror(errno));
}
}
#endif
if (help) {
usage();
exit(0);
@ -605,7 +687,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
if (home_dir && !opts.search_all_files) {
log_debug("Found user's home dir: %s", home_dir);
ag_asprintf(&ignore_file_path, "%s/%s", home_dir, ignore_pattern_files[0]);
ag_asprintf(&ignore_file_path, "%s/.agignore", home_dir);
load_ignore_patterns(root_ignores, ignore_file_path);
free(ignore_file_path);
}
@ -626,13 +708,30 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
buf_len += fread(gitconfig_res + buf_len, 1, 64, gitconfig_file);
} while (!feof(gitconfig_file) && buf_len > 0 && buf_len % 64 == 0);
gitconfig_res[buf_len] = '\0';
log_debug("Found user's global Git excludesfile: %s", gitconfig_res);
if (buf_len == 0) {
free(gitconfig_res);
const char *config_home = getenv("XDG_CONFIG_HOME");
if (config_home) {
ag_asprintf(&gitconfig_res, "%s/%s", config_home, "git/ignore");
} else if (home_dir) {
ag_asprintf(&gitconfig_res, "%s/%s", home_dir, ".config/git/ignore");
} else {
gitconfig_res = ag_strdup("");
}
}
log_debug("global core.excludesfile: %s", gitconfig_res);
load_ignore_patterns(root_ignores, gitconfig_res);
free(gitconfig_res);
pclose(gitconfig_file);
}
}
#ifdef HAVE_PLEDGE
if (pledge("stdio rpath proc", NULL) == -1) {
die("pledge: %s", strerror(errno));
}
#endif
if (opts.context > 0) {
opts.before = opts.context;
opts.after = opts.context;
@ -675,8 +774,13 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
}
if (accepts_query && argc > 0) {
// use the provided query
opts.query = ag_strdup(argv[0]);
if (!needs_query && strlen(argv[0]) == 0) {
// use default query
opts.query = ag_strdup(".");
} else {
// use the provided query
opts.query = ag_strdup(argv[0]);
}
argc--;
argv++;
} else if (!needs_query) {
@ -697,7 +801,10 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
}
char *path = NULL;
char *base_path = NULL;
#ifdef PATH_MAX
char *tmp = NULL;
#endif
opts.paths_len = argc;
if (argc > 0) {
*paths = ag_calloc(sizeof(char *), argc + 1);
@ -710,8 +817,22 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
path[path_len - 1] = '\0';
}
(*paths)[i] = path;
#ifdef PATH_MAX
tmp = ag_malloc(PATH_MAX);
(*base_paths)[i] = realpath(path, tmp);
base_path = realpath(path, tmp);
#else
base_path = realpath(path, NULL);
#endif
if (base_path) {
base_path_len = strlen(base_path);
/* add trailing slash */
if (base_path_len > 1 && base_path[base_path_len - 1] != '/') {
base_path = ag_realloc(base_path, base_path_len + 2);
base_path[base_path_len] = '/';
base_path[base_path_len + 1] = '\0';
}
}
(*base_paths)[i] = base_path;
}
/* Make sure we search these paths instead of stdin. */
opts.search_stream = 0;
@ -720,8 +841,12 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
*paths = ag_malloc(sizeof(char *) * 2);
*base_paths = ag_malloc(sizeof(char *) * 2);
(*paths)[0] = path;
#ifdef PATH_MAX
tmp = ag_malloc(PATH_MAX);
(*base_paths)[0] = realpath(path, tmp);
#else
(*base_paths)[0] = realpath(path, NULL);
#endif
i = 1;
}
(*paths)[i] = NULL;

View file

@ -51,15 +51,18 @@ typedef struct {
int literal_ends_wordchar;
size_t max_matches_per_file;
int max_search_depth;
int mmap;
int multiline;
int one_dev;
int only_matching;
char path_sep;
char *path_to_agignore;
int path_to_ignore;
int print_break;
int print_count;
int print_filename_only;
int print_nonmatching_files;
int print_path;
int print_all_paths;
int print_line_numbers;
int print_long_lines; /* TODO: support this in print.c */
int passthrough;
@ -83,12 +86,13 @@ typedef struct {
int parallel;
int use_thread_affinity;
int vimgrep;
size_t width;
int word_regexp;
int workers;
} cli_options;
/* global options. parse_options gives it sane values, everything else reads from it */
cli_options opts;
extern cli_options opts;
typedef struct option option_t;

View file

@ -1,13 +1,14 @@
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "ignore.h"
#include "log.h"
#include "options.h"
#include "print.h"
#include "search.h"
#include "util.h"
#ifdef _WIN32
#define fprintf(...) fprintf_w32(__VA_ARGS__)
@ -17,6 +18,88 @@ int first_file_match = 1;
const char *color_reset = "\033[0m\033[K";
const char *truncate_marker = " [...]";
__thread struct print_context {
size_t line;
char **context_prev_lines;
size_t prev_line;
size_t last_prev_line;
size_t prev_line_offset;
size_t line_preceding_current_match_offset;
size_t lines_since_last_match;
size_t last_printed_match;
int in_a_match;
int printing_a_match;
} print_context;
void print_init_context(void) {
if (print_context.context_prev_lines != NULL) {
return;
}
print_context.context_prev_lines = ag_calloc(sizeof(char *), (opts.before + 1));
print_context.line = 1;
print_context.prev_line = 0;
print_context.last_prev_line = 0;
print_context.prev_line_offset = 0;
print_context.line_preceding_current_match_offset = 0;
print_context.lines_since_last_match = INT_MAX;
print_context.last_printed_match = 0;
print_context.in_a_match = FALSE;
print_context.printing_a_match = FALSE;
}
void print_cleanup_context(void) {
size_t i;
if (print_context.context_prev_lines == NULL) {
return;
}
for (i = 0; i < opts.before; i++) {
if (print_context.context_prev_lines[i] != NULL) {
free(print_context.context_prev_lines[i]);
}
}
free(print_context.context_prev_lines);
print_context.context_prev_lines = NULL;
}
void print_context_append(const char *line, size_t len) {
if (opts.before == 0) {
return;
}
if (print_context.context_prev_lines[print_context.last_prev_line] != NULL) {
free(print_context.context_prev_lines[print_context.last_prev_line]);
}
print_context.context_prev_lines[print_context.last_prev_line] = ag_strndup(line, len);
print_context.last_prev_line = (print_context.last_prev_line + 1) % opts.before;
}
void print_trailing_context(const char *path, const char *buf, size_t n) {
char sep = '-';
if (opts.ackmate || opts.vimgrep) {
sep = ':';
}
if (print_context.lines_since_last_match != 0 &&
print_context.lines_since_last_match <= opts.after) {
if (opts.print_path == PATH_PRINT_EACH_LINE) {
print_path(path, ':');
}
print_line_number(print_context.line, sep);
fwrite(buf, 1, n, out_fd);
fputc('\n', out_fd);
}
print_context.line++;
if (!print_context.in_a_match && print_context.lines_since_last_match < INT_MAX) {
print_context.lines_since_last_match++;
}
}
void print_path(const char *path, const char sep) {
if (opts.print_path == PATH_PRINT_NOTHING && !opts.vimgrep) {
return;
@ -48,7 +131,12 @@ void print_path_count(const char *path, const char sep, const size_t count) {
}
void print_line(const char *buf, size_t buf_pos, size_t prev_line_offset) {
fwrite(buf + prev_line_offset, 1, buf_pos - prev_line_offset + 1, out_fd);
size_t write_chars = buf_pos - prev_line_offset + 1;
if (opts.width > 0 && opts.width < write_chars) {
write_chars = opts.width;
}
fwrite(buf + prev_line_offset, 1, write_chars, out_fd);
}
void print_binary_file_matches(const char *path) {
@ -58,19 +146,11 @@ void print_binary_file_matches(const char *path) {
}
void print_file_matches(const char *path, const char *buf, const size_t buf_len, const match_t matches[], const size_t matches_len) {
size_t line = 1;
char **context_prev_lines = NULL;
size_t prev_line = 0;
size_t last_prev_line = 0;
size_t prev_line_offset = 0;
size_t cur_match = 0;
size_t lines_since_last_match = INT_MAX;
ssize_t lines_to_print = 0;
size_t last_printed_match = 0;
char sep = '-';
size_t i, j;
int in_a_match = FALSE;
int printing_a_match = FALSE;
int blanks_between_matches = opts.context || opts.after || opts.before;
if (opts.ackmate || opts.vimgrep) {
sep = ':';
@ -92,20 +172,18 @@ void print_file_matches(const char *path, const char *buf, const size_t buf_len,
}
}
context_prev_lines = ag_calloc(sizeof(char *), (opts.before + 1));
for (i = 0; i <= buf_len && (cur_match < matches_len || lines_since_last_match <= opts.after); i++) {
for (i = 0; i <= buf_len && (cur_match < matches_len || print_context.lines_since_last_match <= opts.after); i++) {
if (cur_match < matches_len && i == matches[cur_match].start) {
in_a_match = TRUE;
print_context.in_a_match = TRUE;
/* We found the start of a match */
if (cur_match > 0 && opts.context && lines_since_last_match > (opts.before + opts.after + 1)) {
if (cur_match > 0 && blanks_between_matches && print_context.lines_since_last_match > (opts.before + opts.after + 1)) {
fprintf(out_fd, "--\n");
}
if (lines_since_last_match > 0 && opts.before > 0) {
if (print_context.lines_since_last_match > 0 && opts.before > 0) {
/* TODO: better, but still needs work */
/* print the previous line(s) */
lines_to_print = lines_since_last_match - (opts.after + 1);
lines_to_print = print_context.lines_since_last_match - (opts.after + 1);
if (lines_to_print < 0) {
lines_to_print = 0;
} else if ((size_t)lines_to_print > opts.before) {
@ -113,151 +191,150 @@ void print_file_matches(const char *path, const char *buf, const size_t buf_len,
}
for (j = (opts.before - lines_to_print); j < opts.before; j++) {
prev_line = (last_prev_line + j) % opts.before;
if (context_prev_lines[prev_line] != NULL) {
print_context.prev_line = (print_context.last_prev_line + j) % opts.before;
if (print_context.context_prev_lines[print_context.prev_line] != NULL) {
if (opts.print_path == PATH_PRINT_EACH_LINE) {
print_path(path, ':');
}
print_line_number(line - (opts.before - j), sep);
fprintf(out_fd, "%s\n", context_prev_lines[prev_line]);
print_line_number(print_context.line - (opts.before - j), sep);
fprintf(out_fd, "%s\n", print_context.context_prev_lines[print_context.prev_line]);
}
}
}
lines_since_last_match = 0;
print_context.lines_since_last_match = 0;
}
if (cur_match < matches_len && i == matches[cur_match].end) {
/* We found the end of a match. */
cur_match++;
in_a_match = FALSE;
print_context.in_a_match = FALSE;
}
/* We found the end of a line. */
if (buf[i] == '\n' && opts.before > 0) {
if (context_prev_lines[last_prev_line] != NULL) {
free(context_prev_lines[last_prev_line]);
}
if ((i == buf_len || buf[i] == '\n') && opts.before > 0) {
/* We don't want to strcpy the \n */
context_prev_lines[last_prev_line] = ag_strndup(&buf[prev_line_offset], i - prev_line_offset);
last_prev_line = (last_prev_line + 1) % opts.before;
print_context_append(&buf[print_context.prev_line_offset], i - print_context.prev_line_offset);
}
if (buf[i] == '\n' || i == buf_len) {
if (lines_since_last_match == 0) {
if (i == buf_len || buf[i] == '\n') {
if (print_context.lines_since_last_match == 0) {
if (opts.print_path == PATH_PRINT_EACH_LINE && !opts.search_stream) {
print_path(path, ':');
}
if (opts.ackmate) {
/* print headers for ackmate to parse */
print_line_number(line, ';');
for (; last_printed_match < cur_match; last_printed_match++) {
/* Don't print negative offsets. This isn't quite right, but not many people use --ackmate */
long start = (long)(matches[last_printed_match].start - prev_line_offset);
if (start < 0) {
start = 0;
}
fprintf(out_fd, "%li %li",
print_line_number(print_context.line, ';');
for (; print_context.last_printed_match < cur_match; print_context.last_printed_match++) {
size_t start = matches[print_context.last_printed_match].start - print_context.line_preceding_current_match_offset;
fprintf(out_fd, "%lu %lu",
start,
(long)(matches[last_printed_match].end - matches[last_printed_match].start));
last_printed_match == cur_match - 1 ? fputc(':', out_fd) : fputc(',', out_fd);
matches[print_context.last_printed_match].end - matches[print_context.last_printed_match].start);
print_context.last_printed_match == cur_match - 1 ? fputc(':', out_fd) : fputc(',', out_fd);
}
print_line(buf, i, prev_line_offset);
print_line(buf, i, print_context.prev_line_offset);
} else if (opts.vimgrep) {
for (; last_printed_match < cur_match; last_printed_match++) {
for (; print_context.last_printed_match < cur_match; print_context.last_printed_match++) {
print_path(path, sep);
print_line_number(line, sep);
print_column_number(matches, last_printed_match, prev_line_offset, sep);
print_line(buf, i, prev_line_offset);
print_line_number(print_context.line, sep);
print_column_number(matches, print_context.last_printed_match, print_context.prev_line_offset, sep);
print_line(buf, i, print_context.prev_line_offset);
}
} else {
print_line_number(line, ':');
print_line_number(print_context.line, ':');
int printed_match = FALSE;
if (opts.column) {
print_column_number(matches, last_printed_match, prev_line_offset, ':');
print_column_number(matches, print_context.last_printed_match, print_context.prev_line_offset, ':');
}
if (printing_a_match && opts.color) {
if (print_context.printing_a_match && opts.color) {
fprintf(out_fd, "%s", opts.color_match);
}
for (j = prev_line_offset; j <= i; j++) {
if (last_printed_match < matches_len && j == matches[last_printed_match].end) {
for (j = print_context.prev_line_offset; j <= i; j++) {
/* close highlight of match term */
if (print_context.last_printed_match < matches_len && j == matches[print_context.last_printed_match].end) {
if (opts.color) {
fprintf(out_fd, "%s", color_reset);
}
printing_a_match = FALSE;
last_printed_match++;
print_context.printing_a_match = FALSE;
print_context.last_printed_match++;
printed_match = TRUE;
if (opts.only_matching) {
fputc('\n', out_fd);
}
}
if (last_printed_match < matches_len && j == matches[last_printed_match].start) {
/* skip remaining characters if truncation width exceeded, needs to be done
* before highlight opening */
if (j < buf_len && opts.width > 0 && j - print_context.prev_line_offset >= opts.width) {
if (j < i) {
fputs(truncate_marker, out_fd);
}
fputc('\n', out_fd);
/* prevent any more characters or highlights */
j = i;
print_context.last_printed_match = matches_len;
}
/* open highlight of match term */
if (print_context.last_printed_match < matches_len && j == matches[print_context.last_printed_match].start) {
if (opts.only_matching && printed_match) {
if (opts.print_path == PATH_PRINT_EACH_LINE) {
print_path(path, ':');
}
print_line_number(line, ':');
print_line_number(print_context.line, ':');
if (opts.column) {
print_column_number(matches, last_printed_match, prev_line_offset, ':');
print_column_number(matches, print_context.last_printed_match, print_context.prev_line_offset, ':');
}
}
if (opts.color) {
fprintf(out_fd, "%s", opts.color_match);
}
printing_a_match = TRUE;
print_context.printing_a_match = TRUE;
}
/* Don't print the null terminator */
if (j < buf_len) {
/* if only_matching is set, print only matches and newlines */
if (!opts.only_matching || printing_a_match) {
fputc(buf[j], out_fd);
if (!opts.only_matching || print_context.printing_a_match) {
if (opts.width == 0 || j - print_context.prev_line_offset < opts.width) {
fputc(buf[j], out_fd);
}
}
}
}
if (printing_a_match && opts.color) {
if (print_context.printing_a_match && opts.color) {
fprintf(out_fd, "%s", color_reset);
}
}
} else if (lines_since_last_match <= opts.after) {
/* print context after matching line */
if (opts.print_path == PATH_PRINT_EACH_LINE) {
print_path(path, ':');
}
print_line_number(line, sep);
for (j = prev_line_offset; j < i; j++) {
fputc(buf[j], out_fd);
}
fputc('\n', out_fd);
}
prev_line_offset = i + 1; /* skip the newline */
line++;
if (!in_a_match && lines_since_last_match < INT_MAX) {
lines_since_last_match++;
if (opts.search_stream) {
print_context.last_printed_match = 0;
break;
}
/* print context after matching line */
print_trailing_context(path, &buf[print_context.prev_line_offset], i - print_context.prev_line_offset);
print_context.prev_line_offset = i + 1; /* skip the newline */
if (!print_context.in_a_match) {
print_context.line_preceding_current_match_offset = i + 1;
}
/* File doesn't end with a newline. Print one so the output is pretty. */
if (i == buf_len && buf[i] != '\n' && !opts.search_stream) {
if (i == buf_len && buf[i - 1] != '\n') {
fputc('\n', out_fd);
}
}
}
for (i = 0; i < opts.before; i++) {
if (context_prev_lines[i] != NULL) {
free(context_prev_lines[i]);
}
/* Flush output if stdout is not a tty */
if (opts.stdout_inode) {
fflush(out_fd);
}
free(context_prev_lines);
}
void print_line_number(size_t line, const char sep) {
if (!opts.print_line_numbers) {
return;
}
if (opts.search_stream && opts.stream_line_num) {
line = opts.stream_line_num;
}
if (opts.color) {
fprintf(out_fd, "%s%lu%s%c", opts.color_line_number, (unsigned long)line, color_reset, sep);
} else {

View file

@ -3,6 +3,10 @@
#include "util.h"
void print_init_context(void);
void print_cleanup_context(void);
void print_context_append(const char *line, size_t len);
void print_trailing_context(const char *path, const char *buf, size_t n);
void print_path(const char *path, const char sep);
void print_path_count(const char *path, const char sep, const size_t count);
void print_line(const char *buf, size_t buf_pos, size_t prev_line_offset);

View file

@ -1,10 +1,10 @@
#ifdef _WIN32
#include <windows.h>
#include <stdio.h>
#include "print.h"
#include <io.h>
#include <stdarg.h>
#include "print.h"
#include <stdio.h>
#include <windows.h>
#ifndef FOREGROUND_MASK
#define FOREGROUND_MASK (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY)

View file

@ -7,6 +7,7 @@ typedef struct {
const ignores *ig;
const char *base_path;
size_t base_path_len;
const char *path_start;
} scandir_baton_t;
typedef int (*filter_fp)(const char *path, const struct dirent *, void *);

View file

@ -1,18 +1,33 @@
#include "search.h"
#include "print.h"
#include "scandir.h"
void search_buf(const char *buf, const size_t buf_len,
const char *dir_full_path) {
size_t alpha_skip_lookup[256];
size_t *find_skip_lookup;
uint8_t h_table[H_SIZE] __attribute__((aligned(64)));
work_queue_t *work_queue = NULL;
work_queue_t *work_queue_tail = NULL;
int done_adding_files = 0;
pthread_cond_t files_ready = PTHREAD_COND_INITIALIZER;
pthread_mutex_t stats_mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t work_queue_mtx = PTHREAD_MUTEX_INITIALIZER;
symdir_t *symhash = NULL;
/* Returns: -1 if skipped, otherwise # of matches */
ssize_t search_buf(const char *buf, const size_t buf_len,
const char *dir_full_path) {
int binary = -1; /* 1 = yes, 0 = no, -1 = don't know */
size_t buf_offset = 0;
if (opts.search_stream) {
binary = 0;
} else if (!opts.search_binary_files) {
} else if (!opts.search_binary_files && opts.mmap) { /* if not using mmap, binary files have already been skipped */
binary = is_binary((const void *)buf, buf_len);
if (binary) {
log_debug("File %s is binary. Skipping...", dir_full_path);
return;
return -1;
}
}
@ -38,16 +53,26 @@ void search_buf(const char *buf, const size_t buf_len,
if (!opts.literal && opts.query_len == 1 && opts.query[0] == '.') {
matches_size = 1;
matches = ag_malloc(matches_size * sizeof(match_t));
matches = matches == NULL ? ag_malloc(matches_size * sizeof(match_t)) : matches;
matches[0].start = 0;
matches[0].end = buf_len;
matches_len = 1;
} else if (opts.literal) {
const char *match_ptr = buf;
strncmp_fp ag_strnstr_fp = get_strstr(opts.casing);
while (buf_offset < buf_len) {
match_ptr = ag_strnstr_fp(match_ptr, opts.query, buf_len - buf_offset, opts.query_len, alpha_skip_lookup, find_skip_lookup);
/* hash_strnstr only for little-endian platforms that allow unaligned access */
#if defined(__i386__) || defined(__x86_64__)
/* Decide whether to fall back on boyer-moore */
if ((size_t)opts.query_len < 2 * sizeof(uint16_t) - 1 || opts.query_len >= UCHAR_MAX) {
match_ptr = boyer_moore_strnstr(match_ptr, opts.query, buf_len - buf_offset, opts.query_len, alpha_skip_lookup, find_skip_lookup, opts.casing == CASE_INSENSITIVE);
} else {
match_ptr = hash_strnstr(match_ptr, opts.query, buf_len - buf_offset, opts.query_len, h_table, opts.casing == CASE_SENSITIVE);
}
#else
match_ptr = boyer_moore_strnstr(match_ptr, opts.query, buf_len - buf_offset, opts.query_len, alpha_skip_lookup, find_skip_lookup, opts.casing == CASE_INSENSITIVE);
#endif
if (match_ptr == NULL) {
break;
}
@ -66,8 +91,8 @@ void search_buf(const char *buf, const size_t buf_len,
/* It's a match */
} else {
/* It's not a match */
match_ptr += opts.query_len;
buf_offset = end - buf;
match_ptr += find_skip_lookup[0] - opts.query_len + 1;
buf_offset = match_ptr - buf;
continue;
}
}
@ -111,7 +136,7 @@ void search_buf(const char *buf, const size_t buf_len,
}
} else {
while (buf_offset < buf_len) {
char *line;
const char *line;
size_t line_len = buf_getline(&line, buf, buf_len, buf_offset);
if (!line) {
break;
@ -163,25 +188,16 @@ multiline_done:
pthread_mutex_unlock(&stats_mtx);
}
if (matches_len > 0) {
if (!opts.print_nonmatching_files && (matches_len > 0 || opts.print_all_paths)) {
if (binary == -1 && !opts.print_filename_only) {
binary = is_binary((const void *)buf, buf_len);
}
pthread_mutex_lock(&print_mtx);
if (opts.print_filename_only) {
/* If the --files-without-matches or -L option is passed we should
* not print a matching line. This option currently sets
* opts.print_filename_only and opts.invert_match. Unfortunately
* setting the latter has the side effect of making matches.len = 1
* on a file-without-matches which is not desired behaviour. See
* GitHub issue 206 for the consequences if this behaviour is not
* checked. */
if (!opts.invert_match || matches_len < 2) {
if (opts.print_count) {
print_path_count(dir_full_path, opts.path_sep, (size_t)matches_len);
} else {
print_path(dir_full_path, opts.path_sep);
}
if (opts.print_count) {
print_path_count(dir_full_path, opts.path_sep, (size_t)matches_len);
} else {
print_path(dir_full_path, opts.path_sep);
}
} else if (binary) {
print_binary_file_matches(dir_full_path);
@ -196,33 +212,77 @@ multiline_done:
log_debug("No match in %s", dir_full_path);
}
if (matches_len == 0 && opts.search_stream) {
print_context_append(buf, buf_len - 1);
}
if (matches_size > 0) {
free(matches);
}
/* FIXME: handle case where matches_len > SSIZE_MAX */
return (ssize_t)matches_len;
}
/* Return value: -1 if skipped, otherwise # of matches */
/* TODO: this will only match single lines. multi-line regexes silently don't match */
void search_stream(FILE *stream, const char *path) {
ssize_t search_stream(FILE *stream, const char *path) {
char *line = NULL;
ssize_t matches_count = 0;
ssize_t line_len = 0;
size_t line_cap = 0;
size_t i;
print_init_context();
for (i = 1; (line_len = getline(&line, &line_cap, stream)) > 0; i++) {
ssize_t result;
opts.stream_line_num = i;
search_buf(line, line_len, path);
result = search_buf(line, line_len, path);
if (result > 0) {
if (matches_count == -1) {
matches_count = 0;
}
matches_count += result;
} else if (matches_count <= 0 && result == -1) {
matches_count = -1;
}
if (line[line_len - 1] == '\n') {
line_len--;
}
print_trailing_context(path, line, line_len);
}
free(line);
print_cleanup_context();
return matches_count;
}
void search_file(const char *file_full_path) {
int fd;
int fd = -1;
off_t f_len = 0;
char *buf = NULL;
struct stat statbuf;
int rv = 0;
FILE *pipe = NULL;
int matches_count = -1;
FILE *fp = NULL;
rv = stat(file_full_path, &statbuf);
if (rv != 0) {
log_err("Skipping %s: Error fstat()ing file.", file_full_path);
goto cleanup;
}
if (opts.stdout_inode != 0 && opts.stdout_inode == statbuf.st_ino) {
log_debug("Skipping %s: stdout is redirected to it", file_full_path);
goto cleanup;
}
// handling only regular files and FIFOs
if (!S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
log_err("Skipping %s: Mode %u is not a file.", file_full_path, statbuf.st_mode);
goto cleanup;
}
fd = open(file_full_path, O_RDONLY);
if (fd < 0) {
@ -231,6 +291,7 @@ void search_file(const char *file_full_path) {
goto cleanup;
}
// repeating stat check with file handle to prevent TOCTOU issue
rv = fstat(fd, &statbuf);
if (rv != 0) {
log_err("Skipping %s: Error fstat()ing file.", file_full_path);
@ -242,23 +303,30 @@ void search_file(const char *file_full_path) {
goto cleanup;
}
if ((statbuf.st_mode & S_IFMT) == 0) {
// handling only regular files and FIFOs
if (!S_ISREG(statbuf.st_mode) && !S_ISFIFO(statbuf.st_mode)) {
log_err("Skipping %s: Mode %u is not a file.", file_full_path, statbuf.st_mode);
goto cleanup;
}
print_init_context();
if (statbuf.st_mode & S_IFIFO) {
log_debug("%s is a named pipe. stream searching", file_full_path);
pipe = fdopen(fd, "r");
search_stream(pipe, file_full_path);
fclose(pipe);
fp = fdopen(fd, "r");
matches_count = search_stream(fp, file_full_path);
fclose(fp);
goto cleanup;
}
f_len = statbuf.st_size;
if (f_len == 0) {
log_debug("Skipping %s: file is empty.", file_full_path);
if (opts.query[0] == '.' && opts.query_len == 1 && !opts.literal && opts.search_all_files) {
matches_count = search_buf(buf, f_len, file_full_path);
} else {
log_debug("Skipping %s: file is empty.", file_full_path);
}
goto cleanup;
}
@ -286,42 +354,86 @@ void search_file(const char *file_full_path) {
goto cleanup;
}
#else
buf = mmap(0, f_len, PROT_READ, MAP_SHARED, fd, 0);
if (buf == MAP_FAILED) {
log_err("File %s failed to load: %s.", file_full_path, strerror(errno));
goto cleanup;
}
if (opts.mmap) {
buf = mmap(0, f_len, PROT_READ, MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED) {
log_err("File %s failed to load: %s.", file_full_path, strerror(errno));
goto cleanup;
}
#if HAVE_MADVISE
madvise(buf, f_len, MADV_SEQUENTIAL);
madvise(buf, f_len, MADV_SEQUENTIAL);
#elif HAVE_POSIX_FADVISE
posix_fadvise(fd, 0, f_len, POSIX_MADV_SEQUENTIAL);
posix_fadvise(fd, 0, f_len, POSIX_MADV_SEQUENTIAL);
#endif
} else {
buf = ag_malloc(f_len);
ssize_t bytes_read = 0;
if (!opts.search_binary_files) {
bytes_read += read(fd, buf, ag_min(f_len, 512));
// Optimization: If skipping binary files, don't read the whole buffer before checking if binary or not.
if (is_binary(buf, f_len)) {
log_debug("File %s is binary. Skipping...", file_full_path);
goto cleanup;
}
}
while (bytes_read < f_len) {
bytes_read += read(fd, buf + bytes_read, f_len);
}
if (bytes_read != f_len) {
die("File %s read(): expected to read %u bytes but read %u", file_full_path, f_len, bytes_read);
}
}
#endif
if (opts.search_zip_files) {
ag_compression_type zip_type = is_zipped(buf, f_len);
if (zip_type != AG_NO_COMPRESSION) {
#if HAVE_FOPENCOOKIE
log_debug("%s is a compressed file. stream searching", file_full_path);
fp = decompress_open(fd, "r", zip_type);
matches_count = search_stream(fp, file_full_path);
fclose(fp);
#else
int _buf_len = (int)f_len;
char *_buf = decompress(zip_type, buf, f_len, file_full_path, &_buf_len);
if (_buf == NULL || _buf_len == 0) {
log_err("Cannot decompress zipped file %s", file_full_path);
goto cleanup;
}
search_buf(_buf, _buf_len, file_full_path);
matches_count = search_buf(_buf, _buf_len, file_full_path);
free(_buf);
#endif
goto cleanup;
}
}
search_buf(buf, f_len, file_full_path);
matches_count = search_buf(buf, f_len, file_full_path);
cleanup:
if (opts.print_nonmatching_files && matches_count == 0) {
pthread_mutex_lock(&print_mtx);
print_path(file_full_path, opts.path_sep);
pthread_mutex_unlock(&print_mtx);
opts.match_found = 1;
}
print_cleanup_context();
if (buf != NULL) {
#ifdef _WIN32
UnmapViewOfFile(buf);
#else
munmap(buf, f_len);
if (opts.mmap) {
if (buf != MAP_FAILED) {
munmap(buf, f_len);
}
} else {
free(buf);
}
#endif
}
if (fd != -1) {
@ -421,6 +533,8 @@ void search_dir(ignores *ig, const char *base_path, const char *path, const int
struct dirent *dir = NULL;
scandir_baton_t scandir_baton;
int results = 0;
size_t base_path_len = 0;
const char *path_start = path;
char *dir_full_path = NULL;
const char *ignore_file = NULL;
@ -435,26 +549,29 @@ void search_dir(ignores *ig, const char *base_path, const char *path, const int
return;
}
/* find agignore/gitignore/hgignore/etc files to load ignore patterns from */
/* find .*ignore files to load ignore patterns from */
for (i = 0; opts.skip_vcs_ignores ? (i == 0) : (ignore_pattern_files[i] != NULL); i++) {
ignore_file = ignore_pattern_files[i];
ag_asprintf(&dir_full_path, "%s/%s", path, ignore_file);
if (strcmp(SVN_DIR, ignore_file) == 0) {
load_svn_ignore_patterns(ig, dir_full_path);
} else {
load_ignore_patterns(ig, dir_full_path);
}
load_ignore_patterns(ig, dir_full_path);
free(dir_full_path);
dir_full_path = NULL;
}
if (opts.path_to_agignore) {
load_ignore_patterns(ig, opts.path_to_agignore);
/* path_start is the part of path that isn't in base_path
* base_path will have a trailing '/' because we put it there in parse_options
*/
base_path_len = base_path ? strlen(base_path) : 0;
for (i = 0; ((size_t)i < base_path_len) && (path[i]) && (base_path[i] == path[i]); i++) {
path_start = path + i + 1;
}
log_debug("search_dir: path is '%s', base_path is '%s', path_start is '%s'", path, base_path, path_start);
scandir_baton.ig = ig;
scandir_baton.base_path = base_path;
scandir_baton.base_path_len = base_path ? strlen(base_path) : 0;
scandir_baton.base_path_len = base_path_len;
scandir_baton.path_start = path_start;
results = ag_scandir(path, &dir_list, &filename_filter, &scandir_baton);
if (results == 0) {
log_debug("No results found in directory %s", path);

View file

@ -23,15 +23,17 @@
#include <pthread.h>
#endif
#include "decompress.h"
#include "ignore.h"
#include "log.h"
#include "options.h"
#include "print.h"
#include "util.h"
#include "uthash.h"
#include "util.h"
size_t alpha_skip_lookup[256];
size_t *find_skip_lookup;
extern size_t alpha_skip_lookup[256];
extern size_t *find_skip_lookup;
extern uint8_t h_table[H_SIZE] __attribute__((aligned(64)));
struct work_queue_t {
char *path;
@ -39,13 +41,12 @@ struct work_queue_t {
};
typedef struct work_queue_t work_queue_t;
work_queue_t *work_queue;
work_queue_t *work_queue_tail;
int done_adding_files;
pthread_cond_t files_ready;
pthread_mutex_t print_mtx;
pthread_mutex_t stats_mtx;
pthread_mutex_t work_queue_mtx;
extern work_queue_t *work_queue;
extern work_queue_t *work_queue_tail;
extern int done_adding_files;
extern pthread_cond_t files_ready;
extern pthread_mutex_t stats_mtx;
extern pthread_mutex_t work_queue_mtx;
/* For symlink loop detection */
@ -63,11 +64,11 @@ typedef struct {
UT_hash_handle hh;
} symdir_t;
symdir_t *symhash;
extern symdir_t *symhash;
void search_buf(const char *buf, const size_t buf_len,
const char *dir_full_path);
void search_stream(FILE *stream, const char *path);
ssize_t search_buf(const char *buf, const size_t buf_len,
const char *dir_full_path);
ssize_t search_stream(FILE *stream, const char *path);
void search_file(const char *file_full_path);
void *search_file_worker(void *i);

View file

@ -24,9 +24,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UTHASH_H
#define UTHASH_H
#include <string.h> /* memcmp,strlen */
#include <stddef.h> /* ptrdiff_t */
#include <stdlib.h> /* exit() */
#include <string.h> /* memcmp,strlen */
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
@ -457,24 +457,34 @@ typedef unsigned char uint8_t;
switch (_hj_k) { \
case 11: \
hashv += ((unsigned)_hj_key[10] << 24); \
/* fall through */ \
case 10: \
hashv += ((unsigned)_hj_key[9] << 16); \
/* fall through */ \
case 9: \
hashv += ((unsigned)_hj_key[8] << 8); \
/* fall through */ \
case 8: \
_hj_j += ((unsigned)_hj_key[7] << 24); \
/* fall through */ \
case 7: \
_hj_j += ((unsigned)_hj_key[6] << 16); \
/* fall through */ \
case 6: \
_hj_j += ((unsigned)_hj_key[5] << 8); \
/* fall through */ \
case 5: \
_hj_j += _hj_key[4]; \
/* fall through */ \
case 4: \
_hj_i += ((unsigned)_hj_key[3] << 24); \
/* fall through */ \
case 3: \
_hj_i += ((unsigned)_hj_key[2] << 16); \
/* fall through */ \
case 2: \
_hj_i += ((unsigned)_hj_key[1] << 8); \
/* fall through */ \
case 1: \
_hj_i += _hj_key[0]; \
} \

View file

@ -1,14 +1,15 @@
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "util.h"
#include "config.h"
#include "util.h"
#ifdef _WIN32
#include <windows.h>
#define flockfile(x)
#define funlockfile(x)
#define getc_unlocked(x) getc(x)
@ -20,6 +21,8 @@
} \
return ptr;
FILE *out_fd = NULL;
ag_stats stats;
void *ag_malloc(size_t size) {
void *ptr = malloc(size);
CHECK_AND_RETURN(ptr)
@ -147,14 +150,47 @@ size_t ag_max(size_t a, size_t b) {
return a;
}
size_t ag_min(size_t a, size_t b) {
if (b < a) {
return b;
}
return a;
}
void generate_hash(const char *find, const size_t f_len, uint8_t *h_table, const int case_sensitive) {
int i;
for (i = f_len - sizeof(uint16_t); i >= 0; i--) {
// Add all 2^sizeof(uint16_t) combinations of capital letters to the hash table
int caps_set;
for (caps_set = 0; caps_set < (1 << sizeof(uint16_t)); caps_set++) {
word_t word;
memcpy(&word.as_chars, find + i, sizeof(uint16_t));
int cap_index;
// Capitalize the letters whose corresponding bits in caps_set are 1
for (cap_index = 0; caps_set >> cap_index; cap_index++) {
if ((caps_set >> cap_index) & 1)
word.as_chars[cap_index] -= 'a' - 'A';
}
size_t h;
// Find next free cell
for (h = word.as_word % H_SIZE; h_table[h]; h = (h + 1) % H_SIZE)
;
h_table[h] = i + 1;
// Don't add capital letters if case sensitive
if (case_sensitive)
break;
}
}
}
/* Boyer-Moore strstr */
const char *boyer_moore_strnstr(const char *s, const char *find, const size_t s_len, const size_t f_len,
const size_t alpha_skip_lookup[], const size_t *find_skip_lookup) {
const size_t alpha_skip_lookup[], const size_t *find_skip_lookup, const int case_insensitive) {
ssize_t i;
size_t pos = f_len - 1;
while (pos < s_len) {
for (i = f_len - 1; i >= 0 && s[pos] == find[i]; pos--, i--) {
for (i = f_len - 1; i >= 0 && (case_insensitive ? tolower(s[pos]) : s[pos]) == find[i]; pos--, i--) {
}
if (i < 0) {
return s + pos + 1;
@ -165,34 +201,44 @@ const char *boyer_moore_strnstr(const char *s, const char *find, const size_t s_
return NULL;
}
/* Copy-pasted from above. Yes I know this is bad. One day I might even fix it. */
const char *boyer_moore_strncasestr(const char *s, const char *find, const size_t s_len, const size_t f_len,
const size_t alpha_skip_lookup[], const size_t *find_skip_lookup) {
ssize_t i;
size_t pos = f_len - 1;
// Clang's -fsanitize=alignment (included in -fsanitize=undefined) will flag
// the intentional unaligned access here, so suppress it for this function
NO_SANITIZE_ALIGNMENT const char *hash_strnstr(const char *s, const char *find, const size_t s_len, const size_t f_len, uint8_t *h_table, const int case_sensitive) {
if (s_len < f_len)
return NULL;
while (pos < s_len) {
for (i = f_len - 1; i >= 0 && tolower(s[pos]) == find[i]; pos--, i--) {
// Step through s
const size_t step = f_len - sizeof(uint16_t) + 1;
size_t s_i = f_len - sizeof(uint16_t);
for (; s_i <= s_len - f_len; s_i += step) {
size_t h;
for (h = *(const uint16_t *)(s + s_i) % H_SIZE; h_table[h]; h = (h + 1) % H_SIZE) {
const char *R = s + s_i - (h_table[h] - 1);
size_t i;
// Check putative match
for (i = 0; i < f_len; i++) {
if ((case_sensitive ? R[i] : tolower(R[i])) != find[i])
goto next_hash_cell;
}
return R; // Found
next_hash_cell:;
}
if (i < 0) {
return s + pos + 1;
}
pos += ag_max(alpha_skip_lookup[(unsigned char)s[pos]], find_skip_lookup[i]);
}
// Check tail
for (s_i = s_i - step + 1; s_i <= s_len - f_len; s_i++) {
size_t i;
const char *R = s + s_i;
for (i = 0; i < f_len; i++) {
char s_c = case_sensitive ? R[i] : tolower(R[i]);
if (s_c != find[i])
goto next_start;
}
return R;
next_start:;
}
return NULL;
}
strncmp_fp get_strstr(enum case_behavior casing) {
strncmp_fp ag_strncmp_fp = &boyer_moore_strnstr;
if (casing == CASE_INSENSITIVE) {
ag_strncmp_fp = &boyer_moore_strncasestr;
}
return ag_strncmp_fp;
}
size_t invert_matches(const char *buf, const size_t buf_len, match_t matches[], size_t matches_len) {
size_t i;
size_t match_read_index = 0;
@ -291,6 +337,7 @@ int is_binary(const void *buf, const size_t buf_len) {
size_t i;
if (buf_len == 0) {
/* Is an empty file binary? Is it text? */
return 0;
}
@ -299,7 +346,7 @@ int is_binary(const void *buf, const size_t buf_len) {
return 0;
}
if (buf_len >= 4 && strncmp(buf, "%PDF-", 5) == 0) {
if (buf_len >= 5 && strncmp(buf, "%PDF-", 5) == 0) {
/* PDF. This is binary. */
return 1;
}
@ -379,7 +426,7 @@ int binary_search(const char *needle, char **haystack, int start, int end) {
return -1;
}
mid = (start + end) / 2; /* can screw up on arrays with > 2 billion elements */
mid = start + ((end - start) / 2);
rc = strcmp(needle, haystack[mid]);
if (rc < 0) {
@ -435,13 +482,20 @@ int is_directory(const char *path, const struct dirent *d) {
free(full_path);
return FALSE;
}
#ifdef _WIN32
int is_dir = GetFileAttributesA(full_path) & FILE_ATTRIBUTE_DIRECTORY;
#else
int is_dir = S_ISDIR(s.st_mode);
#endif
free(full_path);
return S_ISDIR(s.st_mode);
return is_dir;
}
int is_symlink(const char *path, const struct dirent *d) {
#ifdef _WIN32
return 0;
char full_path[MAX_PATH + 1] = { 0 };
sprintf(full_path, "%s\\%s", path, d->d_name);
return (GetFileAttributesA(full_path) & FILE_ATTRIBUTE_REPARSE_POINT);
#else
#ifdef HAVE_DIRENT_DTYPE
/* Some filesystems, e.g. ReiserFS, always return a type DT_UNKNOWN from readdir or scandir. */
@ -464,8 +518,8 @@ int is_symlink(const char *path, const struct dirent *d) {
int is_named_pipe(const char *path, const struct dirent *d) {
#ifdef HAVE_DIRENT_DTYPE
if (d->d_type != DT_UNKNOWN) {
return d->d_type == DT_FIFO;
if (d->d_type != DT_UNKNOWN && d->d_type != DT_LNK) {
return d->d_type == DT_FIFO || d->d_type == DT_SOCK;
}
#endif
char *full_path;
@ -476,7 +530,11 @@ int is_named_pipe(const char *path, const struct dirent *d) {
return FALSE;
}
free(full_path);
return S_ISFIFO(s.st_mode);
return S_ISFIFO(s.st_mode)
#ifdef S_ISSOCK
|| S_ISSOCK(s.st_mode)
#endif
;
}
void ag_asprintf(char **ret, const char *fmt, ...) {
@ -565,10 +623,10 @@ ssize_t getline(char **lineptr, size_t *n, FILE *stream) {
}
#endif
ssize_t buf_getline(char **line, char *buf, size_t buf_len, size_t buf_offset) {
char *cur = buf + buf_offset;
ssize_t buf_getline(const char **line, const char *buf, const size_t buf_len, const size_t buf_offset) {
const char *cur = buf + buf_offset;
ssize_t i;
for (i = 0; cur[i] != '\n' && (buf_offset + i < buf_len); i++) {
for (i = 0; (buf_offset + i < buf_len) && cur[i] != '\n'; i++) {
}
*line = cur;
return i;

View file

@ -3,16 +3,16 @@
#include <dirent.h>
#include <pcre.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include "config.h"
#include "log.h"
#include "options.h"
FILE *out_fd;
extern FILE *out_fd;
#ifndef TRUE
#define TRUE 1
@ -22,6 +22,14 @@ FILE *out_fd;
#define FALSE 0
#endif
#define H_SIZE (64 * 1024)
#ifdef __clang__
#define NO_SANITIZE_ALIGNMENT __attribute__((no_sanitize("alignment")))
#else
#define NO_SANITIZE_ALIGNMENT
#endif
void *ag_malloc(size_t size);
void *ag_realloc(void *ptr, size_t size);
void *ag_calloc(size_t nelem, size_t elsize);
@ -34,24 +42,22 @@ typedef struct {
} match_t;
typedef struct {
long total_bytes;
long total_files;
long total_matches;
long total_file_matches;
size_t total_bytes;
size_t total_files;
size_t total_matches;
size_t total_file_matches;
struct timeval time_start;
struct timeval time_end;
} ag_stats;
typedef enum {
AG_NO_COMPRESSION,
AG_GZIP,
AG_COMPRESS,
AG_ZIP
} ag_compression_type;
ag_stats stats;
extern ag_stats stats;
typedef const char *(*strncmp_fp)(const char *, const char *, const size_t, const size_t, const size_t[], const size_t *);
/* Union to translate between chars and words without violating strict aliasing */
typedef union {
char as_chars[sizeof(uint16_t)];
uint16_t as_word;
} word_t;
void free_strings(char **strs, const size_t strs_len);
@ -59,23 +65,20 @@ void generate_alpha_skip(const char *find, size_t f_len, size_t skip_lookup[], c
int is_prefix(const char *s, const size_t s_len, const size_t pos, const int case_sensitive);
size_t suffix_len(const char *s, const size_t s_len, const size_t pos, const int case_sensitive);
void generate_find_skip(const char *find, const size_t f_len, size_t **skip_lookup, const int case_sensitive);
void generate_hash(const char *find, const size_t f_len, uint8_t *H, const int case_sensitive);
/* max is already defined on spec-violating compilers such as MinGW */
size_t ag_max(size_t a, size_t b);
size_t ag_min(size_t a, size_t b);
const char *boyer_moore_strnstr(const char *s, const char *find, const size_t s_len, const size_t f_len,
const size_t alpha_skip_lookup[], const size_t *find_skip_lookup);
const char *boyer_moore_strncasestr(const char *s, const char *find, const size_t s_len, const size_t f_len,
const size_t alpha_skip_lookup[], const size_t *find_skip_lookup);
strncmp_fp get_strstr(enum case_behavior opts);
const size_t alpha_skip_lookup[], const size_t *find_skip_lookup, const int case_insensitive);
const char *hash_strnstr(const char *s, const char *find, const size_t s_len, const size_t f_len, uint8_t *h_table, const int case_sensitive);
size_t invert_matches(const char *buf, const size_t buf_len, match_t matches[], size_t matches_len);
void realloc_matches(match_t **matches, size_t *matches_size, size_t matches_len);
void compile_study(pcre **re, pcre_extra **re_extra, char *q, const int pcre_opts, const int study_opts);
void *decompress(const ag_compression_type zip_type, const void *buf, const int buf_len, const char *dir_full_path, int *new_buf_len);
ag_compression_type is_zipped(const void *buf, const int buf_len);
int is_binary(const void *buf, const size_t buf_len);
int is_regex(const char *query);
@ -95,7 +98,7 @@ void die(const char *fmt, ...);
void ag_asprintf(char **ret, const char *fmt, ...);
ssize_t buf_getline(char **line, char *buf, size_t buf_len, size_t buf_offset);
ssize_t buf_getline(const char **line, const char *buf, const size_t buf_len, const size_t buf_offset);
#ifndef HAVE_FGETLN
char *fgetln(FILE *fp, size_t *lenp);

403
src/zfile.c Normal file
View file

@ -0,0 +1,403 @@
#ifdef __FreeBSD__
#include <sys/endian.h>
#endif
#include <sys/types.h>
#ifdef __CYGWIN__
typedef _off64_t off64_t;
#endif
#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#ifdef HAVE_ERR_H
#include <err.h>
#endif
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#ifdef HAVE_LZMA_H
#include <lzma.h>
#endif
#include "decompress.h"
#if HAVE_FOPENCOOKIE
#define min(a, b) ({ \
__typeof (a) _a = (a); \
__typeof (b) _b = (b); \
_a < _b ? _a : _b; })
static cookie_read_function_t zfile_read;
static cookie_seek_function_t zfile_seek;
static cookie_close_function_t zfile_close;
static const cookie_io_functions_t zfile_io = {
.read = zfile_read,
.write = NULL,
.seek = zfile_seek,
.close = zfile_close,
};
#define KB (1024)
struct zfile {
FILE *in; // Source FILE stream
uint64_t logic_offset, // Logical offset in output (forward seeks)
decode_offset, // Where we've decoded to
actual_len;
uint32_t outbuf_start;
ag_compression_type ctype;
union {
z_stream gz;
lzma_stream lzma;
} stream;
uint8_t inbuf[32 * KB];
uint8_t outbuf[256 * KB];
bool eof;
};
#define CAVAIL_IN(c) ((c)->ctype == AG_GZIP ? (c)->stream.gz.avail_in : (c)->stream.lzma.avail_in)
#define CNEXT_OUT(c) ((c)->ctype == AG_GZIP ? (c)->stream.gz.next_out : (c)->stream.lzma.next_out)
static int
zfile_cookie_init(struct zfile *cookie) {
#ifdef HAVE_LZMA_H
lzma_ret lzrc;
#endif
int rc;
assert(cookie->logic_offset == 0);
assert(cookie->decode_offset == 0);
cookie->actual_len = 0;
switch (cookie->ctype) {
#ifdef HAVE_ZLIB_H
case AG_GZIP:
memset(&cookie->stream.gz, 0, sizeof cookie->stream.gz);
rc = inflateInit2(&cookie->stream.gz, 32 + 15);
if (rc != Z_OK) {
log_err("Unable to initialize zlib: %s", zError(rc));
return EIO;
}
cookie->stream.gz.next_in = NULL;
cookie->stream.gz.avail_in = 0;
cookie->stream.gz.next_out = cookie->outbuf;
cookie->stream.gz.avail_out = sizeof cookie->outbuf;
break;
#endif
#ifdef HAVE_LZMA_H
case AG_XZ:
cookie->stream.lzma = (lzma_stream)LZMA_STREAM_INIT;
lzrc = lzma_auto_decoder(&cookie->stream.lzma, -1, 0);
if (lzrc != LZMA_OK) {
log_err("Unable to initialize lzma_auto_decoder: %d", lzrc);
return EIO;
}
cookie->stream.lzma.next_in = NULL;
cookie->stream.lzma.avail_in = 0;
cookie->stream.lzma.next_out = cookie->outbuf;
cookie->stream.lzma.avail_out = sizeof cookie->outbuf;
break;
#endif
default:
log_err("Unsupported compression type: %d", cookie->ctype);
return EINVAL;
}
cookie->outbuf_start = 0;
cookie->eof = false;
return 0;
}
static void
zfile_cookie_cleanup(struct zfile *cookie) {
switch (cookie->ctype) {
#ifdef HAVE_ZLIB_H
case AG_GZIP:
inflateEnd(&cookie->stream.gz);
break;
#endif
#ifdef HAVE_LZMA_H
case AG_XZ:
lzma_end(&cookie->stream.lzma);
break;
#endif
default:
/* Compiler false positive - unreachable. */
break;
}
}
/*
* Open compressed file 'path' as a (forward-)seekable (and rewindable),
* read-only stream.
*/
FILE *
decompress_open(int fd, const char *mode, ag_compression_type ctype) {
struct zfile *cookie;
FILE *res, *in;
int error;
cookie = NULL;
in = res = NULL;
if (strstr(mode, "w") || strstr(mode, "a")) {
errno = EINVAL;
goto out;
}
in = fdopen(fd, mode);
if (in == NULL)
goto out;
/*
* No validation of compression type is done -- file is assumed to
* match input. In Ag, the compression type is already detected, so
* that's ok.
*/
cookie = malloc(sizeof *cookie);
if (cookie == NULL) {
errno = ENOMEM;
goto out;
}
cookie->in = in;
cookie->logic_offset = 0;
cookie->decode_offset = 0;
cookie->ctype = ctype;
error = zfile_cookie_init(cookie);
if (error != 0) {
errno = error;
goto out;
}
res = fopencookie(cookie, mode, zfile_io);
out:
if (res == NULL) {
if (in != NULL)
fclose(in);
if (cookie != NULL)
free(cookie);
}
return res;
}
/*
* Return number of bytes into buf, 0 on EOF, -1 on error. Update stream
* offset.
*/
static ssize_t
zfile_read(void *cookie_, char *buf, size_t size) {
struct zfile *cookie = cookie_;
size_t nb, ignorebytes;
ssize_t total = 0;
lzma_ret lzret;
int ret;
assert(size <= SSIZE_MAX);
if (size == 0)
return 0;
if (cookie->eof)
return 0;
ret = Z_OK;
lzret = LZMA_OK;
ignorebytes = cookie->logic_offset - cookie->decode_offset;
assert(ignorebytes == 0);
do {
size_t inflated;
/* Drain output buffer first */
while (CNEXT_OUT(cookie) >
&cookie->outbuf[cookie->outbuf_start]) {
size_t left = CNEXT_OUT(cookie) -
&cookie->outbuf[cookie->outbuf_start];
size_t ignoreskip = min(ignorebytes, left);
size_t toread;
if (ignoreskip > 0) {
ignorebytes -= ignoreskip;
left -= ignoreskip;
cookie->outbuf_start += ignoreskip;
cookie->decode_offset += ignoreskip;
}
// Ran out of output before we seek()ed up.
if (ignorebytes > 0)
break;
toread = min(left, size);
memcpy(buf, &cookie->outbuf[cookie->outbuf_start],
toread);
buf += toread;
size -= toread;
left -= toread;
cookie->outbuf_start += toread;
cookie->decode_offset += toread;
cookie->logic_offset += toread;
total += toread;
if (size == 0)
break;
}
if (size == 0)
break;
/*
* If we have not satisfied read, the output buffer must be
* empty.
*/
assert(cookie->stream.gz.next_out ==
&cookie->outbuf[cookie->outbuf_start]);
if ((cookie->ctype == AG_XZ && lzret == LZMA_STREAM_END) ||
(cookie->ctype == AG_GZIP && ret == Z_STREAM_END)) {
cookie->eof = true;
break;
}
/* Read more input if empty */
if (CAVAIL_IN(cookie) == 0) {
nb = fread(cookie->inbuf, 1, sizeof cookie->inbuf,
cookie->in);
if (ferror(cookie->in)) {
warn("error read core");
exit(1);
}
if (nb == 0 && feof(cookie->in)) {
warn("truncated file");
exit(1);
}
if (cookie->ctype == AG_XZ) {
cookie->stream.lzma.avail_in = nb;
cookie->stream.lzma.next_in = cookie->inbuf;
} else {
cookie->stream.gz.avail_in = nb;
cookie->stream.gz.next_in = cookie->inbuf;
}
}
/* Reset stream state to beginning of output buffer */
if (cookie->ctype == AG_XZ) {
cookie->stream.lzma.next_out = cookie->outbuf;
cookie->stream.lzma.avail_out = sizeof cookie->outbuf;
} else {
cookie->stream.gz.next_out = cookie->outbuf;
cookie->stream.gz.avail_out = sizeof cookie->outbuf;
}
cookie->outbuf_start = 0;
if (cookie->ctype == AG_GZIP) {
ret = inflate(&cookie->stream.gz, Z_NO_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END) {
log_err("Found mem/data error while decompressing zlib stream: %s", zError(ret));
return -1;
}
} else {
lzret = lzma_code(&cookie->stream.lzma, LZMA_RUN);
if (lzret != LZMA_OK && lzret != LZMA_STREAM_END) {
log_err("Found mem/data error while decompressing xz/lzma stream: %d", lzret);
return -1;
}
}
inflated = CNEXT_OUT(cookie) - &cookie->outbuf[0];
cookie->actual_len += inflated;
} while (!ferror(cookie->in) && size > 0);
assert(total <= SSIZE_MAX);
return total;
}
static int
zfile_seek(void *cookie_, off64_t *offset_, int whence) {
struct zfile *cookie = cookie_;
off64_t new_offset = 0, offset = *offset_;
if (whence == SEEK_SET) {
new_offset = offset;
} else if (whence == SEEK_CUR) {
new_offset = (off64_t)cookie->logic_offset + offset;
} else {
/* SEEK_END not ok */
return -1;
}
if (new_offset < 0)
return -1;
/* Backward seeks to anywhere but 0 are not ok */
if (new_offset < (off64_t)cookie->logic_offset && new_offset != 0) {
return -1;
}
if (new_offset == 0) {
/* rewind(3) */
cookie->decode_offset = 0;
cookie->logic_offset = 0;
zfile_cookie_cleanup(cookie);
zfile_cookie_init(cookie);
} else if ((uint64_t)new_offset > cookie->logic_offset) {
/* Emulate forward seek by skipping ... */
char *buf;
const size_t bsz = 32 * 1024;
buf = malloc(bsz);
while ((uint64_t)new_offset > cookie->logic_offset) {
size_t diff = min(bsz,
(uint64_t)new_offset - cookie->logic_offset);
ssize_t err = zfile_read(cookie_, buf, diff);
if (err < 0) {
free(buf);
return -1;
}
/* Seek past EOF gets positioned at EOF */
if (err == 0) {
assert(cookie->eof);
new_offset = cookie->logic_offset;
break;
}
}
free(buf);
}
assert(cookie->logic_offset == (uint64_t)new_offset);
*offset_ = new_offset;
return 0;
}
static int
zfile_close(void *cookie_) {
struct zfile *cookie = cookie_;
zfile_cookie_cleanup(cookie);
fclose(cookie->in);
free(cookie);
return 0;
}
#endif /* HAVE_FOPENCOOKIE */

View file

@ -1,8 +1,8 @@
Setup:
$ . $TESTDIR/setup.sh
$ alias ag="$TESTDIR/../ag --workers=1 --parallel --color"
$ echo blahfoofooblah > ./fooblah.txt
$ alias ag="$TESTDIR/../ag --noaffinity --workers=1 --parallel --color"
$ printf 'blahfoofooblah\n' > ./fooblah.txt
Highlights are adjacent:

View file

@ -1,8 +1,8 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo Foo >> ./sample
$ echo bar >> ./sample
$ printf 'Foo\n' >> ./sample
$ printf 'bar\n' >> ./sample
Smart case by default:
@ -29,11 +29,17 @@ Case sensitive mode:
1:Foo
$ ag -s 'F.o' sample
1:Foo
Case insensitive mode:
$ ag foo -i sample
$ ag fOO -i sample
1:Foo
$ ag foo --ignore-case sample
$ ag fOO --ignore-case sample
1:Foo
$ ag 'f.o' -i sample
1:Foo
Case insensitive file regex
$ ag -i -g 'Samp.*'
sample

View file

@ -2,9 +2,9 @@ Setup. Note that we have to turn --color on manually since ag detects that
stdout isn't a tty when running in cram.
$ . $TESTDIR/setup.sh
$ alias ag="$TESTDIR/../ag --workers=1 --parallel --color"
$ echo foo > ./blah.txt
$ echo bar >> ./blah.txt
$ alias ag="$TESTDIR/../ag --noaffinity --workers=1 --parallel --color"
$ printf 'foo\n' > ./blah.txt
$ printf 'bar\n' >> ./blah.txt
Matches should contain colors:

View file

@ -1,7 +1,7 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo "blah\nblah2" > blah.txt
$ printf "blah\nblah2\n" > blah.txt
Ensure column is correct:
@ -12,7 +12,7 @@ Ensure column is correct:
# Test ackmate output. Not quite right, but at least offsets are in the
# ballpark instead of being 9 quintillion
$ ag --ackmate "blah\nb"
$ ag --ackmate "lah\nb"
:blah.txt
1;blah
2;0 6:blah2
2;1 5:blah2

View file

@ -2,11 +2,11 @@ Setup:
$ . $TESTDIR/setup.sh
$ unalias ag
$ alias ag="$TESTDIR/../ag --nocolor --workers=1"
$ echo "blah" > blah.txt
$ echo "blah2" >> blah.txt
$ echo "blah_OTHER" > other_file.txt
$ echo "blah_OTHER" >> other_file.txt
$ alias ag="$TESTDIR/../ag --noaffinity --nocolor --workers=1"
$ printf "blah\n" > blah.txt
$ printf "blah2\n" >> blah.txt
$ printf "blah_OTHER\n" > other_file.txt
$ printf "blah_OTHER\n" >> other_file.txt
Count matches:
@ -16,7 +16,7 @@ Count matches:
Count stream matches:
$ echo 'blah blah blah' | ag --count blah
$ printf 'blah blah blah\n' | ag --count blah
3
Count stream matches per line (not very useful since it does not print zero):

View file

@ -1,8 +1,8 @@
Setup.
$ . $TESTDIR/setup.sh
$ mkdir -p dir0/dir1/dir2
$ echo '*.DS_Store' > dir0/.gitignore
$ echo blah > dir0/dir1/dir2/blah.txt
$ printf '*.DS_Store\n' > dir0/.ignore
$ printf 'blah\n' > dir0/dir1/dir2/blah.txt
$ touch dir0/dir1/.DS_Store
Find blah in blah.txt

View file

@ -0,0 +1,9 @@
Setup:
$ . $TESTDIR/setup.sh
$ printf "hello world\n" >test.txt
Verify ag runs with an empty environment:
$ env -i $TESTDIR/../ag --noaffinity --nocolor --workers=1 --parallel hello
test.txt:1:hello world

View file

@ -1,7 +1,7 @@
Setup.
$ . $TESTDIR/setup.sh
$ touch empty.txt
$ echo foo > nonempty.txt
$ printf 'foo\n' > nonempty.txt
Zero-length match on an empty file should fail silently with return code 1
@ -11,3 +11,12 @@ Zero-length match on an empty file should fail silently with return code 1
A genuine zero-length match should succeed:
$ ag "^" nonempty.txt
1:foo
Empty files should be listed with --unrestricted --files-with-matches (-ul)
$ ag -lu --stats | sed '$d' | sort # Remove the last line about timing which will differ
2 files contained matches
2 files searched
2 matches
4 bytes searched
empty.txt
nonempty.txt

View file

@ -1,8 +1,8 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo foo > ./exitcodes_test.txt
$ echo bar >> ./exitcodes_test.txt
$ printf 'foo\n' > ./exitcodes_test.txt
$ printf 'bar\n' >> ./exitcodes_test.txt
Normal matching:

View file

@ -0,0 +1,17 @@
Setup:
$ . $TESTDIR/../setup.sh
$ printf "hello=你好\n" > test.txt
$ printf "hello=你好\n" >> test.txt
Normal search:
$ $TESTDIR/../../ag --nocolor --workers=1 --parallel 你好
test.txt:1:hello=你好
test.txt:2:hello=你好
Case-insensitive search:
$ $TESTDIR/../../ag --nocolor --workers=1 --parallel -i 你好
test.txt:1:hello=你好
test.txt:2:hello=你好

View file

@ -0,0 +1,17 @@
Setup:
$ . $TESTDIR/../setup.sh
$ printf "hello=你好\n" > test.txt
$ printf "hello=你好\n" >> test.txt
Normal search:
$ $TESTDIR/../../ag --nocolor --workers=1 --parallel 你好
test.txt:1:hello=\xe4\xbd\xa0\xe5\xa5\xbd (esc)
test.txt:2:hello=\xe4\xbd\xa0\xe5\xa5\xbd (esc)
Case-insensitive search:
$ $TESTDIR/../../ag --nocolor --workers=1 --parallel -i 你好
test.txt:1:hello=\xe4\xbd\xa0\xe5\xa5\xbd (esc)
test.txt:2:hello=\xe4\xbd\xa0\xe5\xa5\xbd (esc)

View file

@ -0,0 +1,63 @@
Setup:
$ . $TESTDIR/setup.sh
$ printf 'foo\n' > ./foo.txt
$ printf 'bar\n' > ./bar.txt
$ printf 'foo\nbar\nbaz\n' > ./baz.txt
$ printf 'duck\nanother duck\nyet another duck\n' > ./duck.txt
$ cp duck.txt goose.txt
$ echo "GOOSE!!!" >> ./goose.txt
Files with matches:
$ ag --files-with-matches foo foo.txt
foo.txt
$ ag --files-with-matches foo foo.txt bar.txt
foo.txt
$ ag --files-with-matches foo bar.txt
[1]
$ ag --files-with-matches foo foo.txt bar.txt baz.txt
foo.txt
baz.txt
$ ag --files-with-matches bar foo.txt bar.txt baz.txt
bar.txt
baz.txt
$ ag --files-with-matches foo bar.txt baz.txt
baz.txt
Files without matches:
(Prints names of files in which no line matches query)
$ ag --files-without-matches bar foo.txt
foo.txt
$ ag --files-without-matches bar foo.txt bar.txt
foo.txt
$ ag --files-without-matches bar bar.txt
[1]
$ ag --files-without-matches foo foo.txt bar.txt baz.txt
bar.txt
$ ag --files-without-matches bar foo.txt bar.txt baz.txt
foo.txt
Files with inverted matches:
(Prints names of files in which some line doesn't match query)
$ ag --files-with-matches --invert-match bar bar.txt
[1]
$ ag --files-with-matches --invert-match foo foo.txt bar.txt baz.txt
bar.txt
baz.txt
$ ag --files-with-matches --invert-match bar foo.txt bar.txt baz.txt
foo.txt
baz.txt
Files without inverted matches:
(Prints names of files in which no line doesn't match query,
i.e. where every line matches query)
$ ag --files-without-matches --invert-match duck duck.txt
duck.txt
$ ag --files-without-matches --invert-match duck goose.txt
[1]
$ ag --files-without-matches --invert-match duck duck.txt goose.txt
duck.txt

15
tests/filetype.t Normal file
View file

@ -0,0 +1,15 @@
Setup:
$ . $TESTDIR/setup.sh
$ TEST_FILETYPE_EXT1=`ag --list-file-types | grep -E '^[ \t]+\..+' | head -n 1 | awk '{ print $1 }'`
$ TEST_FILETYPE_EXT2=`ag --list-file-types | grep -E '^[ \t]+\..+' | tail -n 1 | awk '{ print $1 }'`
$ TEST_FILETYPE_DIR=filetype_test
$ mkdir $TEST_FILETYPE_DIR
$ printf "This is filetype test1.\n" > $TEST_FILETYPE_DIR/test.$TEST_FILETYPE_EXT1
$ printf "This is filetype test2.\n" > $TEST_FILETYPE_DIR/test.$TEST_FILETYPE_EXT2
Match only top file type:
$ TEST_FILETYPE_OPTION=`ag --list-file-types | grep -E '^[ \t]+--.+' | head -n 1 | awk '{ print $1 }'`
$ ag 'This is filetype test' --nofilename $TEST_FILETYPE_OPTION $TEST_FILETYPE_DIR
This is filetype test1.

View file

@ -3,10 +3,10 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir hidden_bug
$ cd hidden_bug
$ echo "test" > a.txt
$ printf "test\n" > a.txt
$ git init --quiet
$ if [ ! -d .git/info ] ; then mkdir .git/info ; fi
$ echo "a.txt" > .git/info/exclude
$ printf "a.txt\n" > .git/info/exclude
$ ag --ignore-dir .git test
[1]
@ -21,7 +21,7 @@ Setup:
a.txt:1:test
$ mkdir -p ./.hidden
$ echo 'whatever' > ./.hidden/a.txt
$ printf 'whatever\n' > ./.hidden/a.txt
$ ag whatever
[1]
@ -29,7 +29,7 @@ Setup:
$ ag --hidden whatever
[1]
$ echo "" > .git/info/exclude
$ printf "\n" > .git/info/exclude
$ ag whatever
[1]

View file

@ -2,9 +2,9 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p ./a/b/c
$ echo 'whatever1' > ./a/b/c/blah.yml
$ echo 'whatever2' > ./a/b/foo.yml
$ echo '/a/b/foo.yml' > ./.gitignore
$ printf 'whatever1\n' > ./a/b/c/blah.yml
$ printf 'whatever2\n' > ./a/b/foo.yml
$ printf '/a/b/foo.yml\n' > ./.ignore
Ignore foo.yml but not blah.yml:

View file

@ -2,8 +2,8 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p parent/multi-part
$ echo 'match1' > parent/multi-part/file1.txt
$ echo 'parent/multi-*' > .gitignore
$ printf 'match1\n' > parent/multi-part/file1.txt
$ printf 'parent/multi-*\n' > .ignore
# Ignore directory specified by glob:

View file

@ -2,24 +2,24 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p ./a/b/c
$ echo 'whatever1' > ./a/b/c/foo.yml
$ echo 'whatever2' > ./a/b/c/foo.yml~
$ echo 'whatever3' > ./a/b/c/.foo.yml.swp
$ echo 'whatever4' > ./a/b/c/.foo.yml.swo
$ echo 'whatever5' > ./a/b/foo.yml
$ echo 'whatever6' > ./a/b/foo.yml~
$ echo 'whatever7' > ./a/b/.foo.yml.swp
$ echo 'whatever8' > ./a/b/.foo.yml.swo
$ echo 'whatever9' > ./a/foo.yml
$ echo 'whatever10' > ./a/foo.yml~
$ echo 'whatever11' > ./a/.foo.yml.swp
$ echo 'whatever12' > ./a/.foo.yml.swo
$ echo 'whatever13' > ./foo.yml
$ echo 'whatever14' > ./foo.yml~
$ echo 'whatever15' > ./.foo.yml.swp
$ echo 'whatever16' > ./.foo.yml.swo
$ echo '*~' > ./.gitignore
$ echo '*.sw[po]' >> ./.gitignore
$ printf 'whatever1\n' > ./a/b/c/foo.yml
$ printf 'whatever2\n' > ./a/b/c/foo.yml~
$ printf 'whatever3\n' > ./a/b/c/.foo.yml.swp
$ printf 'whatever4\n' > ./a/b/c/.foo.yml.swo
$ printf 'whatever5\n' > ./a/b/foo.yml
$ printf 'whatever6\n' > ./a/b/foo.yml~
$ printf 'whatever7\n' > ./a/b/.foo.yml.swp
$ printf 'whatever8\n' > ./a/b/.foo.yml.swo
$ printf 'whatever9\n' > ./a/foo.yml
$ printf 'whatever10\n' > ./a/foo.yml~
$ printf 'whatever11\n' > ./a/.foo.yml.swp
$ printf 'whatever12\n' > ./a/.foo.yml.swo
$ printf 'whatever13\n' > ./foo.yml
$ printf 'whatever14\n' > ./foo.yml~
$ printf 'whatever15\n' > ./.foo.yml.swp
$ printf 'whatever16\n' > ./.foo.yml.swo
$ printf '*~\n' > ./.ignore
$ printf '*.sw[po]\n' >> ./.ignore
Ignore all files except foo.yml

View file

@ -2,8 +2,8 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p subdir
$ echo 'match1' > subdir/file1.txt
$ echo 'file1.txt' > .gitignore
$ printf 'match1\n' > subdir/file1.txt
$ printf 'file1.txt\n' > .ignore
Ignore directory specified by name:

View file

@ -1,15 +1,17 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo '*.js' > .gitignore
$ echo '\n*.test.txt' >> .gitignore
$ echo 'targetA' > something.js
$ echo 'targetB' > aFile.test.txt
$ echo 'targetC' > aFile.txt
$ printf '*.js\n' > .ignore
$ printf '*.test.txt\n' >> .ignore
$ printf 'targetA\n' > something.js
$ printf 'targetB\n' > aFile.test.txt
$ printf 'targetC\n' > aFile.txt
$ printf 'targetG\n' > something.min.js
$ mkdir -p subdir
$ echo 'targetD' > subdir/somethingElse.js
$ echo 'targetE' > subdir/anotherFile.test.txt
$ echo 'targetF' > subdir/anotherFile.txt
$ printf 'targetD\n' > subdir/somethingElse.js
$ printf 'targetE\n' > subdir/anotherFile.test.txt
$ printf 'targetF\n' > subdir/anotherFile.txt
$ printf 'targetH\n' > subdir/somethingElse.min.js
Ignore patterns with single extension in root directory:
@ -21,6 +23,11 @@ Ignore patterns with multiple extensions in root directory:
$ ag "targetB"
[1]
*.js ignores *.min.js in root directory:
$ ag "targetG"
[1]
Do not ignore patterns with partial extensions in root directory:
$ ag "targetC"
@ -36,6 +43,11 @@ Ignore patterns with multiple extensions in subdirectory:
$ ag "targetE"
[1]
*.js ignores *.min.js in subdirectory:
$ ag "targetH"
[1]
Do not ignore patterns with partial extensions in subdirectory:
$ ag "targetF"

View file

@ -3,7 +3,7 @@ Setup:
$ . $TESTDIR/setup.sh
$ export HOME=$PWD
$ printf '[core]\nexcludesfile = ~/.gitignore.global' >> $HOME/.gitconfig
$ echo 'PATTERN_MARKER' > .gitignore.global
$ printf 'PATTERN_MARKER\n' > .gitignore.global
Test that the ignore pattern got picked up:

12
tests/ignore_invert.t Normal file
View file

@ -0,0 +1,12 @@
Setup:
$ . $TESTDIR/setup.sh
$ printf 'blah1\n' > ./printme.txt
$ printf 'blah2\n' > ./dontprintme.c
$ printf '*\n' > ./.ignore
$ printf '!*.txt\n' >> ./.ignore
Ignore .gitignore patterns but not .ignore patterns:
$ ag blah
printme.txt:1:blah1

View file

@ -2,9 +2,9 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir subdir
$ echo 'first' > file1.txt
$ echo 'second' > subdir/file2.txt
$ echo '*.txt' > .gitignore
$ printf 'first\n' > file1.txt
$ printf 'second\n' > subdir/file2.txt
$ printf '*.txt\n' > .gitignore
Ignore file based on extension match:

View file

@ -0,0 +1,19 @@
Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p subdir/ignoredir
$ mkdir ignoredir
$ printf 'match1\n' > subdir/ignoredir/file1.txt
$ printf 'match1\n' > ignoredir/file1.txt
$ printf '/ignoredir\n' > subdir/.ignore
Ignore file in subdir/ignoredir, but not in ignoredir:
$ ag match
ignoredir/file1.txt:1:match1
From subdir, ignore file in subdir/ignoredir:
$ cd subdir
$ ag match
[1]

View file

@ -2,11 +2,11 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p ./a/b/c
$ echo 'whatever1' > ./a/b/c/blah.yml
$ echo 'whatever2' > ./a/b/foo.yml
$ echo 'a/b/foo.yml' > ./.gitignore
$ printf 'whatever1\n' > ./a/b/c/blah.yml
$ printf 'whatever2\n' > ./a/b/foo.yml
$ printf 'a/b/foo.yml\n' > ./.gitignore
# TODO: have this work instead of the above
# $ echo 'a/b/*.yml' > ./.gitignore
# $ printf 'a/b/*.yml\n' > ./.gitignore
Ignore foo.yml but not blah.yml:

19
tests/ignore_vcs.t Normal file
View file

@ -0,0 +1,19 @@
Setup:
$ . $TESTDIR/setup.sh
$ printf 'whatever1\n' > ./always.txt
$ printf 'whatever2\n' > ./git.txt
$ printf 'whatever3\n' > ./text.txt
$ printf 'git.txt\n' > ./.gitignore
$ printf 'text.*\n' > ./.ignore
Obey .gitignore and .ignore patterns:
$ ag whatever .
always.txt:1:whatever1
Ignore .gitignore patterns but not .ignore patterns:
$ ag -U whatever . | sort
always.txt:1:whatever1
git.txt:1:whatever2

View file

@ -1,13 +1,13 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'valid: 1' > ./blah.txt
$ echo 'some_string' >> ./blah.txt
$ echo 'valid: 654' >> ./blah.txt
$ echo 'some_other_string' >> ./blah.txt
$ echo 'valid: 0' >> ./blah.txt
$ echo 'valid: 23' >> ./blah.txt
$ echo 'valid: 0' >> ./blah.txt
$ printf 'valid: 1\n' > ./blah.txt
$ printf 'some_string\n' >> ./blah.txt
$ printf 'valid: 654\n' >> ./blah.txt
$ printf 'some_other_string\n' >> ./blah.txt
$ printf 'valid: 0\n' >> ./blah.txt
$ printf 'valid: 23\n' >> ./blah.txt
$ printf 'valid: 0\n' >> ./blah.txt
Search for lines not matching "valid: 0" in blah.txt:

30
tests/line_width.t Normal file
View file

@ -0,0 +1,30 @@
Setup:
$ . $TESTDIR/setup.sh
$ printf "12345678901234567890123456789012345678901234567890\n" >> ./blah.txt
Truncate to width inside input line length:
$ ag -W 20 1 < ./blah.txt
blah.txt:1:12345678901234567890 [...]
Truncate to width inside input line length, long-form:
$ ag --width 20 1 < ./blah.txt
blah.txt:1:12345678901234567890 [...]
Truncate to width outside input line length:
$ ag -W 60 1 < ./blah.txt
blah.txt:1:12345678901234567890123456789012345678901234567890
Truncate to width one less than input line length:
$ ag -W 49 1 < ./blah.txt
blah.txt:1:1234567890123456789012345678901234567890123456789 [...]
Truncate to width exactly input line length:
$ ag -W 50 1 < ./blah.txt
blah.txt:1:12345678901234567890123456789012345678901234567890

View file

@ -12,35 +12,80 @@ Language types are output:
--ada
.ada .adb .ads
--asciidoc
.adoc .ad .asc .asciidoc
--apl
.apl
--asm
.asm .s
--asp
.asp .asa .aspx .asax .ashx .ascx .asmx
--aspx
.asp .asa .aspx .asax .ashx .ascx .asmx
--batch
.bat .cmd
--bazel
.bazel
--bitbake
.bb .bbappend .bbclass .inc
--cc
.c .h .xs
--cfmx
.cfc .cfm .cfml
--chpl
.chpl
--clojure
.clj .cljs .cljc .cljx
.clj .cljs .cljc .cljx .edn
--coffee
.coffee .cjsx
--config
.config
--coq
.coq .g .v
--cpp
.cpp .cc .C .cxx .m .hpp .hh .h .H .hxx
.cpp .cc .C .cxx .m .hpp .hh .h .H .hxx .tpp
--crystal
.cr .ecr
--csharp
.cs
--cshtml
.cshtml
--css
.css
--cython
.pyx .pxd .pxi
--delphi
.pas .int .dfm .nfm .dof .dpk .dproj .groupproj .bdsgroup .bdsproj
.pas .int .dfm .nfm .dof .dpk .dpr .dproj .groupproj .bdsgroup .bdsproj
--dlang
.d .di
--dot
.dot .gv
--dts
.dts .dtsi
--ebuild
.ebuild .eclass
@ -49,13 +94,19 @@ Language types are output:
.el
--elixir
.ex .exs
.ex .eex .exs
--elm
.elm
--erlang
.erl .hrl
--factor
.factor
--fortran
.f .f77 .f90 .f95 .f03 .for .ftn .fpp
.f .F .f77 .f90 .F90 .f95 .f03 .for .ftn .fpp .FPP
--fsharp
.fs .fsi .fsx
@ -63,17 +114,29 @@ Language types are output:
--gettext
.po .pot .mo
--glsl
.vert .tesc .tese .geom .frag .comp
--go
.go
--gradle
.gradle
--groovy
.groovy .gtmpl .gpp .grunit
.groovy .gtmpl .gpp .grunit .gradle
--haml
.haml
--handlebars
.hbs
--haskell
.hs .lhs
.hs .hsig .lhs
--haxe
.hx
--hh
.h
@ -81,23 +144,44 @@ Language types are output:
--html
.htm .html .shtml .xhtml
--idris
.idr .ipkg .lidr
--ini
.ini
--ipython
.ipynb
--isabelle
.thy
--j
.ijs
--jade
.jade
--java
.java .properties
--jinja2
.j2
--js
.js .jsx
.es6 .js .jsx .vue
--json
.json
--jsp
.jsp .jspx .jhtm .jhtml
.jsp .jspx .jhtm .jhtml .jspf .tag .tagf
--julia
.jl
--kotlin
.kt
--less
.less
@ -108,6 +192,9 @@ Language types are output:
--lisp
.lisp .lsp
--log
.log
--lua
.lua
@ -132,12 +219,21 @@ Language types are output:
--mathematica
.m .wl
--md
.markdown .mdown .mdwn .mkdn .mkd .md
--mercury
.m .moo
--naccess
.asa .rsa
--nim
.nim
--nix
.nix
--objc
.m .h
@ -150,9 +246,15 @@ Language types are output:
--octave
.m
--org
.org
--parrot
.pir .pasm .pmc .ops .pod .pg .tg
--pdb
.pdb
--perl
.pl .pm .pm6 .pod .t
@ -162,8 +264,23 @@ Language types are output:
--pike
.pike .pmod
--plist
.plist
--plone
.pt .cpt .metadata .cpy .py
.pt .cpt .metadata .cpy .py .xml .zcml
--powershell
.ps1
--proto
.proto
--ps1
.ps1
--pug
.pug
--puppet
.pp
@ -171,17 +288,29 @@ Language types are output:
--python
.py
--qml
.qml
--racket
.rkt .ss .scm
--rake
.Rakefiles
.Rakefile
--razor
.cshtml
--restructuredtext
.rst
--rs
.rs
--r
.R .Rmd .Rnw .Rtex .Rrst
.r .R .Rmd .Rnw .Rtex .Rrst
--rdoc
.rdoc
--ruby
.rb .rhtml .rjs .rxml .erb .rake .spec
@ -202,7 +331,7 @@ Language types are output:
.scm .ss
--shell
.sh .bash .csh .tcsh .ksh .zsh
.sh .bash .csh .tcsh .ksh .zsh .fish
--smalltalk
.st
@ -213,6 +342,9 @@ Language types are output:
--sql
.sql .ctl
--stata
.do .ado
--stylus
.styl
@ -222,12 +354,30 @@ Language types are output:
--tcl
.tcl .itcl .itk
--terraform
.tf .tfvars
--tex
.tex .cls .sty
--thrift
.thrift
--tla
.tla
--tt
.tt .tt2 .ttml
--toml
.toml
--ts
.ts .tsx
--twig
.twig
--vala
.vala .vapi
@ -235,10 +385,10 @@ Language types are output:
.bas .cls .frm .ctl .vb .resx
--velocity
.vm
.vm .vtl .vsl
--verilog
.v .vh .sv
.v .vh .sv .svh
--vhdl
.vhd .vhdl
@ -246,6 +396,12 @@ Language types are output:
--vim
.vim
--vue
.vue
--wix
.wxi .wxs
--wsdl
.wsdl
@ -253,8 +409,14 @@ Language types are output:
.wadl
--xml
.xml .dtd .xsl .xslt .ent
.xml .dtd .xsl .xslt .xsd .ent .tld .plist .wsdl
--yaml
.yaml .yml
--zeek
.zeek .bro .bif
--zephir
.zep

View file

@ -0,0 +1,40 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'blah abc def' > blah1.txt
$ echo 'abc blah def' > blah2.txt
$ echo 'abc def blah' > blah3.txt
$ echo 'abcblah def' > blah4.txt
$ echo 'abc blahdef' >> blah4.txt
$ echo 'blahx blah' > blah5.txt
$ echo 'abcblah blah blah' > blah6.txt
Match a word of the beginning:
$ ag -wF --column 'blah' blah1.txt
1:1:blah abc def
Match a middle word:
$ ag -wF --column 'blah' blah2.txt
1:5:abc blah def
Match a last word:
$ ag -wF --column 'blah' blah3.txt
1:9:abc def blah
No match:
$ ag -wF --column 'blah' blah4.txt
[1]
Match:
$ ag -wF --column 'blah' blah5.txt
1:7:blahx blah
Case of a word repeating the same part:
$ ag -wF --column 'blah blah' blah6.txt
1:9:abcblah blah blah

View file

@ -1,18 +1,18 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo "blah" > blah.txt
$ echo "blah2" >> blah.txt
$ echo "blah2" > blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt
$ echo "blah2" >> blah2.txt # 10 lines
$ printf "blah\n" > blah.txt
$ printf "blah2\n" >> blah.txt
$ printf "blah2\n" > blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt
$ printf "blah2\n" >> blah2.txt # 10 lines
Max match of 1:

View file

@ -1,9 +1,9 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'what' > blah.txt
$ echo 'ever' >> blah.txt
$ echo 'whatever' >> blah.txt
$ printf 'what\n' > blah.txt
$ printf 'ever\n' >> blah.txt
$ printf 'whatever\n' >> blah.txt
Multiline:

28
tests/negated_options.t Normal file
View file

@ -0,0 +1,28 @@
Setup:
$ . "${TESTDIR}/setup.sh"
Should accept both --no-<option> and --no<option> forms.
(Here we're just parsing out all of the options listed in the `ag` usage help
that can be negated with 'no', and checking to make sure that each of them works
with either form. This is slightly convoluted, but it should ensure that any
options added in the future meet this requirement  assuming they're added to
the usage help, anyway.)
$ test_negated_options() {
> ag --help 2>&1 |
> grep -oiE -- '--\[no-?\][a-z0-9_-]+' |
> cut -d ']' -f '2' |
> sort -u |
> while read option; do
> # The point here is that if the option we're testing is illegal, `ag`
> # will catch it on invocation and dump the usage help, causing the test
> # to produce output and thus fail
> printf 'foo\n' | ag "--no-${option}" -v '^foo$' 2>&1
> printf 'foo\n' | ag "--no${option}" -v '^foo$' 2>&1
> done
> return 0
> }
$ test_negated_options

View file

@ -4,9 +4,12 @@ Setup:
> if [ ! -e "/dev/shm" ]; then
> echo "No /dev/shm. Skipping test."
> exit 80
> elif [ "$(stat -c%d /dev/)" = "$(stat -c%d /dev/shm/)" ]; then
> echo "/dev/shm not a different device. Skipping test."
> exit 80
> fi
$ TEST_TMPDIR=`mktemp -d --tmpdir=/dev/shm ag_test.XXX`
$ echo "blah" > $TEST_TMPDIR/blah.txt
$ printf "blah\n" > $TEST_TMPDIR/blah.txt
$ ln -s $TEST_TMPDIR other_device
Should not descend into /dev/shm symlink when --one-device specified:

View file

@ -1,11 +1,11 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo "the quick brown foxy" > blah.txt
$ echo "blah blah blah" >> blah.txt
$ echo "another foxlike word" >> blah.txt
$ echo "no matches here" >> blah.txt
$ echo "blah blah blah" >> blah.txt
$ printf "the quick brown foxy\n" > blah.txt
$ printf "blah blah blah\n" >> blah.txt
$ printf "another foxlike word\n" >> blah.txt
$ printf "no matches here\n" >> blah.txt
$ printf "blah blah blah\n" >> blah.txt
Only print matches:

View file

@ -1,8 +1,8 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'asdf' > test.txt
$ echo 'AsDf' >> test.txt
$ printf 'asdf\n' > test.txt
$ printf 'AsDf\n' >> test.txt
Smart case search:

View file

@ -2,10 +2,10 @@ Setup:
$ . $TESTDIR/setup.sh
$ unalias ag
$ alias ag="$TESTDIR/../ag --nocolor --workers=1"
$ echo "foo bar" > passthrough_test.txt
$ echo "zoo zar" >> passthrough_test.txt
$ echo "foo test" >> passthrough_test.txt
$ alias ag="$TESTDIR/../ag --noaffinity --nocolor --workers=1"
$ printf "foo bar\n" > passthrough_test.txt
$ printf "zoo zar\n" >> passthrough_test.txt
$ printf "foo test\n" >> passthrough_test.txt
No impact on non-stream:

29
tests/pipecontext.t Normal file
View file

@ -0,0 +1,29 @@
Setup:
$ . $TESTDIR/setup.sh
$ mkdir pipecontext_dir
$ printf "a\nb\nc\n" > pipecontext_test.txt
$ cd pipecontext_dir
Do not use parallel flag, which disables stream input:
$ unalias ag
$ alias ag="$TESTDIR/../ag --nocolor --workers=1"
B flag on pipe:
$ cat ../pipecontext_test.txt | ag --numbers -B1 b
1-a
2:b
C flag on pipe:
$ cat ../pipecontext_test.txt | ag --numbers -C1 b
1-a
2:b
3-c
Just match last line:
$ cat ../pipecontext_test.txt | ag --numbers c
3:c

16
tests/print_all_files.t Normal file
View file

@ -0,0 +1,16 @@
Setup:
$ . $TESTDIR/setup.sh
$ printf 'foo\n' > ./foo.txt
$ printf 'bar\n' > ./bar.txt
$ printf 'baz\n' > ./baz.txt
All files:
$ ag --print-all-files --group foo | sort
1:foo
bar.txt
baz.txt
foo.txt

9
tests/print_end.t Normal file
View file

@ -0,0 +1,9 @@
Setup:
$ . $TESTDIR/setup.sh
$ cp $TESTDIR/print_end.txt .
Print match at the end of a file
$ ag ergneq1
print_end.txt:2:ergneq1

2
tests/print_end.txt Normal file
View file

@ -0,0 +1,2 @@
ergneqergneqergneq
ergneq1

View file

@ -1,7 +1,7 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'blah' > ./blah.txt
$ printf 'blah\n' > ./blah.txt
Feed blah.txt from stdin:

View file

@ -1,4 +1,9 @@
#!/bin/bash
# All cram tests should use this. Make sure that "ag" runs the version
# of ag we just built, and make the output really simple.
alias ag="$TESTDIR/../ag --nocolor --workers=1 --parallel"
# --noaffinity is to stop Travis CI from erroring (it runs in containers so pthread_setaffinity_np fails)
# --workers=1 is to keep all output ordered, to make testing output easier
# shellcheck disable=2139
alias ag="$TESTDIR/../ag --noaffinity --nocolor --workers=1 --parallel"

View file

@ -2,8 +2,8 @@ Setup:
$ . $TESTDIR/setup.sh
$ mkdir -p ./a/bomb
$ echo 'whatever' > ./a/bomb/foo.yml
$ echo '*b/foo.yml' > ./.gitignore
$ printf 'whatever\n' > ./a/bomb/foo.yml
$ printf '*b/foo.yml\n' > ./.gitignore
Ignore foo.yml but not blah.yml:

View file

@ -1,10 +1,10 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'Hello, "Hello, world" programs output "Hello, world".' > ./test_vimgrep.txt
$ echo '"Hello, world" programs are simple programs.' >> ./test_vimgrep.txt
$ echo 'They illustrate the most basic syntax of a programming language' >> ./test_vimgrep.txt
$ echo 'In javascript: alert("Hello, world!");' >> ./test_vimgrep.txt
$ printf 'Hello, "Hello, world" programs output "Hello, world".\n' > ./test_vimgrep.txt
$ printf '"Hello, world" programs are simple programs.\n' >> ./test_vimgrep.txt
$ printf 'They illustrate the most basic syntax of a programming language\n' >> ./test_vimgrep.txt
$ printf 'In javascript: alert("Hello, world!");\n' >> ./test_vimgrep.txt
Search for lines matching "hello" in test_vimgrep.txt:

12
tests/word_regexp.t Normal file
View file

@ -0,0 +1,12 @@
Setup:
$ . $TESTDIR/setup.sh
$ echo 'foo' > blah.txt
$ echo 'bar' >> blah.txt
$ echo 'foobar' >> blah.txt
Word regexp:
$ ag -w 'foo|bar' ./
blah.txt:1:foo
blah.txt:2:bar

View file

@ -1,5 +1,5 @@
%define _bashcompdir %_sysconfdir/bash_completion.d
%define _zshcompdir %{_datadir}/zsh/site-functions
Name: the_silver_searcher
Version: @VERSION@
@ -22,7 +22,7 @@ An attempt to make something better than ack (which itself is better than grep).
Why use Ag?
* It searches code about 35× faster than ack.
* It ignores file patterns from your .gitignore and .hgignore.
* If there are files in your source repo you don't want to search, just add their patterns to a .agignore file. *cough* extern *cough*
* If there are files in your source repo you don't want to search, just add their patterns to a .ignore file. *cough* extern *cough*
* The command name is 33% shorter than ack!
How is it so fast?
@ -62,7 +62,7 @@ rm -rf ${RPM_BUILD_ROOT}
%{_mandir}/*
%config %{_bashcompdir}/ag.bashcomp.sh
%config %{_datadir}/%{name}/completions/ag.bashcomp.sh
%config %{_datadir}/zsh/site-functions/_the_silver_searcher
%changelog
* Thu Dec 5 2013 Emily Strickland <code@emily.st> - 0.18.1-1