3 * Progress indicators for command-line tools
5 * (c) 2011 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
30 #define _FILE_OFFSET_BITS 64
37 # if defined(__riscos)
39 # elif defined(__unix) || defined(unix)
46 /*----- Static data -------------------------------------------------------*/
48 static const char baton
[] = "-\\|/";
50 /*----- Human-friendly unit printing --------------------------------------*/
57 /* --- @prhuman_time@ --- *
59 * Arguments: @FILE *fp@ = stream to print on
60 * @unsigned long n@ = time in seconds to print
64 * Use: Prints a time in some reasonable format. The time takes up
65 * PRHUMAN_TIMEWD character spaces.
68 #define PRHUMAN_TIMEWD 7
70 static void prhuman_time(FILE *fp
, unsigned long n
)
72 const static struct unit utime
[] = {
73 { "s", 60 }, { "m", 60 }, { "h", 24 }, { "d", 0 }
77 const struct unit
*u
= utime
;
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
);
85 /* --- @prhuman_data@ --- *
87 * Arguments: @FILE *fp@ = file to print on
88 * @off_t n@ = size to be printed
92 * Use: Prints a data size in some reasonable format. The data size
93 * takes up PRHUMAN_DATAWD character spaces.
96 #define PRHUMAN_DATAWD 7
98 static void prhuman_data(FILE *fp
, off_t n
)
100 const static struct unit udata
[] = {
101 { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 },
102 { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 },
107 const struct unit
*u
= udata
;
109 while (u
->m
&& x
>= u
->m
) { x
/= u
->m
; u
++; }
110 fprintf(fp
, "%6.1f%s", x
, u
->name
);
113 /*----- Main code ---------------------------------------------------------*/
117 /* --- @fprogress_init@ --- *
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
123 * Returns: Zero on success, nonzero if the file's state is now broken.
125 * Use: Initializes a progress context. Nothing is actually
129 int fprogress_init(fprogress
*f
, const char *name
, FILE *fp
)
135 /* --- Set up the offset --- */
137 if ((o
= ftello(fp
)) >= 0 &&
138 fseeko(fp
, 0, SEEK_END
) >= 0 &&
140 fseeko(fp
, o
, SEEK_SET
) < 0))
142 if (o
!= -1 && sz
!= -1) sz
-= o
;
143 f
->o
= f
->olast
= 0; f
->sz
= sz
;
145 /* --- Set up the file 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
);
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
);
158 /* --- Set up some other stuff --- */
160 f
->start
= f
->last
= time(0);
168 /* --- @fprogress_clear@ --- *
170 * Arguments: @fprogress *f@ = progress context
174 * Use: Clears the progress display from the screen.
177 void fprogress_clear(fprogress
*f
)
179 fprintf(stderr
, "\r%*s\r",
180 sizeof(f
->name
) + 2*PRHUMAN_DATAWD
+ PRHUMAN_TIMEWD
+ BARWD
+ 16,
184 /* --- @fprogress_update@ --- *
186 * Arguments: @fprogress *f@ = progress context
187 * @size_t n@ = how much progress has been made
191 * Use: Maybe updates the display to show that some progress has been
195 void fprogress_update(fprogress
*f
, size_t sz
)
197 time_t now
= time(0);
200 /* --- See if there's anything to do --- */
203 if (difftime(now
, f
->last
) < 1) return;
206 /* --- See if we're going to lose the ETA and percentage indicators --- */
208 if (f
->olast
< f
->sz
&& f
->o
> f
->sz
) fprogress_clear(f
);
211 /* --- Do the initial display --- */
213 fprintf(stderr
, "\r%-*s%c ",
214 (int)sizeof(f
->name
), f
->name
,
216 if (!*f
->bp
) f
->bp
= baton
;
217 prhuman_data(stderr
, f
->o
);
219 /* --- More complicated display if we have a set size --- */
223 prhuman_data(stderr
, f
->sz
);
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
);
233 /* --- @fprogress_done@ --- *
235 * Arguments: @fprogress *f@ = progress context
239 * Use: Clear up the progress context and removes any display.
242 void fprogress_done(fprogress
*f
) { fprogress_clear(f
); }
244 /*----- That's all, folks -------------------------------------------------*/