summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-03-11 16:41:37 -0500
committerTavian Barnes <tavianator@tavianator.com>2017-03-11 16:41:37 -0500
commiteb18f806da9d39e21362080ab4f4ab816eeeb0d5 (patch)
treec75df495cc6a32cb55847f3cbc3dac89d3ad7758
parent74bd445d659bd619d76e109f7b02bb7714eed95f (diff)
downloadbfs-eb18f806da9d39e21362080ab4f4ab816eeeb0d5.tar.xz
Implement -ls and -fls
-rw-r--r--bfs.h1
-rw-r--r--eval.c105
-rw-r--r--parse.c61
-rw-r--r--printf.c119
-rw-r--r--util.c85
-rw-r--r--util.h22
6 files changed, 291 insertions, 102 deletions
diff --git a/bfs.h b/bfs.h
index 708deaa..44387bf 100644
--- a/bfs.h
+++ b/bfs.h
@@ -318,6 +318,7 @@ bool eval_regex(const struct expr *expr, struct eval_state *state);
bool eval_delete(const struct expr *expr, struct eval_state *state);
bool eval_exec(const struct expr *expr, struct eval_state *state);
bool eval_nohidden(const struct expr *expr, struct eval_state *state);
+bool eval_fls(const struct expr *expr, struct eval_state *state);
bool eval_fprint(const struct expr *expr, struct eval_state *state);
bool eval_fprint0(const struct expr *expr, struct eval_state *state);
bool eval_fprintf(const struct expr *expr, struct eval_state *state);
diff --git a/eval.c b/eval.c
index 03dd41d..3cf8965 100644
--- a/eval.c
+++ b/eval.c
@@ -20,6 +20,7 @@
#include <fnmatch.h>
#include <grp.h>
#include <pwd.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -683,6 +684,110 @@ bool eval_perm(const struct expr *expr, struct eval_state *state) {
}
/**
+ * -f?ls action.
+ */
+bool eval_fls(const struct expr *expr, struct eval_state *state) {
+ FILE *file = expr->file;
+ const struct BFTW *ftwbuf = state->ftwbuf;
+ const struct stat *statbuf = fill_statbuf(state);
+ if (!statbuf) {
+ goto done;
+ }
+
+ uintmax_t ino = statbuf->st_ino;
+ uintmax_t blocks = (statbuf->st_blocks + 1)/2;
+ char mode[11];
+ format_mode(statbuf->st_mode, mode);
+ uintmax_t nlink = statbuf->st_nlink;
+ if (fprintf(file, "%9ju %6ju %s %3ju ", ino, blocks, mode, nlink) < 0) {
+ goto error;
+ }
+
+ uintmax_t uid = statbuf->st_uid;
+ struct passwd *pwd = getpwuid(uid);
+ if (pwd) {
+ if (fprintf(file, " %-8s", pwd->pw_name) < 0) {
+ goto error;
+ }
+ } else {
+ if (fprintf(file, " %-8ju", uid) < 0) {
+ goto error;
+ }
+ }
+
+ uintmax_t gid = statbuf->st_gid;
+ struct group *grp = getgrgid(gid);
+ if (grp) {
+ if (fprintf(file, " %-8s", grp->gr_name) < 0) {
+ goto error;
+ }
+ } else {
+ if (fprintf(file, " %-8ju", gid) < 0) {
+ goto error;
+ }
+ }
+
+ uintmax_t size = statbuf->st_size;
+ if (fprintf(file, " %8ju", size) < 0) {
+ goto error;
+ }
+
+ time_t time = statbuf->st_mtim.tv_sec;
+ time_t now = expr->reftime.tv_sec;
+ time_t six_months_ago = now - 6*30*24*60*60;
+ time_t tomorrow = now + 24*60*60;
+ struct tm tm;
+ if (xlocaltime(&time, &tm) != 0) {
+ goto error;
+ }
+ char time_str[256];
+ const char *time_format = "%b %e %H:%M";
+ if (time <= six_months_ago || time >= tomorrow) {
+ time_format = "%b %e %Y";
+ }
+ if (!strftime(time_str, sizeof(time_str), time_format, &tm)) {
+ errno = EOVERFLOW;
+ goto error;
+ }
+ if (fprintf(file, " %s", time_str) < 0) {
+ goto error;
+ }
+
+ if (file == stdout) {
+ if (cfprintf(state->cmdline->cout, " %P", ftwbuf) < 0) {
+ goto error;
+ }
+ } else {
+ if (fprintf(file, " %s", ftwbuf->path) < 0) {
+ goto error;
+ }
+ }
+
+ if (ftwbuf->typeflag == BFTW_LNK) {
+ char *target = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, statbuf->st_size);
+ if (!target) {
+ goto error;
+ }
+ int ret = fprintf(file, " -> %s", target);
+ free(target);
+ if (ret < 0) {
+ goto error;
+ }
+ }
+
+ if (fputc('\n', file) == EOF) {
+ goto error;
+ }
+
+done:
+ return true;
+
+error:
+ eval_error(state);
+ return true;
+}
+
+/**
* -fprint action.
*/
bool eval_fprint(const struct expr *expr, struct eval_state *state) {
diff --git a/parse.c b/parse.c
index 7bed320..af1bc1f 100644
--- a/parse.c
+++ b/parse.c
@@ -802,12 +802,9 @@ static struct expr *parse_const(struct parser_state *state, int value, int arg2)
* Parse -daystart.
*/
static struct expr *parse_daystart(struct parser_state *state, int arg1, int arg2) {
- // Should be called before localtime_r() according to POSIX.1-2004
- tzset();
-
struct tm tm;
- if (!localtime_r(&state->now.tv_sec, &tm)) {
- perror("localtime_r()");
+ if (xlocaltime(&state->now.tv_sec, &tm) != 0) {
+ perror("xlocaltime()");
return NULL;
}
@@ -944,7 +941,6 @@ static int expr_open(struct parser_state *state, struct expr *expr, const char *
expr->file = fopen(path, "wb");
if (!expr->file) {
cfprintf(state->cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", path, strerror(errno));
- free_expr(expr);
return -1;
}
@@ -953,16 +949,38 @@ static int expr_open(struct parser_state *state, struct expr *expr, const char *
}
/**
+ * Parse -fls FILE.
+ */
+static struct expr *parse_fls(struct parser_state *state, int arg1, int arg2) {
+ struct expr *expr = parse_unary_action(state, eval_fls);
+ if (expr) {
+ if (expr_open(state, expr, expr->sdata) != 0) {
+ goto fail;
+ }
+ expr->reftime = state->now;
+ }
+ return expr;
+
+fail:
+ free_expr(expr);
+ return NULL;
+}
+
+/**
* Parse -fprint FILE.
*/
static struct expr *parse_fprint(struct parser_state *state, int arg1, int arg2) {
struct expr *expr = parse_unary_action(state, eval_fprint);
if (expr) {
if (expr_open(state, expr, expr->sdata) != 0) {
- return NULL;
+ goto fail;
}
}
return expr;
+
+fail:
+ free_expr(expr);
+ return NULL;
}
/**
@@ -972,10 +990,14 @@ static struct expr *parse_fprint0(struct parser_state *state, int arg1, int arg2
struct expr *expr = parse_unary_action(state, eval_fprint0);
if (expr) {
if (expr_open(state, expr, expr->sdata) != 0) {
- return NULL;
+ goto fail;
}
}
return expr;
+
+fail:
+ free_expr(expr);
+ return NULL;
}
/**
@@ -1002,16 +1024,19 @@ static struct expr *parse_fprintf(struct parser_state *state, int arg1, int arg2
}
if (expr_open(state, expr, file) != 0) {
- return NULL;
+ goto fail;
}
expr->printf = parse_bfs_printf(format, state->cmdline->cerr);
if (!expr->printf) {
- free_expr(expr);
- return NULL;
+ goto fail;
}
return expr;
+
+fail:
+ free_expr(expr);
+ return NULL;
}
/**
@@ -1113,6 +1138,18 @@ static struct expr *parse_links(struct parser_state *state, int arg1, int arg2)
}
/**
+ * Parse -ls.
+ */
+static struct expr *parse_ls(struct parser_state *state, int arg1, int arg2) {
+ struct expr *expr = parse_nullary_action(state, eval_fls);
+ if (expr) {
+ expr->file = stdout;
+ expr->reftime = state->now;
+ }
+ return expr;
+}
+
+/**
* Parse -mount, -xdev.
*/
static struct expr *parse_mount(struct parser_state *state, int arg1, int arg2) {
@@ -1918,6 +1955,7 @@ static const struct table_entry parse_table[] = {
{"executable", false, parse_access, X_OK},
{"f", false, parse_f},
{"false", false, parse_const, false},
+ {"fls", false, parse_fls},
{"follow", false, parse_follow, BFTW_LOGICAL | BFTW_DETECT_CYCLES, true},
{"fprint", false, parse_fprint},
{"fprint0", false, parse_fprint0},
@@ -1935,6 +1973,7 @@ static const struct table_entry parse_table[] = {
{"iwholename", false, parse_path, true},
{"links", false, parse_links},
{"lname", false, parse_lname, false},
+ {"ls", false, parse_ls},
{"maxdepth", false, parse_depth_limit, false},
{"mindepth", false, parse_depth_limit, true},
{"mmin", false, parse_acmtime, MTIME, MINUTES},
diff --git a/printf.c b/printf.c
index b1ef710..7999010 100644
--- a/printf.c
+++ b/printf.c
@@ -86,16 +86,20 @@ static int bfs_printf_ctime(FILE *file, const struct bfs_printf_directive *direc
static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
const struct timespec *ts = get_time_field(ftwbuf->statbuf, directive->time_field);
- const struct tm *tm = localtime(&ts->tv_sec);
+ struct tm tm;
+ if (xlocaltime(&ts->tv_sec, &tm) != 0) {
+ return -1;
+ }
+
BFS_PRINTF_BUF(buf, "%s %s %2d %.2d:%.2d:%.2d.%09ld0 %4d",
- days[tm->tm_wday],
- months[tm->tm_mon],
- tm->tm_mday,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec,
+ days[tm.tm_wday],
+ months[tm.tm_mon],
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
(long)ts->tv_nsec,
- 1900 + tm->tm_year);
+ 1900 + tm.tm_year);
return fprintf(file, directive->str, buf);
}
@@ -103,7 +107,10 @@ static int bfs_printf_ctime(FILE *file, const struct bfs_printf_directive *direc
/** %A, %C, %T: strftime() */
static int bfs_printf_strftime(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
const struct timespec *ts = get_time_field(ftwbuf->statbuf, directive->time_field);
- const struct tm *tm = localtime(&ts->tv_sec);
+ struct tm tm;
+ if (xlocaltime(&ts->tv_sec, &tm) != 0) {
+ return -1;
+ }
int ret;
char buf[256];
@@ -114,29 +121,29 @@ static int bfs_printf_strftime(FILE *file, const struct bfs_printf_directive *di
ret = snprintf(buf, sizeof(buf), "%lld.%09ld0", (long long)ts->tv_sec, (long)ts->tv_nsec);
break;
case 'k':
- ret = snprintf(buf, sizeof(buf), "%2d", tm->tm_hour);
+ ret = snprintf(buf, sizeof(buf), "%2d", tm.tm_hour);
break;
case 'l':
- ret = snprintf(buf, sizeof(buf), "%2d", (tm->tm_hour + 11)%12 + 1);
+ ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11)%12 + 1);
break;
case 'S':
- ret = snprintf(buf, sizeof(buf), "%.2d.%09ld0", tm->tm_sec, (long)ts->tv_nsec);
+ ret = snprintf(buf, sizeof(buf), "%.2d.%09ld0", tm.tm_sec, (long)ts->tv_nsec);
break;
case '+':
ret = snprintf(buf, sizeof(buf), "%4d-%.2d-%.2d+%.2d:%.2d:%.2d.%09ld0",
- 1900 + tm->tm_year,
- tm->tm_mon + 1,
- tm->tm_mday,
- tm->tm_hour,
- tm->tm_min,
- tm->tm_sec,
+ 1900 + tm.tm_year,
+ tm.tm_mon + 1,
+ tm.tm_mday,
+ tm.tm_hour,
+ tm.tm_min,
+ tm.tm_sec,
(long)ts->tv_nsec);
break;
// POSIX strftime() features
default:
format[1] = directive->c;
- ret = strftime(buf, sizeof(buf), format, tm);
+ ret = strftime(buf, sizeof(buf), format, &tm);
break;
}
@@ -251,78 +258,8 @@ static int bfs_printf_m(FILE *file, const struct bfs_printf_directive *directive
/** %M: symbolic mode */
static int bfs_printf_M(FILE *file, const struct bfs_printf_directive *directive, const struct BFTW *ftwbuf) {
- char buf[] = "----------";
-
- switch (ftwbuf->typeflag) {
- case BFTW_BLK:
- buf[0] = 'b';
- break;
- case BFTW_CHR:
- buf[0] = 'c';
- break;
- case BFTW_DIR:
- buf[0] = 'd';
- break;
- case BFTW_DOOR:
- buf[0] = 'D';
- break;
- case BFTW_FIFO:
- buf[0] = 'p';
- break;
- case BFTW_LNK:
- buf[0] = 'l';
- break;
- case BFTW_SOCK:
- buf[0] = 's';
- break;
- default:
- break;
- }
-
- mode_t mode = ftwbuf->statbuf->st_mode;
-
- if (mode & 00400) {
- buf[1] = 'r';
- }
- if (mode & 00200) {
- buf[2] = 'w';
- }
- if ((mode & 04100) == 04000) {
- buf[3] = 'S';
- } else if (mode & 04000) {
- buf[3] = 's';
- } else if (mode & 00100) {
- buf[3] = 'x';
- }
-
- if (mode & 00040) {
- buf[4] = 'r';
- }
- if (mode & 00020) {
- buf[5] = 'w';
- }
- if ((mode & 02010) == 02000) {
- buf[6] = 'S';
- } else if (mode & 02000) {
- buf[6] = 's';
- } else if (mode & 00010) {
- buf[6] = 'x';
- }
-
- if (mode & 00004) {
- buf[7] = 'r';
- }
- if (mode & 00002) {
- buf[8] = 'w';
- }
- if ((mode & 01001) == 01000) {
- buf[9] = 'T';
- } else if (mode & 01000) {
- buf[9] = 't';
- } else if (mode & 00001) {
- buf[9] = 'x';
- }
-
+ char buf[11];
+ format_mode(ftwbuf->statbuf->st_mode, buf);
return fprintf(file, directive->str, buf);
}
diff --git a/util.c b/util.c
index f3f2c5f..98db1d7 100644
--- a/util.c
+++ b/util.c
@@ -10,11 +10,13 @@
*********************************************************************/
#include "util.h"
+#include "bftw.h"
#include <errno.h>
#include <fcntl.h>
#include <regex.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -117,3 +119,86 @@ char *xregerror(int err, const regex_t *regex) {
}
return str;
}
+
+int xlocaltime(const time_t *timep, struct tm *result) {
+ // Should be called before localtime_r() according to POSIX.1-2004
+ tzset();
+
+ if (localtime_r(timep, result)) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+void format_mode(mode_t mode, char str[11]) {
+ strcpy(str, "----------");
+
+ switch (bftw_mode_to_typeflag(mode)) {
+ case BFTW_BLK:
+ str[0] = 'b';
+ break;
+ case BFTW_CHR:
+ str[0] = 'c';
+ break;
+ case BFTW_DIR:
+ str[0] = 'd';
+ break;
+ case BFTW_DOOR:
+ str[0] = 'D';
+ break;
+ case BFTW_FIFO:
+ str[0] = 'p';
+ break;
+ case BFTW_LNK:
+ str[0] = 'l';
+ break;
+ case BFTW_SOCK:
+ str[0] = 's';
+ break;
+ default:
+ break;
+ }
+
+ if (mode & 00400) {
+ str[1] = 'r';
+ }
+ if (mode & 00200) {
+ str[2] = 'w';
+ }
+ if ((mode & 04100) == 04000) {
+ str[3] = 'S';
+ } else if (mode & 04000) {
+ str[3] = 's';
+ } else if (mode & 00100) {
+ str[3] = 'x';
+ }
+
+ if (mode & 00040) {
+ str[4] = 'r';
+ }
+ if (mode & 00020) {
+ str[5] = 'w';
+ }
+ if ((mode & 02010) == 02000) {
+ str[6] = 'S';
+ } else if (mode & 02000) {
+ str[6] = 's';
+ } else if (mode & 00010) {
+ str[6] = 'x';
+ }
+
+ if (mode & 00004) {
+ str[7] = 'r';
+ }
+ if (mode & 00002) {
+ str[8] = 'w';
+ }
+ if ((mode & 01001) == 01000) {
+ str[9] = 'T';
+ } else if (mode & 01000) {
+ str[9] = 't';
+ } else if (mode & 00001) {
+ str[9] = 'x';
+ }
+}
diff --git a/util.h b/util.h
index 8fa775d..553f37d 100644
--- a/util.h
+++ b/util.h
@@ -18,6 +18,7 @@
#include <regex.h>
#include <stdbool.h>
#include <sys/stat.h>
+#include <time.h>
// Some portability concerns
@@ -101,4 +102,25 @@ int dup_cloexec(int fd);
*/
char *xregerror(int err, const regex_t *regex);
+/**
+ * localtime_r() wrapper that calls tzset() first.
+ *
+ * @param timep
+ * The time_t to convert.
+ * @param result
+ * Buffer to hold the result.
+ * @return 0 on success, -1 on failure.
+ */
+int xlocaltime(const time_t *timep, struct tm *result);
+
+/**
+ * Format a mode like ls -l (e.g. -rw-r--r--).
+ *
+ * @param mode
+ * The mode to format.
+ * @param str
+ * The string to hold the formatted mode.
+ */
+void format_mode(mode_t mode, char str[11]);
+
#endif // BFS_UTIL_H