xref: /freebsd/usr.bin/dpv/dpv.c (revision 041394f38a59889f0e14ace3306df5310cd5aeac)
1*041394f3SDevin Teske /*-
2*041394f3SDevin Teske  * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
3*041394f3SDevin Teske  * All rights reserved.
4*041394f3SDevin Teske  *
5*041394f3SDevin Teske  * Redistribution and use in source and binary forms, with or without
6*041394f3SDevin Teske  * modification, are permitted provided that the following conditions
7*041394f3SDevin Teske  * are met:
8*041394f3SDevin Teske  * 1. Redistributions of source code must retain the above copyright
9*041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer.
10*041394f3SDevin Teske  * 2. Redistributions in binary form must reproduce the above copyright
11*041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer in the
12*041394f3SDevin Teske  *    documentation and/or other materials provided with the distribution.
13*041394f3SDevin Teske  *
14*041394f3SDevin Teske  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*041394f3SDevin Teske  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*041394f3SDevin Teske  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*041394f3SDevin Teske  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*041394f3SDevin Teske  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*041394f3SDevin Teske  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*041394f3SDevin Teske  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*041394f3SDevin Teske  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*041394f3SDevin Teske  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*041394f3SDevin Teske  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*041394f3SDevin Teske  * SUCH DAMAGE.
25*041394f3SDevin Teske  */
26*041394f3SDevin Teske 
27*041394f3SDevin Teske #include <sys/cdefs.h>
28*041394f3SDevin Teske __FBSDID("$FreeBSD$");
29*041394f3SDevin Teske 
30*041394f3SDevin Teske #include <sys/stat.h>
31*041394f3SDevin Teske #include <sys/types.h>
32*041394f3SDevin Teske 
33*041394f3SDevin Teske #define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */
34*041394f3SDevin Teske #include <dialog.h>
35*041394f3SDevin Teske #include <dpv.h>
36*041394f3SDevin Teske #include <err.h>
37*041394f3SDevin Teske #include <errno.h>
38*041394f3SDevin Teske #include <fcntl.h>
39*041394f3SDevin Teske #include <limits.h>
40*041394f3SDevin Teske #include <signal.h>
41*041394f3SDevin Teske #include <stdio.h>
42*041394f3SDevin Teske #include <stdlib.h>
43*041394f3SDevin Teske #include <string.h>
44*041394f3SDevin Teske #include <string_m.h>
45*041394f3SDevin Teske #include <unistd.h>
46*041394f3SDevin Teske 
47*041394f3SDevin Teske #include "dpv_util.h"
48*041394f3SDevin Teske 
49*041394f3SDevin Teske /* Debugging */
50*041394f3SDevin Teske static uint8_t debug = FALSE;
51*041394f3SDevin Teske 
52*041394f3SDevin Teske /* Data to process */
53*041394f3SDevin Teske static struct dpv_file_node *file_list = NULL;
54*041394f3SDevin Teske static unsigned int nfiles = 0;
55*041394f3SDevin Teske 
56*041394f3SDevin Teske /* Data processing */
57*041394f3SDevin Teske static uint8_t line_mode = FALSE;
58*041394f3SDevin Teske static uint8_t no_overrun = FALSE;
59*041394f3SDevin Teske static char *buf = NULL;
60*041394f3SDevin Teske static int fd = -1;
61*041394f3SDevin Teske static int output_type = DPV_OUTPUT_NONE;
62*041394f3SDevin Teske static size_t bsize;
63*041394f3SDevin Teske static char rpath[PATH_MAX];
64*041394f3SDevin Teske 
65*041394f3SDevin Teske /* Extra display information */
66*041394f3SDevin Teske static uint8_t multiple = FALSE; /* `-m' */
67*041394f3SDevin Teske static char *pgm; /* set to argv[0] by main() */
68*041394f3SDevin Teske 
69*041394f3SDevin Teske /* Function prototypes */
70*041394f3SDevin Teske static void	sig_int(int sig);
71*041394f3SDevin Teske static void	usage(void);
72*041394f3SDevin Teske int		main(int argc, char *argv[]);
73*041394f3SDevin Teske static int	operate_common(struct dpv_file_node *file, int out);
74*041394f3SDevin Teske static int	operate_on_bytes(struct dpv_file_node *file, int out);
75*041394f3SDevin Teske static int	operate_on_lines(struct dpv_file_node *file, int out);
76*041394f3SDevin Teske 
77*041394f3SDevin Teske static int
78*041394f3SDevin Teske operate_common(struct dpv_file_node *file, int out)
79*041394f3SDevin Teske {
80*041394f3SDevin Teske 	struct stat sb;
81*041394f3SDevin Teske 
82*041394f3SDevin Teske 	/* Open the file if necessary */
83*041394f3SDevin Teske 	if (fd < 0) {
84*041394f3SDevin Teske 		if (multiple) {
85*041394f3SDevin Teske 			/* Resolve the file path and attempt to open it */
86*041394f3SDevin Teske 			if (realpath(file->path, rpath) == 0 ||
87*041394f3SDevin Teske 			    (fd = open(rpath, O_RDONLY)) < 0) {
88*041394f3SDevin Teske 				warn("%s", file->path);
89*041394f3SDevin Teske 				file->status = DPV_STATUS_FAILED;
90*041394f3SDevin Teske 				return (-1);
91*041394f3SDevin Teske 			}
92*041394f3SDevin Teske 		} else {
93*041394f3SDevin Teske 			/* Assume stdin, but if that's a TTY instead use the
94*041394f3SDevin Teske 			 * highest numbered file descriptor (obtained by
95*041394f3SDevin Teske 			 * generating new fd and then decrementing).
96*041394f3SDevin Teske 			 *
97*041394f3SDevin Teske 			 * NB: /dev/stdin should always be open(2)'able
98*041394f3SDevin Teske 			 */
99*041394f3SDevin Teske 			fd = STDIN_FILENO;
100*041394f3SDevin Teske 			if (isatty(fd)) {
101*041394f3SDevin Teske 				fd = open("/dev/stdin", O_RDONLY);
102*041394f3SDevin Teske 				close(fd--);
103*041394f3SDevin Teske 			}
104*041394f3SDevin Teske 
105*041394f3SDevin Teske 			/* This answer might be wrong, if dpv(3) has (by
106*041394f3SDevin Teske 			 * request) opened an output file or pipe. If we
107*041394f3SDevin Teske 			 * told dpv(3) to open a file, subtract one from
108*041394f3SDevin Teske 			 * previous answer. If instead we told dpv(3) to
109*041394f3SDevin Teske 			 * prepare a pipe output, subtract two.
110*041394f3SDevin Teske 			 */
111*041394f3SDevin Teske 			switch(output_type) {
112*041394f3SDevin Teske 			case DPV_OUTPUT_FILE:
113*041394f3SDevin Teske 				fd -= 1;
114*041394f3SDevin Teske 				break;
115*041394f3SDevin Teske 			case DPV_OUTPUT_SHELL:
116*041394f3SDevin Teske 				fd -= 2;
117*041394f3SDevin Teske 				break;
118*041394f3SDevin Teske 			}
119*041394f3SDevin Teske 		}
120*041394f3SDevin Teske 	}
121*041394f3SDevin Teske 
122*041394f3SDevin Teske 	/* Allocate buffer if necessary */
123*041394f3SDevin Teske 	if (buf == NULL) {
124*041394f3SDevin Teske 		/* Use output block size as buffer size if available */
125*041394f3SDevin Teske 		if (out >= 0) {
126*041394f3SDevin Teske 			if (fstat(out, &sb) != 0) {
127*041394f3SDevin Teske 				warn("%i", out);
128*041394f3SDevin Teske 				file->status = DPV_STATUS_FAILED;
129*041394f3SDevin Teske 				return (-1);
130*041394f3SDevin Teske 			}
131*041394f3SDevin Teske 			if (S_ISREG(sb.st_mode)) {
132*041394f3SDevin Teske 				if (sysconf(_SC_PHYS_PAGES) >
133*041394f3SDevin Teske 				    PHYSPAGES_THRESHOLD)
134*041394f3SDevin Teske 					bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
135*041394f3SDevin Teske 				else
136*041394f3SDevin Teske 					bsize = BUFSIZE_SMALL;
137*041394f3SDevin Teske 			} else
138*041394f3SDevin Teske 				bsize = MAX(sb.st_blksize,
139*041394f3SDevin Teske 				    (blksize_t)sysconf(_SC_PAGESIZE));
140*041394f3SDevin Teske 		} else
141*041394f3SDevin Teske 			bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
142*041394f3SDevin Teske 
143*041394f3SDevin Teske 		/* Attempt to allocate */
144*041394f3SDevin Teske 		if ((buf = malloc(bsize+1)) == NULL) {
145*041394f3SDevin Teske 			end_dialog();
146*041394f3SDevin Teske 			err(EXIT_FAILURE, "Out of memory?!");
147*041394f3SDevin Teske 		}
148*041394f3SDevin Teske 	}
149*041394f3SDevin Teske 
150*041394f3SDevin Teske 	return (0);
151*041394f3SDevin Teske }
152*041394f3SDevin Teske 
153*041394f3SDevin Teske static int
154*041394f3SDevin Teske operate_on_bytes(struct dpv_file_node *file, int out)
155*041394f3SDevin Teske {
156*041394f3SDevin Teske 	int progress;
157*041394f3SDevin Teske 	ssize_t r, w;
158*041394f3SDevin Teske 
159*041394f3SDevin Teske 	if (operate_common(file, out) < 0)
160*041394f3SDevin Teske 		return (-1);
161*041394f3SDevin Teske 
162*041394f3SDevin Teske 	/* [Re-]Fill the buffer */
163*041394f3SDevin Teske 	if ((r = read(fd, buf, bsize)) <= 0) {
164*041394f3SDevin Teske 		if (fd != STDIN_FILENO)
165*041394f3SDevin Teske 			close(fd);
166*041394f3SDevin Teske 		fd = -1;
167*041394f3SDevin Teske 		file->status = DPV_STATUS_DONE;
168*041394f3SDevin Teske 		return (100);
169*041394f3SDevin Teske 	}
170*041394f3SDevin Teske 
171*041394f3SDevin Teske 	/* [Re-]Dump the buffer */
172*041394f3SDevin Teske 	if (out >= 0) {
173*041394f3SDevin Teske 		if ((w = write(out, buf, r)) < 0) {
174*041394f3SDevin Teske 			end_dialog();
175*041394f3SDevin Teske 			err(EXIT_FAILURE, "output");
176*041394f3SDevin Teske 		}
177*041394f3SDevin Teske 		fsync(out);
178*041394f3SDevin Teske 	}
179*041394f3SDevin Teske 
180*041394f3SDevin Teske 	overall_read += r;
181*041394f3SDevin Teske 	file->read += r;
182*041394f3SDevin Teske 
183*041394f3SDevin Teske 	/* Calculate percentage of completion (if possible) */
184*041394f3SDevin Teske 	if (file->length >= 0) {
185*041394f3SDevin Teske 		progress = (file->read * 100 / (file->length > 0 ?
186*041394f3SDevin Teske 		    file->length : 1));
187*041394f3SDevin Teske 
188*041394f3SDevin Teske 		/* If no_overrun, do not return 100% until read >= length */
189*041394f3SDevin Teske 		if (no_overrun && progress == 100 && file->read < file->length)
190*041394f3SDevin Teske 			progress--;
191*041394f3SDevin Teske 
192*041394f3SDevin Teske 		return (progress);
193*041394f3SDevin Teske 	} else
194*041394f3SDevin Teske 		return (-1);
195*041394f3SDevin Teske }
196*041394f3SDevin Teske 
197*041394f3SDevin Teske static int
198*041394f3SDevin Teske operate_on_lines(struct dpv_file_node *file, int out)
199*041394f3SDevin Teske {
200*041394f3SDevin Teske 	char *p;
201*041394f3SDevin Teske 	int progress;
202*041394f3SDevin Teske 	ssize_t r, w;
203*041394f3SDevin Teske 
204*041394f3SDevin Teske 	if (operate_common(file, out) < 0)
205*041394f3SDevin Teske 		return (-1);
206*041394f3SDevin Teske 
207*041394f3SDevin Teske 	/* [Re-]Fill the buffer */
208*041394f3SDevin Teske 	if ((r = read(fd, buf, bsize)) <= 0) {
209*041394f3SDevin Teske 		if (fd != STDIN_FILENO)
210*041394f3SDevin Teske 			close(fd);
211*041394f3SDevin Teske 		fd = -1;
212*041394f3SDevin Teske 		file->status = DPV_STATUS_DONE;
213*041394f3SDevin Teske 		return (100);
214*041394f3SDevin Teske 	}
215*041394f3SDevin Teske 	buf[r] = '\0';
216*041394f3SDevin Teske 
217*041394f3SDevin Teske 	/* [Re-]Dump the buffer */
218*041394f3SDevin Teske 	if (out >= 0) {
219*041394f3SDevin Teske 		if ((w = write(out, buf, r)) < 0) {
220*041394f3SDevin Teske 			end_dialog();
221*041394f3SDevin Teske 			err(EXIT_FAILURE, "output");
222*041394f3SDevin Teske 		}
223*041394f3SDevin Teske 		fsync(out);
224*041394f3SDevin Teske 	}
225*041394f3SDevin Teske 
226*041394f3SDevin Teske 	/* Process the buffer for number of lines */
227*041394f3SDevin Teske 	for (p = buf; p != NULL && *p != '\0';)
228*041394f3SDevin Teske 		if ((p = strchr(p, '\n')) != NULL)
229*041394f3SDevin Teske 			overall_read++, p++, file->read++;
230*041394f3SDevin Teske 
231*041394f3SDevin Teske 	/* Calculate percentage of completion (if possible) */
232*041394f3SDevin Teske 	if (file->length >= 0) {
233*041394f3SDevin Teske 		progress = (file->read * 100 / file->length);
234*041394f3SDevin Teske 
235*041394f3SDevin Teske 		/* If no_overrun, do not return 100% until read >= length */
236*041394f3SDevin Teske 		if (no_overrun && progress == 100 && file->read < file->length)
237*041394f3SDevin Teske 			progress--;
238*041394f3SDevin Teske 
239*041394f3SDevin Teske 		return (progress);
240*041394f3SDevin Teske 	} else
241*041394f3SDevin Teske 		return (-1);
242*041394f3SDevin Teske }
243*041394f3SDevin Teske 
244*041394f3SDevin Teske /*
245*041394f3SDevin Teske  * Takes a list of names that are to correspond to input streams coming from
246*041394f3SDevin Teske  * stdin or fifos and produces necessary config to drive dpv(3) `--gauge'
247*041394f3SDevin Teske  * widget. If the `-d' flag is used, output is instead send to terminal
248*041394f3SDevin Teske  * standard output (and the output can then be saved to a file, piped into
249*041394f3SDevin Teske  * custom [X]dialog(1) invocation, or whatever.
250*041394f3SDevin Teske  */
251*041394f3SDevin Teske int
252*041394f3SDevin Teske main(int argc, char *argv[])
253*041394f3SDevin Teske {
254*041394f3SDevin Teske 	char dummy;
255*041394f3SDevin Teske 	int ch;
256*041394f3SDevin Teske 	int n = 0;
257*041394f3SDevin Teske 	size_t config_size = sizeof(struct dpv_config);
258*041394f3SDevin Teske 	size_t file_node_size = sizeof(struct dpv_file_node);
259*041394f3SDevin Teske 	struct dpv_config *config;
260*041394f3SDevin Teske 	struct dpv_file_node *curfile;
261*041394f3SDevin Teske 	struct sigaction act;
262*041394f3SDevin Teske 
263*041394f3SDevin Teske 	pgm = argv[0]; /* store a copy of invocation name */
264*041394f3SDevin Teske 
265*041394f3SDevin Teske 	/* Allocate config structure */
266*041394f3SDevin Teske 	if ((config = malloc(config_size)) == NULL)
267*041394f3SDevin Teske 		errx(EXIT_FAILURE, "Out of memory?!");
268*041394f3SDevin Teske 	memset((void *)(config), '\0', config_size);
269*041394f3SDevin Teske 
270*041394f3SDevin Teske 	/*
271*041394f3SDevin Teske 	 * Process command-line options
272*041394f3SDevin Teske 	 */
273*041394f3SDevin Teske 	while ((ch = getopt(argc, argv,
274*041394f3SDevin Teske 	    "a:b:dDhi:I:lL:mn:No:p:P:t:TU:wx:X")) != -1) {
275*041394f3SDevin Teske 		switch(ch) {
276*041394f3SDevin Teske 		case 'a': /* additional message text to append */
277*041394f3SDevin Teske 			if (config->aprompt == NULL) {
278*041394f3SDevin Teske 				config->aprompt = malloc(DPV_APROMPT_MAX);
279*041394f3SDevin Teske 				if (config->aprompt == NULL)
280*041394f3SDevin Teske 					errx(EXIT_FAILURE, "Out of memory?!");
281*041394f3SDevin Teske 			}
282*041394f3SDevin Teske 			snprintf(config->aprompt, DPV_APROMPT_MAX, "%s",
283*041394f3SDevin Teske 			    optarg);
284*041394f3SDevin Teske 			break;
285*041394f3SDevin Teske 		case 'b': /* [X]dialog(1) backtitle */
286*041394f3SDevin Teske 			if (config->backtitle != NULL)
287*041394f3SDevin Teske 				free((char *)config->backtitle);
288*041394f3SDevin Teske 			config->backtitle = malloc(strlen(optarg) + 1);
289*041394f3SDevin Teske 			if (config->backtitle == NULL)
290*041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
291*041394f3SDevin Teske 			*(config->backtitle) = '\0';
292*041394f3SDevin Teske 			strcat(config->backtitle, optarg);
293*041394f3SDevin Teske 			break;
294*041394f3SDevin Teske 		case 'd': /* debugging */
295*041394f3SDevin Teske 			debug = TRUE;
296*041394f3SDevin Teske 			config->debug = debug;
297*041394f3SDevin Teske 			break;
298*041394f3SDevin Teske 		case 'D': /* use dialog(1) instead of libdialog */
299*041394f3SDevin Teske 			config->display_type = DPV_DISPLAY_DIALOG;
300*041394f3SDevin Teske 			break;
301*041394f3SDevin Teske 		case 'h': /* help/usage */
302*041394f3SDevin Teske 			usage();
303*041394f3SDevin Teske 			break; /* NOTREACHED */
304*041394f3SDevin Teske 		case 'i': /* status line format string for single-file */
305*041394f3SDevin Teske 			config->status_solo = optarg;
306*041394f3SDevin Teske 			break;
307*041394f3SDevin Teske 		case 'I': /* status line format string for many-files */
308*041394f3SDevin Teske 			config->status_many = optarg;
309*041394f3SDevin Teske 			break;
310*041394f3SDevin Teske 		case 'l': /* Line mode */
311*041394f3SDevin Teske 			line_mode = TRUE;
312*041394f3SDevin Teske 			break;
313*041394f3SDevin Teske 		case 'L': /* custom label size */
314*041394f3SDevin Teske 			config->label_size =
315*041394f3SDevin Teske 			    (int)strtol(optarg, (char **)NULL, 10);
316*041394f3SDevin Teske 			if (config->label_size == 0 && errno == EINVAL)
317*041394f3SDevin Teske 				errx(EXIT_FAILURE,
318*041394f3SDevin Teske 				    "`-L' argument must be numeric");
319*041394f3SDevin Teske 			else if (config->label_size < -1)
320*041394f3SDevin Teske 				config->label_size = -1;
321*041394f3SDevin Teske 			break;
322*041394f3SDevin Teske 		case 'm': /* enable multiple file arguments */
323*041394f3SDevin Teske 			multiple = TRUE;
324*041394f3SDevin Teske 			break;
325*041394f3SDevin Teske 		case 'o': /* `-o path' for sending data-read to file */
326*041394f3SDevin Teske 			output_type = DPV_OUTPUT_FILE;
327*041394f3SDevin Teske 			config->output_type = DPV_OUTPUT_FILE;
328*041394f3SDevin Teske 			config->output = optarg;
329*041394f3SDevin Teske 			break;
330*041394f3SDevin Teske 		case 'n': /* custom number of files per `page' */
331*041394f3SDevin Teske 			config->display_limit =
332*041394f3SDevin Teske 				(int)strtol(optarg, (char **)NULL, 10);
333*041394f3SDevin Teske 			if (config->display_limit == 0 && errno == EINVAL)
334*041394f3SDevin Teske 				errx(EXIT_FAILURE,
335*041394f3SDevin Teske 				    "`-n' argument must be numeric");
336*041394f3SDevin Teske 			else if (config->display_limit < 0)
337*041394f3SDevin Teske 				config->display_limit = -1;
338*041394f3SDevin Teske 			break;
339*041394f3SDevin Teske 		case 'N': /* No overrun (truncate reads of known-length) */
340*041394f3SDevin Teske 			no_overrun = TRUE;
341*041394f3SDevin Teske 			config->options |= DPV_NO_OVERRUN;
342*041394f3SDevin Teske 			break;
343*041394f3SDevin Teske 		case 'p': /* additional message text to use as prefix */
344*041394f3SDevin Teske 			if (config->pprompt == NULL) {
345*041394f3SDevin Teske 				config->pprompt = malloc(DPV_PPROMPT_MAX + 2);
346*041394f3SDevin Teske 				if (config->pprompt == NULL)
347*041394f3SDevin Teske 					errx(EXIT_FAILURE, "Out of memory?!");
348*041394f3SDevin Teske 				/* +2 is for implicit "\n" appended later */
349*041394f3SDevin Teske 			}
350*041394f3SDevin Teske 			snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s",
351*041394f3SDevin Teske 			    optarg);
352*041394f3SDevin Teske 			break;
353*041394f3SDevin Teske 		case 'P': /* custom size for mini-progressbar */
354*041394f3SDevin Teske 			config->pbar_size =
355*041394f3SDevin Teske 			    (int)strtol(optarg, (char **)NULL, 10);
356*041394f3SDevin Teske 			if (config->pbar_size == 0 && errno == EINVAL)
357*041394f3SDevin Teske 				errx(EXIT_FAILURE,
358*041394f3SDevin Teske 				    "`-P' argument must be numeric");
359*041394f3SDevin Teske 			else if (config->pbar_size < -1)
360*041394f3SDevin Teske 				config->pbar_size = -1;
361*041394f3SDevin Teske 			break;
362*041394f3SDevin Teske 		case 't': /* [X]dialog(1) title */
363*041394f3SDevin Teske 			if (config->title != NULL)
364*041394f3SDevin Teske 				free(config->title);
365*041394f3SDevin Teske 			config->title = malloc(strlen(optarg) + 1);
366*041394f3SDevin Teske 			if (config->title == NULL)
367*041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
368*041394f3SDevin Teske 			*(config->title) = '\0';
369*041394f3SDevin Teske 			strcat(config->title, optarg);
370*041394f3SDevin Teske 			break;
371*041394f3SDevin Teske 		case 'T': /* test mode (don't read data, fake it) */
372*041394f3SDevin Teske 			config->options |= DPV_TEST_MODE;
373*041394f3SDevin Teske 			break;
374*041394f3SDevin Teske 		case 'U': /* updates per second */
375*041394f3SDevin Teske 			config->status_updates_per_second =
376*041394f3SDevin Teske 			    (int)strtol(optarg, (char **)NULL, 10);
377*041394f3SDevin Teske 			if (config->status_updates_per_second == 0 &&
378*041394f3SDevin Teske 			    errno == EINVAL)
379*041394f3SDevin Teske 				errx(EXIT_FAILURE,
380*041394f3SDevin Teske 				    "`-U' argument must be numeric");
381*041394f3SDevin Teske 			break;
382*041394f3SDevin Teske 		case 'w': /* `-p' and `-a' widths bump [X]dialog(1) width */
383*041394f3SDevin Teske 			config->options |= DPV_WIDE_MODE;
384*041394f3SDevin Teske 			break;
385*041394f3SDevin Teske 		case 'x': /* `-x cmd' for sending data-read to sh(1) code */
386*041394f3SDevin Teske 			output_type = DPV_OUTPUT_SHELL;
387*041394f3SDevin Teske 			config->output_type = DPV_OUTPUT_SHELL;
388*041394f3SDevin Teske 			config->output = optarg;
389*041394f3SDevin Teske 			break;
390*041394f3SDevin Teske 		case 'X': /* X11 support through x11/xdialog */
391*041394f3SDevin Teske 			config->display_type = DPV_DISPLAY_XDIALOG;
392*041394f3SDevin Teske 			break;
393*041394f3SDevin Teske 		case '?': /* unknown argument (based on optstring) */
394*041394f3SDevin Teske 			/* FALLTHROUGH */
395*041394f3SDevin Teske 		default: /* unhandled argument (based on switch) */
396*041394f3SDevin Teske 			usage();
397*041394f3SDevin Teske 			/* NOTREACHED */
398*041394f3SDevin Teske 		}
399*041394f3SDevin Teske 	}
400*041394f3SDevin Teske 	argc -= optind;
401*041394f3SDevin Teske 	argv += optind;
402*041394f3SDevin Teske 
403*041394f3SDevin Teske 	/* Process remaining arguments as list of names to display */
404*041394f3SDevin Teske 	for (curfile = file_list; n < argc; n++) {
405*041394f3SDevin Teske 		nfiles++;
406*041394f3SDevin Teske 
407*041394f3SDevin Teske 		/* Allocate a new struct for the file argument */
408*041394f3SDevin Teske 		if (curfile == NULL) {
409*041394f3SDevin Teske 			if ((curfile = malloc(file_node_size)) == NULL)
410*041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
411*041394f3SDevin Teske 			memset((void *)(curfile), '\0', file_node_size);
412*041394f3SDevin Teske 			file_list = curfile;
413*041394f3SDevin Teske 		} else {
414*041394f3SDevin Teske 			if ((curfile->next = malloc(file_node_size)) == NULL)
415*041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
416*041394f3SDevin Teske 			memset((void *)(curfile->next), '\0', file_node_size);
417*041394f3SDevin Teske 			curfile = curfile->next;
418*041394f3SDevin Teske 		}
419*041394f3SDevin Teske 		curfile->name = argv[n];
420*041394f3SDevin Teske 
421*041394f3SDevin Teske 		/* Read possible `lines:' prefix from label syntax */
422*041394f3SDevin Teske 		if (sscanf(curfile->name, "%lli:%c", &(curfile->length),
423*041394f3SDevin Teske 		    &dummy) == 2)
424*041394f3SDevin Teske 			curfile->name = strchr(curfile->name, ':') + 1;
425*041394f3SDevin Teske 		else
426*041394f3SDevin Teske 			curfile->length = -1;
427*041394f3SDevin Teske 
428*041394f3SDevin Teske 		/* Read path argument if enabled */
429*041394f3SDevin Teske 		if (multiple) {
430*041394f3SDevin Teske 			if (++n >= argc)
431*041394f3SDevin Teske 				errx(EXIT_FAILURE, "Missing path argument "
432*041394f3SDevin Teske 				    "for label number %i", nfiles);
433*041394f3SDevin Teske 			curfile->path = argv[n];
434*041394f3SDevin Teske 		} else
435*041394f3SDevin Teske 			break;
436*041394f3SDevin Teske 	}
437*041394f3SDevin Teske 
438*041394f3SDevin Teske 	/* Display usage and exit if not given at least one name */
439*041394f3SDevin Teske 	if (nfiles == 0) {
440*041394f3SDevin Teske 		warnx("no labels provided");
441*041394f3SDevin Teske 		usage();
442*041394f3SDevin Teske 		/* NOTREACHED */
443*041394f3SDevin Teske 	}
444*041394f3SDevin Teske 
445*041394f3SDevin Teske 	/*
446*041394f3SDevin Teske 	 * Set cleanup routine for Ctrl-C action
447*041394f3SDevin Teske 	 */
448*041394f3SDevin Teske 	if (config->display_type == DPV_DISPLAY_LIBDIALOG) {
449*041394f3SDevin Teske 		act.sa_handler = sig_int;
450*041394f3SDevin Teske 		sigaction(SIGINT, &act, 0);
451*041394f3SDevin Teske 	}
452*041394f3SDevin Teske 
453*041394f3SDevin Teske 	/* Set status formats and action */
454*041394f3SDevin Teske 	if (line_mode) {
455*041394f3SDevin Teske 		config->status_solo = LINE_STATUS_SOLO;
456*041394f3SDevin Teske 		config->status_many = LINE_STATUS_SOLO;
457*041394f3SDevin Teske 		config->action = operate_on_lines;
458*041394f3SDevin Teske 	} else {
459*041394f3SDevin Teske 		config->status_solo = BYTE_STATUS_SOLO;
460*041394f3SDevin Teske 		config->status_many = BYTE_STATUS_SOLO;
461*041394f3SDevin Teske 		config->action = operate_on_bytes;
462*041394f3SDevin Teske 	}
463*041394f3SDevin Teske 
464*041394f3SDevin Teske 	/*
465*041394f3SDevin Teske 	 * Hand off to dpv(3)...
466*041394f3SDevin Teske 	 */
467*041394f3SDevin Teske 	if (dpv(config, file_list) != 0 && debug)
468*041394f3SDevin Teske 		warnx("dpv(3) returned error!?");
469*041394f3SDevin Teske 
470*041394f3SDevin Teske 	end_dialog();
471*041394f3SDevin Teske 	dpv_free();
472*041394f3SDevin Teske 
473*041394f3SDevin Teske 	exit(EXIT_SUCCESS);
474*041394f3SDevin Teske }
475*041394f3SDevin Teske 
476*041394f3SDevin Teske /*
477*041394f3SDevin Teske  * Interrupt handler to indicate we received a Ctrl-C interrupt.
478*041394f3SDevin Teske  */
479*041394f3SDevin Teske static void
480*041394f3SDevin Teske sig_int(int sig __unused)
481*041394f3SDevin Teske {
482*041394f3SDevin Teske 	dpv_interrupt = TRUE;
483*041394f3SDevin Teske }
484*041394f3SDevin Teske 
485*041394f3SDevin Teske /*
486*041394f3SDevin Teske  * Print short usage statement to stderr and exit with error status.
487*041394f3SDevin Teske  */
488*041394f3SDevin Teske static void
489*041394f3SDevin Teske usage(void)
490*041394f3SDevin Teske {
491*041394f3SDevin Teske 
492*041394f3SDevin Teske 	if (debug) /* No need for usage */
493*041394f3SDevin Teske 		exit(EXIT_FAILURE);
494*041394f3SDevin Teske 
495*041394f3SDevin Teske 	fprintf(stderr, "Usage: %s [options] bytes:label\n", pgm);
496*041394f3SDevin Teske 	fprintf(stderr, "       %s [options] -m bytes1:label1 path1 "
497*041394f3SDevin Teske 	    "[bytes2:label2 path2 ...]\n", pgm);
498*041394f3SDevin Teske 	fprintf(stderr, "OPTIONS:\n");
499*041394f3SDevin Teske #define OPTFMT "\t%-14s %s\n"
500*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-a text",
501*041394f3SDevin Teske 	    "Append text. Displayed below file progress indicators.");
502*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-b backtitle",
503*041394f3SDevin Teske 	    "String to be displayed on the backdrop, at top-left.");
504*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-d",
505*041394f3SDevin Teske 	    "Debug. Write to standard output instead of dialog.");
506*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-D",
507*041394f3SDevin Teske 	    "Use dialog(1) instead of dialog(3) [default].");
508*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-h",
509*041394f3SDevin Teske 	    "Produce this output on standard error and exit.");
510*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-i format",
511*041394f3SDevin Teske 	    "Customize status line format. See fdpv(1) for details.");
512*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-I format",
513*041394f3SDevin Teske 	    "Customize status line format. See fdpv(1) for details.");
514*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-L size",
515*041394f3SDevin Teske 	    "Label size. Must be a number greater than 0, or -1.");
516*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-m",
517*041394f3SDevin Teske 	    "Enable processing of multiple file argiments.");
518*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-n num",
519*041394f3SDevin Teske 	    "Display at-most num files per screen. Default is -1.");
520*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-N",
521*041394f3SDevin Teske 	    "No overrun. Stop reading input at stated length, if any.");
522*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-o file",
523*041394f3SDevin Teske 	    "Output data to file. First %s replaced with label text.");
524*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-p text",
525*041394f3SDevin Teske 	    "Prefix text. Displayed above file progress indicators.");
526*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-P size",
527*041394f3SDevin Teske 	    "Mini-progressbar size. Must be a number greater than 3.");
528*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-t title",
529*041394f3SDevin Teske 	    "Title string to be displayed at top of dialog(1) box.");
530*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-T",
531*041394f3SDevin Teske 	    "Test mode. Don't actually read any data, but fake it.");
532*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-U num",
533*041394f3SDevin Teske 	    "Update status line num times per-second. Default is 2.");
534*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-w",
535*041394f3SDevin Teske 	    "Wide. Width of `-p' and `-a' text bump dialog(1) width.");
536*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-x cmd",
537*041394f3SDevin Teske 	    "Send data to executed cmd. First %s replaced with label.");
538*041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-X",
539*041394f3SDevin Teske 	    "X11. Use Xdialog(1) instead of dialog(1).");
540*041394f3SDevin Teske 	exit(EXIT_FAILURE);
541*041394f3SDevin Teske }
542