diff options
author | Elliott Hughes <enh@google.com> | 2019-05-24 16:01:47 -0700 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2019-05-25 21:28:57 -0500 |
commit | 1dbd86ec825ba92be12e4aafad49804faaa86ec6 (patch) | |
tree | 49d8e3be0b1a50fb42d7f571a97ce16d268df986 | |
parent | 2f9d9096aac148a26254801014feaff057b64fa0 (diff) | |
download | toybox-1dbd86ec825ba92be12e4aafad49804faaa86ec6.tar.gz |
find: add -printf support.
This only implements the format specifiers that I've seen used in the
wild (which is actually a significant fraction of the total supported by
findutils' find). The most obvious gap is in the time support. I'm happy
to add more, but didn't want to add stuff "just because".
I'd say %A@, %C@, and -- for SELinux users -- %Z are probably the most
plausibly useful formats still missing. I don't think the human-readable
date formatting is particularly useful unless someone's seen it actually
used in the wild. The %T+ "full ISO" format being the most likely
exception to that.
Anyway, this is enough for me get started building AOSP with toybox find.
-rw-r--r-- | lib/lib.c | 2 | ||||
-rw-r--r-- | toys/posix/find.c | 83 |
2 files changed, 80 insertions, 5 deletions
@@ -1046,7 +1046,7 @@ char *getdirname(char *name) { char *s = xstrdup(name), *ss = strrchr(s, '/'); - while (*ss && *ss == '/' && s != ss) *ss-- = 0; + while (ss && *ss && *ss == '/' && s != ss) *ss-- = 0; return s; } diff --git a/toys/posix/find.c b/toys/posix/find.c index 5cefbf15..8b757b8e 100644 --- a/toys/posix/find.c +++ b/toys/posix/find.c @@ -46,10 +46,23 @@ config FIND -print Print match with newline -print0 Print match with null -exec Run command with path -execdir Run command in file's dir -ok Ask before exec -okdir Ask before execdir - -delete Remove matching file/dir + -delete Remove matching file/dir + -printf FORMAT Print using format string Commands substitute "{}" with matched file. End with ";" to run each file, or "+" (next argument after "{}") to collect and run with multiple files. + + FORMAT characters are \ escapes and: + %f basename + %g textual gid %G numeric gid + %i decimal inode + %l target of symlink (or empty) + %m octal mode, no leading 0 %M type and mode in ls format + %p filename %P filename without root + %s size in bytes + %T@ last modification unix time with fraction + %u textual uid %U numeric uid + %Z security context */ #define FOR_find @@ -61,6 +74,7 @@ GLOBALS( int topdir, xdev, depth; time_t now; long max_bytes; + char *start; ) struct execdir_data { @@ -535,6 +549,65 @@ static int do_find(struct dirtree *new) // Argument consumed, skip the check. goto cont; + } else if (!strcmp(s, "printf")) { + char *fmt = ss[1], *path, *lnk, *start, mode_str[11], ch; + + print++; + if (check) { + for (; *fmt; fmt++) { + if (*fmt == '\\') { + if (!(ch = unescape(fmt[1]))) + error_exit("bad \\ escape: %c", fmt[1]); + fmt++; + putchar(ch); + } else if (*fmt == '%') { + switch (*++fmt) { + case '%': putchar('%'); break; + case 'f': printf("%s", new->name); break; + case 'G': printf("%d", new->st.st_gid); break; + case 'g': printf("%s", getgroupname(new->st.st_gid)); break; + case 'i': printf("%lld", (long long) new->st.st_ino); break; + case 'l': + path = dirtree_path(new, 0); + lnk = xreadlink(path); + printf("%s", lnk ? lnk : ""); + free(lnk); + free(path); + break; + case 'M': + mode_to_string(new->st.st_mode, mode_str); + printf("%s", mode_str); + break; + case 'm': printf("%o", new->st.st_mode & ~S_IFMT); break; + case 'P': + start = getdirname(TT.start); + path = dirtree_path(new, 0); + printf("%s", path + 1+strlen(start)); + free(path); + free(start); + break; + case 'p': + path = dirtree_path(new, 0); + printf("%s", path); + free(path); + break; + case 's': printf("%lld", (long long) new->st.st_size); break; + case 'T': + switch (*++fmt) { + case '@': + printf("%ld.%ld", new->st.st_mtim.tv_sec, + new->st.st_mtim.tv_nsec); + break; + default: error_exit("bad %%T variant: %%T%c", *fmt); + } + break; + case 'U': printf("%d", new->st.st_uid); break; + case 'u': printf("%s", getusername(new->st.st_uid)); break; + default: error_exit("bad %% specifier: %c", *fmt); + } + } else putchar(*fmt); + } + } } else goto error; // This test can go at the end because we do a syntax checking @@ -586,9 +659,11 @@ void find_main(void) do_find(0); // Loop through paths - for (i = 0; i < len; i++) - dirtree_flagread(ss[i], DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), - do_find); + for (i = 0; i < len; i++) { + TT.start = ss[i]; + dirtree_flagread(TT.start, + DIRTREE_SYMFOLLOW*!!(toys.optflags&(FLAG_H|FLAG_L)), do_find); + } execdir(0, 1); |