Rearrange the file tree.
[u/mdw/catacomb] / progs / cc-progress.c
diff --git a/progs/cc-progress.c b/progs/cc-progress.c
new file mode 100644 (file)
index 0000000..903e80e
--- /dev/null
@@ -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 -------------------------------------------------*/