aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile12
-rw-r--r--avg.c94
-rw-r--r--common.h57
-rw-r--r--deadshred.c400
-rw-r--r--fmt.c189
-rw-r--r--io.c25
-rw-r--r--rnd.c80
-rw-r--r--text.c46
8 files changed, 516 insertions, 387 deletions
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 <sys/mount.h>
+#include <pthread.h>
+#include <libsimple.h>
+
+
+#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 <sys/mount.h>
-#include <pthread.h>
-#include <libsimple.h>
+#include "common.h"
#include <libsimple-arg.h>
+/* 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 <stdin>:");
- }
- 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 <linux/kd.h>
-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 <stdin>:");
- } 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, "<stdin>");
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 <linux/kd.h>
+
+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