Newer
Older
From 59d7a2237500c9439b758910e951bcd5dc48c657 Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Thu, 4 Apr 2013 08:43:05 -0700
Subject: [PATCH] Add --matchdirs and --caseinsensitive to matching
The new --matchdirs option causes pattern matching to include
the full contents of any directories that match the pattern,
including sub-directories.
The --caseinsensitive option simply makes pattern matching case
insensitive.
CHANGES | 5 ++++
README | 4 ++++
doc/tree.1 | 17 +++++++++++++-
tree.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++-----------
4 files changed, 91 insertions(+), 14 deletions(-)
diff --git a/CHANGES b/CHANGES
index 10f2e20..004b86a 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+Version 1.6.1
+ - Added --matchdirs flag so that patterns match against directory names and
+ then subsequently descend into sub-directories.
+ - Added --caseinsensitive flag so patterns match without regards to case.
+
Version 1.6.0
- Re-org of code into multiple files, split HTML and Unix listdir() into
separate functions, various code cleanups and optimizations.
diff --git a/README b/README
index ab58d82..58767e8 100644
--- a/README
+++ b/README
@@ -141,6 +141,10 @@ Ujjwal Kumar
- Suggested that tree backslash spaces like ls does for script use. Made
output more like ls.
+Jason A. Donenfeld
+ - Added --matchdirs flag for matching patterns against directories.
+ - Added --caseinsensitive flag for case insensitive pattern matching.
+
And many others whom I've failed to keep track of. I should have started
this list years ago.
--- a/doc/tree.1
+++ b/doc/tree.1
@@ -21,7 +21,7 @@
.SH NAME
tree \- list contents of directories in a tree-like format.
.SH SYNOPSIS
-\fBtree\fP [\fB-acdfghilnpqrstuvxACDFQNSUX\fP] [\fB-L\fP \fIlevel\fP [\fB-R\fP]] [\fB-H\fP \fIbaseHREF\fP] [\fB-T\fP \fItitle\fP] [\fB-o\fP \fIfilename\fP] [\fB--nolinks\fP] [\fB-P\fP \fIpattern\fP] [\fB-I\fP \fIpattern\fP] [\fB--inodes\fP] [\fB--device\fP] [\fB--noreport\fP] [\fB--dirsfirst\fP] [\fB--version\fP] [\fB--help\fP] [\fB--filelimit\fP \fI#\fP] [\fB--si\fP] [\fB--prune\fP] [\fB--du\fP] [\fB--timefmt\fP \fIformat\fP] [\fIdirectory\fP ...]
+\fBtree\fP [\fB-acdfghilnpqrstuvxACDFQNSUX\fP] [\fB-L\fP \fIlevel\fP [\fB-R\fP]] [\fB-H\fP \fIbaseHREF\fP] [\fB-T\fP \fItitle\fP] [\fB-o\fP \fIfilename\fP] [\fB--nolinks\fP] [\fB-P\fP \fIpattern\fP] [\fB-I\fP \fIpattern\fP] [\fB--inodes\fP] [\fB--device\fP] [\fB--noreport\fP] [\fB--dirsfirst\fP] [\fB--version\fP] [\fB--help\fP] [\fB--filelimit\fP \fI#\fP] [\fB--si\fP] [\fB--prune\fP] [\fB--du\fP] [\fB--timefmt\fP \fIformat\fP] [\fB--caseinsensitive\fP] [\fB--matchdirs\fP] [\fIdirectory\fP ...]
.br
.SH DESCRIPTION
\fITree\fP is a recursive directory listing program that produces a depth
@@ -123,6 +123,19 @@ Prints (implies -D) and formats the date according to the format string
which uses the \fBstrftime\fP(3) syntax.
.PP
.TP
+.B --caseinsensitive
+If a match pattern is specified by the -P option, this will cause the pattern
+to match without regards to the case of each letter.
+.PP
+.TP
+.B --matchdirs
+If a match pattern is specified by the -P option, this will cause the pattern
+to be applied to directory names (in addition to filenames). In the event of a
+match on the directory name, matching is disabled for the directory's
+contents. If the --prune option is used, empty folders that match the pattern
+will not be pruned.
+.PP
+.TP
.B -o \fIfilename\fP
Send output to \fIfilename\fP.
.PP
@@ -314,6 +327,8 @@ Steve Baker (ice@mama.indstate.edu)
HTML output hacked by Francesc Rocher (rocher@econ.udg.es)
.br
Charsets and OS/2 support by Kyosuke Tokoro (NBG01720@nifty.ne.jp)
+.br
+Directory and case matching by Jason A. Donenfeld (Jason@zx2c4.com)
.SH BUGS AND NOTES
Tree does not prune "empty" directories when the -P and -I options are used by
@@ -19,16 +19,17 @@
#include "tree.h"
-static char *version ="$Version: $ tree v1.6.0 (c) 1996 - 2011 by Steve Baker, Thomas Moore, Francesc Rocher, Kyosuke Tokoro $";
-static char *hversion="\t\t tree v1.6.0 %s 1996 - 2011 by Steve Baker and Thomas Moore <br>\n"
+static char *version ="$Version: $ tree v1.6.0 (c) 1996 - 2014 by Steve Baker, Thomas Moore, Francesc Rocher, Kyosuke Tokoro, Jason A. Donenfeld $";
+static char *hversion="\t\t tree v1.6.0 %s 1996 - 2014 by Steve Baker and Thomas Moore <br>\n"
"\t\t HTML output hacked and copyleft %s 1998 by Francesc Rocher <br>\n"
- "\t\t Charsets / OS/2 support %s 2001 by Kyosuke Tokoro\n";
+ "\t\t Charsets / OS/2 support %s 2001 by Kyosuke Tokoro <br>\n"
+ "\t\t Directory and case matching %s 2014 by Jason A. Donenfeld\n";
/* Globals */
bool dflag, lflag, pflag, sflag, Fflag, aflag, fflag, uflag, gflag;
bool qflag, Nflag, Qflag, Dflag, inodeflag, devflag, hflag, Rflag;
bool Hflag, siflag, cflag, Xflag, duflag, pruneflag;
-bool noindent, force_color, nocolor, xdev, noreport, nolinks, flimit, dirsfirst, nosort;
+bool noindent, force_color, nocolor, xdev, noreport, nolinks, flimit, dirsfirst, nosort, matchdirs, caseinsensitive;
char *pattern = NULL, *ipattern = NULL, *host = NULL, *title = "Directory Tree", *sp = " ";
char *timefmt = NULL;
const char *charset = NULL;
@@ -75,12 +76,13 @@ int main(int argc, char **argv)
char sizebuf[64];
off_t size = 0;
mode_t mt;
+ bool needfulltree;
q = p = dtotal = ftotal = 0;
aflag = dflag = fflag = lflag = pflag = sflag = Fflag = uflag = gflag = FALSE;
Dflag = qflag = Nflag = Qflag = Rflag = hflag = Hflag = siflag = cflag = FALSE;
noindent = force_color = nocolor = xdev = noreport = nolinks = FALSE;
- dirsfirst = nosort = inodeflag = devflag = Xflag = FALSE;
+ caseinsensitive = matchdirs = dirsfirst = nosort = inodeflag = devflag = Xflag = FALSE;
duflag = pruneflag = FALSE;
flimit = 0;
dirs = xmalloc(sizeof(int) * (maxdirs=4096));
@@ -350,6 +352,16 @@ int main(int argc, char **argv)
Dflag = TRUE;
break;
}
+ if (!strncmp("--matchdirs",argv[i],11)) {
+ j = strlen(argv[i])-1;
+ matchdirs = TRUE;
+ break;
+ }
+ if (!strncmp("--caseinsensitive",argv[i],17)) {
+ j = strlen(argv[i])-1;
+ caseinsensitive = TRUE;
+ break;
+ }
}
default:
fprintf(stderr,"tree: Invalid argument -`%c'.\n",argv[i][j]);
@@ -387,16 +399,17 @@ int main(int argc, char **argv)
parse_dir_colors();
initlinedraw(0);
+ needfulltree = duflag || pruneflag || matchdirs;
/* Set our listdir function and sanity check options. */
if (Hflag) {
- listdir = (duflag || pruneflag)? html_rlistdir : html_listdir;
+ listdir = needfulltree ? html_rlistdir : html_listdir;
Xflag = FALSE;
} else if (Xflag) {
- listdir = (duflag || pruneflag)? xml_rlistdir : xml_listdir;
+ listdir = needfulltree ? xml_rlistdir : xml_listdir;
colorize = FALSE;
colored = FALSE; /* Do people want colored XML output? */
} else {
- listdir = (duflag || pruneflag)? unix_rlistdir : unix_listdir;
+ listdir = needfulltree ? unix_rlistdir : unix_listdir;
}
if (dflag) pruneflag = FALSE; /* You'll just get nothing otherwise. */
@@ -517,7 +530,8 @@ void usage(int n)
"usage: tree [-acdfghilnpqrstuvxACDFQNSUX] [-H baseHREF] [-T title ] [-L level [-R]]\n"
"\t[-P pattern] [-I pattern] [-o filename] [--version] [--help] [--inodes]\n"
"\t[--device] [--noreport] [--nolinks] [--dirsfirst] [--charset charset]\n"
- "\t[--filelimit[=]#] [--si] [--timefmt[=]<f>] [<directory list>]\n");
+ "\t[--filelimit[=]#] [--si] [--timefmt[=]<f>] [--matchdirs]\n"
+ "\t[--caseinsensitive] [<directory list>]\n");
if (n < 2) exit(0);
fprintf(stderr,
" ------- Listing options -------\n"
@@ -534,6 +548,8 @@ void usage(int n)
" --charset X Use charset X for terminal/HTML and indentation line output.\n"
" --filelimit # Do not descend dirs with more than # files in them.\n"
" --timefmt <f> Print and format time according to the format <f>.\n"
+ " --matchdirs Include directory names in -P pattern matching.\n"
+ " --caseinsensitive Match files without regards to case in -P pattern matching.\n"
" -o filename Output to file instead of stdout.\n"
" -------- File options ---------\n"
" -q Print non-printable characters as '?'.\n"
@@ -689,6 +705,8 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
struct _info **dir, **sav, **p, *sp;
struct stat sb;
int n;
+ u_long lev_tmp;
+ char *tmp_pattern = NULL, *start_rel_path;
*err = NULL;
if (Level >= 0 && lev > Level) return NULL;
@@ -696,7 +714,29 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
stat(d,&sb);
dev = sb.st_dev;
}
+
+ // if the directory name matches, turn off pattern matching for contents
+ if (matchdirs && pattern) {
+ lev_tmp = lev;
+ for (start_rel_path = d + strlen(d); start_rel_path != d; --start_rel_path) {
+ if (*start_rel_path == '/')
+ --lev_tmp;
+ if (lev_tmp <= 0) {
+ if (*start_rel_path)
+ ++start_rel_path;
+ break;
+ }
+ }
+ if (patmatch(start_rel_path,pattern) == 1) {
+ tmp_pattern = pattern;
+ pattern = NULL;
+ }
+ }
sav = dir = read_dir(d,&n);
+ if (tmp_pattern) {
+ pattern = tmp_pattern;
+ tmp_pattern = NULL;
+ }
if (dir == NULL) {
*err = scopy("error opening dir");
return NULL;
@@ -745,7 +785,9 @@ struct _info **getfulltree(char *d, u_long lev, dev_t dev, off_t *size, char **e
saveino((*dir)->inode, (*dir)->dev);
(*dir)->child = getfulltree(path,lev+1,dev,&((*dir)->size),&((*dir)->err));
}
- if (pruneflag && (*dir)->child == NULL) {
+ // prune empty folders, unless they match the requested pattern
+ if (pruneflag && (*dir)->child == NULL &&
+ !(matchdirs && pattern && patmatch((*dir)->name,pattern) == 1)) {
sp = *dir;
for(p=dir;*p;p++) *p = *(p+1);
n--;
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
@@ -869,8 +911,19 @@ char *gnu_getcwd()
}
/*
+ * Returns a lower case version of the argument if case insensitive
+ * mode is enabled. Note that this is static and inline so that we
+ * do not incur penalty when this is running in a tight loop.
+ */
+static inline char cond_lower(char c)
+{
+ return caseinsensitive ? tolower(c) : c;
+}
+
+/*
* Patmatch() code courtesy of Thomas Moore (dark@mama.indstate.edu)
* '|' support added by David MacMahon (davidm@astron.Berkeley.EDU)
+ * Case insensitive support added by Jason A. Donenfeld (Jason@zx2c4.com)
* returns:
* 1 on a match
* 0 on a mismatch
@@ -918,11 +971,11 @@ int patmatch(char *buf, char *pat)
pat += 2;
if(*pat == '\\' && *pat)
pat++;
- if(*buf >= m && *buf <= *pat)
+ if(cond_lower(*buf) >= cond_lower(m) && cond_lower(*buf) <= cond_lower(*pat))
match = n;
if(!*pat)
pat--;
- } else if(*buf == *pat) match = n;
+ } else if(cond_lower(*buf) == cond_lower(*pat)) match = n;
pat++;
}
buf++;
@@ -940,7 +993,7 @@ int patmatch(char *buf, char *pat)
if(*pat)
pat++;
default:
- match = (*buf++ == *pat);
+ match = (cond_lower(*buf++) == cond_lower(*pat));
break;
}
pat++;