Commit 7cad7b37 authored by Leah Neukirchen's avatar Leah Neukirchen

new option -B for breadth first search

We use two trees and add the newly found files to the other one,
then loop until no more files are found.

prune, -t, -L and -x work, depth needs a slight hack;
loop detection and -U doesn't work.
parent e15895fb
## HEAD
* Feature: new option `-B` for breadth first traversal.
## 1.1 (2017-10-29) ## 1.1 (2017-10-29)
* Feature: lr is substantially faster as files only are stat(2)ed if * Feature: lr is substantially faster as files only are stat(2)ed if
......
...@@ -20,6 +20,7 @@ Over find: ...@@ -20,6 +20,7 @@ Over find:
* can sort * can sort
* compute directory sizes * compute directory sizes
* can strip leading `./` * can strip leading `./`
* can do breadth first search
Over ls: Over ls:
* sorts over all files, not per directory * sorts over all files, not per directory
...@@ -46,7 +47,7 @@ Over ls: ...@@ -46,7 +47,7 @@ Over ls:
## Usage: ## Usage:
lr [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-D] [-H|-L] [-1AGQXdhsx] [-U|-o ORD] [-e REGEX]* [-t TEST]* PATH... lr [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-B|-D] [-H|-L] [-1AGQXdhsx] [-U|-o ORD] [-e REGEX]* [-t TEST]* PATH...
The special path argument `-` makes `lr` read file names from standard The special path argument `-` makes `lr` read file names from standard
input, instead of traversing path. input, instead of traversing path.
...@@ -60,6 +61,7 @@ input, instead of traversing path. ...@@ -60,6 +61,7 @@ input, instead of traversing path.
* `-TM`: with `-l`, output mtime (default). * `-TM`: with `-l`, output mtime (default).
* `-S`: BSD stat(1)-inspired output (implies `-Q`). * `-S`: BSD stat(1)-inspired output (implies `-Q`).
* `-f FMT`: custom formatting, see below. * `-f FMT`: custom formatting, see below.
* `-B`: breadth first traversal.
* `-D`: depth first traversal. `prune` does not work, but `entries` * `-D`: depth first traversal. `prune` does not work, but `entries`
and `total` are computed on the fly. and `total` are computed on the fly.
* `-H`: only follow symlinks on command line. * `-H`: only follow symlinks on command line.
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.Nm .Nm
.Op Fl 0 | Fl F | Fl l Oo Fl TA | Fl TC | Fl TM Oc | Fl S | Fl f Ar fmt .Op Fl 0 | Fl F | Fl l Oo Fl TA | Fl TC | Fl TM Oc | Fl S | Fl f Ar fmt
.br .br
.Op Fl D .Op Fl B | Fl D
.Op Fl H | Fl L .Op Fl H | Fl L
.Op Fl 1AGQXdhsx .Op Fl 1AGQXdhsx
.Op Fl U | Fl o Ar ord .Op Fl U | Fl o Ar ord
...@@ -81,6 +81,11 @@ BSD ...@@ -81,6 +81,11 @@ BSD
.It Fl f Ar fmt .It Fl f Ar fmt
Custom formatting, see Custom formatting, see
.Sx FORMATTING . .Sx FORMATTING .
.It Fl B
Use breadth first traversal.
For each depth of the directory tree,
files are sorted and printed,
then the next depth is looked at.
.It Fl D .It Fl D
Use depth first traversal. Use depth first traversal.
.Ic prune .Ic prune
......
...@@ -78,6 +78,7 @@ ...@@ -78,6 +78,7 @@
#define PATH_MAX 4096 #define PATH_MAX 4096
#endif #endif
static int Bflag;
static int Cflag; static int Cflag;
static char *Cflags[64]; static char *Cflags[64];
static int Gflag; static int Gflag;
...@@ -97,7 +98,8 @@ static char *argv0; ...@@ -97,7 +98,8 @@ static char *argv0;
static char *format; static char *format;
static char *ordering; static char *ordering;
static struct expr *expr; static struct expr *expr;
static void *root = NULL; // tree static void *root; // tree
static void *new_root; // tree
static int prune; static int prune;
static size_t prefixl; static size_t prefixl;
static char input_delim = '\n'; static char input_delim = '\n';
...@@ -139,6 +141,7 @@ static off_t maxsize; ...@@ -139,6 +141,7 @@ static off_t maxsize;
static blkcnt_t maxblocks; static blkcnt_t maxblocks;
static unsigned int maxxattr; static unsigned int maxxattr;
static int bflag_depth;
static int maxdepth; static int maxdepth;
static int uwid, gwid, fwid; static int uwid, gwid, fwid;
...@@ -1829,7 +1832,7 @@ print_format(struct fileinfo *fi) ...@@ -1829,7 +1832,7 @@ print_format(struct fileinfo *fi)
} }
void void
print(const void *nodep, const VISIT which, const int depth) tree_print(const void *nodep, const VISIT which, const int depth)
{ {
(void)depth; (void)depth;
...@@ -1837,7 +1840,7 @@ print(const void *nodep, const VISIT which, const int depth) ...@@ -1837,7 +1840,7 @@ print(const void *nodep, const VISIT which, const int depth)
print_format(*(struct fileinfo **)nodep); print_format(*(struct fileinfo **)nodep);
} }
void static void
free_fi(struct fileinfo *fi) free_fi(struct fileinfo *fi)
{ {
if (fi) if (fi)
...@@ -1851,7 +1854,7 @@ callback(const char *fpath, const struct stat *sb, int depth, ino_t entries, off ...@@ -1851,7 +1854,7 @@ callback(const char *fpath, const struct stat *sb, int depth, ino_t entries, off
struct fileinfo *fi = malloc(sizeof (struct fileinfo)); struct fileinfo *fi = malloc(sizeof (struct fileinfo));
fi->fpath = strdup(fpath); fi->fpath = strdup(fpath);
fi->prefixl = prefixl; fi->prefixl = prefixl;
fi->depth = depth; fi->depth = Bflag ? (depth > 0 ? bflag_depth + 1 : 0) : depth;
fi->entries = entries; fi->entries = entries;
fi->total = total; fi->total = total;
fi->color = current_color; fi->color = current_color;
...@@ -1870,8 +1873,10 @@ callback(const char *fpath, const struct stat *sb, int depth, ino_t entries, off ...@@ -1870,8 +1873,10 @@ callback(const char *fpath, const struct stat *sb, int depth, ino_t entries, off
memset(fi->xattr, 0, sizeof fi->xattr); memset(fi->xattr, 0, sizeof fi->xattr);
if (Uflag) { if (Uflag) {
print(&fi, postorder, depth); print_format(fi);
free_fi(fi); free_fi(fi);
} else if (Bflag) {
tsearch(fi, depth > 0 ? &new_root : &root, order);
} else { } else {
// add to the tree, note that this will elimnate duplicate files // add to the tree, note that this will elimnate duplicate files
tsearch(fi, &root, order); tsearch(fi, &root, order);
...@@ -1932,6 +1937,9 @@ recurse(char *path, struct history *h, int guessdir) ...@@ -1932,6 +1937,9 @@ recurse(char *path, struct history *h, int guessdir)
int resolve = Lflag || (Hflag && !h); int resolve = Lflag || (Hflag && !h);
int root = (path[0] == '/' && path[1] == 0); int root = (path[0] == '/' && path[1] == 0);
if (Bflag && h && h->chain)
return 0;
if (need_stat) if (need_stat)
guessdir = 1; guessdir = 1;
...@@ -2025,6 +2033,30 @@ recurse(char *path, struct history *h, int guessdir) ...@@ -2025,6 +2033,30 @@ recurse(char *path, struct history *h, int guessdir)
return 0; return 0;
} }
void
tree_recurse(const void *nodep, const VISIT which, const int depth)
{
struct fileinfo *fi = *((struct fileinfo **)nodep);
(void)depth;
if (which == postorder || which == leaf) {
if (S_ISDIR(fi->sb.st_mode)) {
char path[PATH_MAX];
strcpy(path, fi->fpath);
bflag_depth = fi->depth;
recurse(path, 0, 0);
}
}
}
void
tree_free(void *nodep)
{
free_fi(nodep);
}
int int
traverse_file(FILE *file) traverse_file(FILE *file)
{ {
...@@ -2108,11 +2140,12 @@ main(int argc, char *argv[]) ...@@ -2108,11 +2140,12 @@ main(int argc, char *argv[])
setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
while ((c = getopt(argc, argv, "01AC:DFGHLQST:UXde:f:lho:st:x")) != -1) while ((c = getopt(argc, argv, "01ABC:DFGHLQST:UXde:f:lho:st:x")) != -1)
switch (c) { switch (c) {
case '0': format = zero_format; input_delim = 0; Qflag = 0; break; case '0': format = zero_format; input_delim = 0; Qflag = 0; break;
case '1': expr = chain(parse_expr("depth == 0 || prune"), EXPR_AND, expr); break; case '1': expr = chain(parse_expr("depth == 0 || prune"), EXPR_AND, expr); break;
case 'A': expr = chain(expr, EXPR_AND, parse_expr("!(path ~~ \"*/.*\" && prune) && path != \".\"")); break; case 'A': expr = chain(expr, EXPR_AND, parse_expr("!(path ~~ \"*/.*\" && prune) && path != \".\"")); break;
case 'B': Bflag++; Dflag = 0; Uflag = 0; need_stat++; break;
case 'C': case 'C':
if ((unsigned int)Cflag < if ((unsigned int)Cflag <
sizeof Cflags / sizeof Cflags[0]) { sizeof Cflags / sizeof Cflags[0]) {
...@@ -2123,7 +2156,7 @@ main(int argc, char *argv[]) ...@@ -2123,7 +2156,7 @@ main(int argc, char *argv[])
} }
Gflag += 2; // force color on Gflag += 2; // force color on
break; break;
case 'D': Dflag++; break; case 'D': Dflag++; Bflag = 0; break;
case 'F': format = type_format; break; case 'F': format = type_format; break;
case 'G': Gflag++; break; case 'G': Gflag++; break;
case 'H': Hflag++; break; case 'H': Hflag++; break;
...@@ -2131,7 +2164,7 @@ main(int argc, char *argv[]) ...@@ -2131,7 +2164,7 @@ main(int argc, char *argv[])
case 'Q': Qflag++; break; case 'Q': Qflag++; break;
case 'S': Qflag++; format = stat_format; break; case 'S': Qflag++; format = stat_format; break;
case 'T': Tflag = timeflag(optarg); break; case 'T': Tflag = timeflag(optarg); break;
case 'U': Uflag++; break; case 'U': Uflag++; Bflag = 0; break;
case 'X': Xflag++; break; case 'X': Xflag++; break;
case 'd': expr = chain(parse_expr("type == d && prune || print"), EXPR_AND, expr); break; case 'd': expr = chain(parse_expr("type == d && prune || print"), EXPR_AND, expr); break;
case 'e': expr = chain(expr, EXPR_AND, case 'e': expr = chain(expr, EXPR_AND,
...@@ -2147,7 +2180,7 @@ main(int argc, char *argv[]) ...@@ -2147,7 +2180,7 @@ main(int argc, char *argv[])
case 'x': xflag++; break; case 'x': xflag++; break;
default: default:
fprintf(stderr, fprintf(stderr,
"Usage: %s [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-D] [-H|-L] [-1AGQdhsx]\n" "Usage: %s [-0|-F|-l [-TA|-TC|-TM]|-S|-f FMT] [-B|-D] [-H|-L] [-1AGQdhsx]\n"
" [-U|-o ORD] [-e REGEX]* [-t TEST]* [-C [COLOR:]PATH]* PATH...\n", argv0); " [-U|-o ORD] [-e REGEX]* [-t TEST]* [-C [COLOR:]PATH]* PATH...\n", argv0);
exit(2); exit(2);
} }
...@@ -2202,8 +2235,19 @@ main(int argc, char *argv[]) ...@@ -2202,8 +2235,19 @@ main(int argc, char *argv[])
traverse(argv[i]); traverse(argv[i]);
} }
if (!Uflag) if (Bflag) {
twalk(root, print); while (root) {
twalk(root, tree_print);
twalk(root, tree_recurse);
tdestroy(root, tree_free);
root = new_root;
new_root = 0;
}
} else if (!Uflag) {
twalk(root, tree_print);
// no need to destroy here, we are done
}
return status; return status;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment