From eb18f806da9d39e21362080ab4f4ab816eeeb0d5 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 11 Mar 2017 16:41:37 -0500 Subject: Implement -ls and -fls --- bfs.h | 1 + eval.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ parse.c | 61 ++++++++++++++++++++++++++------ printf.c | 119 +++++++++++++++------------------------------------------------ util.c | 85 +++++++++++++++++++++++++++++++++++++++++++++ util.h | 22 ++++++++++++ 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 #include #include +#include #include #include #include @@ -682,6 +683,110 @@ bool eval_perm(const struct expr *expr, struct eval_state *state) { return false; } +/** + * -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. */ 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; } @@ -952,6 +948,24 @@ static int expr_open(struct parser_state *state, struct expr *expr, const char * return 0; } +/** + * 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. */ @@ -959,10 +973,14 @@ 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; } /** @@ -1112,6 +1137,18 @@ static struct expr *parse_links(struct parser_state *state, int arg1, int arg2) return parse_test_icmp(state, eval_links); } +/** + * 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. */ @@ -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 #include #include #include #include +#include #include #include #include @@ -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 #include #include +#include // 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 -- cgit v1.2.3