From 44cff01e5bbe04ff991ede843e96f0c2d83d20c6 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Thu, 19 Sep 2024 18:03:17 +0200 Subject: Split into multiple C files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- Makefile | 12 +- avg.c | 94 ++++++++++++++ common.h | 57 +++++++++ deadshred.c | 400 +++--------------------------------------------------------- fmt.c | 189 ++++++++++++++++++++++++++++ io.c | 25 ++++ rnd.c | 80 ++++++++++++ text.c | 46 +++++++ 8 files changed, 516 insertions(+), 387 deletions(-) create mode 100644 avg.c create mode 100644 common.h create mode 100644 fmt.c create mode 100644 io.c create mode 100644 rnd.c create mode 100644 text.c diff --git a/Makefile b/Makefile index 1e91bbd..bfe5c26 100644 --- a/Makefile +++ b/Makefile @@ -4,9 +4,15 @@ CONFIGFILE = config.mk include $(CONFIGFILE) OBJ =\ - deadshred.o - -HDR = + deadshred.o\ + io.o\ + fmt.o\ + text.o\ + avg.o\ + rnd.o + +HDR =\ + common.h all: deadshred $(OBJ): $(HDR) diff --git a/avg.c b/avg.c new file mode 100644 index 0000000..4078c50 --- /dev/null +++ b/avg.c @@ -0,0 +1,94 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +/* TODO deal with machine and process suspension */ + + +static struct timespec write_average_begin_times[5]; +static off_t write_average_amounts[ELEMSOF(write_average_begin_times)] = {0}; +static int write_average_i = 0; + + +static int +was_write_average_overrun(int i, const struct timespec *now, int seconds) +{ + struct timespec diff; + libsimple_difftimespec(&diff, now, &write_average_begin_times[i]); + if (diff.tv_sec >= seconds) + return 1; + if (diff.tv_sec == seconds - 1 && diff.tv_nsec >= DECISECONDS(9)) + return 1; + return 0; +} + + +static void +shift_write_average(void) +{ + write_average_i--; + memmove(&write_average_begin_times[0], &write_average_begin_times[1], + (size_t)write_average_i * sizeof(*write_average_begin_times)); + memmove(&write_average_amounts[0], &write_average_amounts[1], + (size_t)write_average_i * sizeof(*write_average_amounts)); +} + + +void +wravg_init(const struct timespec *start_time) +{ + write_average_begin_times[0] = *start_time; +} + + +void +wravg_update_write(ssize_t amount) +{ + if (amount > 0) + write_average_amounts[write_average_i] += (off_t)amount; +} + + +void +wravg_update_time(const struct timespec *now) +{ + if (was_write_average_overrun(write_average_i, now, 1)) { + write_average_i++; + if (write_average_i == ELEMSOF(write_average_amounts)) + shift_write_average(); + write_average_begin_times[write_average_i] = *now; + write_average_amounts[write_average_i] = 0; + } + while (write_average_i && was_write_average_overrun(0, now, (int)ELEMSOF(write_average_amounts))) + shift_write_average(); +} + + +const char * +wravg_get(const struct timespec *now) +{ + static char buf[512]; + + struct timespec write_average_time; + off_t write_average_sum = 0; + double write_average; + int i; + + for (i = 0; i <= write_average_i; i++) + write_average_sum += write_average_amounts[i]; + + libsimple_difftimespec(&write_average_time, now, &write_average_begin_times[0]); + + if (write_average_time.tv_sec < 0) + return "-"; + if (write_average_time.tv_sec == 0 && write_average_time.tv_nsec < CENTISECONDS(10)) + return "-"; + if (write_average_sum == 0) + return "0"; + + write_average = (double)write_average_time.tv_nsec; + write_average /= WHOLE_SECOND; + write_average += (double)write_average_time.tv_sec; + write_average = (double)write_average_sum / write_average; + return humanbytespersecond(write_average, buf); +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..875375c --- /dev/null +++ b/common.h @@ -0,0 +1,57 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include + + +#define NANOSECONDS(X) X##L +#define MICROSECONDS(X) NANOSECONDS(X##000) +#define MILLISECONDS(X) MICROSECONDS(X##000) +#define CENTISECONDS(X) MILLISECONDS(X##0) +#define DECISECONDS(X) CENTISECONDS(X##0) +#define WHOLE_SECOND DECISECONDS(10) + + +enum direction { + FORWARDS = 0, + BACKWARDS = 1 +}; + +struct span { + off_t start; + off_t end; + off_t bad; + size_t blocksize; +}; + + +/* io.c */ +off_t filesize(int fd, const char *fname); + +/* fmt.c */ +const char *humansize1000(off_t s, char *buf); +const char *humansize1024(off_t s, char *buf); +#if defined(__GNUC__) +__attribute__((__pure__)) +#endif +off_t unhumansize(const char *s, char flag); +const char *durationstr(const struct timespec *dur, char *buf, int second_decimals); +const char *humanbytespersecond(double bytes_per_second, char *buf); + +/* text.c */ +int have_micro_symbol(void); + +/* avg.c */ +void wravg_init(const struct timespec *start_time); +void wravg_update_write(ssize_t amount); +void wravg_update_time(const struct timespec *now); +const char *wravg_get(const struct timespec *now); + +/* rnd.c */ +#if defined(__GNUC__) +__attribute__((__const__)) +#endif +size_t max_blksize(void); +void init_random(int fd, const char *fname); +const char *get_random(size_t needed); +void used_random(ssize_t amount); diff --git a/deadshred.c b/deadshred.c index 50528c4..5956115 100644 --- a/deadshred.c +++ b/deadshred.c @@ -1,30 +1,11 @@ /* See LICENSE file for copyright and license details. */ -#include -#include -#include +#include "common.h" #include +/* TODO why has the program started to freeze (since multithreading?)*/ -USAGE("[-o offset] [-l length | -e postend] device [< random-source]"); - -#define NANOSECONDS(X) X##L -#define MICROSECONDS(X) NANOSECONDS(X##000) -#define MILLISECONDS(X) MICROSECONDS(X##000) -#define CENTISECONDS(X) MILLISECONDS(X##0) -#define DECISECONDS(X) CENTISECONDS(X##0) -#define WHOLE_SECOND DECISECONDS(10) -enum direction { - FORWARDS = 0, - BACKWARDS = 1 -}; - -struct span { - off_t start; - off_t end; - off_t bad; - size_t blocksize; -}; +USAGE("[-o offset] [-l length | -e postend] device [< random-source]"); static _Atomic volatile sig_atomic_t exiting = 0; @@ -36,10 +17,6 @@ static size_t spans_size = 0; static off_t shredded = 0; static off_t total_size = 0; -static char reservoir[128U << 10]; -static size_t reservoir_off = sizeof(reservoir); -static int use_stdin; - static char total_size_1000[256]; static char total_size_1024[256]; @@ -58,12 +35,7 @@ static const struct timespec poll_timeout = {0, MILLISECONDS(500)}; static int progress_print_sig_pipe[2]; static pthread_mutex_t progress_mutex; -/* TODO deal with machine and process suspension */ -static struct timespec write_average_begin_times[5]; -static off_t write_average_amounts[ELEMSOF(write_average_begin_times)] = {0}; -static int write_average_i = 0; - -static enum direction direction = FORWARDS; +static enum direction direction = FORWARDS; /* TODO add option (-b) to switch start direction */ static uintmax_t pass_nr = 1; @@ -75,60 +47,6 @@ signal_handler(int signo) } -static off_t -filesize(int fd, const char *fname) -{ - struct stat st; - - if (fstat(fd, &st)) - eprintf("fstat %s:", fname); - - switch (st.st_mode & S_IFMT) { - case S_IFREG: - break; - case S_IFBLK: - if (ioctl(fd, BLKGETSIZE64, &st.st_size) < 0) - eprintf("ioctl %s BLKGETSIZE64:", fname); - break; - default: - eprintf("%s: not a regular file or block device", fname); - } - - return st.st_size; -} - - -static void -ensure_random(size_t needed) -{ - size_t off; - ssize_t r; - - if (sizeof(reservoir) - reservoir_off >= needed) - return; - - if (!use_stdin) { - libsimple_random_bytes(&libsimple_random_bits, NULL, reservoir, reservoir_off); - reservoir_off = 0; - return; - } - - for (off = 0; off < reservoir_off;) { - r = read(STDIN_FILENO, &reservoir[off], reservoir_off - off); - if (r <= 0) { - if (!r) - eprintf("random source depleted"); - if (errno == EINTR) - continue; - eprintf("read :"); - } - off += (size_t)r; - } - - reservoir_off = 0; -} - - static void add_span(off_t off, off_t amount, size_t blocksize, int try_join) { @@ -160,222 +78,6 @@ add_span(off_t off, off_t amount, size_t blocksize, int try_join) } -static char * -humansize1000(off_t s, char *buf) -{ - const char *units = "kMGTPEZYRQ"; - size_t unit = 0; - if (s < 1000) { - sprintf(buf, "%u B", (unsigned)s); - return buf; - } - s /= 100; - while (units[unit + 1U] && s >= 10000) { - s /= 1000; - unit++; - } - sprintf(buf, "%u.%u %cB", (unsigned)s / 10U, (unsigned)s % 10U, units[unit]); - return buf; -} - - -static char * -humansize1024(off_t s, char *buf) -{ - const char *units = "KMGTPEZYRQ"; - size_t unit = 0; - if (s < 1024) { - sprintf(buf, "%u B", (unsigned)s); - return buf; - } - while (units[unit + 1U] && s >= 1024 * 1024) { - s /= 1024; - unit++; - } - sprintf(buf, "%lu.%lu %ciB", (unsigned long int)s / 1024UL, (unsigned long int)(s * 10 % 10240) / 1024UL, units[unit]); - return buf; -} - - -#if defined(__GNUC__) -__attribute__((__pure__)) -#endif -static off_t -unhumansize(const char *s, char flag) -{ - off_t sum = 0, term, digit, divisor, power, base; - - if (!isdigit(s[0]) && !(s[0] == '-' && isdigit(s[1]))) - usage(); - - do { - divisor = 1; - term = 0; - while (isdigit(*s)) { - digit = (*s++ & 15); - if (term > (OFF_MAX - digit) / 10) - eprintf("value of -%c flag is too large", flag); - term = term * 10 + digit; - } - if (*s == '.') { - s++; - while (isdigit(*s)) { - digit = (*s++ & 15); - if (term > (OFF_MAX - digit) / 10) - eprintf("value of -%c flag is too large", flag); - term = term * 10 + digit; - divisor *= 10; - } - } - - power = 0; - switch (*s) { - case 'Q': power++; /* fall through */ - case 'R': power++; /* fall through */ - case 'Y': power++; /* fall through */ - case 'Z': power++; /* fall through */ - case 'E': power++; /* fall through */ - case 'P': power++; /* fall through */ - case 'T': power++; /* fall through */ - case 'G': power++; /* fall through */ - case 'M': power++; /* fall through */ - case 'k': case 'K': power++; - if (s[1] == 'i' || s[2] == 'B') { - base = 1024; - s = &s[3]; - } else if (s[1] == 'B') { - base = 1000; - s = &s[2]; - } else { - base = 1024; - s = &s[1]; - } - while (power) { - term *= base; - power--; - } - break; - case 'B': - if (!power && divisor > 1) - usage(); - s++; - break; - default: - break; - } - sum += term /= divisor; - - while (*s == ' ' || *s == ',' || *s == '+') - s++; - - } while (isdigit(s[0]) || (s[0] == '.' && isdigit(s[1]))); - - return sum; -} - - -static const char * -durationstr(const struct timespec *dur, char *buf, int second_decimals) -{ - uintmax_t ss, s, m, h, d, ss_div = (uintmax_t)WHOLE_SECOND; - char *p; - const char *unit; - int i; - - if (dur->tv_sec < 0 || dur->tv_nsec < 0) - return "-"; - - if (second_decimals < 0) - second_decimals = 0; - else if (second_decimals > 9) - second_decimals = 9; - - for (i = 0; i < second_decimals; i++) - ss_div /= 10U; - ss = (uintmax_t)dur->tv_nsec / ss_div; - s = (uintmax_t)dur->tv_sec % 60U; - m = (uintmax_t)dur->tv_sec / 60U % 60U; - h = (uintmax_t)dur->tv_sec / 60U / 60U % 24U; - d = (uintmax_t)dur->tv_sec / 60U / 60U / 24U; - - p = buf; - if (d) - p += sprintf(p, "%ju days, ", d); - if (h) { - p += sprintf(p, "%ju:%02ju:%02ju", h, m, s); - unit = "hours"; - } else if (m) { - p += sprintf(p, "%ju:%02ju", m, s); - unit = "minutes"; - } else { - p += sprintf(p, "%ju", s); - unit = "seconds"; - } - if (second_decimals) - p += sprintf(p, ".%0*ju", second_decimals, ss); - p += sprintf(p, " %s", unit); - - return buf; -} - - -#if defined(__linux__) -#include -static int -have_micro_symbol(void) -{ - static int ret = -1; - if (ret < 0) { - struct unimapdesc desc; - struct unipair *pairs = NULL; - size_t i; - ret = 1; - desc.entry_ct = 0; - desc.entries = NULL; - if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) - if (!desc.entry_ct) - goto out; - desc.entries = pairs = ecalloc(desc.entry_ct, sizeof(*pairs)); - if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) - goto out; - for (i = 0; i < desc.entry_ct; i++) - if (desc.entries[i++].unicode == 0xB5U) - goto out; - ret = 0; - out: - free(pairs); - } - return ret; -} -#else -# define have_micro_symbol() 1 -#endif - - -static int -was_write_average_overrun(int i, const struct timespec *now, int seconds) -{ - struct timespec diff; - libsimple_difftimespec(&diff, now, &write_average_begin_times[i]); - if (diff.tv_sec >= seconds) - return 1; - if (diff.tv_sec == seconds - 1 && diff.tv_nsec >= DECISECONDS(9)) - return 1; - return 0; -} - - -static void -shift_write_average(void) -{ - write_average_i--; - memmove(&write_average_begin_times[0], &write_average_begin_times[1], - (size_t)write_average_i * sizeof(*write_average_begin_times)); - memmove(&write_average_amounts[0], &write_average_amounts[1], - (size_t)write_average_i * sizeof(*write_average_amounts)); -} - - static void print_progress(int done, const struct timespec *now) { @@ -390,48 +92,8 @@ print_progress(int done, const struct timespec *now) char subbuf5[256]; char subbuf6[256]; char subbuf7[256]; - char write_average_buf[512]; struct timespec since_success = {-1, 0}; - struct timespec time_spent, write_average_time; - int i; - off_t write_average_sum = 0; - double write_average; - - for (i = 0; i <= write_average_i; i++) - write_average_sum += write_average_amounts[i]; - libsimple_difftimespec(&write_average_time, now, &write_average_begin_times[0]); - if (write_average_time.tv_sec < 0 || (write_average_time.tv_sec == 0 && write_average_time.tv_nsec < CENTISECONDS(10))) { - stpcpy(write_average_buf, "-"); - } else if (write_average_sum == 0) { - stpcpy(write_average_buf, "0"); - } else { - const char *units_big = "kMGTPEZYRQ"; - const char *units_small = "munpfazyrq"; - int unit = -1; - write_average = (double)write_average_time.tv_nsec; - write_average /= WHOLE_SECOND; - write_average += (double)write_average_time.tv_sec; - write_average = (double)write_average_sum / write_average; - if (write_average < (double)0.01f) { - do { - write_average *= 1000; - unit++; - } while (units_small[unit + 1] && write_average < (double)0.01f); - if (units_small[unit] == 'u' && have_micro_symbol()) - sprintf(write_average_buf, "%.02lf µB/s", write_average); - else - sprintf(write_average_buf, "%.02lf %cB/s", write_average, units_small[unit]); - } else { - while (units_big[unit + 1] && write_average >= 1000) { - write_average /= 1000; - unit++; - } - if (unit < 0) - sprintf(write_average_buf, "%.02lf B/s", write_average); - else - sprintf(write_average_buf, "%.02lf %cB/s", write_average, units_big[unit]); - } - } + struct timespec time_spent; if (last_success.tv_sec >= 0) { libsimple_difftimespec(&since_success, now, &last_success); @@ -465,7 +127,7 @@ print_progress(int done, const struct timespec *now) humansize1024(bad_bytes, subbuf4), /* } line 3 { */ durationstr(&time_spent, subbuf5, 1), - write_average_buf, + wravg_get(now), /* } line 4 { */ durationstr(&since_success, subbuf6, 2), /* } line 5 { */ @@ -485,21 +147,6 @@ print_progress(int done, const struct timespec *now) } -static void -update_progress(const struct timespec *now) -{ - if (was_write_average_overrun(write_average_i, now, 1)) { - write_average_i++; - if (write_average_i == ELEMSOF(write_average_amounts)) - shift_write_average(); - write_average_begin_times[write_average_i] = *now; - write_average_amounts[write_average_i] = 0; - } - while (write_average_i && was_write_average_overrun(0, now, (int)ELEMSOF(write_average_amounts))) - shift_write_average(); -} - - static void * progress_print_loop(void *user) { @@ -550,7 +197,7 @@ progress_print_loop(void *user) terminate = 1; } print_progress(0, &now); - update_progress(&now); + wravg_update_time(&now); pthread_mutex_unlock(&progress_mutex); } while (!terminate); @@ -566,6 +213,7 @@ shredspan(int fd, struct span *span, const char *fname) struct timespec now, when = {0, 0}; int bad = span->bad > 0; int first_fail = 1; + const char *random_data; pthread_mutex_lock(&progress_mutex); @@ -588,7 +236,6 @@ shredspan(int fd, struct span *span, const char *fname) libsimple_sumtimespec(&when, &now, &progress_print_interval); write(progress_print_sig_pipe[1], &now, sizeof(now)); } - ensure_random(span->blocksize); if (direction == FORWARDS) { n = MIN((off_t)span->blocksize, span->end - off); } else { @@ -600,9 +247,10 @@ shredspan(int fd, struct span *span, const char *fname) if (!n) break; } + random_data = get_random(span->blocksize); pthread_mutex_unlock(&progress_mutex); pwrite_again: - r = pwrite(fd, &reservoir[reservoir_off], (size_t)n, off); + r = pwrite(fd, random_data, (size_t)n, off); if (r < 0) { if (errno == EINTR) { if (exiting) { @@ -649,8 +297,8 @@ shredspan(int fd, struct span *span, const char *fname) bad_writes += 1U; } shredded += (off_t)r; - reservoir_off += (size_t)r; - write_average_amounts[write_average_i] += (off_t)r; + used_random(r); + wravg_update_write(r); last_success = now; if (span->bad) { bad_bytes -= (off_t)r; @@ -748,30 +396,13 @@ main(int argc, char *argv[]) if (fd < 0) eprintf("open %s O_WRONLY|O_DSYNC:", argv[0]); - use_stdin = !isatty(STDIN_FILENO); - if (use_stdin) { - struct stat st; - if (fstat(STDIN_FILENO, &st)) { - if (errno == EBADF) - use_stdin = 0; - else - eprintf("fstat :"); - } else { - if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) - weprintf("stdin is open but is not a TTY, character device, FIFO, or socket"); - } - } - if (!use_stdin) { - libsimple_srand(); - } + init_random(STDIN_FILENO, ""); spans = emalloc(sizeof(*spans)); spans[0].start = 0; spans[0].end = filesize(fd, argv[0]); spans[0].bad = 0; - spans[0].blocksize = sizeof(reservoir); - while (spans[0].blocksize & (spans[0].blocksize - 1U)) - spans[0].blocksize &= (spans[0].blocksize - 1U); + spans[0].blocksize = max_blksize(); nspans = 1U; spans_size = 1U; @@ -813,7 +444,7 @@ main(int argc, char *argv[]) if (clock_gettime(clck, &start_time)) eprintf("clock_gettime %s:", clkcstr); } - write_average_begin_times[0] = start_time; + wravg_init(&start_time); last_success = start_time; while (nspans) { @@ -824,6 +455,7 @@ main(int argc, char *argv[]) memmove(&spans[0], &spans[i], (nspans -= i) * sizeof(*spans)); break; } + /* TODO bug: this is sometimes reached immediately on write failure */ for (i = 0, j = nspans, nspans -= old_nspans; i < nspans;) spans[i++] = spans[--j]; direction ^= 1; diff --git a/fmt.c b/fmt.c new file mode 100644 index 0000000..4c931dd --- /dev/null +++ b/fmt.c @@ -0,0 +1,189 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static const char *units_big = "KMGTPEZYRQ"; +static const char *units_small = "munpfazyrq"; /* 'u' should be "µ" */ + + +const char * +humansize1000(off_t s, char *buf) +{ + size_t unit = 0; + if (s < 1000) { + sprintf(buf, "%u B", (unsigned)s); + return buf; + } + s /= 100; + while (units_big[unit + 1U] && s >= 10000) { + s /= 1000; + unit++; + } + sprintf(buf, "%u.%u %cB", (unsigned)s / 10U, (unsigned)s % 10U, units_big[unit]); + return buf; +} + + +const char * +humansize1024(off_t s, char *buf) +{ + size_t unit = 0; + if (s < 1024) { + sprintf(buf, "%u B", (unsigned)s); + return buf; + } + while (units_big[unit + 1U] && s >= 1024 * 1024) { + s /= 1024; + unit++; + } + sprintf(buf, "%lu.%lu %ciB", (unsigned long int)s / 1024UL, (unsigned long int)(s * 10 % 10240) / 1024UL, units_big[unit]); + return buf; +} + + +off_t +unhumansize(const char *s, char flag) +{ + off_t sum = 0, term, digit, divisor, power, base; + + if (!isdigit(s[0]) && !(s[0] == '-' && isdigit(s[1]))) + eprintf("value of -%c is not a file size", flag); + + do { + divisor = 1; + term = 0; + while (isdigit(*s)) { + digit = (*s++ & 15); + if (term > (OFF_MAX - digit) / 10) + eprintf("value of -%c option is too large", flag); + term = term * 10 + digit; + } + if (*s == '.') { + s++; + while (isdigit(*s)) { + digit = (*s++ & 15); + if (term > (OFF_MAX - digit) / 10) + eprintf("value of -%c option is too large", flag); + term = term * 10 + digit; + divisor *= 10; + } + } + + power = 0; + switch (*s) { + case 'Q': power++; /* fall through */ + case 'R': power++; /* fall through */ + case 'Y': power++; /* fall through */ + case 'Z': power++; /* fall through */ + case 'E': power++; /* fall through */ + case 'P': power++; /* fall through */ + case 'T': power++; /* fall through */ + case 'G': power++; /* fall through */ + case 'M': power++; /* fall through */ + case 'k': case 'K': power++; + if (s[1] == 'i' || s[2] == 'B') { + base = 1024; + s = &s[3]; + } else if (s[1] == 'B') { + base = 1000; + s = &s[2]; + } else { + base = 1024; + s = &s[1]; + } + while (power) { + term *= base; + power--; + } + break; + case 'B': + s++; + /* fall through */ + default: + if (divisor > 1) + eprintf("value of -%c contains non-integer exact byte count", flag); + break; + } + sum += term /= divisor; + + while (*s == ' ' || *s == ',' || *s == '+') + s++; + + } while (isdigit(s[0]) || (s[0] == '.' && isdigit(s[1]))); + + return sum; +} + + +const char * +durationstr(const struct timespec *dur, char *buf, int second_decimals) +{ + uintmax_t ss, s, m, h, d, ss_div = (uintmax_t)WHOLE_SECOND; + char *p; + const char *unit; + int i; + + if (dur->tv_sec < 0 || dur->tv_nsec < 0) + return "-"; + + if (second_decimals < 0) + second_decimals = 0; + else if (second_decimals > 9) + second_decimals = 9; + + for (i = 0; i < second_decimals; i++) + ss_div /= 10U; + ss = (uintmax_t)dur->tv_nsec / ss_div; + s = (uintmax_t)dur->tv_sec % 60U; + m = (uintmax_t)dur->tv_sec / 60U % 60U; + h = (uintmax_t)dur->tv_sec / 60U / 60U % 24U; + d = (uintmax_t)dur->tv_sec / 60U / 60U / 24U; + + p = buf; + if (d) + p += sprintf(p, "%ju days, ", d); + if (h) { + p += sprintf(p, "%ju:%02ju:%02ju", h, m, s); + unit = "hours"; + } else if (m) { + p += sprintf(p, "%ju:%02ju", m, s); + unit = "minutes"; + } else { + p += sprintf(p, "%ju", s); + unit = "seconds"; + } + if (second_decimals) + p += sprintf(p, ".%0*ju", second_decimals, ss); + p += sprintf(p, " %s", unit); + + return buf; +} + + +const char * +humanbytespersecond(double bytes_per_second, char *buf) +{ + int unit = -1; + + if (bytes_per_second < (double)0.01f) { + do { + bytes_per_second *= 1000; + unit++; + } while (units_small[unit + 1] && bytes_per_second < (double)0.01f); + if (units_small[unit] == 'u' && have_micro_symbol()) + sprintf(buf, "%.02lf µB/s", bytes_per_second); + else + sprintf(buf, "%.02lf %cB/s", bytes_per_second, units_small[unit]); + } else { + while (units_big[unit + 1] && bytes_per_second >= 1000) { + bytes_per_second /= 1000; + unit++; + } + if (unit < 0) + sprintf(buf, "%.02lf B/s", bytes_per_second); + else + sprintf(buf, "%.02lf %cB/s", bytes_per_second, units_big[unit]); + } + + return buf; +} diff --git a/io.c b/io.c new file mode 100644 index 0000000..6ee9e29 --- /dev/null +++ b/io.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +off_t +filesize(int fd, const char *fname) +{ + struct stat st; + + if (fstat(fd, &st)) + eprintf("fstat %s:", fname); + + switch (st.st_mode & S_IFMT) { + case S_IFREG: + break; + case S_IFBLK: + if (ioctl(fd, BLKGETSIZE64, &st.st_size) < 0) + eprintf("ioctl %s BLKGETSIZE64:", fname); + break; + default: + eprintf("%s: not a regular file or block device", fname); + } + + return st.st_size; +} diff --git a/rnd.c b/rnd.c new file mode 100644 index 0000000..7e24bf2 --- /dev/null +++ b/rnd.c @@ -0,0 +1,80 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +static char reservoir[128U << 10]; +static size_t reservoir_off = sizeof(reservoir); +static int random_fd; +static const char *random_fname; + + +size_t +max_blksize(void) +{ + size_t ret = sizeof(reservoir); + while (ret & (ret - 1U)) + ret &= ret - 1U; + return ret; +} + + +void +init_random(int fd, const char *fname) +{ + struct stat st; + + random_fname = fname; + random_fd = fd; + + errno = 0; + if (isatty(random_fd) || errno == EBADF) { + use_builtin_generator: + random_fd = -1; + libsimple_srand(); + } else if (fstat(random_fd, &st)) { + if (errno == EBADF) + goto use_builtin_generator; + eprintf("fstat %s:", random_fname); + } else if (S_ISFIFO(st.st_mode) || S_ISCHR(st.st_mode) || S_ISSOCK(st.st_mode)) { + eprintf("%s is of a unsupported inode type (must be TTY, character device, FIFO, or socket)", fname); + } +} + + +const char * +get_random(size_t needed) +{ + size_t off; + ssize_t r; + + if (sizeof(reservoir) - reservoir_off >= needed) + return &reservoir[reservoir_off]; + + if (random_fd < 0) { + libsimple_random_bytes(&libsimple_random_bits, NULL, reservoir, reservoir_off); + reservoir_off = 0; + return reservoir; + } + + for (off = 0; off < reservoir_off;) { + r = read(random_fd, &reservoir[off], reservoir_off - off); + if (r <= 0) { + if (!r) + eprintf("random source depleted"); + if (errno == EINTR) + continue; + eprintf("read %s:", random_fname); + } + off += (size_t)r; + } + + reservoir_off = 0; + return reservoir; +} + + +void +used_random(ssize_t amount) +{ + reservoir_off += (size_t)amount; +} diff --git a/text.c b/text.c new file mode 100644 index 0000000..abea26a --- /dev/null +++ b/text.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + + +#if defined(__linux__) +# include + +int +have_micro_symbol(void) +{ + static int ret = -1; + if (ret < 0) { + struct unimapdesc desc; + struct unipair *pairs = NULL; + size_t i; + ret = 1; + desc.entry_ct = 0; + desc.entries = NULL; + if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) + if (!desc.entry_ct) + goto out; + desc.entries = pairs = ecalloc(desc.entry_ct, sizeof(*pairs)); + if (ioctl(STDIN_FILENO, GIO_UNIMAP, &desc)) + goto out; + for (i = 0; i < desc.entry_ct; i++) + if (desc.entries[i++].unicode == 0xB5U) + goto out; + ret = 0; + out: + free(pairs); + } + return ret; +} + +#else + +# if defined(__GNUC__) +__attribute__((__const__)) +# endif +int +have_micro_symbol(void) +{ + return 1; +} + +#endif -- cgit v1.2.3-70-g09d2