progress.c revision 1.3
11.3Schristos/*	$NetBSD: progress.c,v 1.3 2003/01/22 03:24:21 christos Exp $ */
21.1Sjhawk
31.1Sjhawk/*-
41.1Sjhawk * Copyright (c) 2003 The NetBSD Foundation, Inc.
51.1Sjhawk * All rights reserved.
61.1Sjhawk *
71.1Sjhawk * This code is derived from software contributed to The NetBSD Foundation
81.1Sjhawk * by John Hawkinson.
91.1Sjhawk *
101.1Sjhawk * Redistribution and use in source and binary forms, with or without
111.1Sjhawk * modification, are permitted provided that the following conditions
121.1Sjhawk * are met:
131.1Sjhawk * 1. Redistributions of source code must retain the above copyright
141.1Sjhawk *    notice, this list of conditions and the following disclaimer.
151.1Sjhawk * 2. Redistributions in binary form must reproduce the above copyright
161.1Sjhawk *    notice, this list of conditions and the following disclaimer in the
171.1Sjhawk *    documentation and/or other materials provided with the distribution.
181.1Sjhawk * 3. Neither the name of The NetBSD Foundation nor the names of its
191.1Sjhawk *    contributors may be used to endorse or promote products derived
201.1Sjhawk *    from this software without specific prior written permission.
211.1Sjhawk *
221.1Sjhawk * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
231.1Sjhawk * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
241.1Sjhawk * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
251.1Sjhawk * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
261.1Sjhawk * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
271.1Sjhawk * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
281.1Sjhawk * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
291.1Sjhawk * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
301.1Sjhawk * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
311.1Sjhawk * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
321.1Sjhawk * POSSIBILITY OF SUCH DAMAGE.
331.1Sjhawk */
341.1Sjhawk
351.1Sjhawk#include <sys/cdefs.h>
361.1Sjhawk#ifndef lint
371.3Schristos__RCSID("$NetBSD: progress.c,v 1.3 2003/01/22 03:24:21 christos Exp $");
381.1Sjhawk#endif				/* not lint */
391.1Sjhawk
401.1Sjhawk#include <sys/types.h>
411.1Sjhawk#include <sys/param.h>
421.1Sjhawk#include <sys/socket.h>
431.1Sjhawk#include <sys/ioctl.h>
441.1Sjhawk#include <sys/time.h>
451.1Sjhawk#include <sys/wait.h>
461.1Sjhawk#include <netinet/in.h>
471.1Sjhawk#include <arpa/ftp.h>
481.1Sjhawk
491.1Sjhawk#include <ctype.h>
501.1Sjhawk#include <err.h>
511.1Sjhawk#include <errno.h>
521.1Sjhawk#include <fcntl.h>
531.1Sjhawk#include <glob.h>
541.1Sjhawk#include <signal.h>
551.1Sjhawk#include <limits.h>
561.1Sjhawk#include <netdb.h>
571.1Sjhawk#include <stdio.h>
581.1Sjhawk#include <stdlib.h>
591.1Sjhawk#include <string.h>
601.1Sjhawk#include <termios.h>
611.1Sjhawk#include <time.h>
621.1Sjhawk#include <tzfile.h>
631.1Sjhawk#include <unistd.h>
641.1Sjhawk
651.1Sjhawk#define GLOBAL			/* force GLOBAL decls in progressbar.h to be
661.1Sjhawk				 * declared */
671.1Sjhawk#include "progressbar.h"
681.1Sjhawk
691.1Sjhawkstatic void usage(void);
701.1Sjhawkint main(int, char *[]);
711.1Sjhawk
721.1Sjhawkstatic void
731.1Sjhawkusage(void)
741.1Sjhawk{
751.1Sjhawk	fprintf(stderr,
761.1Sjhawk	    "usage: %s [-z] [-f file] [-l length] cmd [args...]\n",
771.1Sjhawk	    getprogname());
781.1Sjhawk	exit(1);
791.1Sjhawk}
801.1Sjhawk
811.1Sjhawk
821.1Sjhawkint
831.1Sjhawkmain(int argc, char *argv[])
841.1Sjhawk{
851.1Sjhawk	static char fb_buf[BUFSIZ];
861.1Sjhawk	char *infile = NULL;
871.1Sjhawk	pid_t pid;
881.1Sjhawk	int ch, fd, outpipe[2], waitstat;
891.1Sjhawk	int lflag = 0, zflag = 0;
901.1Sjhawk	ssize_t nr, nw, off;
911.1Sjhawk	struct stat statb;
921.3Schristos	struct ttysize ts;
931.1Sjhawk
941.1Sjhawk	setprogname(argv[0]);
951.1Sjhawk
961.1Sjhawk	/* defaults: Read from stdin, 0 filesize (no completion estimate) */
971.1Sjhawk	fd = STDIN_FILENO;
981.1Sjhawk	filesize = 0;
991.1Sjhawk
1001.1Sjhawk	while ((ch = getopt(argc, argv, "f:l:z")) != -1)
1011.1Sjhawk		switch (ch) {
1021.1Sjhawk		case 'f':
1031.1Sjhawk			infile = optarg;
1041.1Sjhawk			break;
1051.1Sjhawk		case 'l':
1061.1Sjhawk			lflag++;
1071.1Sjhawk			filesize = strtoull(optarg, NULL, 0);
1081.1Sjhawk			break;
1091.1Sjhawk		case 'z':
1101.1Sjhawk			zflag++;
1111.1Sjhawk			break;
1121.1Sjhawk		default:
1131.1Sjhawk		case '?':
1141.1Sjhawk			usage();
1151.1Sjhawk			/* NOTREACHED */
1161.1Sjhawk		}
1171.1Sjhawk	argc -= optind;
1181.1Sjhawk	argv += optind;
1191.1Sjhawk
1201.1Sjhawk	if (argc < 1)
1211.1Sjhawk		usage();
1221.1Sjhawk	if (infile && (fd = open(infile, O_RDONLY, 0)) < 0)
1231.1Sjhawk		err(1, "%s", infile);
1241.1Sjhawk
1251.1Sjhawk	/* stat() to get the filesize unless overridden, or -z */
1261.1Sjhawk	if (!zflag && !lflag && (fstat(fd, &statb) == 0))
1271.1Sjhawk		filesize = statb.st_size;
1281.1Sjhawk
1291.1Sjhawk	/* gzip -l the file if we have the name and -z is given */
1301.1Sjhawk	if (zflag && !lflag && infile != NULL) {
1311.1Sjhawk		FILE *gzipsizepipe;
1321.1Sjhawk		char buf[256], *cmd;
1331.1Sjhawk		long size;
1341.1Sjhawk
1351.1Sjhawk		/*
1361.1Sjhawk		 * Read second word of last line of gzip -l output. Looks like:
1371.1Sjhawk		 * % gzip -l ../etc.tgz
1381.1Sjhawk		 *   compressed uncompressed  ratio uncompressed_name
1391.1Sjhawk		 * 	 119737       696320  82.8% ../etc.tar
1401.1Sjhawk		 */
1411.1Sjhawk
1421.1Sjhawk		asprintf(&cmd, "gzip -l %s", infile);
1431.1Sjhawk		if ((gzipsizepipe = popen(cmd, "r")) == NULL)
1441.1Sjhawk			err(1, "reading compressed file length");
1451.1Sjhawk		for (; fgets(buf, 256, gzipsizepipe) != NULL;)
1461.1Sjhawk		    continue;
1471.1Sjhawk		sscanf(buf, "%*d %ld", &size);
1481.1Sjhawk		filesize = size;
1491.1Sjhawk		if (pclose(gzipsizepipe) < 0)
1501.1Sjhawk			err(1, "closing compressed file length pipe");
1511.1Sjhawk		free(cmd);
1521.1Sjhawk	}
1531.1Sjhawk	/* Pipe input through gzip -dc if -z is given */
1541.1Sjhawk	if (zflag) {
1551.1Sjhawk		pid_t gzippid;
1561.1Sjhawk		int gzippipe[2];
1571.1Sjhawk
1581.1Sjhawk		if (pipe(gzippipe) < 0)
1591.1Sjhawk			err(1, "gzip pipe");
1601.1Sjhawk		gzippid = fork();
1611.1Sjhawk		if (gzippid < 0)
1621.1Sjhawk			err(1, "fork for gzip");
1631.1Sjhawk
1641.1Sjhawk		if (gzippid) {
1651.1Sjhawk			/* parent */
1661.1Sjhawk			dup2(gzippipe[0], fd);
1671.1Sjhawk			close(gzippipe[0]);
1681.1Sjhawk			close(gzippipe[1]);
1691.1Sjhawk		} else {
1701.1Sjhawk			dup2(gzippipe[1], STDOUT_FILENO);
1711.1Sjhawk			dup2(fd, STDIN_FILENO);
1721.1Sjhawk			close(gzippipe[0]);
1731.1Sjhawk			close(gzippipe[1]);
1741.1Sjhawk			if (execlp("gzip", "gzip", "-dc", NULL))
1751.1Sjhawk				err(1, "exec()ing gzip");
1761.1Sjhawk		}
1771.1Sjhawk	}
1781.1Sjhawk
1791.1Sjhawk	/* Initialize progressbar.c's global state */
1801.1Sjhawk	bytes = 0;
1811.1Sjhawk	progress = 1;
1821.1Sjhawk	ttyout = stdout;
1831.3Schristos
1841.3Schristos	if (ioctl(fileno(ttyout), TIOCGSIZE, &ts) == -1) {
1851.3Schristos		warn("could not get tty window size");
1861.3Schristos		ttywidth = 80;
1871.3Schristos	} else
1881.3Schristos		ttywidth = ts.ts_cols;
1891.1Sjhawk
1901.1Sjhawk	if (pipe(outpipe) < 0)
1911.1Sjhawk		err(1, "output pipe");
1921.1Sjhawk	pid = fork();
1931.1Sjhawk	if (pid < 0)
1941.1Sjhawk		err(1, "fork for output pipe");
1951.1Sjhawk
1961.1Sjhawk	if (pid == 0) {
1971.1Sjhawk		/* child */
1981.1Sjhawk		dup2(outpipe[0], STDIN_FILENO);
1991.1Sjhawk		close(outpipe[0]);
2001.1Sjhawk		close(outpipe[1]);
2011.1Sjhawk		execvp(argv[0], argv);
2021.1Sjhawk		err(1, "could not exec %s", argv[0]);
2031.1Sjhawk	}
2041.1Sjhawk	close(outpipe[0]);
2051.1Sjhawk
2061.1Sjhawk	progressmeter(-1);
2071.1Sjhawk	while ((nr = read(fd, fb_buf, BUFSIZ)) > 0)
2081.2Senami		for (off = 0; nr; nr -= nw, off += nw, bytes += nw)
2091.2Senami			if ((nw = write(outpipe[1], fb_buf + off,
2101.2Senami			    (size_t) nr)) < 0)
2111.2Senami				err(1, "writing %d bytes to output pipe", nr);
2121.1Sjhawk	close(outpipe[1]);
2131.1Sjhawk
2141.1Sjhawk	wait(&waitstat);
2151.1Sjhawk
2161.1Sjhawk	progressmeter(1);
2171.1Sjhawk	return 0;
2181.1Sjhawk}
219