xref: /freebsd/usr.bin/dpv/dpv.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1041394f3SDevin Teske /*-
221838a13SDevin Teske  * Copyright (c) 2013-2016 Devin Teske <dteske@FreeBSD.org>
3041394f3SDevin Teske  * All rights reserved.
4041394f3SDevin Teske  *
5041394f3SDevin Teske  * Redistribution and use in source and binary forms, with or without
6041394f3SDevin Teske  * modification, are permitted provided that the following conditions
7041394f3SDevin Teske  * are met:
8041394f3SDevin Teske  * 1. Redistributions of source code must retain the above copyright
9041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer.
10041394f3SDevin Teske  * 2. Redistributions in binary form must reproduce the above copyright
11041394f3SDevin Teske  *    notice, this list of conditions and the following disclaimer in the
12041394f3SDevin Teske  *    documentation and/or other materials provided with the distribution.
13041394f3SDevin Teske  *
14041394f3SDevin Teske  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15041394f3SDevin Teske  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16041394f3SDevin Teske  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17041394f3SDevin Teske  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18041394f3SDevin Teske  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19041394f3SDevin Teske  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20041394f3SDevin Teske  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21041394f3SDevin Teske  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22041394f3SDevin Teske  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23041394f3SDevin Teske  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24041394f3SDevin Teske  * SUCH DAMAGE.
25041394f3SDevin Teske  */
26041394f3SDevin Teske 
27041394f3SDevin Teske #include <sys/cdefs.h>
28041394f3SDevin Teske #include <sys/stat.h>
29041394f3SDevin Teske #include <sys/types.h>
30041394f3SDevin Teske 
31041394f3SDevin Teske #define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */
32041394f3SDevin Teske #include <dialog.h>
33041394f3SDevin Teske #include <dpv.h>
34041394f3SDevin Teske #include <err.h>
35041394f3SDevin Teske #include <errno.h>
36041394f3SDevin Teske #include <fcntl.h>
37041394f3SDevin Teske #include <limits.h>
38041394f3SDevin Teske #include <signal.h>
39041394f3SDevin Teske #include <stdio.h>
40041394f3SDevin Teske #include <stdlib.h>
41041394f3SDevin Teske #include <string.h>
42041394f3SDevin Teske #include <string_m.h>
43041394f3SDevin Teske #include <unistd.h>
44041394f3SDevin Teske 
45041394f3SDevin Teske #include "dpv_util.h"
46041394f3SDevin Teske 
47041394f3SDevin Teske /* Debugging */
48041394f3SDevin Teske static uint8_t debug = FALSE;
49041394f3SDevin Teske 
50041394f3SDevin Teske /* Data to process */
51041394f3SDevin Teske static struct dpv_file_node *file_list = NULL;
52041394f3SDevin Teske static unsigned int nfiles = 0;
53041394f3SDevin Teske 
54041394f3SDevin Teske /* Data processing */
55041394f3SDevin Teske static uint8_t line_mode = FALSE;
56041394f3SDevin Teske static uint8_t no_overrun = FALSE;
57041394f3SDevin Teske static char *buf = NULL;
58041394f3SDevin Teske static int fd = -1;
59041394f3SDevin Teske static int output_type = DPV_OUTPUT_NONE;
60041394f3SDevin Teske static size_t bsize;
61041394f3SDevin Teske static char rpath[PATH_MAX];
62041394f3SDevin Teske 
63041394f3SDevin Teske /* Extra display information */
64041394f3SDevin Teske static uint8_t multiple = FALSE; /* `-m' */
65041394f3SDevin Teske static char *pgm; /* set to argv[0] by main() */
66041394f3SDevin Teske 
67041394f3SDevin Teske /* Function prototypes */
68041394f3SDevin Teske static void	sig_int(int sig);
69041394f3SDevin Teske static void	usage(void);
70041394f3SDevin Teske int		main(int argc, char *argv[]);
71041394f3SDevin Teske static int	operate_common(struct dpv_file_node *file, int out);
72041394f3SDevin Teske static int	operate_on_bytes(struct dpv_file_node *file, int out);
73041394f3SDevin Teske static int	operate_on_lines(struct dpv_file_node *file, int out);
74041394f3SDevin Teske 
75041394f3SDevin Teske static int
operate_common(struct dpv_file_node * file,int out)76041394f3SDevin Teske operate_common(struct dpv_file_node *file, int out)
77041394f3SDevin Teske {
78041394f3SDevin Teske 	struct stat sb;
79041394f3SDevin Teske 
80041394f3SDevin Teske 	/* Open the file if necessary */
81041394f3SDevin Teske 	if (fd < 0) {
82041394f3SDevin Teske 		if (multiple) {
83041394f3SDevin Teske 			/* Resolve the file path and attempt to open it */
84041394f3SDevin Teske 			if (realpath(file->path, rpath) == 0 ||
85041394f3SDevin Teske 			    (fd = open(rpath, O_RDONLY)) < 0) {
86041394f3SDevin Teske 				warn("%s", file->path);
87041394f3SDevin Teske 				file->status = DPV_STATUS_FAILED;
88041394f3SDevin Teske 				return (-1);
89041394f3SDevin Teske 			}
90041394f3SDevin Teske 		} else {
91041394f3SDevin Teske 			/* Assume stdin, but if that's a TTY instead use the
92041394f3SDevin Teske 			 * highest numbered file descriptor (obtained by
93041394f3SDevin Teske 			 * generating new fd and then decrementing).
94041394f3SDevin Teske 			 *
95041394f3SDevin Teske 			 * NB: /dev/stdin should always be open(2)'able
96041394f3SDevin Teske 			 */
97041394f3SDevin Teske 			fd = STDIN_FILENO;
98041394f3SDevin Teske 			if (isatty(fd)) {
99041394f3SDevin Teske 				fd = open("/dev/stdin", O_RDONLY);
100041394f3SDevin Teske 				close(fd--);
101041394f3SDevin Teske 			}
102041394f3SDevin Teske 
103041394f3SDevin Teske 			/* This answer might be wrong, if dpv(3) has (by
104041394f3SDevin Teske 			 * request) opened an output file or pipe. If we
105041394f3SDevin Teske 			 * told dpv(3) to open a file, subtract one from
106041394f3SDevin Teske 			 * previous answer. If instead we told dpv(3) to
107041394f3SDevin Teske 			 * prepare a pipe output, subtract two.
108041394f3SDevin Teske 			 */
109041394f3SDevin Teske 			switch(output_type) {
110041394f3SDevin Teske 			case DPV_OUTPUT_FILE:
111041394f3SDevin Teske 				fd -= 1;
112041394f3SDevin Teske 				break;
113041394f3SDevin Teske 			case DPV_OUTPUT_SHELL:
114041394f3SDevin Teske 				fd -= 2;
115041394f3SDevin Teske 				break;
116041394f3SDevin Teske 			}
117041394f3SDevin Teske 		}
118041394f3SDevin Teske 	}
119041394f3SDevin Teske 
120041394f3SDevin Teske 	/* Allocate buffer if necessary */
121041394f3SDevin Teske 	if (buf == NULL) {
122041394f3SDevin Teske 		/* Use output block size as buffer size if available */
123041394f3SDevin Teske 		if (out >= 0) {
124041394f3SDevin Teske 			if (fstat(out, &sb) != 0) {
125041394f3SDevin Teske 				warn("%i", out);
126041394f3SDevin Teske 				file->status = DPV_STATUS_FAILED;
127041394f3SDevin Teske 				return (-1);
128041394f3SDevin Teske 			}
129041394f3SDevin Teske 			if (S_ISREG(sb.st_mode)) {
130041394f3SDevin Teske 				if (sysconf(_SC_PHYS_PAGES) >
131041394f3SDevin Teske 				    PHYSPAGES_THRESHOLD)
132041394f3SDevin Teske 					bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
133041394f3SDevin Teske 				else
134041394f3SDevin Teske 					bsize = BUFSIZE_SMALL;
135041394f3SDevin Teske 			} else
136041394f3SDevin Teske 				bsize = MAX(sb.st_blksize,
137041394f3SDevin Teske 				    (blksize_t)sysconf(_SC_PAGESIZE));
138041394f3SDevin Teske 		} else
139041394f3SDevin Teske 			bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
140041394f3SDevin Teske 
141041394f3SDevin Teske 		/* Attempt to allocate */
142041394f3SDevin Teske 		if ((buf = malloc(bsize+1)) == NULL) {
143041394f3SDevin Teske 			end_dialog();
144041394f3SDevin Teske 			err(EXIT_FAILURE, "Out of memory?!");
145041394f3SDevin Teske 		}
146041394f3SDevin Teske 	}
147041394f3SDevin Teske 
148041394f3SDevin Teske 	return (0);
149041394f3SDevin Teske }
150041394f3SDevin Teske 
151041394f3SDevin Teske static int
operate_on_bytes(struct dpv_file_node * file,int out)152041394f3SDevin Teske operate_on_bytes(struct dpv_file_node *file, int out)
153041394f3SDevin Teske {
154041394f3SDevin Teske 	int progress;
155041394f3SDevin Teske 	ssize_t r, w;
156041394f3SDevin Teske 
157041394f3SDevin Teske 	if (operate_common(file, out) < 0)
158041394f3SDevin Teske 		return (-1);
159041394f3SDevin Teske 
160041394f3SDevin Teske 	/* [Re-]Fill the buffer */
161041394f3SDevin Teske 	if ((r = read(fd, buf, bsize)) <= 0) {
162041394f3SDevin Teske 		if (fd != STDIN_FILENO)
163041394f3SDevin Teske 			close(fd);
164041394f3SDevin Teske 		fd = -1;
165041394f3SDevin Teske 		file->status = DPV_STATUS_DONE;
166041394f3SDevin Teske 		return (100);
167041394f3SDevin Teske 	}
168041394f3SDevin Teske 
169041394f3SDevin Teske 	/* [Re-]Dump the buffer */
170041394f3SDevin Teske 	if (out >= 0) {
171041394f3SDevin Teske 		if ((w = write(out, buf, r)) < 0) {
172041394f3SDevin Teske 			end_dialog();
173041394f3SDevin Teske 			err(EXIT_FAILURE, "output");
174041394f3SDevin Teske 		}
175041394f3SDevin Teske 		fsync(out);
176041394f3SDevin Teske 	}
177041394f3SDevin Teske 
178964b46aaSDevin Teske 	dpv_overall_read += r;
179041394f3SDevin Teske 	file->read += r;
180041394f3SDevin Teske 
181041394f3SDevin Teske 	/* Calculate percentage of completion (if possible) */
182041394f3SDevin Teske 	if (file->length >= 0) {
183041394f3SDevin Teske 		progress = (file->read * 100 / (file->length > 0 ?
184041394f3SDevin Teske 		    file->length : 1));
185041394f3SDevin Teske 
186041394f3SDevin Teske 		/* If no_overrun, do not return 100% until read >= length */
187041394f3SDevin Teske 		if (no_overrun && progress == 100 && file->read < file->length)
188041394f3SDevin Teske 			progress--;
189041394f3SDevin Teske 
190041394f3SDevin Teske 		return (progress);
191041394f3SDevin Teske 	} else
192041394f3SDevin Teske 		return (-1);
193041394f3SDevin Teske }
194041394f3SDevin Teske 
195041394f3SDevin Teske static int
operate_on_lines(struct dpv_file_node * file,int out)196041394f3SDevin Teske operate_on_lines(struct dpv_file_node *file, int out)
197041394f3SDevin Teske {
198041394f3SDevin Teske 	char *p;
199041394f3SDevin Teske 	int progress;
200041394f3SDevin Teske 	ssize_t r, w;
201041394f3SDevin Teske 
202041394f3SDevin Teske 	if (operate_common(file, out) < 0)
203041394f3SDevin Teske 		return (-1);
204041394f3SDevin Teske 
205041394f3SDevin Teske 	/* [Re-]Fill the buffer */
206041394f3SDevin Teske 	if ((r = read(fd, buf, bsize)) <= 0) {
207041394f3SDevin Teske 		if (fd != STDIN_FILENO)
208041394f3SDevin Teske 			close(fd);
209041394f3SDevin Teske 		fd = -1;
210041394f3SDevin Teske 		file->status = DPV_STATUS_DONE;
211041394f3SDevin Teske 		return (100);
212041394f3SDevin Teske 	}
213041394f3SDevin Teske 	buf[r] = '\0';
214041394f3SDevin Teske 
215041394f3SDevin Teske 	/* [Re-]Dump the buffer */
216041394f3SDevin Teske 	if (out >= 0) {
217041394f3SDevin Teske 		if ((w = write(out, buf, r)) < 0) {
218041394f3SDevin Teske 			end_dialog();
219041394f3SDevin Teske 			err(EXIT_FAILURE, "output");
220041394f3SDevin Teske 		}
221041394f3SDevin Teske 		fsync(out);
222041394f3SDevin Teske 	}
223041394f3SDevin Teske 
224041394f3SDevin Teske 	/* Process the buffer for number of lines */
225041394f3SDevin Teske 	for (p = buf; p != NULL && *p != '\0';)
226041394f3SDevin Teske 		if ((p = strchr(p, '\n')) != NULL)
227964b46aaSDevin Teske 			dpv_overall_read++, p++, file->read++;
228041394f3SDevin Teske 
229041394f3SDevin Teske 	/* Calculate percentage of completion (if possible) */
230041394f3SDevin Teske 	if (file->length >= 0) {
231041394f3SDevin Teske 		progress = (file->read * 100 / file->length);
232041394f3SDevin Teske 
233041394f3SDevin Teske 		/* If no_overrun, do not return 100% until read >= length */
234041394f3SDevin Teske 		if (no_overrun && progress == 100 && file->read < file->length)
235041394f3SDevin Teske 			progress--;
236041394f3SDevin Teske 
237041394f3SDevin Teske 		return (progress);
238041394f3SDevin Teske 	} else
239041394f3SDevin Teske 		return (-1);
240041394f3SDevin Teske }
241041394f3SDevin Teske 
242041394f3SDevin Teske /*
243041394f3SDevin Teske  * Takes a list of names that are to correspond to input streams coming from
244041394f3SDevin Teske  * stdin or fifos and produces necessary config to drive dpv(3) `--gauge'
245041394f3SDevin Teske  * widget. If the `-d' flag is used, output is instead send to terminal
246041394f3SDevin Teske  * standard output (and the output can then be saved to a file, piped into
247041394f3SDevin Teske  * custom [X]dialog(1) invocation, or whatever.
248041394f3SDevin Teske  */
249041394f3SDevin Teske int
main(int argc,char * argv[])250041394f3SDevin Teske main(int argc, char *argv[])
251041394f3SDevin Teske {
252041394f3SDevin Teske 	char dummy;
253041394f3SDevin Teske 	int ch;
254041394f3SDevin Teske 	int n = 0;
255041394f3SDevin Teske 	size_t config_size = sizeof(struct dpv_config);
256041394f3SDevin Teske 	size_t file_node_size = sizeof(struct dpv_file_node);
257041394f3SDevin Teske 	struct dpv_config *config;
258041394f3SDevin Teske 	struct dpv_file_node *curfile;
259041394f3SDevin Teske 	struct sigaction act;
260041394f3SDevin Teske 
261041394f3SDevin Teske 	pgm = argv[0]; /* store a copy of invocation name */
262041394f3SDevin Teske 
263041394f3SDevin Teske 	/* Allocate config structure */
264041394f3SDevin Teske 	if ((config = malloc(config_size)) == NULL)
265041394f3SDevin Teske 		errx(EXIT_FAILURE, "Out of memory?!");
266041394f3SDevin Teske 	memset((void *)(config), '\0', config_size);
267041394f3SDevin Teske 
268041394f3SDevin Teske 	/*
269041394f3SDevin Teske 	 * Process command-line options
270041394f3SDevin Teske 	 */
271041394f3SDevin Teske 	while ((ch = getopt(argc, argv,
27256b38aa6SDevin Teske 	    "a:b:dDhi:I:klL:mn:No:p:P:t:TU:wx:X")) != -1) {
273041394f3SDevin Teske 		switch(ch) {
274041394f3SDevin Teske 		case 'a': /* additional message text to append */
275041394f3SDevin Teske 			if (config->aprompt == NULL) {
276041394f3SDevin Teske 				config->aprompt = malloc(DPV_APROMPT_MAX);
277041394f3SDevin Teske 				if (config->aprompt == NULL)
278041394f3SDevin Teske 					errx(EXIT_FAILURE, "Out of memory?!");
279041394f3SDevin Teske 			}
280041394f3SDevin Teske 			snprintf(config->aprompt, DPV_APROMPT_MAX, "%s",
281041394f3SDevin Teske 			    optarg);
282041394f3SDevin Teske 			break;
283041394f3SDevin Teske 		case 'b': /* [X]dialog(1) backtitle */
284041394f3SDevin Teske 			if (config->backtitle != NULL)
285041394f3SDevin Teske 				free((char *)config->backtitle);
286041394f3SDevin Teske 			config->backtitle = malloc(strlen(optarg) + 1);
287041394f3SDevin Teske 			if (config->backtitle == NULL)
288041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
289041394f3SDevin Teske 			*(config->backtitle) = '\0';
290041394f3SDevin Teske 			strcat(config->backtitle, optarg);
291041394f3SDevin Teske 			break;
292041394f3SDevin Teske 		case 'd': /* debugging */
293041394f3SDevin Teske 			debug = TRUE;
294041394f3SDevin Teske 			config->debug = debug;
295041394f3SDevin Teske 			break;
296041394f3SDevin Teske 		case 'D': /* use dialog(1) instead of libdialog */
297041394f3SDevin Teske 			config->display_type = DPV_DISPLAY_DIALOG;
298041394f3SDevin Teske 			break;
299041394f3SDevin Teske 		case 'h': /* help/usage */
300041394f3SDevin Teske 			usage();
301041394f3SDevin Teske 			break; /* NOTREACHED */
302041394f3SDevin Teske 		case 'i': /* status line format string for single-file */
303041394f3SDevin Teske 			config->status_solo = optarg;
304041394f3SDevin Teske 			break;
305041394f3SDevin Teske 		case 'I': /* status line format string for many-files */
306041394f3SDevin Teske 			config->status_many = optarg;
307041394f3SDevin Teske 			break;
30856b38aa6SDevin Teske 		case 'k': /* keep tite */
30956b38aa6SDevin Teske 			config->keep_tite = TRUE;
31056b38aa6SDevin Teske 			break;
311041394f3SDevin Teske 		case 'l': /* Line mode */
312041394f3SDevin Teske 			line_mode = TRUE;
313041394f3SDevin Teske 			break;
314041394f3SDevin Teske 		case 'L': /* custom label size */
315041394f3SDevin Teske 			config->label_size =
316041394f3SDevin Teske 			    (int)strtol(optarg, (char **)NULL, 10);
317041394f3SDevin Teske 			if (config->label_size == 0 && errno == EINVAL)
318041394f3SDevin Teske 				errx(EXIT_FAILURE,
319041394f3SDevin Teske 				    "`-L' argument must be numeric");
320041394f3SDevin Teske 			else if (config->label_size < -1)
321041394f3SDevin Teske 				config->label_size = -1;
322041394f3SDevin Teske 			break;
323041394f3SDevin Teske 		case 'm': /* enable multiple file arguments */
324041394f3SDevin Teske 			multiple = TRUE;
325041394f3SDevin Teske 			break;
326041394f3SDevin Teske 		case 'o': /* `-o path' for sending data-read to file */
327041394f3SDevin Teske 			output_type = DPV_OUTPUT_FILE;
328041394f3SDevin Teske 			config->output_type = DPV_OUTPUT_FILE;
329041394f3SDevin Teske 			config->output = optarg;
330041394f3SDevin Teske 			break;
331041394f3SDevin Teske 		case 'n': /* custom number of files per `page' */
332041394f3SDevin Teske 			config->display_limit =
333041394f3SDevin Teske 				(int)strtol(optarg, (char **)NULL, 10);
334041394f3SDevin Teske 			if (config->display_limit == 0 && errno == EINVAL)
335041394f3SDevin Teske 				errx(EXIT_FAILURE,
336041394f3SDevin Teske 				    "`-n' argument must be numeric");
337041394f3SDevin Teske 			else if (config->display_limit < 0)
338041394f3SDevin Teske 				config->display_limit = -1;
339041394f3SDevin Teske 			break;
340041394f3SDevin Teske 		case 'N': /* No overrun (truncate reads of known-length) */
341041394f3SDevin Teske 			no_overrun = TRUE;
342041394f3SDevin Teske 			config->options |= DPV_NO_OVERRUN;
343041394f3SDevin Teske 			break;
344041394f3SDevin Teske 		case 'p': /* additional message text to use as prefix */
345041394f3SDevin Teske 			if (config->pprompt == NULL) {
346041394f3SDevin Teske 				config->pprompt = malloc(DPV_PPROMPT_MAX + 2);
347041394f3SDevin Teske 				if (config->pprompt == NULL)
348041394f3SDevin Teske 					errx(EXIT_FAILURE, "Out of memory?!");
349041394f3SDevin Teske 				/* +2 is for implicit "\n" appended later */
350041394f3SDevin Teske 			}
351041394f3SDevin Teske 			snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s",
352041394f3SDevin Teske 			    optarg);
353041394f3SDevin Teske 			break;
354041394f3SDevin Teske 		case 'P': /* custom size for mini-progressbar */
355041394f3SDevin Teske 			config->pbar_size =
356041394f3SDevin Teske 			    (int)strtol(optarg, (char **)NULL, 10);
357041394f3SDevin Teske 			if (config->pbar_size == 0 && errno == EINVAL)
358041394f3SDevin Teske 				errx(EXIT_FAILURE,
359041394f3SDevin Teske 				    "`-P' argument must be numeric");
360041394f3SDevin Teske 			else if (config->pbar_size < -1)
361041394f3SDevin Teske 				config->pbar_size = -1;
362041394f3SDevin Teske 			break;
363041394f3SDevin Teske 		case 't': /* [X]dialog(1) title */
364041394f3SDevin Teske 			if (config->title != NULL)
365041394f3SDevin Teske 				free(config->title);
366041394f3SDevin Teske 			config->title = malloc(strlen(optarg) + 1);
367041394f3SDevin Teske 			if (config->title == NULL)
368041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
369041394f3SDevin Teske 			*(config->title) = '\0';
370041394f3SDevin Teske 			strcat(config->title, optarg);
371041394f3SDevin Teske 			break;
372041394f3SDevin Teske 		case 'T': /* test mode (don't read data, fake it) */
373041394f3SDevin Teske 			config->options |= DPV_TEST_MODE;
374041394f3SDevin Teske 			break;
375041394f3SDevin Teske 		case 'U': /* updates per second */
376041394f3SDevin Teske 			config->status_updates_per_second =
377041394f3SDevin Teske 			    (int)strtol(optarg, (char **)NULL, 10);
378041394f3SDevin Teske 			if (config->status_updates_per_second == 0 &&
379041394f3SDevin Teske 			    errno == EINVAL)
380041394f3SDevin Teske 				errx(EXIT_FAILURE,
381041394f3SDevin Teske 				    "`-U' argument must be numeric");
382041394f3SDevin Teske 			break;
383041394f3SDevin Teske 		case 'w': /* `-p' and `-a' widths bump [X]dialog(1) width */
384041394f3SDevin Teske 			config->options |= DPV_WIDE_MODE;
385041394f3SDevin Teske 			break;
386041394f3SDevin Teske 		case 'x': /* `-x cmd' for sending data-read to sh(1) code */
387041394f3SDevin Teske 			output_type = DPV_OUTPUT_SHELL;
388041394f3SDevin Teske 			config->output_type = DPV_OUTPUT_SHELL;
389041394f3SDevin Teske 			config->output = optarg;
390041394f3SDevin Teske 			break;
391041394f3SDevin Teske 		case 'X': /* X11 support through x11/xdialog */
392041394f3SDevin Teske 			config->display_type = DPV_DISPLAY_XDIALOG;
393041394f3SDevin Teske 			break;
394041394f3SDevin Teske 		case '?': /* unknown argument (based on optstring) */
395041394f3SDevin Teske 			/* FALLTHROUGH */
396041394f3SDevin Teske 		default: /* unhandled argument (based on switch) */
397041394f3SDevin Teske 			usage();
398041394f3SDevin Teske 			/* NOTREACHED */
399041394f3SDevin Teske 		}
400041394f3SDevin Teske 	}
401041394f3SDevin Teske 	argc -= optind;
402041394f3SDevin Teske 	argv += optind;
403041394f3SDevin Teske 
404041394f3SDevin Teske 	/* Process remaining arguments as list of names to display */
405041394f3SDevin Teske 	for (curfile = file_list; n < argc; n++) {
406041394f3SDevin Teske 		nfiles++;
407041394f3SDevin Teske 
408041394f3SDevin Teske 		/* Allocate a new struct for the file argument */
409041394f3SDevin Teske 		if (curfile == NULL) {
410041394f3SDevin Teske 			if ((curfile = malloc(file_node_size)) == NULL)
411041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
412041394f3SDevin Teske 			memset((void *)(curfile), '\0', file_node_size);
413041394f3SDevin Teske 			file_list = curfile;
414041394f3SDevin Teske 		} else {
415041394f3SDevin Teske 			if ((curfile->next = malloc(file_node_size)) == NULL)
416041394f3SDevin Teske 				errx(EXIT_FAILURE, "Out of memory?!");
417041394f3SDevin Teske 			memset((void *)(curfile->next), '\0', file_node_size);
418041394f3SDevin Teske 			curfile = curfile->next;
419041394f3SDevin Teske 		}
420041394f3SDevin Teske 		curfile->name = argv[n];
421041394f3SDevin Teske 
422041394f3SDevin Teske 		/* Read possible `lines:' prefix from label syntax */
423041394f3SDevin Teske 		if (sscanf(curfile->name, "%lli:%c", &(curfile->length),
424041394f3SDevin Teske 		    &dummy) == 2)
425041394f3SDevin Teske 			curfile->name = strchr(curfile->name, ':') + 1;
426041394f3SDevin Teske 		else
427041394f3SDevin Teske 			curfile->length = -1;
428041394f3SDevin Teske 
429041394f3SDevin Teske 		/* Read path argument if enabled */
430041394f3SDevin Teske 		if (multiple) {
431041394f3SDevin Teske 			if (++n >= argc)
432041394f3SDevin Teske 				errx(EXIT_FAILURE, "Missing path argument "
433041394f3SDevin Teske 				    "for label number %i", nfiles);
434041394f3SDevin Teske 			curfile->path = argv[n];
435041394f3SDevin Teske 		} else
436041394f3SDevin Teske 			break;
437041394f3SDevin Teske 	}
438041394f3SDevin Teske 
439041394f3SDevin Teske 	/* Display usage and exit if not given at least one name */
440041394f3SDevin Teske 	if (nfiles == 0) {
441041394f3SDevin Teske 		warnx("no labels provided");
442041394f3SDevin Teske 		usage();
443041394f3SDevin Teske 		/* NOTREACHED */
444041394f3SDevin Teske 	}
445041394f3SDevin Teske 
446041394f3SDevin Teske 	/*
447041394f3SDevin Teske 	 * Set cleanup routine for Ctrl-C action
448041394f3SDevin Teske 	 */
449041394f3SDevin Teske 	if (config->display_type == DPV_DISPLAY_LIBDIALOG) {
450041394f3SDevin Teske 		act.sa_handler = sig_int;
451041394f3SDevin Teske 		sigaction(SIGINT, &act, 0);
452041394f3SDevin Teske 	}
453041394f3SDevin Teske 
454041394f3SDevin Teske 	/* Set status formats and action */
455041394f3SDevin Teske 	if (line_mode) {
456041394f3SDevin Teske 		config->status_solo = LINE_STATUS_SOLO;
457041394f3SDevin Teske 		config->status_many = LINE_STATUS_SOLO;
458041394f3SDevin Teske 		config->action = operate_on_lines;
459041394f3SDevin Teske 	} else {
460041394f3SDevin Teske 		config->status_solo = BYTE_STATUS_SOLO;
461041394f3SDevin Teske 		config->status_many = BYTE_STATUS_SOLO;
462041394f3SDevin Teske 		config->action = operate_on_bytes;
463041394f3SDevin Teske 	}
464041394f3SDevin Teske 
465041394f3SDevin Teske 	/*
466041394f3SDevin Teske 	 * Hand off to dpv(3)...
467041394f3SDevin Teske 	 */
468041394f3SDevin Teske 	if (dpv(config, file_list) != 0 && debug)
469041394f3SDevin Teske 		warnx("dpv(3) returned error!?");
470041394f3SDevin Teske 
47156b38aa6SDevin Teske 	if (!config->keep_tite)
472041394f3SDevin Teske 		end_dialog();
473041394f3SDevin Teske 	dpv_free();
474041394f3SDevin Teske 
475041394f3SDevin Teske 	exit(EXIT_SUCCESS);
476041394f3SDevin Teske }
477041394f3SDevin Teske 
478041394f3SDevin Teske /*
479041394f3SDevin Teske  * Interrupt handler to indicate we received a Ctrl-C interrupt.
480041394f3SDevin Teske  */
481041394f3SDevin Teske static void
sig_int(int sig __unused)482041394f3SDevin Teske sig_int(int sig __unused)
483041394f3SDevin Teske {
484041394f3SDevin Teske 	dpv_interrupt = TRUE;
485041394f3SDevin Teske }
486041394f3SDevin Teske 
487041394f3SDevin Teske /*
488041394f3SDevin Teske  * Print short usage statement to stderr and exit with error status.
489041394f3SDevin Teske  */
490041394f3SDevin Teske static void
usage(void)491041394f3SDevin Teske usage(void)
492041394f3SDevin Teske {
493041394f3SDevin Teske 
494041394f3SDevin Teske 	if (debug) /* No need for usage */
495041394f3SDevin Teske 		exit(EXIT_FAILURE);
496041394f3SDevin Teske 
497*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, "Usage: %s [options] [bytes:]label\n", pgm);
498*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, "       %s [options] -m [bytes1:]label1 path1 "
499*a85aa7f9SMateusz Piotrowski 	    "[[bytes2:]label2 path2 ...]\n", pgm);
500041394f3SDevin Teske 	fprintf(stderr, "OPTIONS:\n");
501041394f3SDevin Teske #define OPTFMT "\t%-14s %s\n"
502041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-a text",
503041394f3SDevin Teske 	    "Append text. Displayed below file progress indicators.");
504041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-b backtitle",
505041394f3SDevin Teske 	    "String to be displayed on the backdrop, at top-left.");
506041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-D",
507041394f3SDevin Teske 	    "Use dialog(1) instead of dialog(3) [default].");
508*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, OPTFMT, "-d",
509*a85aa7f9SMateusz Piotrowski 	    "Debug. Write to standard output instead of dialog.");
510041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-h",
511041394f3SDevin Teske 	    "Produce this output on standard error and exit.");
512041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-I format",
513041394f3SDevin Teske 	    "Customize status line format. See fdpv(1) for details.");
514*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, OPTFMT, "-i format",
515*a85aa7f9SMateusz Piotrowski 	    "Customize status line format. See fdpv(1) for details.");
516041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-L size",
517041394f3SDevin Teske 	    "Label size. Must be a number greater than 0, or -1.");
518041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-m",
519041394f3SDevin Teske 	    "Enable processing of multiple file argiments.");
520041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-N",
521041394f3SDevin Teske 	    "No overrun. Stop reading input at stated length, if any.");
522*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, OPTFMT, "-n num",
523*a85aa7f9SMateusz Piotrowski 	    "Display at-most num files per screen. Default is -1.");
524041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-o file",
525041394f3SDevin Teske 	    "Output data to file. First %s replaced with label text.");
526041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-P size",
527041394f3SDevin Teske 	    "Mini-progressbar size. Must be a number greater than 3.");
528*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, OPTFMT, "-p text",
529*a85aa7f9SMateusz Piotrowski 	    "Prefix text. Displayed above file progress indicators.");
530041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-T",
531041394f3SDevin Teske 	    "Test mode. Don't actually read any data, but fake it.");
532*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, OPTFMT, "-t title",
533*a85aa7f9SMateusz Piotrowski 	    "Title string to be displayed at top of dialog(1) box.");
534041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-U num",
535041394f3SDevin Teske 	    "Update status line num times per-second. Default is 2.");
536041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-w",
537041394f3SDevin Teske 	    "Wide. Width of `-p' and `-a' text bump dialog(1) width.");
538041394f3SDevin Teske 	fprintf(stderr, OPTFMT, "-X",
539041394f3SDevin Teske 	    "X11. Use Xdialog(1) instead of dialog(1).");
540*a85aa7f9SMateusz Piotrowski 	fprintf(stderr, OPTFMT, "-x cmd",
541*a85aa7f9SMateusz Piotrowski 	    "Send data to executed cmd. First %s replaced with label.");
542041394f3SDevin Teske 	exit(EXIT_FAILURE);
543041394f3SDevin Teske }
544