summaryrefslogtreecommitdiffstats
path: root/printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'printf.c')
-rw-r--r--printf.c927
1 files changed, 0 insertions, 927 deletions
diff --git a/printf.c b/printf.c
deleted file mode 100644
index 8fdde41..0000000
--- a/printf.c
+++ /dev/null
@@ -1,927 +0,0 @@
-/****************************************************************************
- * bfs *
- * Copyright (C) 2017-2022 Tavian Barnes <tavianator@tavianator.com> *
- * *
- * Permission to use, copy, modify, and/or distribute this software for any *
- * purpose with or without fee is hereby granted. *
- * *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES *
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF *
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR *
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES *
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN *
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *
- ****************************************************************************/
-
-#include "printf.h"
-#include "bftw.h"
-#include "color.h"
-#include "ctx.h"
-#include "darray.h"
-#include "diag.h"
-#include "dir.h"
-#include "dstring.h"
-#include "expr.h"
-#include "mtab.h"
-#include "pwcache.h"
-#include "stat.h"
-#include "util.h"
-#include "xtime.h"
-#include <assert.h>
-#include <errno.h>
-#include <grp.h>
-#include <pwd.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-/**
- * A function implementing a printf directive.
- */
-typedef int bfs_printf_fn(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf);
-
-/**
- * A single printf directive like %f or %#4m. The whole format string is stored
- * as a darray of these.
- */
-struct bfs_printf {
- /** The printing function to invoke. */
- bfs_printf_fn *fn;
- /** String data associated with this directive. */
- char *str;
- /** The stat field to print. */
- enum bfs_stat_field stat_field;
- /** Character data associated with this directive. */
- char c;
- /** Some data used by the directive. */
- const void *ptr;
-};
-
-/** Print some text as-is. */
-static int bfs_printf_literal(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- size_t len = dstrlen(directive->str);
- if (fwrite(directive->str, 1, len, cfile->file) == len) {
- return 0;
- } else {
- return -1;
- }
-}
-
-/** \c: flush */
-static int bfs_printf_flush(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- return fflush(cfile->file);
-}
-
-/** Check if we can safely colorize this directive. */
-static bool should_color(CFILE *cfile, const struct bfs_printf *directive) {
- return cfile->colors && strcmp(directive->str, "%s") == 0;
-}
-
-/**
- * Print a value to a temporary buffer before formatting it.
- */
-#define BFS_PRINTF_BUF(buf, format, ...) \
- char buf[256]; \
- int ret = snprintf(buf, sizeof(buf), format, __VA_ARGS__); \
- assert(ret >= 0 && (size_t)ret < sizeof(buf)); \
- (void)ret
-
-/** %a, %c, %t: ctime() */
-static int bfs_printf_ctime(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- // Not using ctime() itself because GNU find adds nanoseconds
- static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
- static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
-
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- const struct timespec *ts = bfs_stat_time(statbuf, directive->stat_field);
- if (!ts) {
- return -1;
- }
-
- 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,
- (long)ts->tv_nsec,
- 1900 + tm.tm_year);
-
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %A, %B/%W, %C, %T: strftime() */
-static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- const struct timespec *ts = bfs_stat_time(statbuf, directive->stat_field);
- if (!ts) {
- return -1;
- }
-
- struct tm tm;
- if (xlocaltime(&ts->tv_sec, &tm) != 0) {
- return -1;
- }
-
- int ret;
- char buf[256];
- char format[] = "% ";
- switch (directive->c) {
- // Non-POSIX strftime() features
- case '@':
- ret = snprintf(buf, sizeof(buf), "%lld.%09ld0", (long long)ts->tv_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,
- (long)ts->tv_nsec);
- break;
- case 'k':
- ret = snprintf(buf, sizeof(buf), "%2d", tm.tm_hour);
- break;
- case 'l':
- ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11)%12 + 1);
- break;
- case 's':
- ret = snprintf(buf, sizeof(buf), "%lld", (long long)ts->tv_sec);
- break;
- case 'S':
- ret = snprintf(buf, sizeof(buf), "%.2d.%09ld0", tm.tm_sec, (long)ts->tv_nsec);
- break;
- case 'T':
- ret = snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d.%09ld0",
- 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);
- break;
- }
-
- assert(ret >= 0 && (size_t)ret < sizeof(buf));
- (void)ret;
-
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %b: blocks */
-static int bfs_printf_b(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512;
- BFS_PRINTF_BUF(buf, "%ju", blocks);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %d: depth */
-static int bfs_printf_d(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- return fprintf(cfile->file, directive->str, (intmax_t)ftwbuf->depth);
-}
-
-/** %D: device */
-static int bfs_printf_D(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->dev);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %f: file name */
-static int bfs_printf_f(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- if (should_color(cfile, directive)) {
- return cfprintf(cfile, "%pF", ftwbuf);
- } else {
- return fprintf(cfile->file, directive->str, ftwbuf->path + ftwbuf->nameoff);
- }
-}
-
-/** %F: file system type */
-static int bfs_printf_F(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- const char *type = bfs_fstype(directive->ptr, statbuf);
- return fprintf(cfile->file, directive->str, type);
-}
-
-/** %G: gid */
-static int bfs_printf_G(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->gid);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %g: group name */
-static int bfs_printf_g(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- const struct bfs_groups *groups = directive->ptr;
- const struct group *grp = groups ? bfs_getgrgid(groups, statbuf->gid) : NULL;
- if (!grp) {
- return bfs_printf_G(cfile, directive, ftwbuf);
- }
-
- return fprintf(cfile->file, directive->str, grp->gr_name);
-}
-
-/** %h: leading directories */
-static int bfs_printf_h(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- char *copy = NULL;
- const char *buf;
-
- if (ftwbuf->nameoff > 0) {
- size_t len = ftwbuf->nameoff;
- if (len > 1) {
- --len;
- }
-
- buf = copy = strndup(ftwbuf->path, len);
- } else if (ftwbuf->path[0] == '/') {
- buf = "/";
- } else {
- buf = ".";
- }
-
- if (!buf) {
- return -1;
- }
-
- int ret;
- if (should_color(cfile, directive)) {
- ret = cfprintf(cfile, "${di}%s${rs}", buf);
- } else {
- ret = fprintf(cfile->file, directive->str, buf);
- }
-
- free(copy);
- return ret;
-}
-
-/** %H: current root */
-static int bfs_printf_H(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- if (should_color(cfile, directive)) {
- if (ftwbuf->depth == 0) {
- return cfprintf(cfile, "%pP", ftwbuf);
- } else {
- return cfprintf(cfile, "${di}%s${rs}", ftwbuf->root);
- }
- } else {
- return fprintf(cfile->file, directive->str, ftwbuf->root);
- }
-}
-
-/** %i: inode */
-static int bfs_printf_i(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->ino);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %k: 1K blocks */
-static int bfs_printf_k(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024;
- BFS_PRINTF_BUF(buf, "%ju", blocks);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %l: link target */
-static int bfs_printf_l(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- char *buf = NULL;
- const char *target = "";
-
- if (ftwbuf->type == BFS_LNK) {
- if (should_color(cfile, directive)) {
- return cfprintf(cfile, "%pL", ftwbuf);
- }
-
- const struct bfs_stat *statbuf = bftw_cached_stat(ftwbuf, BFS_STAT_NOFOLLOW);
- size_t len = statbuf ? statbuf->size : 0;
-
- target = buf = xreadlinkat(ftwbuf->at_fd, ftwbuf->at_path, len);
- if (!target) {
- return -1;
- }
- }
-
- int ret = fprintf(cfile->file, directive->str, target);
- free(buf);
- return ret;
-}
-
-/** %m: mode */
-static int bfs_printf_m(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- return fprintf(cfile->file, directive->str, (unsigned int)(statbuf->mode & 07777));
-}
-
-/** %M: symbolic mode */
-static int bfs_printf_M(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- char buf[11];
- xstrmode(statbuf->mode, buf);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %n: link count */
-static int bfs_printf_n(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->nlink);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %p: full path */
-static int bfs_printf_p(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- if (should_color(cfile, directive)) {
- return cfprintf(cfile, "%pP", ftwbuf);
- } else {
- return fprintf(cfile->file, directive->str, ftwbuf->path);
- }
-}
-
-/** %P: path after root */
-static int bfs_printf_P(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- size_t offset = strlen(ftwbuf->root);
- if (ftwbuf->path[offset] == '/') {
- ++offset;
- }
-
- if (should_color(cfile, directive)) {
- if (ftwbuf->depth == 0) {
- return 0;
- }
-
- struct BFTW copybuf = *ftwbuf;
- copybuf.path += offset;
- copybuf.nameoff -= offset;
- return cfprintf(cfile, "%pP", &copybuf);
- } else {
- return fprintf(cfile->file, directive->str, ftwbuf->path + offset);
- }
-}
-
-/** %s: size */
-static int bfs_printf_s(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->size);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %S: sparseness */
-static int bfs_printf_S(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- double sparsity;
- if (statbuf->size == 0 && statbuf->blocks == 0) {
- sparsity = 1.0;
- } else {
- sparsity = (double)BFS_STAT_BLKSIZE*statbuf->blocks/statbuf->size;
- }
- return fprintf(cfile->file, directive->str, sparsity);
-}
-
-/** %U: uid */
-static int bfs_printf_U(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- BFS_PRINTF_BUF(buf, "%ju", (uintmax_t)statbuf->uid);
- return fprintf(cfile->file, directive->str, buf);
-}
-
-/** %u: user name */
-static int bfs_printf_u(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags);
- if (!statbuf) {
- return -1;
- }
-
- const struct bfs_users *users = directive->ptr;
- const struct passwd *pwd = users ? bfs_getpwuid(users, statbuf->uid) : NULL;
- if (!pwd) {
- return bfs_printf_U(cfile, directive, ftwbuf);
- }
-
- return fprintf(cfile->file, directive->str, pwd->pw_name);
-}
-
-static const char *bfs_printf_type(enum bfs_type type) {
- switch (type) {
- case BFS_BLK:
- return "b";
- case BFS_CHR:
- return "c";
- case BFS_DIR:
- return "d";
- case BFS_DOOR:
- return "D";
- case BFS_FIFO:
- return "p";
- case BFS_LNK:
- return "l";
- case BFS_REG:
- return "f";
- case BFS_SOCK:
- return "s";
- default:
- return "U";
- }
-}
-
-/** %y: type */
-static int bfs_printf_y(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- const char *type = bfs_printf_type(ftwbuf->type);
- return fprintf(cfile->file, directive->str, type);
-}
-
-/** %Y: target type */
-static int bfs_printf_Y(CFILE *cfile, const struct bfs_printf *directive, const struct BFTW *ftwbuf) {
- int error = 0;
-
- if (ftwbuf->type != BFS_LNK) {
- return bfs_printf_y(cfile, directive, ftwbuf);
- }
-
- const char *type = "U";
-
- const struct bfs_stat *statbuf = bftw_stat(ftwbuf, BFS_STAT_FOLLOW);
- if (statbuf) {
- type = bfs_printf_type(bfs_mode_to_type(statbuf->mode));
- } else {
- switch (errno) {
- case ELOOP:
- type = "L";
- break;
- case ENOENT:
- case ENOTDIR:
- type = "N";
- break;
- default:
- type = "?";
- error = errno;
- break;
- }
- }
-
- int ret = fprintf(cfile->file, directive->str, type);
- if (error != 0) {
- ret = -1;
- errno = error;
- }
- return ret;
-}
-
-/**
- * Append a literal string to the chain.
- */
-static int append_literal(const struct bfs_ctx *ctx, struct bfs_printf **format, char **literal) {
- if (dstrlen(*literal) == 0) {
- return 0;
- }
-
- struct bfs_printf directive = {
- .fn = bfs_printf_literal,
- .str = *literal,
- };
-
- if (DARRAY_PUSH(format, &directive) != 0) {
- bfs_perror(ctx, "DARRAY_PUSH()");
- return -1;
- }
-
- *literal = dstralloc(0);
- if (!*literal) {
- bfs_perror(ctx, "dstralloc()");
- return -1;
- }
-
- return 0;
-}
-
-/**
- * Append a printf directive to the chain.
- */
-static int append_directive(const struct bfs_ctx *ctx, struct bfs_printf **format, char **literal, struct bfs_printf *directive) {
- if (append_literal(ctx, format, literal) != 0) {
- return -1;
- }
-
- if (DARRAY_PUSH(format, directive) != 0) {
- bfs_perror(ctx, "DARRAY_PUSH()");
- return -1;
- }
-
- return 0;
-}
-
-int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const char *format) {
- expr->printf = NULL;
-
- char *literal = dstralloc(0);
- if (!literal) {
- bfs_perror(ctx, "dstralloc()");
- goto error;
- }
-
- for (const char *i = format; *i; ++i) {
- char c = *i;
-
- if (c == '\\') {
- c = *++i;
-
- if (c >= '0' && c < '8') {
- c = 0;
- for (int j = 0; j < 3 && *i >= '0' && *i < '8'; ++i, ++j) {
- c *= 8;
- c += *i - '0';
- }
- --i;
- goto one_char;
- }
-
- switch (c) {
- case 'a': c = '\a'; break;
- case 'b': c = '\b'; break;
- case 'f': c = '\f'; break;
- case 'n': c = '\n'; break;
- case 'r': c = '\r'; break;
- case 't': c = '\t'; break;
- case 'v': c = '\v'; break;
- case '\\': c = '\\'; break;
-
- case 'c':
- {
- struct bfs_printf directive = {
- .fn = bfs_printf_flush,
- };
- if (append_directive(ctx, &expr->printf, &literal, &directive) != 0) {
- goto error;
- }
- goto done;
- }
-
- case '\0':
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Incomplete escape sequence '\\'.\n");
- goto error;
-
- default:
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Unrecognized escape sequence '\\%c'.\n", c);
- goto error;
- }
- } else if (c == '%') {
- if (i[1] == '%') {
- c = *++i;
- goto one_char;
- }
-
- struct bfs_printf directive = {
- .str = dstralloc(2),
- };
- if (!directive.str) {
- goto directive_error;
- }
- if (dstrapp(&directive.str, c) != 0) {
- bfs_perror(ctx, "dstrapp()");
- goto directive_error;
- }
-
- const char *specifier = "s";
-
- // Parse any flags
- bool must_be_numeric = false;
- while (true) {
- c = *++i;
-
- switch (c) {
- case '#':
- case '0':
- case '+':
- must_be_numeric = true;
- BFS_FALLTHROUGH;
- case ' ':
- case '-':
- if (strchr(directive.str, c)) {
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Duplicate flag '%c'.\n", c);
- goto directive_error;
- }
- if (dstrapp(&directive.str, c) != 0) {
- bfs_perror(ctx, "dstrapp()");
- goto directive_error;
- }
- continue;
- }
-
- break;
- }
-
- // Parse the field width
- while (c >= '0' && c <= '9') {
- if (dstrapp(&directive.str, c) != 0) {
- bfs_perror(ctx, "dstrapp()");
- goto directive_error;
- }
- c = *++i;
- }
-
- // Parse the precision
- if (c == '.') {
- do {
- if (dstrapp(&directive.str, c) != 0) {
- bfs_perror(ctx, "dstrapp()");
- goto directive_error;
- }
- c = *++i;
- } while (c >= '0' && c <= '9');
- }
-
- switch (c) {
- case 'a':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_ATIME;
- break;
- case 'b':
- directive.fn = bfs_printf_b;
- break;
- case 'c':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_CTIME;
- break;
- case 'd':
- directive.fn = bfs_printf_d;
- specifier = "jd";
- break;
- case 'D':
- directive.fn = bfs_printf_D;
- break;
- case 'f':
- directive.fn = bfs_printf_f;
- break;
- case 'F':
- directive.ptr = bfs_ctx_mtab(ctx);
- if (!directive.ptr) {
- int error = errno;
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Couldn't parse the mount table: %s.\n", strerror(error));
- goto directive_error;
- }
- directive.fn = bfs_printf_F;
- break;
- case 'g':
- directive.ptr = bfs_ctx_groups(ctx);
- if (!directive.ptr) {
- int error = errno;
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Couldn't parse the group table: %s.\n", strerror(error));
- goto directive_error;
- }
- directive.fn = bfs_printf_g;
- break;
- case 'G':
- directive.fn = bfs_printf_G;
- break;
- case 'h':
- directive.fn = bfs_printf_h;
- break;
- case 'H':
- directive.fn = bfs_printf_H;
- break;
- case 'i':
- directive.fn = bfs_printf_i;
- break;
- case 'k':
- directive.fn = bfs_printf_k;
- break;
- case 'l':
- directive.fn = bfs_printf_l;
- break;
- case 'm':
- directive.fn = bfs_printf_m;
- specifier = "o";
- break;
- case 'M':
- directive.fn = bfs_printf_M;
- break;
- case 'n':
- directive.fn = bfs_printf_n;
- break;
- case 'p':
- directive.fn = bfs_printf_p;
- break;
- case 'P':
- directive.fn = bfs_printf_P;
- break;
- case 's':
- directive.fn = bfs_printf_s;
- break;
- case 'S':
- directive.fn = bfs_printf_S;
- specifier = "g";
- break;
- case 't':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_MTIME;
- break;
- case 'u':
- directive.ptr = bfs_ctx_users(ctx);
- if (!directive.ptr) {
- int error = errno;
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Couldn't parse the user table: %s.\n", strerror(error));
- goto directive_error;
- }
- directive.fn = bfs_printf_u;
- break;
- case 'U':
- directive.fn = bfs_printf_U;
- break;
- case 'w':
- directive.fn = bfs_printf_ctime;
- directive.stat_field = BFS_STAT_BTIME;
- break;
- case 'y':
- directive.fn = bfs_printf_y;
- break;
- case 'Y':
- directive.fn = bfs_printf_Y;
- break;
-
- case 'A':
- directive.stat_field = BFS_STAT_ATIME;
- goto directive_strftime;
- case 'B':
- case 'W':
- directive.stat_field = BFS_STAT_BTIME;
- goto directive_strftime;
- case 'C':
- directive.stat_field = BFS_STAT_CTIME;
- goto directive_strftime;
- case 'T':
- directive.stat_field = BFS_STAT_MTIME;
- goto directive_strftime;
-
- directive_strftime:
- directive.fn = bfs_printf_strftime;
- c = *++i;
- if (!c) {
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Incomplete time specifier '%s%c'.\n", directive.str, i[-1]);
- goto directive_error;
- } else if (strchr("%+@aAbBcCdDeFgGhHIjklmMnprRsStTuUVwWxXyYzZ", c)) {
- directive.c = c;
- } else {
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Unrecognized time specifier '%%%c%c'.\n", i[-1], c);
- goto directive_error;
- }
- break;
-
- case '\0':
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Incomplete format specifier '%s'.\n", directive.str);
- goto directive_error;
-
- default:
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Unrecognized format specifier '%%%c'.\n", c);
- goto directive_error;
- }
-
- if (must_be_numeric && strcmp(specifier, "s") == 0) {
- bfs_expr_error(ctx, expr);
- bfs_error(ctx, "Invalid flags '%s' for string format '%%%c'.\n", directive.str + 1, c);
- goto directive_error;
- }
-
- if (dstrcat(&directive.str, specifier) != 0) {
- bfs_perror(ctx, "dstrcat()");
- goto directive_error;
- }
-
- if (append_directive(ctx, &expr->printf, &literal, &directive) != 0) {
- goto directive_error;
- }
-
- continue;
-
- directive_error:
- dstrfree(directive.str);
- goto error;
- }
-
- one_char:
- if (dstrapp(&literal, c) != 0) {
- bfs_perror(ctx, "dstrapp()");
- goto error;
- }
- }
-
-done:
- if (append_literal(ctx, &expr->printf, &literal) != 0) {
- goto error;
- }
- dstrfree(literal);
- return 0;
-
-error:
- dstrfree(literal);
- bfs_printf_free(expr->printf);
- expr->printf = NULL;
- return -1;
-}
-
-int bfs_printf(CFILE *cfile, const struct bfs_printf *format, const struct BFTW *ftwbuf) {
- int ret = 0, error = 0;
-
- for (size_t i = 0; i < darray_length(format); ++i) {
- const struct bfs_printf *directive = &format[i];
- if (directive->fn(cfile, directive, ftwbuf) < 0) {
- ret = -1;
- error = errno;
- }
- }
-
- errno = error;
- return ret;
-}
-
-void bfs_printf_free(struct bfs_printf *format) {
- for (size_t i = 0; i < darray_length(format); ++i) {
- dstrfree(format[i].str);
- }
- darray_free(format);
-}