commit 0e1eff5b95f2e5962b8941fe706d320a7eca469b from: Omar Polo date: Wed Jun 23 08:38:20 2021 UTC "humanize" byte progress i.e. trasform XYZ bytes to something readable commit - 27766d481f3a15b6ad16822034ca247c99c3e456 commit + 0e1eff5b95f2e5962b8941fe706d320a7eca469b blob - 3c547ec903b20ca8a885da9f9414990f23cef090 blob + 882b805359b1bdb158feb783460d4148b22e0aa5 --- ChangeLog +++ ChangeLog @@ -1,3 +1,7 @@ +2021-06-23 Omar Polo + + * telescope.c (handle_imsg_buf): "humanize" byte progress (i.e. trasform XYZ bytes to something readable) + 2021-06-22 Omar Polo * parse.y (colorname): support 256 colors blob - /dev/null blob + e4cb402b1b1ebfea020505a9d210f9c31fef15fc (mode 644) --- /dev/null +++ compat/fmt_scaled.c @@ -0,0 +1,301 @@ +/* $OpenBSD: fmt_scaled.c,v 1.19 2020/10/12 22:08:34 deraadt Exp $ */ + +/* + * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * fmt_scaled: Format numbers scaled for human comprehension + * scan_scaled: Scan numbers in this format. + * + * "Human-readable" output uses 4 digits max, and puts a unit suffix at + * the end. Makes output compact and easy-to-read esp. on huge disks. + * Formatting code was originally in OpenBSD "df", converted to library routine. + * Scanning code written for OpenBSD libutil. + */ + +#include "compat.h" + +#include +#include +#include +#include +#include +#include + +typedef enum { + NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6 +} unit_type; + +/* These three arrays MUST be in sync! XXX make a struct */ +static const unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA }; +static const char scale_chars[] = "BKMGTPE"; +static const long long scale_factors[] = { + 1LL, + 1024LL, + 1024LL*1024, + 1024LL*1024*1024, + 1024LL*1024*1024*1024, + 1024LL*1024*1024*1024*1024, + 1024LL*1024*1024*1024*1024*1024, +}; +#define SCALE_LENGTH (sizeof(units)/sizeof(units[0])) + +#define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */ + +/* Convert the given input string "scaled" into numeric in "result". + * Return 0 on success, -1 and errno set on error. + */ +int +scan_scaled(char *scaled, long long *result) +{ + char *p = scaled; + int sign = 0; + unsigned int i, ndigits = 0, fract_digits = 0; + long long scale_fact = 1, whole = 0, fpart = 0; + + /* Skip leading whitespace */ + while (isascii((unsigned char)*p) && isspace((unsigned char)*p)) + ++p; + + /* Then at most one leading + or - */ + while (*p == '-' || *p == '+') { + if (*p == '-') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = -1; + ++p; + } else if (*p == '+') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = +1; + ++p; + } + } + + /* Main loop: Scan digits, find decimal point, if present. + * We don't allow exponentials, so no scientific notation + * (but note that E for Exa might look like e to some!). + * Advance 'p' to end, to get scale factor. + */ + for (; isascii((unsigned char)*p) && + (isdigit((unsigned char)*p) || *p=='.'); ++p) { + if (*p == '.') { + if (fract_digits > 0) { /* oops, more than one '.' */ + errno = EINVAL; + return -1; + } + fract_digits = 1; + continue; + } + + i = (*p) - '0'; /* whew! finally a digit we can use */ + if (fract_digits > 0) { + if (fract_digits >= MAX_DIGITS-1) + /* ignore extra fractional digits */ + continue; + fract_digits++; /* for later scaling */ + if (fpart > LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } + fpart *= 10; + if (i > LLONG_MAX - fpart) { + errno = ERANGE; + return -1; + } + fpart += i; + } else { /* normal digit */ + if (++ndigits >= MAX_DIGITS) { + errno = ERANGE; + return -1; + } + if (whole > LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } + whole *= 10; + if (i > LLONG_MAX - whole) { + errno = ERANGE; + return -1; + } + whole += i; + } + } + + if (sign) { + whole *= sign; + fpart *= sign; + } + + /* If no scale factor given, we're done. fraction is discarded. */ + if (!*p) { + *result = whole; + return 0; + } + + /* Validate scale factor, and scale whole and fraction by it. */ + for (i = 0; i < SCALE_LENGTH; i++) { + + /* Are we there yet? */ + if (*p == scale_chars[i] || + *p == tolower((unsigned char)scale_chars[i])) { + + /* If it ends with alphanumerics after the scale char, bad. */ + if (isalnum((unsigned char)*(p+1))) { + errno = EINVAL; + return -1; + } + scale_fact = scale_factors[i]; + + /* check for overflow and underflow after scaling */ + if (whole > LLONG_MAX / scale_fact || + whole < LLONG_MIN / scale_fact) { + errno = ERANGE; + return -1; + } + + /* scale whole part */ + whole *= scale_fact; + + /* truncate fpart so it does't overflow. + * then scale fractional part. + */ + while (fpart >= LLONG_MAX / scale_fact) { + fpart /= 10; + fract_digits--; + } + fpart *= scale_fact; + if (fract_digits > 0) { + for (i = 0; i < fract_digits -1; i++) + fpart /= 10; + } + whole += fpart; + *result = whole; + return 0; + } + } + + /* Invalid unit or character */ + errno = EINVAL; + return -1; +} + +/* Format the given "number" into human-readable form in "result". + * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE. + * Return 0 on success, -1 and errno set if error. + */ +int +fmt_scaled(long long number, char *result) +{ + long long abval, fract = 0; + unsigned int i; + unit_type unit = NONE; + + /* Not every negative long long has a positive representation. */ + if (number == LLONG_MIN) { + errno = ERANGE; + return -1; + } + + abval = llabs(number); + + /* Also check for numbers that are just too darned big to format. */ + if (abval / 1024 >= scale_factors[SCALE_LENGTH-1]) { + errno = ERANGE; + return -1; + } + + /* scale whole part; get unscaled fraction */ + for (i = 0; i < SCALE_LENGTH; i++) { + if (abval/1024 < scale_factors[i]) { + unit = units[i]; + fract = (i == 0) ? 0 : abval % scale_factors[i]; + number /= scale_factors[i]; + if (i > 0) + fract /= scale_factors[i - 1]; + break; + } + } + + fract = (10 * fract + 512) / 1024; + /* if the result would be >= 10, round main number */ + if (fract >= 10) { + if (number >= 0) + number++; + else + number--; + fract = 0; + } else if (fract < 0) { + /* shouldn't happen */ + fract = 0; + } + + if (number == 0) + strlcpy(result, "0B", FMT_SCALED_STRSIZE); + else if (unit == NONE || number >= 100 || number <= -100) { + if (fract >= 5) { + if (number >= 0) + number++; + else + number--; + } + (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c", + number, scale_chars[unit]); + } else + (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c", + number, fract, scale_chars[unit]); + + return 0; +} + +#ifdef MAIN +/* + * This is the original version of the program in the man page. + * Copy-and-paste whatever you need from it. + */ +int +main(int argc, char **argv) +{ + char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE]; + long long ninput = 10483892, result; + + if (scan_scaled(cinput, &result) == 0) + printf("\"%s\" -> %lld\n", cinput, result); + else + perror(cinput); + + if (fmt_scaled(ninput, buf) == 0) + printf("%lld -> \"%s\"\n", ninput, buf); + else + fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno)); + + return 0; +} +#endif blob - 804d271dfd33fc7371e6312e838546d6490dfaec blob + d95ba1c075a7767ce9abad6533e9512f84e0bd95 --- compat.h +++ compat.h @@ -35,9 +35,12 @@ #ifdef HAVE_LIBUTIL # include # include +# include #else # include "compat/imsg.h" # include "compat/ohash.h" +# define FMT_SCALED_STRSIZE 7 /* minus sign, 4 digits, suffix, null byte */ +int fmt_scaled(long long, char *); #endif #ifndef HAVE_ASPRINTF blob - 197094d148d98d5113efad3d6bd01f9a877d85fc blob + 5f991cb0b9e5f2d856b1632b261af7ea89dd8055 --- configure.ac +++ configure.ac @@ -61,6 +61,7 @@ AC_CHECK_LIB(util, imsg_init, [], [ AC_LIBOBJ(imsg) AC_LIBOBJ(imsg-buffer) AC_LIBOBJ(ohash) + AC_LIBOBJ(fmt_scaled) ]) AC_CHECK_FUNCS([asr_run]) blob - 8f703a8004b5c1182a58ab48dc598b2fab8dc327 blob + 8fbb6fe92c489712e4e4f0a01e9542bcb16d995b --- telescope.c +++ telescope.c @@ -342,7 +342,7 @@ handle_imsg_buf(struct imsg *imsg, size_t datalen) { struct tab *tab; int l; - char *page; + char *page, buf[FMT_SCALED_STRSIZE] = {0}; tab = tab_by_id(imsg->hdr.peerid); @@ -353,9 +353,10 @@ handle_imsg_buf(struct imsg *imsg, size_t datalen) die(); } else { write(tab->fd, imsg->data, datalen); - l = asprintf(&page, "Writing \"%s\"... (%zu bytes)\n", + fmt_scaled(tab->bytes, buf); + l = asprintf(&page, "Saving to \"%s\"... (%s)\n", tab->path, - tab->bytes); + buf); if (l == -1) die(); load_page_from_str(tab, page); @@ -370,7 +371,7 @@ handle_imsg_eof(struct imsg *imsg, size_t datalen) { struct tab *tab; int l; - char *page; + char *page, buf[FMT_SCALED_STRSIZE] = {0}; tab = tab_by_id(imsg->hdr.peerid); @@ -378,9 +379,10 @@ handle_imsg_eof(struct imsg *imsg, size_t datalen) if (!tab->buffer.page.free(&tab->buffer.page)) die(); } else { - l = asprintf(&page, "Wrote %s (%zu bytes)\n", + fmt_scaled(tab->bytes, buf); + l = asprintf(&page, "Saved to \"%s\" (%s)\n", tab->path, - tab->bytes); + buf); if (l == -1) die(); load_page_from_str(tab, page);