From 3e2b8706c33bdca5fd1503b2a5bc1b5db6ddc16a Mon Sep 17 00:00:00 2001 From: Brian Malehorn Date: Sat, 16 Jul 2016 02:50:28 -0700 Subject: [PATCH] scandir.c: traverse in consistent order `readdir()` returns directories in an implementation-dependent order. In practice it's based on a hash of the filenames, aka random: $ ls 01.txt 02.txt 03.txt $ ag -l hello 02.txt 03.txt 01.txt This commit calls `qsort()` on the output of `readdir()`. Now, results are almost always sorted alphabetically: $ ag -l hello 01.txt 02.txt 03.txt This doesn't guarantee 100% sorted order - worker threads will still race each other - but 99% of the time it will be sorted completely. This means you can largely expect to get the same `ag` output on different OSes or different checkouts. Plus, if you're running a slow `ag` command and see the "foo" directory: $ ls ack bar foo quux $ ag hello foo/hello.txt 1:hello ... you can be sure there are no matches in "ack" and "bar". This commit partially fixes #107. --- src/scandir.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/scandir.c b/src/scandir.c index 50cb595d4..d2d2a3641 100644 --- a/src/scandir.c +++ b/src/scandir.c @@ -4,6 +4,13 @@ #include "scandir.h" #include "util.h" +static int dirent_cmp(const void *a, const void *b) { + struct dirent *const *c = a; + struct dirent *const *d = b; + return strcmp((*c)->d_name, (*d)->d_name); +} + + int ag_scandir(const char *dirname, struct dirent ***namelist, filter_fp filter, @@ -57,6 +64,8 @@ int ag_scandir(const char *dirname, results_len++; } + qsort(names, results_len, sizeof(struct dirent *), dirent_cmp); + closedir(dirp); *namelist = names; return results_len;