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
39 # if defined(__riscos)
41 # elif defined(__unix) || defined(unix)
48 /*----- Static data -------------------------------------------------------*/
50 static const char baton
[] = "-\\|/";
52 /*----- Human-friendly unit printing --------------------------------------*/
59 /* --- @prhuman_time@ --- *
61 * Arguments: @FILE *fp@ = stream to print on
62 * @unsigned long n@ = time in seconds to print
66 * Use: Prints a time in some reasonable format. The time takes up
67 * PRHUMAN_TIMEWD character spaces.
70 #define PRHUMAN_TIMEWD 7
72 static void prhuman_time(FILE *fp
, unsigned long n
)
74 const static struct unit utime
[] = {
75 { "s", 60 }, { "m", 60 }, { "h", 24 }, { "d", 0 }
79 const struct unit
*u
= utime
;
81 while (u
[1].m
&& n
> u
[0].m
*u
[1].m
) { n
/= u
->m
; u
++; }
82 m
= n
/ u
[1].m
; n
%= u
[0].m
;
83 if (m
) fprintf(fp
, "%3lu%s%02lu%s", m
, u
[1].name
, n
, u
[0].name
);
84 else fprintf(fp
, " %2lu%s", n
, u
[0].name
);
87 /* --- @prhuman_data@ --- *
89 * Arguments: @FILE *fp@ = file to print on
90 * @off_t n@ = size to be printed
94 * Use: Prints a data size in some reasonable format. The data size
95 * takes up PRHUMAN_DATAWD character spaces.
98 #define PRHUMAN_DATAWD 7
100 static void prhuman_data(FILE *fp
, off_t n
)
102 const static struct unit udata
[] = {
103 { " ", 1024 }, { "k", 1024 }, { "M", 1024 }, { "G", 1024 },
104 { "T", 1024 }, { "P", 1024 }, { "E", 1024 }, { "Z", 1024 },
109 const struct unit
*u
= udata
;
111 while (u
->m
&& x
>= u
->m
) { x
/= u
->m
; u
++; }
112 fprintf(fp
, "%6.1f%s", x
, u
->name
);
115 /*----- Main code ---------------------------------------------------------*/
119 /* --- @fprogress_init@ --- *
121 * Arguments: @fprogress *f@ = progress context to be initialized
122 * @const char *name@ = file name string to show
123 * @FILE *fp@ = file we're reading from
125 * Returns: Zero on success, nonzero if the file's state is now broken.
127 * Use: Initializes a progress context. Nothing is actually
131 int fprogress_init(fprogress
*f
, const char *name
, FILE *fp
)
138 /* --- Set up the offset --- */
140 o
= lseek(fileno(fp
), 0, SEEK_CUR
);
141 if (fstat(fileno(fp
), &st
)) return (-1);
142 sz
= (S_ISREG(st
.st_mode
)) ? st
.st_size
: -1;
143 if (o
!= -1 && sz
!= -1) sz
-= o
;
144 f
->o
= f
->olast
= 0; f
->sz
= sz
;
146 /* --- Set up the file name --- */
149 if (n
< sizeof(f
->name
))
150 strcpy(f
->name
, name
);
151 else if ((p
= strchr(name
+ n
- sizeof(f
->name
) + 4, PATHSEP
)) != 0)
152 sprintf(f
->name
, "...%s", p
);
154 p
= strrchr(name
, PATHSEP
);
155 if (!p
) sprintf(f
->name
, "%.*s...", (int)sizeof(f
->name
) - 4, name
);
156 else sprintf(f
->name
, "...%.*s...", (int)sizeof(f
->name
) - 7, p
);
159 /* --- Set up some other stuff --- */
161 f
->start
= f
->last
= time(0);
169 /* --- @fprogress_clear@ --- *
171 * Arguments: @fprogress *f@ = progress context
175 * Use: Clears the progress display from the screen.
178 void fprogress_clear(fprogress
*f
)
180 fprintf(stderr
, "\r%*s\r",
181 (int)(sizeof(f
->name
) + 2*PRHUMAN_DATAWD
+
182 PRHUMAN_TIMEWD
+ BARWD
+ 16),
186 /* --- @fprogress_update@ --- *
188 * Arguments: @fprogress *f@ = progress context
189 * @size_t n@ = how much progress has been made
193 * Use: Maybe updates the display to show that some progress has been
197 void fprogress_update(fprogress
*f
, size_t sz
)
199 time_t now
= time(0);
202 /* --- See if there's anything to do --- */
205 if (difftime(now
, f
->last
) < 1) return;
208 /* --- See if we're going to lose the ETA and percentage indicators --- */
210 if (f
->olast
< f
->sz
&& f
->o
> f
->sz
) fprogress_clear(f
);
213 /* --- Do the initial display --- */
215 fprintf(stderr
, "\r%-*s%c ",
216 (int)sizeof(f
->name
), f
->name
,
218 if (!*f
->bp
) f
->bp
= baton
;
219 prhuman_data(stderr
, f
->o
);
221 /* --- More complicated display if we have a set size --- */
225 prhuman_data(stderr
, f
->sz
);
227 n
= (f
->o
*BARWD
+ f
->sz
/2)/f
->sz
;
228 for (i
= 0; i
< n
; i
++) fputc('.', stderr
);
229 fprintf(stderr
, "%*s] %3d%% ETA ", BARWD
- n
, "",
230 (int)((f
->o
*100 + 50)/f
->sz
));
231 prhuman_time(stderr
, difftime(now
, f
->start
)*(f
->sz
- f
->o
)/f
->o
);
235 /* --- @fprogress_done@ --- *
237 * Arguments: @fprogress *f@ = progress context
241 * Use: Clear up the progress context and removes any display.
244 void fprogress_done(fprogress
*f
) { fprogress_clear(f
); }
246 /*----- That's all, folks -------------------------------------------------*/