1274116Sdteske/*-
2295110Sdteske * Copyright (c) 2013-2016 Devin Teske <dteske@FreeBSD.org>
3274116Sdteske * All rights reserved.
4274116Sdteske *
5274116Sdteske * Redistribution and use in source and binary forms, with or without
6274116Sdteske * modification, are permitted provided that the following conditions
7274116Sdteske * are met:
8274116Sdteske * 1. Redistributions of source code must retain the above copyright
9274116Sdteske *    notice, this list of conditions and the following disclaimer.
10274116Sdteske * 2. Redistributions in binary form must reproduce the above copyright
11274116Sdteske *    notice, this list of conditions and the following disclaimer in the
12274116Sdteske *    documentation and/or other materials provided with the distribution.
13274116Sdteske *
14274116Sdteske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15274116Sdteske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16274116Sdteske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17274116Sdteske * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18274116Sdteske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19274116Sdteske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20274116Sdteske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21274116Sdteske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22274116Sdteske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23274116Sdteske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24274116Sdteske * SUCH DAMAGE.
25274116Sdteske */
26274116Sdteske
27274116Sdteske#include <sys/cdefs.h>
28274116Sdteske__FBSDID("$FreeBSD$");
29274116Sdteske
30274116Sdteske#include <sys/stat.h>
31274116Sdteske#include <sys/types.h>
32274116Sdteske
33274116Sdteske#define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */
34274116Sdteske#include <dialog.h>
35274116Sdteske#include <dpv.h>
36274116Sdteske#include <err.h>
37274116Sdteske#include <errno.h>
38274116Sdteske#include <fcntl.h>
39274116Sdteske#include <limits.h>
40274116Sdteske#include <signal.h>
41274116Sdteske#include <stdio.h>
42274116Sdteske#include <stdlib.h>
43274116Sdteske#include <string.h>
44274116Sdteske#include <string_m.h>
45274116Sdteske#include <unistd.h>
46274116Sdteske
47274116Sdteske#include "dpv_util.h"
48274116Sdteske
49274116Sdteske/* Debugging */
50274116Sdteskestatic uint8_t debug = FALSE;
51274116Sdteske
52274116Sdteske/* Data to process */
53274116Sdteskestatic struct dpv_file_node *file_list = NULL;
54274116Sdteskestatic unsigned int nfiles = 0;
55274116Sdteske
56274116Sdteske/* Data processing */
57274116Sdteskestatic uint8_t line_mode = FALSE;
58274116Sdteskestatic uint8_t no_overrun = FALSE;
59274116Sdteskestatic char *buf = NULL;
60274116Sdteskestatic int fd = -1;
61274116Sdteskestatic int output_type = DPV_OUTPUT_NONE;
62274116Sdteskestatic size_t bsize;
63274116Sdteskestatic char rpath[PATH_MAX];
64274116Sdteske
65274116Sdteske/* Extra display information */
66274116Sdteskestatic uint8_t multiple = FALSE; /* `-m' */
67274116Sdteskestatic char *pgm; /* set to argv[0] by main() */
68274116Sdteske
69274116Sdteske/* Function prototypes */
70274116Sdteskestatic void	sig_int(int sig);
71274116Sdteskestatic void	usage(void);
72274116Sdteskeint		main(int argc, char *argv[]);
73274116Sdteskestatic int	operate_common(struct dpv_file_node *file, int out);
74274116Sdteskestatic int	operate_on_bytes(struct dpv_file_node *file, int out);
75274116Sdteskestatic int	operate_on_lines(struct dpv_file_node *file, int out);
76274116Sdteske
77274116Sdteskestatic int
78274116Sdteskeoperate_common(struct dpv_file_node *file, int out)
79274116Sdteske{
80274116Sdteske	struct stat sb;
81274116Sdteske
82274116Sdteske	/* Open the file if necessary */
83274116Sdteske	if (fd < 0) {
84274116Sdteske		if (multiple) {
85274116Sdteske			/* Resolve the file path and attempt to open it */
86274116Sdteske			if (realpath(file->path, rpath) == 0 ||
87274116Sdteske			    (fd = open(rpath, O_RDONLY)) < 0) {
88274116Sdteske				warn("%s", file->path);
89274116Sdteske				file->status = DPV_STATUS_FAILED;
90274116Sdteske				return (-1);
91274116Sdteske			}
92274116Sdteske		} else {
93274116Sdteske			/* Assume stdin, but if that's a TTY instead use the
94274116Sdteske			 * highest numbered file descriptor (obtained by
95274116Sdteske			 * generating new fd and then decrementing).
96274116Sdteske			 *
97274116Sdteske			 * NB: /dev/stdin should always be open(2)'able
98274116Sdteske			 */
99274116Sdteske			fd = STDIN_FILENO;
100274116Sdteske			if (isatty(fd)) {
101274116Sdteske				fd = open("/dev/stdin", O_RDONLY);
102274116Sdteske				close(fd--);
103274116Sdteske			}
104274116Sdteske
105274116Sdteske			/* This answer might be wrong, if dpv(3) has (by
106274116Sdteske			 * request) opened an output file or pipe. If we
107274116Sdteske			 * told dpv(3) to open a file, subtract one from
108274116Sdteske			 * previous answer. If instead we told dpv(3) to
109274116Sdteske			 * prepare a pipe output, subtract two.
110274116Sdteske			 */
111274116Sdteske			switch(output_type) {
112274116Sdteske			case DPV_OUTPUT_FILE:
113274116Sdteske				fd -= 1;
114274116Sdteske				break;
115274116Sdteske			case DPV_OUTPUT_SHELL:
116274116Sdteske				fd -= 2;
117274116Sdteske				break;
118274116Sdteske			}
119274116Sdteske		}
120274116Sdteske	}
121274116Sdteske
122274116Sdteske	/* Allocate buffer if necessary */
123274116Sdteske	if (buf == NULL) {
124274116Sdteske		/* Use output block size as buffer size if available */
125274116Sdteske		if (out >= 0) {
126274116Sdteske			if (fstat(out, &sb) != 0) {
127274116Sdteske				warn("%i", out);
128274116Sdteske				file->status = DPV_STATUS_FAILED;
129274116Sdteske				return (-1);
130274116Sdteske			}
131274116Sdteske			if (S_ISREG(sb.st_mode)) {
132274116Sdteske				if (sysconf(_SC_PHYS_PAGES) >
133274116Sdteske				    PHYSPAGES_THRESHOLD)
134274116Sdteske					bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
135274116Sdteske				else
136274116Sdteske					bsize = BUFSIZE_SMALL;
137274116Sdteske			} else
138274116Sdteske				bsize = MAX(sb.st_blksize,
139274116Sdteske				    (blksize_t)sysconf(_SC_PAGESIZE));
140274116Sdteske		} else
141274116Sdteske			bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
142274116Sdteske
143274116Sdteske		/* Attempt to allocate */
144274116Sdteske		if ((buf = malloc(bsize+1)) == NULL) {
145274116Sdteske			end_dialog();
146274116Sdteske			err(EXIT_FAILURE, "Out of memory?!");
147274116Sdteske		}
148274116Sdteske	}
149274116Sdteske
150274116Sdteske	return (0);
151274116Sdteske}
152274116Sdteske
153274116Sdteskestatic int
154274116Sdteskeoperate_on_bytes(struct dpv_file_node *file, int out)
155274116Sdteske{
156274116Sdteske	int progress;
157274116Sdteske	ssize_t r, w;
158274116Sdteske
159274116Sdteske	if (operate_common(file, out) < 0)
160274116Sdteske		return (-1);
161274116Sdteske
162274116Sdteske	/* [Re-]Fill the buffer */
163274116Sdteske	if ((r = read(fd, buf, bsize)) <= 0) {
164274116Sdteske		if (fd != STDIN_FILENO)
165274116Sdteske			close(fd);
166274116Sdteske		fd = -1;
167274116Sdteske		file->status = DPV_STATUS_DONE;
168274116Sdteske		return (100);
169274116Sdteske	}
170274116Sdteske
171274116Sdteske	/* [Re-]Dump the buffer */
172274116Sdteske	if (out >= 0) {
173274116Sdteske		if ((w = write(out, buf, r)) < 0) {
174274116Sdteske			end_dialog();
175274116Sdteske			err(EXIT_FAILURE, "output");
176274116Sdteske		}
177274116Sdteske		fsync(out);
178274116Sdteske	}
179274116Sdteske
180275040Sdteske	dpv_overall_read += r;
181274116Sdteske	file->read += r;
182274116Sdteske
183274116Sdteske	/* Calculate percentage of completion (if possible) */
184274116Sdteske	if (file->length >= 0) {
185274116Sdteske		progress = (file->read * 100 / (file->length > 0 ?
186274116Sdteske		    file->length : 1));
187274116Sdteske
188274116Sdteske		/* If no_overrun, do not return 100% until read >= length */
189274116Sdteske		if (no_overrun && progress == 100 && file->read < file->length)
190274116Sdteske			progress--;
191274116Sdteske
192274116Sdteske		return (progress);
193274116Sdteske	} else
194274116Sdteske		return (-1);
195274116Sdteske}
196274116Sdteske
197274116Sdteskestatic int
198274116Sdteskeoperate_on_lines(struct dpv_file_node *file, int out)
199274116Sdteske{
200274116Sdteske	char *p;
201274116Sdteske	int progress;
202274116Sdteske	ssize_t r, w;
203274116Sdteske
204274116Sdteske	if (operate_common(file, out) < 0)
205274116Sdteske		return (-1);
206274116Sdteske
207274116Sdteske	/* [Re-]Fill the buffer */
208274116Sdteske	if ((r = read(fd, buf, bsize)) <= 0) {
209274116Sdteske		if (fd != STDIN_FILENO)
210274116Sdteske			close(fd);
211274116Sdteske		fd = -1;
212274116Sdteske		file->status = DPV_STATUS_DONE;
213274116Sdteske		return (100);
214274116Sdteske	}
215274116Sdteske	buf[r] = '\0';
216274116Sdteske
217274116Sdteske	/* [Re-]Dump the buffer */
218274116Sdteske	if (out >= 0) {
219274116Sdteske		if ((w = write(out, buf, r)) < 0) {
220274116Sdteske			end_dialog();
221274116Sdteske			err(EXIT_FAILURE, "output");
222274116Sdteske		}
223274116Sdteske		fsync(out);
224274116Sdteske	}
225274116Sdteske
226274116Sdteske	/* Process the buffer for number of lines */
227274116Sdteske	for (p = buf; p != NULL && *p != '\0';)
228274116Sdteske		if ((p = strchr(p, '\n')) != NULL)
229275040Sdteske			dpv_overall_read++, p++, file->read++;
230274116Sdteske
231274116Sdteske	/* Calculate percentage of completion (if possible) */
232274116Sdteske	if (file->length >= 0) {
233274116Sdteske		progress = (file->read * 100 / file->length);
234274116Sdteske
235274116Sdteske		/* If no_overrun, do not return 100% until read >= length */
236274116Sdteske		if (no_overrun && progress == 100 && file->read < file->length)
237274116Sdteske			progress--;
238274116Sdteske
239274116Sdteske		return (progress);
240274116Sdteske	} else
241274116Sdteske		return (-1);
242274116Sdteske}
243274116Sdteske
244274116Sdteske/*
245274116Sdteske * Takes a list of names that are to correspond to input streams coming from
246274116Sdteske * stdin or fifos and produces necessary config to drive dpv(3) `--gauge'
247274116Sdteske * widget. If the `-d' flag is used, output is instead send to terminal
248274116Sdteske * standard output (and the output can then be saved to a file, piped into
249274116Sdteske * custom [X]dialog(1) invocation, or whatever.
250274116Sdteske */
251274116Sdteskeint
252274116Sdteskemain(int argc, char *argv[])
253274116Sdteske{
254274116Sdteske	char dummy;
255274116Sdteske	int ch;
256274116Sdteske	int n = 0;
257274116Sdteske	size_t config_size = sizeof(struct dpv_config);
258274116Sdteske	size_t file_node_size = sizeof(struct dpv_file_node);
259274116Sdteske	struct dpv_config *config;
260274116Sdteske	struct dpv_file_node *curfile;
261274116Sdteske	struct sigaction act;
262274116Sdteske
263274116Sdteske	pgm = argv[0]; /* store a copy of invocation name */
264274116Sdteske
265274116Sdteske	/* Allocate config structure */
266274116Sdteske	if ((config = malloc(config_size)) == NULL)
267274116Sdteske		errx(EXIT_FAILURE, "Out of memory?!");
268274116Sdteske	memset((void *)(config), '\0', config_size);
269274116Sdteske
270274116Sdteske	/*
271274116Sdteske	 * Process command-line options
272274116Sdteske	 */
273274116Sdteske	while ((ch = getopt(argc, argv,
274295110Sdteske	    "a:b:dDhi:I:klL:mn:No:p:P:t:TU:wx:X")) != -1) {
275274116Sdteske		switch(ch) {
276274116Sdteske		case 'a': /* additional message text to append */
277274116Sdteske			if (config->aprompt == NULL) {
278274116Sdteske				config->aprompt = malloc(DPV_APROMPT_MAX);
279274116Sdteske				if (config->aprompt == NULL)
280274116Sdteske					errx(EXIT_FAILURE, "Out of memory?!");
281274116Sdteske			}
282274116Sdteske			snprintf(config->aprompt, DPV_APROMPT_MAX, "%s",
283274116Sdteske			    optarg);
284274116Sdteske			break;
285274116Sdteske		case 'b': /* [X]dialog(1) backtitle */
286274116Sdteske			if (config->backtitle != NULL)
287274116Sdteske				free((char *)config->backtitle);
288274116Sdteske			config->backtitle = malloc(strlen(optarg) + 1);
289274116Sdteske			if (config->backtitle == NULL)
290274116Sdteske				errx(EXIT_FAILURE, "Out of memory?!");
291274116Sdteske			*(config->backtitle) = '\0';
292274116Sdteske			strcat(config->backtitle, optarg);
293274116Sdteske			break;
294274116Sdteske		case 'd': /* debugging */
295274116Sdteske			debug = TRUE;
296274116Sdteske			config->debug = debug;
297274116Sdteske			break;
298274116Sdteske		case 'D': /* use dialog(1) instead of libdialog */
299274116Sdteske			config->display_type = DPV_DISPLAY_DIALOG;
300274116Sdteske			break;
301274116Sdteske		case 'h': /* help/usage */
302274116Sdteske			usage();
303274116Sdteske			break; /* NOTREACHED */
304274116Sdteske		case 'i': /* status line format string for single-file */
305274116Sdteske			config->status_solo = optarg;
306274116Sdteske			break;
307274116Sdteske		case 'I': /* status line format string for many-files */
308274116Sdteske			config->status_many = optarg;
309274116Sdteske			break;
310295110Sdteske		case 'k': /* keep tite */
311295110Sdteske			config->keep_tite = TRUE;
312295110Sdteske			break;
313274116Sdteske		case 'l': /* Line mode */
314274116Sdteske			line_mode = TRUE;
315274116Sdteske			break;
316274116Sdteske		case 'L': /* custom label size */
317274116Sdteske			config->label_size =
318274116Sdteske			    (int)strtol(optarg, (char **)NULL, 10);
319274116Sdteske			if (config->label_size == 0 && errno == EINVAL)
320274116Sdteske				errx(EXIT_FAILURE,
321274116Sdteske				    "`-L' argument must be numeric");
322274116Sdteske			else if (config->label_size < -1)
323274116Sdteske				config->label_size = -1;
324274116Sdteske			break;
325274116Sdteske		case 'm': /* enable multiple file arguments */
326274116Sdteske			multiple = TRUE;
327274116Sdteske			break;
328274116Sdteske		case 'o': /* `-o path' for sending data-read to file */
329274116Sdteske			output_type = DPV_OUTPUT_FILE;
330274116Sdteske			config->output_type = DPV_OUTPUT_FILE;
331274116Sdteske			config->output = optarg;
332274116Sdteske			break;
333274116Sdteske		case 'n': /* custom number of files per `page' */
334274116Sdteske			config->display_limit =
335274116Sdteske				(int)strtol(optarg, (char **)NULL, 10);
336274116Sdteske			if (config->display_limit == 0 && errno == EINVAL)
337274116Sdteske				errx(EXIT_FAILURE,
338274116Sdteske				    "`-n' argument must be numeric");
339274116Sdteske			else if (config->display_limit < 0)
340274116Sdteske				config->display_limit = -1;
341274116Sdteske			break;
342274116Sdteske		case 'N': /* No overrun (truncate reads of known-length) */
343274116Sdteske			no_overrun = TRUE;
344274116Sdteske			config->options |= DPV_NO_OVERRUN;
345274116Sdteske			break;
346274116Sdteske		case 'p': /* additional message text to use as prefix */
347274116Sdteske			if (config->pprompt == NULL) {
348274116Sdteske				config->pprompt = malloc(DPV_PPROMPT_MAX + 2);
349274116Sdteske				if (config->pprompt == NULL)
350274116Sdteske					errx(EXIT_FAILURE, "Out of memory?!");
351274116Sdteske				/* +2 is for implicit "\n" appended later */
352274116Sdteske			}
353274116Sdteske			snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s",
354274116Sdteske			    optarg);
355274116Sdteske			break;
356274116Sdteske		case 'P': /* custom size for mini-progressbar */
357274116Sdteske			config->pbar_size =
358274116Sdteske			    (int)strtol(optarg, (char **)NULL, 10);
359274116Sdteske			if (config->pbar_size == 0 && errno == EINVAL)
360274116Sdteske				errx(EXIT_FAILURE,
361274116Sdteske				    "`-P' argument must be numeric");
362274116Sdteske			else if (config->pbar_size < -1)
363274116Sdteske				config->pbar_size = -1;
364274116Sdteske			break;
365274116Sdteske		case 't': /* [X]dialog(1) title */
366274116Sdteske			if (config->title != NULL)
367274116Sdteske				free(config->title);
368274116Sdteske			config->title = malloc(strlen(optarg) + 1);
369274116Sdteske			if (config->title == NULL)
370274116Sdteske				errx(EXIT_FAILURE, "Out of memory?!");
371274116Sdteske			*(config->title) = '\0';
372274116Sdteske			strcat(config->title, optarg);
373274116Sdteske			break;
374274116Sdteske		case 'T': /* test mode (don't read data, fake it) */
375274116Sdteske			config->options |= DPV_TEST_MODE;
376274116Sdteske			break;
377274116Sdteske		case 'U': /* updates per second */
378274116Sdteske			config->status_updates_per_second =
379274116Sdteske			    (int)strtol(optarg, (char **)NULL, 10);
380274116Sdteske			if (config->status_updates_per_second == 0 &&
381274116Sdteske			    errno == EINVAL)
382274116Sdteske				errx(EXIT_FAILURE,
383274116Sdteske				    "`-U' argument must be numeric");
384274116Sdteske			break;
385274116Sdteske		case 'w': /* `-p' and `-a' widths bump [X]dialog(1) width */
386274116Sdteske			config->options |= DPV_WIDE_MODE;
387274116Sdteske			break;
388274116Sdteske		case 'x': /* `-x cmd' for sending data-read to sh(1) code */
389274116Sdteske			output_type = DPV_OUTPUT_SHELL;
390274116Sdteske			config->output_type = DPV_OUTPUT_SHELL;
391274116Sdteske			config->output = optarg;
392274116Sdteske			break;
393274116Sdteske		case 'X': /* X11 support through x11/xdialog */
394274116Sdteske			config->display_type = DPV_DISPLAY_XDIALOG;
395274116Sdteske			break;
396274116Sdteske		case '?': /* unknown argument (based on optstring) */
397274116Sdteske			/* FALLTHROUGH */
398274116Sdteske		default: /* unhandled argument (based on switch) */
399274116Sdteske			usage();
400274116Sdteske			/* NOTREACHED */
401274116Sdteske		}
402274116Sdteske	}
403274116Sdteske	argc -= optind;
404274116Sdteske	argv += optind;
405274116Sdteske
406274116Sdteske	/* Process remaining arguments as list of names to display */
407274116Sdteske	for (curfile = file_list; n < argc; n++) {
408274116Sdteske		nfiles++;
409274116Sdteske
410274116Sdteske		/* Allocate a new struct for the file argument */
411274116Sdteske		if (curfile == NULL) {
412274116Sdteske			if ((curfile = malloc(file_node_size)) == NULL)
413274116Sdteske				errx(EXIT_FAILURE, "Out of memory?!");
414274116Sdteske			memset((void *)(curfile), '\0', file_node_size);
415274116Sdteske			file_list = curfile;
416274116Sdteske		} else {
417274116Sdteske			if ((curfile->next = malloc(file_node_size)) == NULL)
418274116Sdteske				errx(EXIT_FAILURE, "Out of memory?!");
419274116Sdteske			memset((void *)(curfile->next), '\0', file_node_size);
420274116Sdteske			curfile = curfile->next;
421274116Sdteske		}
422274116Sdteske		curfile->name = argv[n];
423274116Sdteske
424274116Sdteske		/* Read possible `lines:' prefix from label syntax */
425274116Sdteske		if (sscanf(curfile->name, "%lli:%c", &(curfile->length),
426274116Sdteske		    &dummy) == 2)
427274116Sdteske			curfile->name = strchr(curfile->name, ':') + 1;
428274116Sdteske		else
429274116Sdteske			curfile->length = -1;
430274116Sdteske
431274116Sdteske		/* Read path argument if enabled */
432274116Sdteske		if (multiple) {
433274116Sdteske			if (++n >= argc)
434274116Sdteske				errx(EXIT_FAILURE, "Missing path argument "
435274116Sdteske				    "for label number %i", nfiles);
436274116Sdteske			curfile->path = argv[n];
437274116Sdteske		} else
438274116Sdteske			break;
439274116Sdteske	}
440274116Sdteske
441274116Sdteske	/* Display usage and exit if not given at least one name */
442274116Sdteske	if (nfiles == 0) {
443274116Sdteske		warnx("no labels provided");
444274116Sdteske		usage();
445274116Sdteske		/* NOTREACHED */
446274116Sdteske	}
447274116Sdteske
448274116Sdteske	/*
449274116Sdteske	 * Set cleanup routine for Ctrl-C action
450274116Sdteske	 */
451274116Sdteske	if (config->display_type == DPV_DISPLAY_LIBDIALOG) {
452274116Sdteske		act.sa_handler = sig_int;
453274116Sdteske		sigaction(SIGINT, &act, 0);
454274116Sdteske	}
455274116Sdteske
456274116Sdteske	/* Set status formats and action */
457274116Sdteske	if (line_mode) {
458274116Sdteske		config->status_solo = LINE_STATUS_SOLO;
459274116Sdteske		config->status_many = LINE_STATUS_SOLO;
460274116Sdteske		config->action = operate_on_lines;
461274116Sdteske	} else {
462274116Sdteske		config->status_solo = BYTE_STATUS_SOLO;
463274116Sdteske		config->status_many = BYTE_STATUS_SOLO;
464274116Sdteske		config->action = operate_on_bytes;
465274116Sdteske	}
466274116Sdteske
467274116Sdteske	/*
468274116Sdteske	 * Hand off to dpv(3)...
469274116Sdteske	 */
470274116Sdteske	if (dpv(config, file_list) != 0 && debug)
471274116Sdteske		warnx("dpv(3) returned error!?");
472274116Sdteske
473295110Sdteske	if (!config->keep_tite)
474295110Sdteske		end_dialog();
475274116Sdteske	dpv_free();
476274116Sdteske
477274116Sdteske	exit(EXIT_SUCCESS);
478274116Sdteske}
479274116Sdteske
480274116Sdteske/*
481274116Sdteske * Interrupt handler to indicate we received a Ctrl-C interrupt.
482274116Sdteske */
483274116Sdteskestatic void
484274116Sdteskesig_int(int sig __unused)
485274116Sdteske{
486274116Sdteske	dpv_interrupt = TRUE;
487274116Sdteske}
488274116Sdteske
489274116Sdteske/*
490274116Sdteske * Print short usage statement to stderr and exit with error status.
491274116Sdteske */
492274116Sdteskestatic void
493274116Sdteskeusage(void)
494274116Sdteske{
495274116Sdteske
496274116Sdteske	if (debug) /* No need for usage */
497274116Sdteske		exit(EXIT_FAILURE);
498274116Sdteske
499274116Sdteske	fprintf(stderr, "Usage: %s [options] bytes:label\n", pgm);
500274116Sdteske	fprintf(stderr, "       %s [options] -m bytes1:label1 path1 "
501274116Sdteske	    "[bytes2:label2 path2 ...]\n", pgm);
502274116Sdteske	fprintf(stderr, "OPTIONS:\n");
503274116Sdteske#define OPTFMT "\t%-14s %s\n"
504274116Sdteske	fprintf(stderr, OPTFMT, "-a text",
505274116Sdteske	    "Append text. Displayed below file progress indicators.");
506274116Sdteske	fprintf(stderr, OPTFMT, "-b backtitle",
507274116Sdteske	    "String to be displayed on the backdrop, at top-left.");
508274116Sdteske	fprintf(stderr, OPTFMT, "-d",
509274116Sdteske	    "Debug. Write to standard output instead of dialog.");
510274116Sdteske	fprintf(stderr, OPTFMT, "-D",
511274116Sdteske	    "Use dialog(1) instead of dialog(3) [default].");
512274116Sdteske	fprintf(stderr, OPTFMT, "-h",
513274116Sdteske	    "Produce this output on standard error and exit.");
514274116Sdteske	fprintf(stderr, OPTFMT, "-i format",
515274116Sdteske	    "Customize status line format. See fdpv(1) for details.");
516274116Sdteske	fprintf(stderr, OPTFMT, "-I format",
517274116Sdteske	    "Customize status line format. See fdpv(1) for details.");
518274116Sdteske	fprintf(stderr, OPTFMT, "-L size",
519274116Sdteske	    "Label size. Must be a number greater than 0, or -1.");
520274116Sdteske	fprintf(stderr, OPTFMT, "-m",
521274116Sdteske	    "Enable processing of multiple file argiments.");
522274116Sdteske	fprintf(stderr, OPTFMT, "-n num",
523274116Sdteske	    "Display at-most num files per screen. Default is -1.");
524274116Sdteske	fprintf(stderr, OPTFMT, "-N",
525274116Sdteske	    "No overrun. Stop reading input at stated length, if any.");
526274116Sdteske	fprintf(stderr, OPTFMT, "-o file",
527274116Sdteske	    "Output data to file. First %s replaced with label text.");
528274116Sdteske	fprintf(stderr, OPTFMT, "-p text",
529274116Sdteske	    "Prefix text. Displayed above file progress indicators.");
530274116Sdteske	fprintf(stderr, OPTFMT, "-P size",
531274116Sdteske	    "Mini-progressbar size. Must be a number greater than 3.");
532274116Sdteske	fprintf(stderr, OPTFMT, "-t title",
533274116Sdteske	    "Title string to be displayed at top of dialog(1) box.");
534274116Sdteske	fprintf(stderr, OPTFMT, "-T",
535274116Sdteske	    "Test mode. Don't actually read any data, but fake it.");
536274116Sdteske	fprintf(stderr, OPTFMT, "-U num",
537274116Sdteske	    "Update status line num times per-second. Default is 2.");
538274116Sdteske	fprintf(stderr, OPTFMT, "-w",
539274116Sdteske	    "Wide. Width of `-p' and `-a' text bump dialog(1) width.");
540274116Sdteske	fprintf(stderr, OPTFMT, "-x cmd",
541274116Sdteske	    "Send data to executed cmd. First %s replaced with label.");
542274116Sdteske	fprintf(stderr, OPTFMT, "-X",
543274116Sdteske	    "X11. Use Xdialog(1) instead of dialog(1).");
544274116Sdteske	exit(EXIT_FAILURE);
545274116Sdteske}
546