@@ -60,6 +60,7 @@ __MBSDID("$MidnightBSD: src/usr.bin/progress/progress.c,v 1.4 2012/12/31 17:14:3
6060
6161static void broken_pipe (int unused );
6262static void usage (void );
63+ static off_t gzip_uncompressed_size (const char * );
6364
6465long long
6566strsuftoll (const char * desc , const char * val ,
@@ -157,26 +158,7 @@ main(int argc, char *argv[])
157158
158159 /* gzip -l the file if we have the name and -z is given */
159160 if (zflag && !lflag && infile != NULL ) {
160- FILE * gzipsizepipe ;
161- char buf [256 ], * cp , * cmd ;
162-
163- /*
164- * Read second word of last line of gzip -l output. Looks like:
165- * % gzip -l ../etc.tgz
166- * compressed uncompressed ratio uncompressed_name
167- * 119737 696320 82.8% ../etc.tar
168- */
169-
170- asprintf (& cmd , "gzip -l %s" , infile );
171- if ((gzipsizepipe = popen (cmd , "r" )) == NULL )
172- err (1 , "reading compressed file length" );
173- for (; fgets (buf , 256 , gzipsizepipe ) != NULL ;)
174- continue ;
175- strtoimax (buf , & cp , 10 );
176- filesize = strtoimax (cp , NULL , 10 );
177- if (pclose (gzipsizepipe ) < 0 )
178- err (1 , "closing compressed file length pipe" );
179- free (cmd );
161+ filesize = gzip_uncompressed_size (infile );
180162 }
181163 /* Pipe input through gzip -dc if -z is given */
182164 if (zflag ) {
@@ -250,16 +232,18 @@ main(int argc, char *argv[])
250232 cmdstat = 0 ;
251233 while (pid || gzippid ) {
252234 deadpid = wait (& ws );
235+ if (deadpid == -1 && errno == EINTR )
236+ continue ;
237+ if (deadpid == -1 )
238+ break ;
239+
253240 /*
254241 * We need to exit with an error if the command (or gzip)
255242 * exited abnormally.
256243 * Unfortunately we can't generate a true 'exited by signal'
257244 * error without sending the signal to ourselves :-(
258245 */
259246 ws = WIFSIGNALED (ws ) ? WTERMSIG (ws ) : WEXITSTATUS (ws );
260-
261- if (deadpid != -1 && errno == EINTR )
262- continue ;
263247 if (deadpid == pid ) {
264248 pid = 0 ;
265249 cmdstat = ws ;
@@ -280,3 +264,74 @@ main(int argc, char *argv[])
280264
281265 exit (cmdstat ? cmdstat : gzipstat );
282266}
267+
268+ static off_t
269+ gzip_uncompressed_size (const char * path )
270+ {
271+ char buf [256 ];
272+ char lastline [256 ];
273+ char * cp , * endp ;
274+ ssize_t nr ;
275+ pid_t pid ;
276+ int status ;
277+ int pfd [2 ];
278+ off_t uncompressed ;
279+
280+ if (path == NULL )
281+ errx (1 , "gzip length: missing path" );
282+ if (pipe (pfd ) < 0 )
283+ err (1 , "pipe" );
284+ pid = fork ();
285+ if (pid < 0 )
286+ err (1 , "fork" );
287+ if (pid == 0 ) {
288+ (void )dup2 (pfd [1 ], STDOUT_FILENO );
289+ close (pfd [0 ]);
290+ close (pfd [1 ]);
291+ execlp ("gzip" , "gzip" , "-l" , "--" , path , (char * )NULL );
292+ _exit (127 );
293+ }
294+
295+ close (pfd [1 ]);
296+ lastline [0 ] = '\0' ;
297+ /*
298+ * Read all output, keep the last line.
299+ * gzip -l output contains a header; the last line has numbers.
300+ */
301+ while ((nr = read (pfd [0 ], buf , sizeof (buf ) - 1 )) > 0 ) {
302+ char * line , * nl ;
303+
304+ buf [nr ] = '\0' ;
305+ line = buf ;
306+ while ((nl = strchr (line , '\n' )) != NULL ) {
307+ size_t len = (size_t )(nl - line );
308+ if (len >= sizeof (lastline ))
309+ len = sizeof (lastline ) - 1 ;
310+ memcpy (lastline , line , len );
311+ lastline [len ] = '\0' ;
312+ line = nl + 1 ;
313+ }
314+ }
315+ close (pfd [0 ]);
316+
317+ if (waitpid (pid , & status , 0 ) < 0 )
318+ err (1 , "waitpid" );
319+ if (!WIFEXITED (status ) || WEXITSTATUS (status ) != 0 )
320+ errx (1 , "gzip -l failed for %s" , path );
321+
322+ /*
323+ * Parse: <compressed> <uncompressed> ...
324+ */
325+ errno = 0 ;
326+ (void )strtoimax (lastline , & cp , 10 );
327+ if (errno != 0 || cp == lastline )
328+ errx (1 , "unexpected gzip -l output for %s" , path );
329+ while (* cp == ' ' || * cp == '\t' )
330+ cp ++ ;
331+ errno = 0 ;
332+ uncompressed = (off_t )strtoimax (cp , & endp , 10 );
333+ if (errno != 0 || endp == cp )
334+ errx (1 , "unexpected gzip -l output for %s" , path );
335+
336+ return uncompressed ;
337+ }
0 commit comments