/****************************************************************************
* 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"