configure.ac: Replace with a new version.
[u/mdw/catacomb] / 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 -------------------------------------------------*/