--- NEWS | 5 +++++ doc/coreutils.texi | 7 ++++++- src/test.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-)
diff --git a/NEWS b/NEWS index dd3ee9c..3445b17 100644 --- a/NEWS +++ b/NEWS @@ -27,6 +27,11 @@ GNU coreutils NEWS -*- outline -*- The 2008 edition of POSIX dropped the requirement that arguments like '+2' must be treated as file names. +** New features + + test now have the -E flag which tests that a file exists and is + an empty directory. + * Noteworthy changes in release 8.25 (2016-01-20) [stable] diff --git a/doc/coreutils.texi b/doc/coreutils.texi index 45706bd..e399986 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -12420,7 +12420,7 @@ Exit status: @end display @menu -* File type tests:: -[bcdfhLpSt] +* File type tests:: -[bcEdfhLpSt] * Access permission tests:: -[gkruwxOG] * File characteristic tests:: -e -s -nt -ot -ef * String tests:: -z -n = == != @@ -12449,6 +12449,11 @@ True if @var{file} exists and is a block special device. @cindex character special check True if @var{file} exists and is a character special device. +@item -E @var{file} +@opindex -E +@cindex empty directory check +True if @var{file} exists, is a directory, and is empty. + @item -d @var{file} @opindex -d @cindex directory check diff --git a/src/test.c b/src/test.c index 8ac7467..eb9c43a 100644 --- a/src/test.c +++ b/src/test.c @@ -27,6 +27,7 @@ #endif #include <config.h> +#include <dirent.h> #include <stdio.h> #include <sys/types.h> @@ -179,6 +180,39 @@ get_mtime (char const *filename, struct timespec *mtime) return ok; } +/* Return true iff DIR is empty. DIR must be a directory. */ +static bool +empty_p (char const *dirname) +{ + DIR *dir; + struct dirent *de; + + dir = opendir (dirname); + if (!dir) + { + error (0, errno, "%s", dirname); + test_exit (TEST_FAILURE); + } + + while (errno = 0, (de = readdir (dir))) + { + if (de->d_name[0] == '.') + if (de->d_name[1 + (de->d_name[1] == '.')] == '\0') + continue; + closedir (dir); + return 0; + } + + if (errno) + { + error (0, errno, "%s", dirname); + closedir (dir); + test_exit (TEST_FAILURE); + } + closedir (dir); + return 1; +} + /* Return true if S is one of the test command's binary operators. */ static bool binop (char const *s) @@ -457,6 +491,12 @@ unary_operator (void) return (stat (argv[pos - 1], &stat_buf) == 0 && S_ISDIR (stat_buf.st_mode)); + case 'E': /* File is an empty directory? */ + unary_advance (); + return (stat (argv[pos - 1], &stat_buf) == 0 + && S_ISDIR (stat_buf.st_mode) + && empty_p (argv[pos - 1])); + case 's': /* File has something in it? */ unary_advance (); return (stat (argv[pos - 1], &stat_buf) == 0 @@ -590,7 +630,8 @@ test_unop (char const *op) case 'f': case 'g': case 'h': case 'k': case 'n': case 'o': case 'p': case 'r': case 's': case 't': case 'u': case 'w': case 'x': case 'z': - case 'G': case 'L': case 'O': case 'S': case 'N': + case 'E': case 'G': case 'L': case 'O': case 'S': + case 'N': return true; default: return false; @@ -760,6 +801,7 @@ EXPRESSION is true or false and sets exit status. It is one of:\n\ -c FILE FILE exists and is character special\n\ -d FILE FILE exists and is a directory\n\ -e FILE FILE exists\n\ + -E FILE FILE exists and is an empty directory\n\ "), stdout); fputs (_("\ -f FILE FILE exists and is a regular file\n\ -- 2.8.0