chiark / gitweb /
Rearrange the file tree.
[catacomb] / progs / cc-progress.c
1 /* -*-c-*-
2  *
3  * Progress indicators for command-line tools
4  *
5  * (c) 2011 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of Catacomb.
11  *
12  * Catacomb is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * Catacomb is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with Catacomb; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #define _FILE_OFFSET_BITS 64
31
32 #include "config.h"
33
34 #include "cc.h"
35
36 #ifndef PATHSEP
37 #  if defined(__riscos)
38 #    define PATHSEP '.'
39 #  elif defined(__unix) || defined(unix)
40 #    define PATHSEP '/'
41 #  else
42 #    define PATHSEP '\\'
43 #  endif
44 #endif
45
46 /*----- Static data -------------------------------------------------------*/
47
48 static const char baton[] = "-\\|/";
49
50 /*----- Human-friendly unit printing --------------------------------------*/
51
52 struct unit {
53   const char *name;
54   int m;
55 };
56
57 /* --- @prhuman_time@ --- *
58  *
59  * Arguments:   @FILE *fp@ = stream to print on
60  *              @unsigned long n@ = time in seconds to print
61  *
62  * Returns:     ---
63  *
64  * Use:         Prints a time in some reasonable format.  The time takes up
65  *              PRHUMAN_TIMEWD character spaces.
66  */
67
68 #define PRHUMAN_TIMEWD 7
69
70 static void prhuman_time(FILE *fp, unsigned long n)
71 {
72   const static struct unit utime[] = {
73     { "s",  60 }, { "m", 60 }, { "h", 24 }, { "d", 0 }
74   };
75
76   unsigned long m = 0;
77   const struct unit *u = utime;
78
79   while (u[1].m && n > u[0].m*u[1].m) { n /= u->m; u++; }
80   m = n / u[1].m; n %= u[0].m;
81   if (m) fprintf(fp, "%3lu%s%02lu%s", m, u[1].name, n, u[0].name);
82   else fprintf(fp, "    %2lu%s", n, u[0].name);
83 }
84
85 /* --- @prhuman_data@ --- *
86  *
87  * Arguments:   @FILE *fp@ = file to print on
88  *              @off_t n@ = size to be printed
89  *
90  * Returns:     ---
91  *
92  * Use:         Prints a data size in some reasonable format.  The data size
93  *              takes up PRHUMAN_DATAWD character spaces.
94  */
95
96 #define PRHUMAN_DATAWD 7
97
98 static void prhuman_data(FILE *fp, off_t n)
99 {
100   const static struct unit udata[] = {
101     { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 },
102     { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 },
103     { "Y",    0 }
104   };
105
106   double x = n;
107   const struct unit *u = udata;
108
109   while (u->m && x >= u->m) { x /= u->m; u++; }
110   fprintf(fp, "%6.1f%s", x, u->name);
111 }
112
113 /*----- Main code ---------------------------------------------------------*/
114
115 #define BARWD 16
116
117 /* --- @fprogress_init@ --- *
118  *
119  * Arguments:   @fprogress *f@ = progress context to be initialized
120  *              @const char *name@ = file name string to show
121  *              @FILE *fp@ = file we're reading from
122  *
123  * Returns:     Zero on success, nonzero if the file's state is now broken.
124  *
125  * Use:         Initializes a progress context.  Nothing is actually
126  *              displayed yet.
127  */
128
129 int fprogress_init(fprogress *f, const char *name, FILE *fp)
130 {
131   const char *p;
132   off_t o, sz = -1;
133   size_t n;
134
135   /* --- Set up the offset --- */
136
137   if ((o = ftello(fp)) >= 0 &&
138       fseeko(fp, 0, SEEK_END) >= 0 &&
139       (sz = ftello(fp),
140        fseeko(fp, o, SEEK_SET) < 0))
141     return (-1);
142   if (o != -1 && sz != -1) sz -= o;
143   f->o = f->olast = 0; f->sz = sz;
144
145   /* --- Set up the file name --- */
146
147   n = strlen(name);
148   if (n < sizeof(f->name))
149     strcpy(f->name, name);
150   else if ((p = strchr(name + n - sizeof(f->name) + 4, PATHSEP)) != 0)
151     sprintf(f->name, "...%s", p);
152   else {
153     p = strrchr(name, PATHSEP);
154     if (!p) sprintf(f->name, "%.*s...", (int)sizeof(f->name) - 4, name);
155     else sprintf(f->name, "...%.*s...", (int)sizeof(f->name) - 7, p);
156   }
157
158   /* --- Set up some other stuff --- */
159
160   f->start = f->last = time(0);
161   f->bp = baton;
162
163   /* --- Done --- */
164
165   return (0);
166 }
167
168 /* --- @fprogress_clear@ --- *
169  *
170  * Arguments:   @fprogress *f@ = progress context
171  *
172  * Returns:     ---
173  *
174  * Use:         Clears the progress display from the screen.
175  */
176
177 void fprogress_clear(fprogress *f)
178 {
179   fprintf(stderr, "\r%*s\r",
180           sizeof(f->name) + 2*PRHUMAN_DATAWD + PRHUMAN_TIMEWD + BARWD + 16,
181           "");
182 }
183
184 /* --- @fprogress_update@ --- *
185  *
186  * Arguments:   @fprogress *f@ = progress context
187  *              @size_t n@ = how much progress has been made
188  *
189  * Returns:     ---
190  *
191  * Use:         Maybe updates the display to show that some progress has been
192  *              made.
193  */
194
195 void fprogress_update(fprogress *f, size_t sz)
196 {
197   time_t now = time(0);
198   int i, n;
199
200   /* --- See if there's anything to do --- */
201
202   f->o += sz;
203   if (difftime(now, f->last) < 1) return;
204   f->last = now;
205
206   /* --- See if we're going to lose the ETA and percentage indicators --- */
207
208   if (f->olast < f->sz && f->o > f->sz) fprogress_clear(f);
209   f->olast = f->o;
210
211   /* --- Do the initial display --- */
212
213   fprintf(stderr, "\r%-*s%c ",
214           (int)sizeof(f->name), f->name,
215           *f->bp++);
216   if (!*f->bp) f->bp = baton;
217   prhuman_data(stderr, f->o);
218
219   /* --- More complicated display if we have a set size --- */
220
221   if (f->sz > f->o) {
222     fputc('/', stderr);
223     prhuman_data(stderr, f->sz);
224     fputs(" [", stderr);
225     n = (f->o*BARWD + f->sz/2)/f->sz;
226     for (i = 0; i < n; i++) fputc('.', stderr);
227     fprintf(stderr, "%*s] %3d%% ETA ", BARWD - n, "",
228             (int)((f->o*100 + 50)/f->sz));
229     prhuman_time(stderr, difftime(now, f->start)*(f->sz - f->o)/f->o);
230   }
231 }
232
233 /* --- @fprogress_done@ --- *
234  *
235  * Arguments:   @fprogress *f@ = progress context
236  *
237  * Returns:     ---
238  *
239  * Use:         Clear up the progress context and removes any display.
240  */
241
242 void fprogress_done(fprogress *f) { fprogress_clear(f); }
243
244 /*----- That's all, folks -------------------------------------------------*/