From 7230e71362ae2a3c943894b53dae6718344f1661 Mon Sep 17 00:00:00 2001 From: dteske Date: Tue, 25 Nov 2014 13:47:53 +0000 Subject: MFC r274116: Add new libraries/utilities for data throughput visualization. dpv(3): dialog progress view library dpv(1): stream data from stdin or multiple paths with dialog progress view figpar(3): configuration file parsing library MFC r274120, r274121, r274123, r274124, r274144, r274146, r274159, r274192, r274203, r274209, r274226, r274270, and r274851: Fixes following r274116 Reviews: D714 Relnotes: New libdpv/libfigpar and dpv(1) utility Reviewed by: jelischer, shurd Discussed at: MeetBSD California 2014 Vendor/Dev Summit Discussed on: -current Thanks to: ngie, ian, jelischer, shurd, bapt --- usr.bin/dpv/Makefile | 12 ++ usr.bin/dpv/dpv.1 | 430 +++++++++++++++++++++++++++++++++++++++ usr.bin/dpv/dpv.c | 541 +++++++++++++++++++++++++++++++++++++++++++++++++ usr.bin/dpv/dpv_util.h | 68 +++++++ 4 files changed, 1051 insertions(+) create mode 100644 usr.bin/dpv/Makefile create mode 100644 usr.bin/dpv/dpv.1 create mode 100644 usr.bin/dpv/dpv.c create mode 100644 usr.bin/dpv/dpv_util.h (limited to 'usr.bin/dpv') diff --git a/usr.bin/dpv/Makefile b/usr.bin/dpv/Makefile new file mode 100644 index 0000000..6f89c0f --- /dev/null +++ b/usr.bin/dpv/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PROG= dpv + +CFLAGS+= -I${.CURDIR} + +DPADD= ${LIBDPV} ${LIBDIALOG} ${LIBFIGPAR} ${LIBNCURSESW} ${LIBUTIL} ${LIBM} +LDADD= -ldpv -ldialog -lfigpar -lncursesw -lutil -lm + +WARNS?= 6 + +.include diff --git a/usr.bin/dpv/dpv.1 b/usr.bin/dpv/dpv.1 new file mode 100644 index 0000000..c8d321b --- /dev/null +++ b/usr.bin/dpv/dpv.1 @@ -0,0 +1,430 @@ +.\" Copyright (c) 2013-2014 Devin Teske +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd Sep 7, 2014 +.Dt DPV 1 +.Os +.Sh NAME +.Nm dpv +.Nd stream data from stdin or multiple paths with dialog progress view +.Sh SYNOPSIS +.Nm +.Op options +.Ar [bytes:]label +.Nm +.Op options +.Fl m +.Ar [bytes1:]label1 +.Ar path1 +.Op Ar [bytes2:]label2 path2 ... +.Sh DESCRIPTION +.Nm +provides a dialog progress view, allowing a user to see current throughput rate +and total data transferred for one or more streams. +.Pp +The +.Nm +utility has two main modes for processing input. +.Pp +The default input mode, without +.Ql Fl m , +.Nm +reads bytes from standard input. +A label for the data must be provided. +.Pp +The secondary input mode, with +.Ql Fl m , +.Nm +reads multiple paths +.Pq up to 2047 or Dq ARG_MAX/2-1 , +sequentially. +.Pp +Data read in either mode is either thrown away +.Pq default , +sent to a spawned instance of the program specified via +.Ql Fl x Ar cmd , +or sent to a unique file specified by +.Ql Fl o Ar file . +.Pp +With or without +.Ql Fl m , +progress is displayed using one of +.Xr dialog 3 +.Pq default , +.Xr dialog 1 +.Pq see Ql Fl D , +or instead +.Xr Xdialog 1 +.Pq see Ql Fl X . +.Pp +The following options are available: +.Bl -tag -width ".Fl b Ar backtitle" +.It Fl a Ar text +Display +.Ar text +below the file progress indicator(s). +.It Fl b Ar backtitle +Display +.Ar backtitle +on the backdrop, at top-left, behind the dialog widget. +When using +.Xr Xdialog 1 , +this is displayed inside the window +.Pq at the top +followed by a separator line. +.It Fl d +Debug mode. +Print dialog prompt data to standard out and provide additional debugging on +standard error. +.It Fl D +Do not use the default interface of +.Xr dialog 3 , +but instead spawn an instance of +.Xr dialog 1 . +The path to +.Xr dialog 1 +is taken from the +.Ev DIALOG +environment variable or simply +.Dq Li dialog +if unset or NULL. +.It Fl h +Produce a short syntax usage with brief option descriptions and exit. +Output is produced on standard error. +.It Fl i Ar format +Customize the single-file format string used to update the status line. +Ignored when using either +.Ql Fl D +or +.Ql Fl X +which lack the ability to display the status line +.Pq containing bytes/rate/thread information . +Default value +is +.Dq Li %'10lli bytes read @ %'9.1f bytes/sec. . +This format is used when handling one file. +.It Fl I Ar format +Customize the multi-file format string used to update the status line. +Ignored when using either +.Ql Fl D +or +.Ql Fl X +which lack the ability to display the status line +.Pq containing bytes/rate/thread information . +Default value +is +.Dq Li %'10lli bytes read @ %'9.1f bytes/sec. [%i/%i busy/wait] . +This format is used when handling more than one file. +.It Fl l +Line mode. Read lines from input instead of bytes. +.It Fl L Ar size +Label size. +If negative, shrink to longest label width. +.It Fl m +Multi-input mode. +Instead of reading bytes from standard input, read from a set of paths +.Pq one for each label . +By default, each path is processed sequentially in the order given. +.It Fl n Ar num +Display at-most +.Ar num +progress indicators per screen. +If zero, display as many as possible. +If negative, only display the main progress indicator. +Default is 0. +Maximum value is 10. +.It Fl N +No overrun. +If enabled, stop reading known-length inputs when input reaches stated length. +.It Fl o Ar file +Output data to +.Ar file . +The first occurrence of +.Ql %s +.Pq if any +in +.Ql Ar file +will be replaced with the +.Ar label +text. +.It Fl p Ar text +Display +.Ar text +above the file progress indicator(s). +.It Fl P Ar size +Mini-progressbar size. +If negative, don't display mini-progressbars +.Pq only the large overall progress indicator is shown . +If zero, auto-adjust based on number of files to read. +When zero and only one file to read, defaults to -1. +When zero and more than one file to read, defaults to 17. +.It Fl t Ar title +Display +.Ar title +atop the dialog box. +Note that if you use this option at the same time as +.Ql Fl X +and +.Ql Fl b Ar backtitle , +the +.Ar backtitle +and +.Ar title +are effectively switched +.Pq see BUGS section below . +.It Fl T +Test mode. +Simulate reading a number of bytes, divided evenly across the number of files, +while stepping through each percent value of each file to process. +Appends +.Dq Li [TEST MODE] +to the status line +.Pq to override, use Ql Fl u Ar format . +No data is actually read. +.It Fl U Ar num +Update status line +.Ar num +times per-second. +Default value is +.Ql Li 2 . +A value of +.Ql Li 0 +disables status line updates. +If negative, update the status line as fast as possible. +Ignored when using either +.Ql Fl D +or +.Ql Fl X +which lack the ability to display the status line +.Pq containing bytes/rate/thread information . +.It Fl w +Wide mode. +Allows long +.Ar text +arguments used with +.Ql Fl p +and +.Ql Fl a +to bump the dialog width. +Prompts wider than the maximum width will wrap +.Pq unless using Xr Xdialog 1 ; see BUGS section below . +.It Fl x Ar cmd +Execute +.Ar cmd +.Pq via Xr sh 1 +and send it data that has been read. +Data is available to +.Ar cmd +on standard input. +With +.Ql Fl m , +.Ar cmd +is executed once for each +.Ar path +argument. +The first occurrence of +.Ql %s +.Pq if any +in +.Ql Ar cmd +will be replaced with the +.Ar label +text. +.It Fl X +Enable X11 mode by using +.Xr Xdialog 1 +instead of +.Xr dialog 1 +or +.Xr dialog 3 . +.El +.Sh ENVIRONMENT +The following environment variables are referenced by +.Nm : +.Bl -tag -width ".Ev USE_COLOR" +.It Ev DIALOG +Override command string used to launch +.Xr dialog 1 +.Pq requires Ql Fl D +or +.Xr Xdialog 1 +.Pq requires Ql Fl X ; +default is either +.Ql dialog +.Pq for Ql Fl D +or +.Ql Xdialog +.Pq for Ql Fl X . +.It Ev DIALOGRC +If set and non-NULL, path to +.Ql .dialogrc +file. +.It Ev HOME +If +.Ql Ev $DIALOGRC +is either not set or NULL, used as a prefix to +.Ql .dialogrc +.Pq i.e., Ql $HOME/.dialogrc . +.It Ev USE_COLOR +If set and NULL, disables the use of color when using +.Xr dialog 1 +.Pq does not apply to Xr Xdialog 1 . +.El +.Sh DEPENDENCIES +If using +.Ql Fl D , +.Xr dialog 1 +is required. +.Pp +If using +.Ql Fl X , +.Xr Xdialog 1 +is required. +.Sh FILES +.Bl -tag -width ".Pa $HOME/.dialogrc" -compact +.It Pa $HOME/.dialogrc +.El +.Sh EXAMPLES +.Pp +Simple example to show how fast +.Xr yes 1 +produces lines +.Pq usually about ten-million per-second; your results may vary : +.Bd -literal -offset indent +yes | dpv -l yes +.Ed +.Pp +Display progress while timing how long it takes +.Xr yes 1 +to produce a half-billion lines +.Pq usually under one minute; your results may vary : +.Bd -literal -offset indent +time yes | dpv -Nl 500000000:yes +.Ed +.Pp +An example to watch how quickly a file is transferred using +.Xr nc 1 : +.Bd -literal -offset indent +dpv -x "nc -w 1 somewhere.com 3000" -m label file +.Ed +.Pp +A similar example, transferring a file from another process and passing the +expected size to +.Nm : +.Bd -literal -offset indent +cat file | dpv -x "nc -w 1 somewhere.com 3000" 12345:label +.Ed +.Pp +A more complicated example: +.Bd -literal -offset indent +tar cf - . | dpv -x "gzip -9 > out.tgz" \\ + $( du -s . | awk '{print $1 * 1024}' ):label +.Ed +.Pp +Taking an image of a disk: +.Bd -literal -offset indent +dpv -o disk-image.img -m label /dev/ada0 +.Ed +.Pp +Writing an image back to a disk: +.Bd -literal -offset indent +dpv -o /dev/ada0 -m label disk-image.img +.Ed +.Pp +Zeroing a disk: +.Bd -literal -offset indent +dpv -o /dev/md42 < /dev/zero +.Ed +.Pp +.Sh BUGS +.Xr Xdialog 1 , +when given both +.Ql Fl -title Ar title +.Pq see above Ql Fl t Ar title +and +.Ql Fl -backtitle Ar backtitle +.Pq see above Ql Fl b Ar backtitle , +displays the backtitle in place of the title and vice-versa. +.Pp +.Xr Xdialog 1 +does not wrap long prompt texts received after initial launch. +This is a known issue with the +.Ql --gauge +widget in +.Xr Xdialog 1 . +.Pp +.Xr dialog 1 +does not display the first character after a series of escaped escape-sequences +(e.g., ``\\\\n'' produces ``\\'' instead of ``\\n''). +This is a known issue with +.Xr dialog 1 +and does not affect +.Xr dialog 3 +or +.Xr Xdialog 1 . +.Pp +If your application ignores +.Ev USE_COLOR +when set and NULL before calling +.Xr dpv 1 +with color escape sequences anyway, +.Xr dialog 3 +and +.Xr dialog 1 +may not render properly. +Workaround is to detect when +.Ev USE_COLOR +is set and NULL and either not use color escape sequences at that time or use +.Xr unset 1 +.Xr [ sh 1 ] +or +.Xr unsetenv 1 +.Xr [ csh 1 ] +to unset +.Ev USE_COLOR , +forcing interpretation of color sequences. +This does not effect +.Xr Xdialog 1 , +which renders the color escape sequences as plain text. +See +.Do Li +embedded "\\Z" sequences +.Dc +in +.Xr dialog 1 +for additional information. +.Sh SEE ALSO +.Xr dialog 1 , +.Xr dialog 3 , +.Xr sh 1 , +.Xr Xdialog 1 +.Sh HISTORY +A +.Nm +utility first appeared in +.Fx 11.0 . +.Sh AUTHORS +.An Devin Teske Aq dteske@FreeBSD.org diff --git a/usr.bin/dpv/dpv.c b/usr.bin/dpv/dpv.c new file mode 100644 index 0000000..4e006f6 --- /dev/null +++ b/usr.bin/dpv/dpv.c @@ -0,0 +1,541 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dpv_util.h" + +/* Debugging */ +static uint8_t debug = FALSE; + +/* Data to process */ +static struct dpv_file_node *file_list = NULL; +static unsigned int nfiles = 0; + +/* Data processing */ +static uint8_t line_mode = FALSE; +static uint8_t no_overrun = FALSE; +static char *buf = NULL; +static int fd = -1; +static int output_type = DPV_OUTPUT_NONE; +static size_t bsize; +static char rpath[PATH_MAX]; + +/* Extra display information */ +static uint8_t multiple = FALSE; /* `-m' */ +static char *pgm; /* set to argv[0] by main() */ + +/* Function prototypes */ +static void sig_int(int sig); +static void usage(void); +int main(int argc, char *argv[]); +static int operate_common(struct dpv_file_node *file, int out); +static int operate_on_bytes(struct dpv_file_node *file, int out); +static int operate_on_lines(struct dpv_file_node *file, int out); + +static int +operate_common(struct dpv_file_node *file, int out) +{ + struct stat sb; + + /* Open the file if necessary */ + if (fd < 0) { + if (multiple) { + /* Resolve the file path and attempt to open it */ + if (realpath(file->path, rpath) == 0 || + (fd = open(rpath, O_RDONLY)) < 0) { + warn("%s", file->path); + file->status = DPV_STATUS_FAILED; + return (-1); + } + } else { + /* Assume stdin, but if that's a TTY instead use the + * highest numbered file descriptor (obtained by + * generating new fd and then decrementing). + * + * NB: /dev/stdin should always be open(2)'able + */ + fd = STDIN_FILENO; + if (isatty(fd)) { + fd = open("/dev/stdin", O_RDONLY); + close(fd--); + } + + /* This answer might be wrong, if dpv(3) has (by + * request) opened an output file or pipe. If we + * told dpv(3) to open a file, subtract one from + * previous answer. If instead we told dpv(3) to + * prepare a pipe output, subtract two. + */ + switch(output_type) { + case DPV_OUTPUT_FILE: + fd -= 1; + break; + case DPV_OUTPUT_SHELL: + fd -= 2; + break; + } + } + } + + /* Allocate buffer if necessary */ + if (buf == NULL) { + /* Use output block size as buffer size if available */ + if (out >= 0) { + if (fstat(out, &sb) != 0) { + warn("%i", out); + file->status = DPV_STATUS_FAILED; + return (-1); + } + if (S_ISREG(sb.st_mode)) { + if (sysconf(_SC_PHYS_PAGES) > + PHYSPAGES_THRESHOLD) + bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); + else + bsize = BUFSIZE_SMALL; + } else + bsize = MAX(sb.st_blksize, + (blksize_t)sysconf(_SC_PAGESIZE)); + } else + bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); + + /* Attempt to allocate */ + if ((buf = malloc(bsize+1)) == NULL) { + end_dialog(); + err(EXIT_FAILURE, "Out of memory?!"); + } + } + + return (0); +} + +static int +operate_on_bytes(struct dpv_file_node *file, int out) +{ + int progress; + ssize_t r, w; + + if (operate_common(file, out) < 0) + return (-1); + + /* [Re-]Fill the buffer */ + if ((r = read(fd, buf, bsize)) <= 0) { + if (fd != STDIN_FILENO) + close(fd); + fd = -1; + file->status = DPV_STATUS_DONE; + return (100); + } + + /* [Re-]Dump the buffer */ + if (out >= 0) { + if ((w = write(out, buf, r)) < 0) { + end_dialog(); + err(EXIT_FAILURE, "output"); + } + fsync(out); + } + + dpv_overall_read += r; + file->read += r; + + /* Calculate percentage of completion (if possible) */ + if (file->length >= 0) { + progress = (file->read * 100 / (file->length > 0 ? + file->length : 1)); + + /* If no_overrun, do not return 100% until read >= length */ + if (no_overrun && progress == 100 && file->read < file->length) + progress--; + + return (progress); + } else + return (-1); +} + +static int +operate_on_lines(struct dpv_file_node *file, int out) +{ + char *p; + int progress; + ssize_t r, w; + + if (operate_common(file, out) < 0) + return (-1); + + /* [Re-]Fill the buffer */ + if ((r = read(fd, buf, bsize)) <= 0) { + if (fd != STDIN_FILENO) + close(fd); + fd = -1; + file->status = DPV_STATUS_DONE; + return (100); + } + buf[r] = '\0'; + + /* [Re-]Dump the buffer */ + if (out >= 0) { + if ((w = write(out, buf, r)) < 0) { + end_dialog(); + err(EXIT_FAILURE, "output"); + } + fsync(out); + } + + /* Process the buffer for number of lines */ + for (p = buf; p != NULL && *p != '\0';) + if ((p = strchr(p, '\n')) != NULL) + dpv_overall_read++, p++, file->read++; + + /* Calculate percentage of completion (if possible) */ + if (file->length >= 0) { + progress = (file->read * 100 / file->length); + + /* If no_overrun, do not return 100% until read >= length */ + if (no_overrun && progress == 100 && file->read < file->length) + progress--; + + return (progress); + } else + return (-1); +} + +/* + * Takes a list of names that are to correspond to input streams coming from + * stdin or fifos and produces necessary config to drive dpv(3) `--gauge' + * widget. If the `-d' flag is used, output is instead send to terminal + * standard output (and the output can then be saved to a file, piped into + * custom [X]dialog(1) invocation, or whatever. + */ +int +main(int argc, char *argv[]) +{ + char dummy; + int ch; + int n = 0; + size_t config_size = sizeof(struct dpv_config); + size_t file_node_size = sizeof(struct dpv_file_node); + struct dpv_config *config; + struct dpv_file_node *curfile; + struct sigaction act; + + pgm = argv[0]; /* store a copy of invocation name */ + + /* Allocate config structure */ + if ((config = malloc(config_size)) == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + memset((void *)(config), '\0', config_size); + + /* + * Process command-line options + */ + while ((ch = getopt(argc, argv, + "a:b:dDhi:I:lL:mn:No:p:P:t:TU:wx:X")) != -1) { + switch(ch) { + case 'a': /* additional message text to append */ + if (config->aprompt == NULL) { + config->aprompt = malloc(DPV_APROMPT_MAX); + if (config->aprompt == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + } + snprintf(config->aprompt, DPV_APROMPT_MAX, "%s", + optarg); + break; + case 'b': /* [X]dialog(1) backtitle */ + if (config->backtitle != NULL) + free((char *)config->backtitle); + config->backtitle = malloc(strlen(optarg) + 1); + if (config->backtitle == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + *(config->backtitle) = '\0'; + strcat(config->backtitle, optarg); + break; + case 'd': /* debugging */ + debug = TRUE; + config->debug = debug; + break; + case 'D': /* use dialog(1) instead of libdialog */ + config->display_type = DPV_DISPLAY_DIALOG; + break; + case 'h': /* help/usage */ + usage(); + break; /* NOTREACHED */ + case 'i': /* status line format string for single-file */ + config->status_solo = optarg; + break; + case 'I': /* status line format string for many-files */ + config->status_many = optarg; + break; + case 'l': /* Line mode */ + line_mode = TRUE; + break; + case 'L': /* custom label size */ + config->label_size = + (int)strtol(optarg, (char **)NULL, 10); + if (config->label_size == 0 && errno == EINVAL) + errx(EXIT_FAILURE, + "`-L' argument must be numeric"); + else if (config->label_size < -1) + config->label_size = -1; + break; + case 'm': /* enable multiple file arguments */ + multiple = TRUE; + break; + case 'o': /* `-o path' for sending data-read to file */ + output_type = DPV_OUTPUT_FILE; + config->output_type = DPV_OUTPUT_FILE; + config->output = optarg; + break; + case 'n': /* custom number of files per `page' */ + config->display_limit = + (int)strtol(optarg, (char **)NULL, 10); + if (config->display_limit == 0 && errno == EINVAL) + errx(EXIT_FAILURE, + "`-n' argument must be numeric"); + else if (config->display_limit < 0) + config->display_limit = -1; + break; + case 'N': /* No overrun (truncate reads of known-length) */ + no_overrun = TRUE; + config->options |= DPV_NO_OVERRUN; + break; + case 'p': /* additional message text to use as prefix */ + if (config->pprompt == NULL) { + config->pprompt = malloc(DPV_PPROMPT_MAX + 2); + if (config->pprompt == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + /* +2 is for implicit "\n" appended later */ + } + snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s", + optarg); + break; + case 'P': /* custom size for mini-progressbar */ + config->pbar_size = + (int)strtol(optarg, (char **)NULL, 10); + if (config->pbar_size == 0 && errno == EINVAL) + errx(EXIT_FAILURE, + "`-P' argument must be numeric"); + else if (config->pbar_size < -1) + config->pbar_size = -1; + break; + case 't': /* [X]dialog(1) title */ + if (config->title != NULL) + free(config->title); + config->title = malloc(strlen(optarg) + 1); + if (config->title == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + *(config->title) = '\0'; + strcat(config->title, optarg); + break; + case 'T': /* test mode (don't read data, fake it) */ + config->options |= DPV_TEST_MODE; + break; + case 'U': /* updates per second */ + config->status_updates_per_second = + (int)strtol(optarg, (char **)NULL, 10); + if (config->status_updates_per_second == 0 && + errno == EINVAL) + errx(EXIT_FAILURE, + "`-U' argument must be numeric"); + break; + case 'w': /* `-p' and `-a' widths bump [X]dialog(1) width */ + config->options |= DPV_WIDE_MODE; + break; + case 'x': /* `-x cmd' for sending data-read to sh(1) code */ + output_type = DPV_OUTPUT_SHELL; + config->output_type = DPV_OUTPUT_SHELL; + config->output = optarg; + break; + case 'X': /* X11 support through x11/xdialog */ + config->display_type = DPV_DISPLAY_XDIALOG; + break; + case '?': /* unknown argument (based on optstring) */ + /* FALLTHROUGH */ + default: /* unhandled argument (based on switch) */ + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + /* Process remaining arguments as list of names to display */ + for (curfile = file_list; n < argc; n++) { + nfiles++; + + /* Allocate a new struct for the file argument */ + if (curfile == NULL) { + if ((curfile = malloc(file_node_size)) == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + memset((void *)(curfile), '\0', file_node_size); + file_list = curfile; + } else { + if ((curfile->next = malloc(file_node_size)) == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + memset((void *)(curfile->next), '\0', file_node_size); + curfile = curfile->next; + } + curfile->name = argv[n]; + + /* Read possible `lines:' prefix from label syntax */ + if (sscanf(curfile->name, "%lli:%c", &(curfile->length), + &dummy) == 2) + curfile->name = strchr(curfile->name, ':') + 1; + else + curfile->length = -1; + + /* Read path argument if enabled */ + if (multiple) { + if (++n >= argc) + errx(EXIT_FAILURE, "Missing path argument " + "for label number %i", nfiles); + curfile->path = argv[n]; + } else + break; + } + + /* Display usage and exit if not given at least one name */ + if (nfiles == 0) { + warnx("no labels provided"); + usage(); + /* NOTREACHED */ + } + + /* + * Set cleanup routine for Ctrl-C action + */ + if (config->display_type == DPV_DISPLAY_LIBDIALOG) { + act.sa_handler = sig_int; + sigaction(SIGINT, &act, 0); + } + + /* Set status formats and action */ + if (line_mode) { + config->status_solo = LINE_STATUS_SOLO; + config->status_many = LINE_STATUS_SOLO; + config->action = operate_on_lines; + } else { + config->status_solo = BYTE_STATUS_SOLO; + config->status_many = BYTE_STATUS_SOLO; + config->action = operate_on_bytes; + } + + /* + * Hand off to dpv(3)... + */ + if (dpv(config, file_list) != 0 && debug) + warnx("dpv(3) returned error!?"); + + end_dialog(); + dpv_free(); + + exit(EXIT_SUCCESS); +} + +/* + * Interrupt handler to indicate we received a Ctrl-C interrupt. + */ +static void +sig_int(int sig __unused) +{ + dpv_interrupt = TRUE; +} + +/* + * Print short usage statement to stderr and exit with error status. + */ +static void +usage(void) +{ + + if (debug) /* No need for usage */ + exit(EXIT_FAILURE); + + fprintf(stderr, "Usage: %s [options] bytes:label\n", pgm); + fprintf(stderr, " %s [options] -m bytes1:label1 path1 " + "[bytes2:label2 path2 ...]\n", pgm); + fprintf(stderr, "OPTIONS:\n"); +#define OPTFMT "\t%-14s %s\n" + fprintf(stderr, OPTFMT, "-a text", + "Append text. Displayed below file progress indicators."); + fprintf(stderr, OPTFMT, "-b backtitle", + "String to be displayed on the backdrop, at top-left."); + fprintf(stderr, OPTFMT, "-d", + "Debug. Write to standard output instead of dialog."); + fprintf(stderr, OPTFMT, "-D", + "Use dialog(1) instead of dialog(3) [default]."); + fprintf(stderr, OPTFMT, "-h", + "Produce this output on standard error and exit."); + fprintf(stderr, OPTFMT, "-i format", + "Customize status line format. See fdpv(1) for details."); + fprintf(stderr, OPTFMT, "-I format", + "Customize status line format. See fdpv(1) for details."); + fprintf(stderr, OPTFMT, "-L size", + "Label size. Must be a number greater than 0, or -1."); + fprintf(stderr, OPTFMT, "-m", + "Enable processing of multiple file argiments."); + fprintf(stderr, OPTFMT, "-n num", + "Display at-most num files per screen. Default is -1."); + fprintf(stderr, OPTFMT, "-N", + "No overrun. Stop reading input at stated length, if any."); + fprintf(stderr, OPTFMT, "-o file", + "Output data to file. First %s replaced with label text."); + fprintf(stderr, OPTFMT, "-p text", + "Prefix text. Displayed above file progress indicators."); + fprintf(stderr, OPTFMT, "-P size", + "Mini-progressbar size. Must be a number greater than 3."); + fprintf(stderr, OPTFMT, "-t title", + "Title string to be displayed at top of dialog(1) box."); + fprintf(stderr, OPTFMT, "-T", + "Test mode. Don't actually read any data, but fake it."); + fprintf(stderr, OPTFMT, "-U num", + "Update status line num times per-second. Default is 2."); + fprintf(stderr, OPTFMT, "-w", + "Wide. Width of `-p' and `-a' text bump dialog(1) width."); + fprintf(stderr, OPTFMT, "-x cmd", + "Send data to executed cmd. First %s replaced with label."); + fprintf(stderr, OPTFMT, "-X", + "X11. Use Xdialog(1) instead of dialog(1)."); + exit(EXIT_FAILURE); +} diff --git a/usr.bin/dpv/dpv_util.h b/usr.bin/dpv/dpv_util.h new file mode 100644 index 0000000..36990be --- /dev/null +++ b/usr.bin/dpv/dpv_util.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DPV_UTIL_H_ +#define _DPV_UTIL_H_ + +/* Limits */ +#define BUFSIZE_MAX (2 * 1024 * 1024) + /* Buffer size for read(2) input */ +#ifndef MAXPHYS +#define MAXPHYS (128 * 1024) + /* max raw I/O transfer size */ +#endif + +/* + * Memory strategry threshold, in pages: if physmem is larger than this, + * use a large buffer. + */ +#define PHYSPAGES_THRESHOLD (32 * 1024) + +/* + * Small (default) buffer size in bytes. It's inefficient for this to be + * smaller than MAXPHYS. + */ +#define BUFSIZE_SMALL (MAXPHYS) + +/* + * Math macros + */ +#undef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#undef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +/* + * Extra display information + */ +#define BYTE_STATUS_SOLO "%'10lli bytes read @ %'9.1f bytes/sec." +#define BYTE_STATUS_MANY (BYTE_STATUS_SOLO " [%i/%i busy/wait]") +#define LINE_STATUS_SOLO "%'10lli lines read @ %'9.1f lines/sec." +#define LINE_STATUS_MANY (LINE_STATUS_SOLO " [%i/%i busy/wait]") + +#endif /* !_DPV_UTIL_H_ */ -- cgit v1.1