X-Git-Url: https://git.distorted.org.uk/u/mdw/catacomb/blobdiff_plain/ba6e6b64033b1f9de49feccb5c9cd438354481f7..0f00dc4c8eb47e67bc0f148c2dd109f73a451e0a:/progs/cc-progress.c diff --git a/progs/cc-progress.c b/progs/cc-progress.c new file mode 100644 index 0000000..903e80e --- /dev/null +++ b/progs/cc-progress.c @@ -0,0 +1,244 @@ +/* -*-c-*- + * + * Progress indicators for command-line tools + * + * (c) 2011 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Catacomb. + * + * Catacomb is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * Catacomb is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with Catacomb; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#define _FILE_OFFSET_BITS 64 + +#include "config.h" + +#include "cc.h" + +#ifndef PATHSEP +# if defined(__riscos) +# define PATHSEP '.' +# elif defined(__unix) || defined(unix) +# define PATHSEP '/' +# else +# define PATHSEP '\\' +# endif +#endif + +/*----- Static data -------------------------------------------------------*/ + +static const char baton[] = "-\\|/"; + +/*----- Human-friendly unit printing --------------------------------------*/ + +struct unit { + const char *name; + int m; +}; + +/* --- @prhuman_time@ --- * + * + * Arguments: @FILE *fp@ = stream to print on + * @unsigned long n@ = time in seconds to print + * + * Returns: --- + * + * Use: Prints a time in some reasonable format. The time takes up + * PRHUMAN_TIMEWD character spaces. + */ + +#define PRHUMAN_TIMEWD 7 + +static void prhuman_time(FILE *fp, unsigned long n) +{ + const static struct unit utime[] = { + { "s", 60 }, { "m", 60 }, { "h", 24 }, { "d", 0 } + }; + + unsigned long m = 0; + const struct unit *u = utime; + + while (u[1].m && n > u[0].m*u[1].m) { n /= u->m; u++; } + m = n / u[1].m; n %= u[0].m; + if (m) fprintf(fp, "%3lu%s%02lu%s", m, u[1].name, n, u[0].name); + else fprintf(fp, " %2lu%s", n, u[0].name); +} + +/* --- @prhuman_data@ --- * + * + * Arguments: @FILE *fp@ = file to print on + * @off_t n@ = size to be printed + * + * Returns: --- + * + * Use: Prints a data size in some reasonable format. The data size + * takes up PRHUMAN_DATAWD character spaces. + */ + +#define PRHUMAN_DATAWD 7 + +static void prhuman_data(FILE *fp, off_t n) +{ + const static struct unit udata[] = { + { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 }, + { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 }, + { "Y", 0 } + }; + + double x = n; + const struct unit *u = udata; + + while (u->m && x >= u->m) { x /= u->m; u++; } + fprintf(fp, "%6.1f%s", x, u->name); +} + +/*----- Main code ---------------------------------------------------------*/ + +#define BARWD 16 + +/* --- @fprogress_init@ --- * + * + * Arguments: @fprogress *f@ = progress context to be initialized + * @const char *name@ = file name string to show + * @FILE *fp@ = file we're reading from + * + * Returns: Zero on success, nonzero if the file's state is now broken. + * + * Use: Initializes a progress context. Nothing is actually + * displayed yet. + */ + +int fprogress_init(fprogress *f, const char *name, FILE *fp) +{ + const char *p; + off_t o, sz = -1; + size_t n; + + /* --- Set up the offset --- */ + + if ((o = ftello(fp)) >= 0 && + fseeko(fp, 0, SEEK_END) >= 0 && + (sz = ftello(fp), + fseeko(fp, o, SEEK_SET) < 0)) + return (-1); + if (o != -1 && sz != -1) sz -= o; + f->o = f->olast = 0; f->sz = sz; + + /* --- Set up the file name --- */ + + n = strlen(name); + if (n < sizeof(f->name)) + strcpy(f->name, name); + else if ((p = strchr(name + n - sizeof(f->name) + 4, PATHSEP)) != 0) + sprintf(f->name, "...%s", p); + else { + p = strrchr(name, PATHSEP); + if (!p) sprintf(f->name, "%.*s...", (int)sizeof(f->name) - 4, name); + else sprintf(f->name, "...%.*s...", (int)sizeof(f->name) - 7, p); + } + + /* --- Set up some other stuff --- */ + + f->start = f->last = time(0); + f->bp = baton; + + /* --- Done --- */ + + return (0); +} + +/* --- @fprogress_clear@ --- * + * + * Arguments: @fprogress *f@ = progress context + * + * Returns: --- + * + * Use: Clears the progress display from the screen. + */ + +void fprogress_clear(fprogress *f) +{ + fprintf(stderr, "\r%*s\r", + sizeof(f->name) + 2*PRHUMAN_DATAWD + PRHUMAN_TIMEWD + BARWD + 16, + ""); +} + +/* --- @fprogress_update@ --- * + * + * Arguments: @fprogress *f@ = progress context + * @size_t n@ = how much progress has been made + * + * Returns: --- + * + * Use: Maybe updates the display to show that some progress has been + * made. + */ + +void fprogress_update(fprogress *f, size_t sz) +{ + time_t now = time(0); + int i, n; + + /* --- See if there's anything to do --- */ + + f->o += sz; + if (difftime(now, f->last) < 1) return; + f->last = now; + + /* --- See if we're going to lose the ETA and percentage indicators --- */ + + if (f->olast < f->sz && f->o > f->sz) fprogress_clear(f); + f->olast = f->o; + + /* --- Do the initial display --- */ + + fprintf(stderr, "\r%-*s%c ", + (int)sizeof(f->name), f->name, + *f->bp++); + if (!*f->bp) f->bp = baton; + prhuman_data(stderr, f->o); + + /* --- More complicated display if we have a set size --- */ + + if (f->sz > f->o) { + fputc('/', stderr); + prhuman_data(stderr, f->sz); + fputs(" [", stderr); + n = (f->o*BARWD + f->sz/2)/f->sz; + for (i = 0; i < n; i++) fputc('.', stderr); + fprintf(stderr, "%*s] %3d%% ETA ", BARWD - n, "", + (int)((f->o*100 + 50)/f->sz)); + prhuman_time(stderr, difftime(now, f->start)*(f->sz - f->o)/f->o); + } +} + +/* --- @fprogress_done@ --- * + * + * Arguments: @fprogress *f@ = progress context + * + * Returns: --- + * + * Use: Clear up the progress context and removes any display. + */ + +void fprogress_done(fprogress *f) { fprogress_clear(f); } + +/*----- That's all, folks -------------------------------------------------*/