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

Reviews:	D714
Reviewed by:	jelischer, shurd
Discussed at:	MeetBSD California 2014 Vendor/Dev Summit
Discussed on:	-current
MFC after:	21 days
X-MFC-to:	stable/10 stable/9
This commit is contained in:
Devin Teske 2014-11-04 23:46:01 +00:00
parent bccb6d5aa1
commit 041394f38a
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=274116
29 changed files with 5984 additions and 1 deletions

View File

@ -42,12 +42,14 @@ SUBDIR= ${SUBDIR_ORDERED} \
libcrypt \
libdevinfo \
libdevstat \
libdpv \
libdwarf \
libedit \
${_libevent} \
libexecinfo \
libexpat \
libfetch \
libfigpar \
libgeom \
${_libgpib} \
${_libgssapi} \

16
lib/libdpv/Makefile Normal file
View File

@ -0,0 +1,16 @@
# $FreeBSD$
LIB= dpv
SHLIB_MAJOR= 1
INCS= dpv.h
MAN= dpv.3
MLINKS= dpv.3 dpv_free.3
CFLAGS+= -I${.CURDIR}
LDFLAGS+= -ldialog -lfigpar -lncurses -lutil
SRCS= dialog_util.c dialogrc.c dprompt.c dpv.c status.c util.c
WARNS?= 6
.include <bsd.lib.mk>

633
lib/libdpv/dialog_util.c Normal file
View File

@ -0,0 +1,633 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/ioctl.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <limits.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include "dialog_util.h"
#include "dpv.h"
#include "dpv_private.h"
extern char **environ;
#define TTY_DEFAULT_ROWS 24
#define TTY_DEFAULT_COLS 80
/* [X]dialog(1) characteristics */
uint8_t dialog_test = 0;
uint8_t use_dialog = 0;
uint8_t use_libdialog = 1;
uint8_t use_xdialog = 0;
uint8_t use_color = 1;
char dialog[PATH_MAX] = DIALOG;
/* [X]dialog(1) functionality */
char *title = NULL;
char *backtitle = NULL;
int dheight = 0;
int dwidth = 0;
char *dargv[64] = { NULL };
/* TTY/Screen characteristics */
static struct winsize *maxsize = NULL;
/* Function prototypes */
static void tty_maxsize_update(void);
static void x11_maxsize_update(void);
/*
* Update row/column fields of `maxsize' global (used by dialog_maxrows() and
* dialog_maxcols()). If the `maxsize' pointer is NULL, it will be initialized.
* The `ws_row' and `ws_col' fields of `maxsize' are updated to hold current
* maximum height and width (respectively) for a dialog(1) widget based on the
* active TTY size.
*
* This function is called automatically by dialog_maxrows/cols() to reflect
* changes in terminal size in-between calls.
*/
static void
tty_maxsize_update(void)
{
int fd = STDIN_FILENO;
struct termios t;
if (maxsize == NULL) {
if ((maxsize = malloc(sizeof(struct winsize))) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
memset((void *)maxsize, '\0', sizeof(struct winsize));
}
if (!isatty(fd))
fd = open("/dev/tty", O_RDONLY);
if ((tcgetattr(fd, &t) < 0) || (ioctl(fd, TIOCGWINSZ, maxsize) < 0)) {
maxsize->ws_row = TTY_DEFAULT_ROWS;
maxsize->ws_col = TTY_DEFAULT_COLS;
}
}
/*
* Update row/column fields of `maxsize' global (used by dialog_maxrows() and
* dialog_maxcols()). If the `maxsize' pointer is NULL, it will be initialized.
* The `ws_row' and `ws_col' fields of `maxsize' are updated to hold current
* maximum height and width (respectively) for an Xdialog(1) widget based on
* the active video resolution of the X11 environment.
*
* This function is called automatically by dialog_maxrows/cols() to initialize
* `maxsize'. Since video resolution changes are less common and more obtrusive
* than changes to terminal size, the dialog_maxrows/cols() functions only call
* this function when `maxsize' is set to NULL.
*/
static void
x11_maxsize_update(void)
{
FILE *f = NULL;
char *cols;
char *cp;
char *rows;
char cmdbuf[LINE_MAX];
char rbuf[LINE_MAX];
if (maxsize == NULL) {
if ((maxsize = malloc(sizeof(struct winsize))) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
memset((void *)maxsize, '\0', sizeof(struct winsize));
}
/* Assemble the command necessary to get X11 sizes */
snprintf(cmdbuf, LINE_MAX, "%s --print-maxsize 2>&1", dialog);
fflush(STDIN_FILENO); /* prevent popen(3) from seeking on stdin */
if ((f = popen(cmdbuf, "r")) == NULL) {
if (debug)
warnx("WARNING! Command `%s' failed", cmdbuf);
return;
}
/* Read in the line returned from Xdialog(1) */
if ((fgets(rbuf, LINE_MAX, f) == NULL) || (pclose(f) < 0))
return;
/* Check for X11-related errors */
if (strncmp(rbuf, "Xdialog: Error", 14) == 0)
return;
/* Parse expected output: MaxSize: YY, XXX */
if ((rows = strchr(rbuf, ' ')) == NULL)
return;
if ((cols = strchr(rows, ',')) != NULL) {
/* strtonum(3) doesn't like trailing junk */
*(cols++) = '\0';
if ((cp = strchr(cols, '\n')) != NULL)
*cp = '\0';
}
/* Convert to unsigned short */
maxsize->ws_row = (unsigned short)strtonum(
rows, 0, USHRT_MAX, (const char **)NULL);
maxsize->ws_col = (unsigned short)strtonum(
cols, 0, USHRT_MAX, (const char **)NULL);
}
/*
* Return the current maximum height (rows) for an [X]dialog(1) widget.
*/
int
dialog_maxrows(void)
{
if (use_xdialog && maxsize == NULL)
x11_maxsize_update(); /* initialize maxsize for GUI */
else if (!use_xdialog)
tty_maxsize_update(); /* update maxsize for TTY */
return (maxsize->ws_row);
}
/*
* Return the current maximum width (cols) for an [X]dialog(1) widget.
*/
int
dialog_maxcols(void)
{
if (use_xdialog && maxsize == NULL)
x11_maxsize_update(); /* initialize maxsize for GUI */
else if (!use_xdialog)
tty_maxsize_update(); /* update maxsize for TTY */
if (use_dialog || use_libdialog) {
if (use_shadow)
return (maxsize->ws_col - 2);
else
return (maxsize->ws_col);
} else
return (maxsize->ws_col);
}
/*
* Return the current maximum width (cols) for the terminal.
*/
int
tty_maxcols(void)
{
if (use_xdialog && maxsize == NULL)
x11_maxsize_update(); /* initialize maxsize for GUI */
else if (!use_xdialog)
tty_maxsize_update(); /* update maxsize for TTY */
return (maxsize->ws_col);
}
/*
* Spawn an [X]dialog(1) `--gauge' box with a `--prompt' value of init_prompt.
* Writes the resulting process ID to the pid_t pointed at by `pid'. Returns a
* file descriptor (int) suitable for writing data to the [X]dialog(1) instance
* (data written to the file descriptor is seen as standard-in by the spawned
* [X]dialog(1) process).
*/
int
dialog_spawn_gauge(char *init_prompt, pid_t *pid)
{
char dummy_init[2] = "";
char *cp;
int height;
int width;
int error;
posix_spawn_file_actions_t action;
#if DIALOG_SPAWN_DEBUG
unsigned int i;
#endif
unsigned int n = 0;
int stdin_pipe[2] = { -1, -1 };
/* Override `dialog' with a path from ENV_DIALOG if provided */
if ((cp = getenv(ENV_DIALOG)) != NULL)
snprintf(dialog, PATH_MAX, "%s", cp);
/* For Xdialog(1), set ENV_XDIALOG_HIGH_DIALOG_COMPAT */
setenv(ENV_XDIALOG_HIGH_DIALOG_COMPAT, "1", 1);
/* Constrain the height/width */
height = dialog_maxrows();
if (backtitle != NULL)
height -= use_shadow ? 5 : 4;
if (dheight < height)
height = dheight;
width = dialog_maxcols();
if (dwidth < width)
width = dwidth;
/* Populate argument array */
dargv[n++] = dialog;
if (title != NULL) {
if ((dargv[n] = malloc(8)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
sprintf(dargv[n++], "--title");
dargv[n++] = title;
}
if (backtitle != NULL) {
if ((dargv[n] = malloc(12)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
sprintf(dargv[n++], "--backtitle");
dargv[n++] = backtitle;
}
if (use_color) {
if ((dargv[n] = malloc(11)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
sprintf(dargv[n++], "--colors");
}
if (use_xdialog) {
if ((dargv[n] = malloc(7)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
sprintf(dargv[n++], "--left");
/*
* NOTE: Xdialog(1)'s `--wrap' appears to be broken for the
* `--gauge' widget prompt-updates. Add it anyway (in-case it
* gets fixed in some later release).
*/
if ((dargv[n] = malloc(7)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
sprintf(dargv[n++], "--wrap");
}
if ((dargv[n] = malloc(8)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
sprintf(dargv[n++], "--gauge");
dargv[n++] = use_xdialog ? dummy_init : init_prompt;
if ((dargv[n] = malloc(40)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
snprintf(dargv[n++], 40, "%u", height);
if ((dargv[n] = malloc(40)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
snprintf(dargv[n++], 40, "%u", width);
dargv[n] = NULL;
/* Open a pipe(2) to communicate with [X]dialog(1) */
if (pipe(stdin_pipe) < 0)
err(EXIT_FAILURE, "%s: pipe(2)", __func__);
/* Fork [X]dialog(1) process */
#if DIALOG_SPAWN_DEBUG
fprintf(stderr, "%s: spawning `", __func__);
for (i = 0; i < n; i++) {
if (i == 0)
fprintf(stderr, "%s", dargv[i]);
else if (*dargv[i] == '-' && *(dargv[i] + 1) == '-')
fprintf(stderr, " %s", dargv[i]);
else
fprintf(stderr, " \"%s\"", dargv[i]);
}
fprintf(stderr, "'\n");
#endif
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO);
posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
error = posix_spawnp(pid, dialog, &action,
(const posix_spawnattr_t *)NULL, dargv, environ);
if (error != 0)
err(EXIT_FAILURE, "%s: posix_spawnp(3)", __func__);
/* NB: Do not free(3) *dargv[], else SIGSEGV */
return (stdin_pipe[1]);
}
/*
* Returns the number of lines in buffer pointed to by `prompt'. Takes both
* newlines and escaped-newlines into account.
*/
unsigned int
dialog_prompt_numlines(const char *prompt, uint8_t nlstate)
{
uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */
const char *cp = prompt;
unsigned int nlines = 1;
if (prompt == NULL || *prompt == '\0')
return (0);
while (*cp != '\0') {
if (use_dialog) {
if (strncmp(cp, "\\n", 2) == 0) {
cp++;
nlines++;
nls = TRUE; /* See declaration comment */
} else if (*cp == '\n') {
if (!nls)
nlines++;
nls = FALSE; /* See declaration comment */
}
} else if (use_libdialog) {
if (*cp == '\n')
nlines++;
} else if (strncmp(cp, "\\n", 2) == 0) {
cp++;
nlines++;
}
cp++;
}
return (nlines);
}
/*
* Returns the length in bytes of the longest line in buffer pointed to by
* `prompt'. Takes newlines and escaped newlines into account. Also discounts
* dialog(1) color escape codes if enabled (via `use_color' global).
*/
unsigned int
dialog_prompt_longestline(const char *prompt, uint8_t nlstate)
{
uint8_t backslash = 0;
uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */
const char *p = prompt;
int longest = 0;
int n = 0;
/* `prompt' parameter is required */
if (prompt == NULL)
return (0);
if (*prompt == '\0')
return (0); /* shortcut */
/* Loop until the end of the string */
while (*p != '\0') {
/* dialog(1) and dialog(3) will render literal newlines */
if (use_dialog || use_libdialog) {
if (*p == '\n') {
if (!use_libdialog && nls)
n++;
else {
if (n > longest)
longest = n;
n = 0;
}
nls = FALSE; /* See declaration comment */
p++;
continue;
}
}
/* Check for backslash character */
if (*p == '\\') {
/* If second backslash, count as a single-char */
if ((backslash ^= 1) == 0)
n++;
} else if (backslash) {
if (*p == 'n' && !use_libdialog) { /* new line */
/* NB: dialog(3) ignores escaped newlines */
nls = TRUE; /* See declaration comment */
if (n > longest)
longest = n;
n = 0;
} else if (use_color && *p == 'Z') {
if (*++p != '\0')
p++;
backslash = 0;
continue;
} else /* [X]dialog(1)/dialog(3) only expand those */
n += 2;
backslash = 0;
} else
n++;
p++;
}
if (n > longest)
longest = n;
return (longest);
}
/*
* Returns a pointer to the last line in buffer pointed to by `prompt'. Takes
* both newlines (if using dialog(1) versus Xdialog(1)) and escaped newlines
* into account. If no newlines (escaped or otherwise) appear in the buffer,
* `prompt' is returned. If passed a NULL pointer, returns NULL.
*/
char *
dialog_prompt_lastline(char *prompt, uint8_t nlstate)
{
uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */
char *lastline;
char *p;
if (prompt == NULL)
return (NULL);
if (*prompt == '\0')
return (prompt); /* shortcut */
lastline = p = prompt;
while (*p != '\0') {
/* dialog(1) and dialog(3) will render literal newlines */
if (use_dialog || use_libdialog) {
if (*p == '\n') {
if (use_libdialog || !nls)
lastline = p + 1;
nls = FALSE; /* See declaration comment */
}
}
/* dialog(3) does not expand escaped newlines */
if (use_libdialog) {
p++;
continue;
}
if (*p == '\\' && *(p + 1) != '\0' && *(++p) == 'n') {
nls = TRUE; /* See declaration comment */
lastline = p + 1;
}
p++;
}
return (lastline);
}
/*
* Returns the number of extra lines generated by wrapping the text in buffer
* pointed to by `prompt' within `ncols' columns (for prompts, this should be
* dwidth - 4). Also discounts dialog(1) color escape codes if enabled (via
* `use_color' global).
*/
int
dialog_prompt_wrappedlines(char *prompt, int ncols, uint8_t nlstate)
{
uint8_t backslash = 0;
uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */
char *cp;
char *p = prompt;
int n = 0;
int wlines = 0;
/* `prompt' parameter is required */
if (p == NULL)
return (0);
if (*p == '\0')
return (0); /* shortcut */
/* Loop until the end of the string */
while (*p != '\0') {
/* dialog(1) and dialog(3) will render literal newlines */
if (use_dialog || use_libdialog) {
if (*p == '\n') {
if (use_dialog || !nls)
n = 0;
nls = FALSE; /* See declaration comment */
}
}
/* Check for backslash character */
if (*p == '\\') {
/* If second backslash, count as a single-char */
if ((backslash ^= 1) == 0)
n++;
} else if (backslash) {
if (*p == 'n' && !use_libdialog) { /* new line */
/* NB: dialog(3) ignores escaped newlines */
nls = TRUE; /* See declaration comment */
n = 0;
} else if (use_color && *p == 'Z') {
if (*++p != '\0')
p++;
backslash = 0;
continue;
} else /* [X]dialog(1)/dialog(3) only expand those */
n += 2;
backslash = 0;
} else
n++;
/* Did we pass the width barrier? */
if (n > ncols) {
/*
* Work backward to find the first whitespace on-which
* dialog(1) will wrap the line (but don't go before
* the start of this line).
*/
cp = p;
while (n > 1 && !isspace(*cp)) {
cp--;
n--;
}
if (n > 0 && isspace(*cp))
p = cp;
wlines++;
n = 1;
}
p++;
}
return (wlines);
}
/*
* Returns zero if the buffer pointed to by `prompt' contains an escaped
* newline but only if appearing after any/all literal newlines. This is
* specific to dialog(1) and does not apply to Xdialog(1).
*
* As an attempt to make shell scripts easier to read, dialog(1) will "eat"
* the first literal newline after an escaped newline. This however has a bug
* in its implementation in that rather than allowing `\\n\n' to be treated
* similar to `\\n' or `\n', dialog(1) expands the `\\n' and then translates
* the following literal newline (with or without characters between [!]) into
* a single space.
*
* If you want to be compatible with Xdialog(1), it is suggested that you not
* use literal newlines (they aren't supported); but if you have to use them,
* go right ahead. But be forewarned... if you set $DIALOG in your environment
* to something other than `cdialog' (our current dialog(1)), then it should
* do the same thing w/respect to how to handle a literal newline after an
* escaped newline (you could do no wrong by translating every literal newline
* into a space but only when you've previously encountered an escaped one;
* this is what dialog(1) is doing).
*
* The ``newline state'' (or nlstate for short; as I'm calling it) is helpful
* if you plan to combine multiple strings into a single prompt text. In lead-
* up to this procedure, a common task is to calculate and utilize the widths
* and heights of each piece of prompt text to later be combined. However, if
* (for example) the first string ends in a positive newline state (has an
* escaped newline without trailing literal), the first literal newline in the
* second string will be mangled.
*
* The return value of this function should be used as the `nlstate' argument
* to dialog_*() functions that require it to allow accurate calculations in
* the event such information is needed.
*/
uint8_t
dialog_prompt_nlstate(const char *prompt)
{
const char *cp;
if (prompt == NULL)
return 0;
/*
* Work our way backward from the end of the string for efficiency.
*/
cp = prompt + strlen(prompt);
while (--cp >= prompt) {
/*
* If we get to a literal newline first, this prompt ends in a
* clean state for rendering with dialog(1). Otherwise, if we
* get to an escaped newline first, this prompt ends in an un-
* clean state (following literal will be mangled; see above).
*/
if (*cp == '\n')
return (0);
else if (*cp == 'n' && --cp > prompt && *cp == '\\')
return (1);
}
return (0); /* no newlines (escaped or otherwise) */
}
/*
* Free allocated items initialized by tty_maxsize_update() and
* x11_maxsize_update()
*/
void
dialog_maxsize_free(void)
{
if (maxsize != NULL) {
free(maxsize);
maxsize = NULL;
}
}

73
lib/libdpv/dialog_util.h Normal file
View File

@ -0,0 +1,73 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _DIALOG_UTIL_H_
#define _DIALOG_UTIL_H_
#include <sys/types.h>
#include "dialogrc.h"
#define DIALOG_SPAWN_DEBUG 0 /* Debug spawning of [X]dialog(1) */
/* dialog(3) and [X]dialog(1) characteristics */
#define DIALOG "dialog"
#define XDIALOG "Xdialog"
#define PROMPT_MAX 16384
#define ENV_DIALOG "DIALOG"
#define ENV_USE_COLOR "USE_COLOR"
#define ENV_XDIALOG_HIGH_DIALOG_COMPAT "XDIALOG_HIGH_DIALOG_COMPAT"
extern uint8_t dialog_test;
extern uint8_t use_libdialog;
extern uint8_t use_dialog;
extern uint8_t use_xdialog;
extern uint8_t use_color;
extern char dialog[];
/* dialog(3) and [X]dialog(1) functionality */
extern char *title, *backtitle;
extern int dheight, dwidth;
__BEGIN_DECLS
uint8_t dialog_prompt_nlstate(const char *_prompt);
void dialog_gauge_free(void);
void dialog_maxsize_free(void);
char *dialog_prompt_lastline(char *_prompt, uint8_t _nlstate);
int dialog_maxcols(void);
int dialog_maxrows(void);
int dialog_prompt_wrappedlines(char *_prompt, int _ncols,
uint8_t _nlstate);
int dialog_spawn_gauge(char *_init_prompt, pid_t *_pid);
int tty_maxcols(void);
#define tty_maxrows() dialog_maxrows()
unsigned int dialog_prompt_longestline(const char *_prompt,
uint8_t _nlstate);
unsigned int dialog_prompt_numlines(const char *_prompt, uint8_t _nlstate);
__END_DECLS
#endif /* !_DIALOG_UTIL_H_ */

359
lib/libdpv/dialogrc.c Normal file
View File

@ -0,0 +1,359 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <figpar.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string_m.h>
#include "dialogrc.h"
#define STR_BUFSIZE 255
/* dialog(1) `.dialogrc' characteristics */
uint8_t use_colors = 1;
uint8_t use_shadow = 1;
char gauge_color[STR_BUFSIZE] = "47b"; /* (BLUE,WHITE,ON) */
char separator[STR_BUFSIZE] = "";
/* Function prototypes */
static int setattr(struct config *, uint32_t, char *, char *);
static int setbool(struct config *, uint32_t, char *, char *);
static int setnum(struct config *, uint32_t, char *, char *);
static int setstr(struct config *, uint32_t, char *, char *);
/*
* Anatomy of DIALOGRC (~/.dialogrc by default)
* NOTE: Must appear after private function prototypes (above)
* NB: Brace-initialization of union requires cast to *first* member of union
*/
static struct config dialogrc_config[] = {
/* TYPE Directive DEFAULT HANDLER */
{TYPE_INT, "aspect", {(void *)0}, &setnum},
{TYPE_STR, "separate_widget", {separator}, &setstr},
{TYPE_INT, "tab_len", {(void *)0}, &setnum},
{TYPE_BOOL, "visit_items", {(void *)0}, &setbool},
{TYPE_BOOL, "use_shadow", {(void *)1}, &setbool},
{TYPE_BOOL, "use_colors", {(void *)1}, &setbool},
{TYPE_STR, "screen_color", {NULL}, &setattr},
{TYPE_STR, "shadow_color", {NULL}, &setattr},
{TYPE_STR, "dialog_color", {NULL}, &setattr},
{TYPE_STR, "title_color", {NULL}, &setattr},
{TYPE_STR, "border_color", {NULL}, &setattr},
{TYPE_STR, "button_active_color", {NULL}, &setattr},
{TYPE_STR, "button_inactive_color", {NULL}, &setattr},
{TYPE_STR, "button_key_active_color", {NULL}, &setattr},
{TYPE_STR, "button_key_inactive_color", {NULL}, &setattr},
{TYPE_STR, "button_label_active_color", {NULL}, &setattr},
{TYPE_STR, "button_label_inactive_color", {NULL}, &setattr},
{TYPE_STR, "inputbox_color", {NULL}, &setattr},
{TYPE_STR, "inputbox_border_color", {NULL}, &setattr},
{TYPE_STR, "searchbox_color", {NULL}, &setattr},
{TYPE_STR, "searchbox_title_color", {NULL}, &setattr},
{TYPE_STR, "searchbox_border_color", {NULL}, &setattr},
{TYPE_STR, "position_indicator_color", {NULL}, &setattr},
{TYPE_STR, "menubox_color", {NULL}, &setattr},
{TYPE_STR, "menubox_border_color", {NULL}, &setattr},
{TYPE_STR, "item_color", {NULL}, &setattr},
{TYPE_STR, "item_selected_color", {NULL}, &setattr},
{TYPE_STR, "tag_color", {NULL}, &setattr},
{TYPE_STR, "tag_selected_color", {NULL}, &setattr},
{TYPE_STR, "tag_key_color", {NULL}, &setattr},
{TYPE_STR, "tag_key_selected_color", {NULL}, &setattr},
{TYPE_STR, "check_color", {NULL}, &setattr},
{TYPE_STR, "check_selected_color", {NULL}, &setattr},
{TYPE_STR, "uarrow_color", {NULL}, &setattr},
{TYPE_STR, "darrow_color", {NULL}, &setattr},
{TYPE_STR, "itemhelp_color", {NULL}, &setattr},
{TYPE_STR, "form_active_text_color", {NULL}, &setattr},
{TYPE_STR, "form_text_color", {NULL}, &setattr},
{TYPE_STR, "form_item_readonly_color", {NULL}, &setattr},
{TYPE_STR, "gauge_color", {gauge_color}, &setattr},
{0, NULL, {0}, NULL}
};
/*
* figpar call-back for interpreting value as .dialogrc `Attribute'
*/
static int
setattr(struct config *option, uint32_t line __unused,
char *directive __unused, char *value)
{
char *cp = value;
char *val;
size_t len;
char attrbuf[4];
if (option == NULL) {
warnx("%s:%d:%s: Missing callback parameter", __FILE__,
__LINE__, __func__);
return (-1); /* Abort processing */
}
/* Allocate memory for the data if not already done */
if (option->value.str == NULL) {
if ((option->value.str = malloc(STR_BUFSIZE)) == NULL)
return (-1);
}
/*
* If the first character is left-parenthesis, the format is
* `(background,foreground,highlight)' otherwise, we should take it
* as a reference to another color.
*/
if (*cp != '(') {
/* Copy the [current] value from the referenced color */
val = dialogrc_config_option(cp)->value.str;
if (val != NULL)
snprintf(option->value.str, STR_BUFSIZE, "%s", val);
return (0);
} else
cp++;
strtolower(cp);
/* Initialize the attrbuf (fg,bg,hi,NUL) */
attrbuf[0] = '0';
attrbuf[1] = '0';
attrbuf[2] = 'B'; /* \ZB = disable; \Zb = enable (see dialog(1)) */
attrbuf[3] = '\0';
/* Interpret the foreground color */
if (strncmp(cp, "red,", 4) == 0) attrbuf[0] = '1';
else if (strncmp(cp, "green,", 6) == 0) attrbuf[0] = '2';
else if (strncmp(cp, "yellow,", 7) == 0) attrbuf[0] = '3';
else if (strncmp(cp, "blue,", 5) == 0) attrbuf[0] = '4';
else if (strncmp(cp, "magenta,", 8) == 0) attrbuf[0] = '5';
else if (strncmp(cp, "cyan,", 5) == 0) attrbuf[0] = '6';
else if (strncmp(cp, "white,", 6) == 0) attrbuf[0] = '7';
else if (strncmp(cp, "black,", 6) == 0) attrbuf[0] = '8';
/* Advance to the background color */
cp = strchr(cp, ',');
if (cp == NULL)
goto write_attrbuf;
else
cp++;
/* Interpret the background color */
if (strncmp(cp, "red,", 4) == 0) attrbuf[1] = '1';
else if (strncmp(cp, "green,", 6) == 0) attrbuf[1] = '2';
else if (strncmp(cp, "yellow,", 7) == 0) attrbuf[1] = '3';
else if (strncmp(cp, "blue,", 5) == 0) attrbuf[1] = '4';
else if (strncmp(cp, "magenta,", 8) == 0) attrbuf[1] = '5';
else if (strncmp(cp, "cyan,", 5) == 0) attrbuf[1] = '6';
else if (strncmp(cp, "white,", 6) == 0) attrbuf[1] = '7';
else if (strncmp(cp, "black,", 6) == 0) attrbuf[1] = '8';
/* Advance to the highlight */
cp = strchr(cp, ',');
if (cp == NULL)
goto write_attrbuf;
else
cp++;
/* Trim trailing parenthesis */
len = strlen(cp);
if (cp[len - 1] == ')')
cp[len - 1] = '\0';
/* Interpret the highlight (initialized to off above) */
if (strcmp(cp, "on") == 0 || strncmp(cp, "on,", 3) == 0)
attrbuf[2] = 'b'; /* \Zb = enable bold (see dialog(1)) */
write_attrbuf:
sprintf(option->value.str, "%s", attrbuf);
return (0);
}
/*
* figpar call-back for interpreting value as .dialogrc `Boolean'
*/
static int
setbool(struct config *option, uint32_t line __unused,
char *directive __unused, char *value)
{
if (option == NULL) {
warnx("%s:%d:%s: Missing callback parameter", __FILE__,
__LINE__, __func__);
return (-1); /* Abort processing */
}
/* Assume ON, check for OFF (case-insensitive) */
option->value.boolean = 1;
strtolower(value);
if (strcmp(value, "off") == 0)
option->value.boolean = 0;
return (0);
}
/*
* figpar call-back for interpreting value as .dialogrc `Number'
*/
static int
setnum(struct config *option, uint32_t line __unused, char *directive __unused,
char *value)
{
if (option == NULL) {
warnx("%s:%d:%s: Missing callback parameter", __FILE__,
__LINE__, __func__);
return (-1); /* Abort processing */
}
/* Convert the string to a 32-bit signed integer */
option->value.num = (int32_t)strtol(value, (char **)NULL, 10);
return (0);
}
/*
* figpar call-back for interpreting value as .dialogrc `String'
*/
static int
setstr(struct config *option, uint32_t line __unused, char *directive __unused,
char *value)
{
size_t len;
if (option == NULL) {
warnx("%s:%d:%s: Missing callback parameter", __FILE__,
__LINE__, __func__);
return (-1); /* Abort processing */
}
/* Allocate memory for the data if not already done */
if (option->value.str == NULL) {
if ((option->value.str = malloc(STR_BUFSIZE)) == NULL)
return (-1);
}
/* Trim leading quote */
if (*value == '"')
value++;
/* Write the data into the buffer */
snprintf(option->value.str, STR_BUFSIZE, "%s", value);
/* Trim trailing quote */
len = strlen(option->value.str);
if (option->value.str[len - 1] == '"')
option->value.str[len - 1] = '\0';
return (0);
}
/*
* Parse (in order of preference) $DIALOGRC or `$HOME/.dialogrc'. Returns zero
* on success, -1 on failure (and errno should be consulted).
*/
int
parse_dialogrc(void)
{
char *cp;
int res;
size_t len;
char path[PATH_MAX];
/* Allow $DIALOGRC to override `$HOME/.dialogrc' default */
if ((cp = getenv(ENV_DIALOGRC)) != NULL && *cp != '\0')
snprintf(path, PATH_MAX, "%s", cp);
else if ((cp = getenv(ENV_HOME)) != NULL) {
/* Copy $HOME into buffer and append trailing `/' if missing */
snprintf(path, PATH_MAX, "%s", cp);
len = strlen(path);
cp = path + len;
if (len > 0 && len < (PATH_MAX - 1) && *(cp - 1) != '/') {
*cp++ = '/';
*cp = '\0';
len++;
}
/* If we still have room, shove in the name of rc file */
if (len < (PATH_MAX - 1))
snprintf(cp, PATH_MAX - len, "%s", DIALOGRC);
} else {
/* Like dialog(1), don't process a file if $HOME is unset */
errno = ENOENT;
return (-1);
}
/* Process file (either $DIALOGRC if set, or `$HOME/.dialogrc') */
res = parse_config(dialogrc_config, path, NULL, BREAK_ON_EQUALS);
/* Set some globals based on what we parsed */
use_shadow = dialogrc_config_option("use_shadow")->value.boolean;
use_colors = dialogrc_config_option("use_colors")->value.boolean;
snprintf(gauge_color, STR_BUFSIZE, "%s",
dialogrc_config_option("gauge_color")->value.str);
return (res);
}
/*
* Return a pointer to the `.dialogrc' config option specific to `directive' or
* static dummy_config (full of NULLs) if none found (see get_config_option(3);
* part of figpar(3)).
*/
struct config *
dialogrc_config_option(const char *directive)
{
return (get_config_option(dialogrc_config, directive));
}
/*
* Free allocated items initialized by setattr() (via parse_config() callback
* matrix [dialogrc_config] used in parse_dialogrc() above).
*/
void
dialogrc_free(void)
{
char *value;
uint32_t n;
for (n = 0; dialogrc_config[n].directive != NULL; n++) {
if (dialogrc_config[n].action != &setattr)
continue;
value = dialogrc_config[n].value.str;
if (value != NULL && value != gauge_color) {
free(dialogrc_config[n].value.str);
dialogrc_config[n].value.str = NULL;
}
}
}

56
lib/libdpv/dialogrc.h Normal file
View File

@ -0,0 +1,56 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _DIALOGRC_H_
#define _DIALOGRC_H_
#include <sys/types.h>
#include <figpar.h>
/* dialog(3) dlg_color_table[] attributes */
#define GAUGE_ATTR 33 /* entry used for gauge_color */
/* dialog(1) characteristics */
#define DIALOGRC ".dialogrc"
#define ENV_DIALOGRC "DIALOGRC"
#define ENV_HOME "HOME"
/* dialog(1) `.dialogrc' characteristics */
extern uint8_t use_colors;
extern uint8_t use_shadow;
extern char gauge_color[];
extern char separator[];
__BEGIN_DECLS
void dialogrc_free(void);
int parse_dialogrc(void);
struct config *dialogrc_config_option(const char *_directive);
__END_DECLS
#endif /* !_DIALOGRC_H_ */

770
lib/libdpv/dprompt.c Normal file
View File

@ -0,0 +1,770 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */
#include <dialog.h>
#include <err.h>
#include <libutil.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string_m.h>
#include <unistd.h>
#include "dialog_util.h"
#include "dialogrc.h"
#include "dprompt.h"
#include "dpv.h"
#include "dpv_private.h"
#define FLABEL_MAX 1024
static int fheight = 0; /* initialized by dprompt_init() */
static char dprompt[PROMPT_MAX + 1] = "";
static char *dprompt_pos = (char *)(0); /* treated numerically */
/* Display characteristics */
#define FM_DONE 0x01
#define FM_FAIL 0x02
#define FM_PEND 0x04
static uint8_t dprompt_free_mask;
static char *done = NULL;
static char *fail = NULL;
static char *pend = NULL;
int display_limit = DISPLAY_LIMIT_DEFAULT; /* Max entries to show */
int label_size = LABEL_SIZE_DEFAULT; /* Max width for labels */
int pbar_size = PBAR_SIZE_DEFAULT; /* Mini-progressbar size */
static int gauge_percent = 0;
static int done_size, done_lsize, done_rsize;
static int fail_size, fail_lsize, fail_rsize;
static int mesg_size, mesg_lsize, mesg_rsize;
static int pend_size, pend_lsize, pend_rsize;
static int pct_lsize, pct_rsize;
static void *gauge = NULL;
#define SPIN_SIZE 4
static char spin[SPIN_SIZE + 1] = "/-\\|";
static char msg[PROMPT_MAX + 1];
static char *spin_cp = spin;
/* Function prototypes */
static char spin_char(void);
static int dprompt_add_files(struct dpv_file_node *file_list,
struct dpv_file_node *curfile, int pct);
/*
* Returns a pointer to the current spin character in the spin string and
* advances the global position to the next character for the next call.
*/
static char
spin_char(void)
{
char ch;
if (spin_cp == '\0')
spin_cp = spin;
ch = *spin_cp;
/* Advance the spinner to the next char */
if (++spin_cp >= (spin + SPIN_SIZE))
spin_cp = spin;
return (ch);
}
/*
* Initialize heights and widths based on various strings and environment
* variables (such as ENV_USE_COLOR).
*/
void
dprompt_init(struct dpv_file_node *file_list)
{
uint8_t nls = 0;
int len;
int max_cols;
int max_rows;
int nthfile;
int numlines;
struct dpv_file_node *curfile;
/*
* Initialize dialog(3) `colors' support and draw backtitle
*/
if (use_libdialog && !debug) {
init_dialog(stdin, stdout);
dialog_vars.colors = 1;
if (backtitle != NULL) {
dialog_vars.backtitle = (char *)backtitle;
dlg_put_backtitle();
}
}
/* Calculate width of dialog(3) or [X]dialog(1) --gauge box */
dwidth = label_size + pbar_size + 9;
/*
* Calculate height of dialog(3) or [X]dialog(1) --gauge box
*/
dheight = 5;
max_rows = dialog_maxrows();
/* adjust max_rows for backtitle and/or dialog(3) statusLine */
if (backtitle != NULL)
max_rows -= use_shadow ? 3 : 2;
if (use_libdialog && use_shadow)
max_rows -= 2;
/* add lines for `-p text' */
numlines = dialog_prompt_numlines(pprompt, 0);
if (debug)
warnx("`-p text' is %i line%s long", numlines,
numlines == 1 ? "" : "s");
dheight += numlines;
/* adjust dheight for various implementations */
if (use_dialog) {
dheight -= dialog_prompt_nlstate(pprompt);
nls = dialog_prompt_nlstate(pprompt);
} else if (use_xdialog) {
if (pprompt == NULL || *pprompt == '\0')
dheight++;
} else if (use_libdialog) {
if (pprompt != NULL && *pprompt != '\0')
dheight--;
}
/* limit the number of display items (necessary per dialog(1,3)) */
if (display_limit == 0 || display_limit > DPV_DISPLAY_LIMIT)
display_limit = DPV_DISPLAY_LIMIT;
/* verify fheight will fit (stop if we hit 1) */
for (; display_limit > 0; display_limit--) {
nthfile = numlines = 0;
fheight = (int)dpv_nfiles > display_limit ?
(unsigned int)display_limit : dpv_nfiles;
for (curfile = file_list; curfile != NULL;
curfile = curfile->next) {
nthfile++;
numlines += dialog_prompt_numlines(curfile->name, nls);
if ((nthfile % display_limit) == 0) {
if (numlines > fheight)
fheight = numlines;
numlines = nthfile = 0;
}
}
if (numlines > fheight)
fheight = numlines;
if ((dheight + fheight +
(int)dialog_prompt_numlines(aprompt, use_dialog) -
(use_dialog ? (int)dialog_prompt_nlstate(aprompt) : 0))
<= max_rows)
break;
}
/* don't show any items if we run the risk of hitting a blank set */
if ((max_rows - (use_shadow ? 5 : 4)) >= fheight)
dheight += fheight;
else
fheight = 0;
/* add lines for `-a text' */
numlines = dialog_prompt_numlines(aprompt, use_dialog);
if (debug)
warnx("`-a text' is %i line%s long", numlines,
numlines == 1 ? "" : "s");
dheight += numlines;
/* If using Xdialog(1), adjust accordingly (based on testing) */
if (use_xdialog)
dheight += dheight / 4;
/* For wide mode, long prefix (`pprompt') or append (`aprompt')
* strings will bump width */
if (wide) {
len = (int)dialog_prompt_longestline(pprompt, 0); /* !nls */
if ((len + 4) > dwidth)
dwidth = len + 4;
len = (int)dialog_prompt_longestline(aprompt, 1); /* nls */
if ((len + 4) > dwidth)
dwidth = len + 4;
}
/* Enforce width constraints to maximum values */
max_cols = dialog_maxcols();
if (max_cols > 0 && dwidth > max_cols)
dwidth = max_cols;
/* Optimize widths to sane values*/
if (pbar_size > dwidth - 9) {
pbar_size = dwidth - 9;
label_size = 0;
/* -9 = "| - [" ... "] |" */
}
if (pbar_size < 0)
label_size = dwidth - 8;
/* -8 = "| " ... " - |" */
else if (label_size > (dwidth - pbar_size - 9) || wide)
label_size = no_labels ? 0 : dwidth - pbar_size - 9;
/* -9 = "| " ... " - [" ... "] |" */
/* Hide labels if requested */
if (no_labels)
label_size = 0;
/* Touch up the height (now that we know dwidth) */
dheight += dialog_prompt_wrappedlines(pprompt, dwidth - 4, 0);
dheight += dialog_prompt_wrappedlines(aprompt, dwidth - 4, 1);
if (debug)
warnx("dheight = %i dwidth = %i fheight = %i",
dheight, dwidth, fheight);
/* Calculate left/right portions of % */
pct_lsize = (pbar_size - 4) / 2; /* -4 == printf("%-3s%%", pct) */
pct_rsize = pct_lsize;
/* If not evenly divisible by 2, increment the right-side */
if ((pct_rsize + pct_rsize + 4) != pbar_size)
pct_rsize++;
/* Initialize "Done" text */
if (done == NULL && (done = msg_done) == NULL) {
if ((done = getenv(ENV_MSG_DONE)) != NULL)
done_size = strlen(done);
else {
done_size = strlen(DPV_DONE_DEFAULT);
if ((done = malloc(done_size + 1)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
dprompt_free_mask |= FM_DONE;
snprintf(done, done_size + 1, DPV_DONE_DEFAULT);
}
}
if (pbar_size < done_size) {
done_lsize = done_rsize = 0;
*(done + pbar_size) = '\0';
done_size = pbar_size;
} else {
/* Calculate left/right portions for mini-progressbar */
done_lsize = (pbar_size - done_size) / 2;
done_rsize = done_lsize;
/* If not evenly divisible by 2, increment the right-side */
if ((done_rsize + done_size + done_lsize) != pbar_size)
done_rsize++;
}
/* Initialize "Fail" text */
if (fail == NULL && (fail = msg_fail) == NULL) {
if ((fail = getenv(ENV_MSG_FAIL)) != NULL)
fail_size = strlen(fail);
else {
fail_size = strlen(DPV_FAIL_DEFAULT);
if ((fail = malloc(fail_size + 1)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
dprompt_free_mask |= FM_FAIL;
snprintf(fail, fail_size + 1, DPV_FAIL_DEFAULT);
}
}
if (pbar_size < fail_size) {
fail_lsize = fail_rsize = 0;
*(fail + pbar_size) = '\0';
fail_size = pbar_size;
} else {
/* Calculate left/right portions for mini-progressbar */
fail_lsize = (pbar_size - fail_size) / 2;
fail_rsize = fail_lsize;
/* If not evenly divisible by 2, increment the right-side */
if ((fail_rsize + fail_size + fail_lsize) != pbar_size)
fail_rsize++;
}
/* Initialize "Pending" text */
if (pend == NULL && (pend = msg_pending) == NULL) {
if ((pend = getenv(ENV_MSG_PENDING)) != NULL)
pend_size = strlen(pend);
else {
pend_size = strlen(DPV_PENDING_DEFAULT);
if ((pend = malloc(pend_size + 1)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
dprompt_free_mask |= FM_PEND;
snprintf(pend, pend_size + 1, DPV_PENDING_DEFAULT);
}
}
if (pbar_size < pend_size) {
pend_lsize = pend_rsize = 0;
*(pend + pbar_size) = '\0';
pend_size = pbar_size;
} else {
/* Calculate left/right portions for mini-progressbar */
pend_lsize = (pbar_size - pend_size) / 2;
pend_rsize = pend_lsize;
/* If not evenly divisible by 2, increment the right-side */
if ((pend_rsize + pend_lsize + pend_size) != pbar_size)
pend_rsize++;
}
if (debug)
warnx("label_size = %i pbar_size = %i", label_size, pbar_size);
dprompt_clear();
}
/*
* Clear the [X]dialog(1) `--gauge' prompt buffer.
*/
void
dprompt_clear(void)
{
*dprompt = '\0';
dprompt_pos = dprompt;
}
/*
* Append to the [X]dialog(1) `--gauge' prompt buffer. Syntax is like printf(3)
* and returns the number of bytes appended to the buffer.
*/
int
dprompt_add(const char *format, ...)
{
int len;
va_list ap;
if (dprompt_pos >= (dprompt + PROMPT_MAX))
return (0);
va_start(ap, format);
len = vsnprintf(dprompt_pos, (size_t)(PROMPT_MAX -
(dprompt_pos - dprompt)), format, ap);
va_end(ap);
if (len == -1)
errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow",
__func__);
if ((dprompt_pos + len) < (dprompt + PROMPT_MAX))
dprompt_pos += len;
else
dprompt_pos = dprompt + PROMPT_MAX;
return (len);
}
/*
* Append active files to the [X]dialog(1) `--gauge' prompt buffer. Syntax
* requires a pointer to the head of the dpv_file_node linked-list. Returns the
* number of files processed successfully.
*/
static int
dprompt_add_files(struct dpv_file_node *file_list,
struct dpv_file_node *curfile, int pct)
{
char c;
char bold_code = 'b'; /* default: enabled */
char color_code = '4'; /* default: blue */
uint8_t after_curfile = curfile != NULL ? FALSE : TRUE;
uint8_t nls = 0;
char *cp;
char *lastline;
char *name;
const char *bg_code;
const char *estext;
const char *format;
enum dprompt_state dstate;
int estext_lsize;
int estext_rsize;
int estext_size;
int flabel_size;
int hlen;
int lsize;
int nlines = 0;
int nthfile = 0;
int pwidth;
int rsize;
struct dpv_file_node *fp;
char flabel[FLABEL_MAX + 1];
char human[32];
char pbar[pbar_size + 16]; /* +15 for optional color */
char pbar_cap[sizeof(pbar)];
char pbar_fill[sizeof(pbar)];
/* Override color defaults with that of main progress bar */
if (use_colors || use_shadow) { /* NB: shadow enables color */
color_code = gauge_color[0];
/* NB: str[1] aka bg is unused */
bold_code = gauge_color[2];
}
/*
* Create mini-progressbar for current file (if applicable)
*/
*pbar = '\0';
if (pbar_size >= 0 && pct >= 0 && curfile != NULL &&
(curfile->length >= 0 || dialog_test)) {
snprintf(pbar, pbar_size + 1, "%*s%3u%%%*s", pct_lsize, "",
pct, pct_rsize, "");
if (use_color) {
/* Calculate the fill-width of progressbar */
pwidth = pct * pbar_size / 100;
/* Round up based on one-tenth of a percent */
if ((pct * pbar_size % 100) > 50)
pwidth++;
/*
* Make two copies of pbar. Make one represent the fill
* and the other the remainder (cap). We'll insert the
* ANSI delimiter in between.
*/
*pbar_fill = '\0';
*pbar_cap = '\0';
strncat(pbar_fill, (const char *)(pbar), dwidth);
*(pbar_fill + pwidth) = '\0';
strncat(pbar_cap, (const char *)(pbar+pwidth), dwidth);
/* Finalize the mini [color] progressbar */
snprintf(pbar, sizeof(pbar),
"\\Z%c\\Zr\\Z%c%s%s%s\\Zn", bold_code, color_code,
pbar_fill, "\\ZR", pbar_cap);
}
}
for (fp = file_list; fp != NULL; fp = fp->next) {
flabel_size = label_size;
name = fp->name;
nthfile++;
/*
* Support multiline filenames (where the filename is taken as
* the last line and the text leading up to the last line can
* be used as (for example) a heading/separator between files.
*/
if (use_dialog)
nls = dialog_prompt_nlstate(pprompt);
nlines += dialog_prompt_numlines(name, nls);
lastline = dialog_prompt_lastline(name, 1);
if (name != lastline) {
c = *lastline;
*lastline = '\0';
dprompt_add("%s", name);
*lastline = c;
name = lastline;
}
/* Support color codes (for dialog(1,3)) in file names */
if ((use_dialog || use_libdialog) && use_color) {
cp = name;
while (*cp != '\0') {
if (*cp == '\\' && *(cp + 1) != '\0' &&
*(++cp) == 'Z' && *(cp + 1) != '\0') {
cp++;
flabel_size += 3;
}
cp++;
}
if (flabel_size > FLABEL_MAX)
flabel_size = FLABEL_MAX;
}
/* If no mini-progressbar, increase label width */
if (pbar_size < 0 && flabel_size <= FLABEL_MAX - 2 &&
no_labels == FALSE)
flabel_size += 2;
/* If name is too long, add an ellipsis */
if (snprintf(flabel, flabel_size + 1, "%s", name) >
flabel_size) sprintf(flabel + flabel_size - 3, "...");
/*
* Append the label (processing the current file differently)
*/
if (fp == curfile && pct < 100) {
/*
* Add an ellipsis to current file name if it will fit.
* There may be an ellipsis already from truncating the
* label (in which case, we already have one).
*/
cp = flabel + strlen(flabel);
if (cp < (flabel + flabel_size))
snprintf(cp, flabel_size -
(cp - flabel) + 1, "...");
/* Append label (with spinner and optional color) */
dprompt_add("%s%-*s%s %c", use_color ? "\\Zb" : "",
flabel_size, flabel, use_color ? "\\Zn" : "",
spin_char());
} else
dprompt_add("%-*s%s %s", flabel_size,
flabel, use_color ? "\\Zn" : "", " ");
/*
* Append pbar/status (processing the current file differently)
*/
dstate = DPROMPT_NONE;
if (fp->msg != NULL)
dstate = DPROMPT_CUSTOM_MSG;
else if (pbar_size < 0)
dstate = DPROMPT_NONE;
else if (pbar_size < 4)
dstate = DPROMPT_MINIMAL;
else if (after_curfile)
dstate = DPROMPT_PENDING;
else if (fp == curfile) {
if (*pbar == '\0') {
if (fp->length < 0)
dstate = DPROMPT_DETAILS;
else if (fp->status == DPV_STATUS_RUNNING)
dstate = DPROMPT_DETAILS;
else
dstate = DPROMPT_END_STATE;
}
else if (dialog_test) /* status/length ignored */
dstate = pct < 100 ?
DPROMPT_PBAR : DPROMPT_END_STATE;
else if (fp->status == DPV_STATUS_RUNNING)
dstate = fp->length < 0 ?
DPROMPT_DETAILS : DPROMPT_PBAR;
else /* not running */
dstate = fp->length < 0 ?
DPROMPT_DETAILS : DPROMPT_END_STATE;
} else { /* before curfile */
if (dialog_test)
dstate = DPROMPT_END_STATE;
else
dstate = fp->length < 0 ?
DPROMPT_DETAILS : DPROMPT_END_STATE;
}
format = use_color ?
" [\\Z%c%s%-*s%s%-*s\\Zn]\\n" :
" [%-*s%s%-*s]\\n";
if (fp->status == DPV_STATUS_FAILED) {
bg_code = "\\Zr\\Z1"; /* Red */
estext_lsize = fail_lsize;
estext_rsize = fail_rsize;
estext_size = fail_size;
estext = fail;
} else { /* e.g., DPV_STATUS_DONE */
bg_code = "\\Zr\\Z2"; /* Green */
estext_lsize = done_lsize;
estext_rsize = done_rsize;
estext_size = done_size;
estext = done;
}
switch (dstate) {
case DPROMPT_PENDING: /* Future file(s) */
dprompt_add(" [%-*s%s%-*s]\\n",
pend_lsize, "", pend, pend_rsize, "");
break;
case DPROMPT_PBAR: /* Current file */
dprompt_add(" [%s]\\n", pbar);
break;
case DPROMPT_END_STATE: /* Past/Current file(s) */
if (use_color)
dprompt_add(format, bold_code, bg_code,
estext_lsize, "", estext,
estext_rsize, "");
else
dprompt_add(format,
estext_lsize, "", estext,
estext_rsize, "");
break;
case DPROMPT_DETAILS: /* Past/Current file(s) */
humanize_number(human, pbar_size + 2, fp->read, "",
HN_AUTOSCALE, HN_NOSPACE | HN_DIVISOR_1000);
/* Calculate center alignment */
hlen = (int)strlen(human);
lsize = (pbar_size - hlen) / 2;
rsize = lsize;
if ((lsize+hlen+rsize) != pbar_size)
rsize++;
if (use_color)
dprompt_add(format, bold_code, bg_code,
lsize, "", human, rsize, "");
else
dprompt_add(format,
lsize, "", human, rsize, "");
break;
case DPROMPT_CUSTOM_MSG: /* File-specific message override */
snprintf(msg, PROMPT_MAX + 1, "%s", fp->msg);
if (pbar_size < (mesg_size = strlen(msg))) {
mesg_lsize = mesg_rsize = 0;
*(msg + pbar_size) = '\0';
mesg_size = pbar_size;
} else {
mesg_lsize = (pbar_size - mesg_size) / 2;
mesg_rsize = mesg_lsize;
if ((mesg_rsize + mesg_size + mesg_lsize)
!= pbar_size)
mesg_rsize++;
}
if (use_color)
dprompt_add(format, bold_code, bg_code,
mesg_lsize, "", msg, mesg_rsize, "");
else
dprompt_add(format, mesg_lsize, "", msg,
mesg_rsize, "");
break;
case DPROMPT_MINIMAL: /* Short progress bar, minimal room */
if (use_color)
dprompt_add(format, bold_code, bg_code,
pbar_size, "", "", 0, "");
else
dprompt_add(format, pbar_size, "", "", 0, "");
break;
case DPROMPT_NONE: /* pbar_size < 0 */
/* FALLTHROUGH */
default:
dprompt_add(" \\n");
/*
* NB: Leading space required for the case when
* spin_char() returns a single backslash [\] which
* without the space, changes the meaning of `\n'
*/
}
/* Stop building if we've hit the internal limit */
if (nthfile >= display_limit)
break;
/* If this is the current file, all others are pending */
if (fp == curfile)
after_curfile = TRUE;
}
/*
* Since we cannot change the height/width of the [X]dialog(1) widget
* after spawn, to make things look nice let's pad the height so that
* the `-a text' always appears in the same spot.
*
* NOTE: fheight is calculated in dprompt_init(). It represents the
* maximum height required to display the set of items (broken up into
* pieces of display_limit chunks) whose names contain the most
* newlines for any given set.
*/
while (nlines < fheight) {
dprompt_add("\n");
nlines++;
}
return (nthfile);
}
/*
* Process the dpv_file_node linked-list of named files, re-generating the
* [X]dialog(1) `--gauge' prompt text for the current state of transfers.
*/
void
dprompt_recreate(struct dpv_file_node *file_list,
struct dpv_file_node *curfile, int pct)
{
size_t len;
/*
* Re-Build the prompt text
*/
dprompt_clear();
if (display_limit > 0)
dprompt_add_files(file_list, curfile, pct);
/* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
if (use_xdialog) {
/* Replace `\n' with `\n\\n\n' in dprompt */
len = strlen(dprompt);
len += strcount(dprompt, "\\n") * 5; /* +5 chars per count */
if (len > PROMPT_MAX)
errx(EXIT_FAILURE, "%s: Oops, dprompt buffer overflow "
"(%zu > %i)", __func__, len, PROMPT_MAX);
if (replaceall(dprompt, "\\n", "\n\\n\n") < 0)
err(EXIT_FAILURE, "%s: replaceall()", __func__);
}
else if (use_libdialog)
strexpandnl(dprompt);
}
/*
* Print the [X]dialog(1) `--gauge' prompt text to a buffer.
*/
int
dprompt_sprint(char * restrict str, const char *prefix, const char *append)
{
return (snprintf(str, PROMPT_MAX, "%s%s%s%s", use_color ? "\\Zn" : "",
prefix ? prefix : "", dprompt, append ? append : ""));
}
/*
* Print the [X]dialog(1) `--gauge' prompt text to file descriptor fd (could
* be STDOUT_FILENO or a pipe(2) file descriptor to actual [X]dialog(1)).
*/
void
dprompt_dprint(int fd, const char *prefix, const char *append, int overall)
{
int percent = gauge_percent;
if (overall >= 0 && overall <= 100)
gauge_percent = percent = overall;
dprintf(fd, "XXX\n%s%s%s%s\nXXX\n%i\n", use_color ? "\\Zn" : "",
prefix ? prefix : "", dprompt, append ? append : "", percent);
fsync(fd);
}
/*
* Print the dialog(3) `gauge' prompt text using libdialog.
*/
void
dprompt_libprint(const char *prefix, const char *append, int overall)
{
int percent = gauge_percent;
char buf[DPV_PPROMPT_MAX + DPV_APROMPT_MAX + DPV_DISPLAY_LIMIT * 1024];
dprompt_sprint(buf, prefix, append);
if (overall >= 0 && overall <= 100)
gauge_percent = percent = overall;
gauge = dlg_reallocate_gauge(gauge, title == NULL ? "" : title,
buf, dheight, dwidth, percent);
dlg_update_gauge(gauge, percent);
}
/*
* Free allocated items initialized by dprompt_init()
*/
void
dprompt_free(void)
{
if ((dprompt_free_mask & FM_DONE) != 0) {
dprompt_free_mask ^= FM_DONE;
free(done);
done = NULL;
}
if ((dprompt_free_mask & FM_FAIL) != 0) {
dprompt_free_mask ^= FM_FAIL;
free(fail);
fail = NULL;
}
if ((dprompt_free_mask & FM_PEND) != 0) {
dprompt_free_mask ^= FM_PEND;
free(pend);
pend = NULL;
}
}

59
lib/libdpv/dprompt.h Normal file
View File

@ -0,0 +1,59 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _DPROMPT_H_
#define _DPROMPT_H_
#include <sys/cdefs.h>
#include "dpv.h"
/* Display characteristics */
#define ENV_MSG_DONE "msg_done"
#define ENV_MSG_FAIL "msg_fail"
#define ENV_MSG_PENDING "msg_pending"
extern int display_limit;
extern int label_size;
extern int pbar_size;
__BEGIN_DECLS
void dprompt_clear(void);
void dprompt_dprint(int _fd, const char *_prefix, const char *_append,
int _overall);
void dprompt_free(void);
void dprompt_init(struct dpv_file_node *_file_list);
void dprompt_libprint(const char *_prefix, const char *_append,
int _overall);
void dprompt_recreate(struct dpv_file_node *_file_list,
struct dpv_file_node *_curfile, int _pct);
int dprompt_add(const char *_format, ...);
int dprompt_sprint(char * restrict _str, const char *_prefix,
const char *_append);
__END_DECLS
#endif /* !_DPROMPT_H_ */

510
lib/libdpv/dpv.3 Normal file
View File

@ -0,0 +1,510 @@
.\" 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 Oct 24, 2014
.Dt DPV 3
.Os
.Sh NAME
.Nm dpv
.Nd dialog progress view library
.Sh LIBRARY
.Lb libdpv
.Sh SYNOPSIS
.In dpv.h
.Ft int
.Fo dpv
.Fa "struct dpv_config *config, struct dpv_file_node *file_list"
.Fc
.Ft void
.Fo dpv_free
.Fa "void"
.Fc
.Sh DESCRIPTION
The
.Nm
library provides an interface for creating complex
.Dq gauge
widgets for displaying progress on various actions.
The
.Nm
library can display progress with one of
.Xr dialog 3 ,
.Xr dialog 1 ,
or
.Xr Xdialog 1
.Pq x11/xdialog from the ports tree .
.Pp
The
.Fn dpv
.Fa config
argument contains the following properties for configuring global display
features:
.Bd -literal -offset indent
struct dpv_config {
enum dpv_display display_type; /* Def. DPV_DISPLAY_LIBDIALOG */
enum dpv_output output_type; /* Default DPV_OUTPUT_NONE */
int debug; /* Enable debug on stderr */
int display_limit; /* Files/page. Default -1 */
int label_size; /* Label size. Default 28 */
int pbar_size; /* Mini-progress size */
int dialog_updates_per_second; /* Default 16 */
int status_updates_per_second; /* Default 2 */
uint16_t options; /* Default 0 (none) */
char *title; /* Widget title */
char *backtitle; /* Widget backtitle */
char *aprompt; /* Append. Default NULL */
char *pprompt; /* Prefix. Default NULL */
char *msg_done; /* Default `Done' */
char *msg_fail; /* Default `Fail' */
char *msg_pending; /* Default `Pending' */
char *output; /* Output format string */
const char *status_solo; /* dialog(3) solo-status format.
* Default DPV_STATUS_SOLO */
const char *status_many; /* dialog(3) many-status format.
* Default DPV_STATUS_MANY */
/*
* Function pointer; action to perform data transfer
*/
int (*action)(struct dpv_file_node *file, int out);
};
enum dpv_display {
DPV_DISPLAY_LIBDIALOG = 0, /* Use dialog(3) (default) */
DPV_DISPLAY_STDOUT, /* Use stdout */
DPV_DISPLAY_DIALOG, /* Use spawned dialog(1) */
DPV_DISPLAY_XDIALOG, /* Use spawned Xdialog(1) */
};
enum dpv_output {
DPV_OUTPUT_NONE = 0, /* No output (default) */
DPV_OUTPUT_FILE, /* Read `output' member as file path */
DPV_OUTPUT_SHELL, /* Read `output' member as shell cmd */
};
.Ed
.Pp
The
.Va options
member of the
.Fn dpv
.Fa config
argument is a mask of bit fields indicating various processing options.
Possible flags are as follows:
.Bl -tag -width DPV_NO_OVERRUN
.It Dv DPV_TEST_MODE
Enable test mode.
In test mode, the
.Fn action
callback of the
.Fa config
argument is not called but instead simulated-data is used to drive progress.
Appends
.Dq [TEST MODE]
to the status line
.Po
to override, set the
.Va status_format
member of the
.Fn dpv
.Fa config
argument;
e.g., to
.Dv DPV_STATUS_DEFAULT
.Pc .
.It Dv DPV_WIDE_MODE
Enable wide mode.
In wide mode, the length of the
.Va aprompt
and
.Va pprompt
members of the
.Fn dpv
.Fa config
argument will bump the width of the gauge widget.
Prompts wider than the maximum width will wrap
.Po
unless using
.Xr Xdialog 1 ;
see BUGS section below
.Pc .
.It Dv DPV_NO_LABELS
Disables the display of labels associated with each transfer
.Po
.Va label_size
member of
.Fn dpv
.Fa config
argument is ignored
.Pc .
.It Dv DPV_USE_COLOR
Force the use of color even if the
.Va display_type
does not support color
.Po
.Ev USE_COLOR
environment variable is ignored
.Pc .
.It Dv DPV_NO_OVERRUN
When enabled, callbacks for the current
.Vt dpv_file_node
are terminated when
.Fn action
returns 100 or greater
.Po
alleviates the need to change the
.Va status
of the current
.Vt dpv_file_node
but may also cause file truncation if the stream exceeds expected length
.Pc .
.El
.Pp
The
.Fa file_list
argument to
.Fn dpv
is a pointer to a
.Dq linked-list ,
described as follows in
.In dpv.h :
.Bd -literal -offset indent
struct dpv_file_node {
enum dpv_status status; /* status of read operation */
char *msg; /* display instead of "Done/Fail" */
char *name; /* name of file to read */
char *path; /* path to file */
long long length; /* expected size */
long long read; /* number units read (e.g., bytes) */
struct dpv_file_node *next;/* pointer to next (end with NULL) */
};
.Ed
.Pp
For each of the items in the
.Fa file_list
.Dq linked-list
argument, the
.Fn action
callback member of the
.Fn dpv
.Fa config
argument is called.
The
.Fn action
function should perform a
.Dq nominal
action on the file and return.
The return value of
.Vt int
represents the current progress percentage
.Pq 0-100
for the current file.
.Pp
The
.Fn action
callback provides two variables for each call.
.Fa file
provides a reference to the current
.Vt dpv_file_node
being processed.
.Fa out
provides a file descriptor where the data should go.
.Pp
If the
.Va output
member of the
.Fn dpv
.Fa config
argument was set to DPV_OUTPUT_NONE
.Pq default ; when invoking Fn dpv ,
the
.Fa out
file descriptor of
.Fn action
will be zero and should be ignored.
If
.Fa output
was set to DPV_OUTPUT_FILE,
.Fa out
will be an open file descriptor to a file.
If
.Fa output
was set to DPV_OUTPUT_SHELL,
.Fa out
will be an open file descriptor to a pipe for a spawned shell program.
When
.Fa out
is greater than zero, you should write any data you have read back to
.Fa out .
.Pp
To abort
.Fn dpv ,
either from the
.Fn action
callback or asynchronously from a signal handler, two globals are provided via
.In dpv.h :
.Bd -literal -offset indent
extern int dpv_interrupt; /* Set to TRUE in interrupt handler */
extern int dpv_abort; /* Set to true in callback to abort */
.Ed
.Pp
These globals are not automatically reset and must be manually maintained.
Don't forget to reset these globals before subsequent invocations of
.Fn dpv
when making multiple calls from the same program.
.Pp
In addition, the
.Va status
member of the
.Fn action
.Fa file
argument can be used to control callbacks for the current file.
The
.Va status
member can be set to any of the following from
.In dpv.h :
.Bd -literal -offset indent
enum dpv_status {
DPV_STATUS_RUNNING = 0, /* Running (default) */
DPV_STATUS_DONE, /* Completed */
DPV_STATUS_FAILED, /* Oops, something went wrong */
};
.Ed
.Pp
The default
.Fa status
is zero, DPV_STATUS_RUNING, which keeps the callbacks coming for the current
.Fn file .
Setting
.Ql file->status
to anything other than DPV_STATUS_RUNNING will cause
.Fn dpv
to loop to the next file, effecting the next callback, if any.
.Pp
The
.Fn action
callback is responsible for calculating percentages and
.Pq recommended
maintaining a
.Nm
global counter so
.Fn dpv
can display throughput statistics.
Percentages are reported through the
.Vt int
return value of the
.Fn action
callback.
Throughput statistics are calculated from the following global
.Vt int
in
.In dpv.h :
.Bd -literal -offset indent
extern int dpv_overall_read;
.Ed
.Pp
This should be set to the number of bytes that have been read for all files.
Throughput information is displayed in the status line
.Pq only available when using Xr dialog 3
at the bottom of the screen.
See DPV_DISPLAY_LIBDIALOG above.
.Pp
Note that
.Va dpv_overall_read
does not have to represent bytes.
For example, you can change the
.Va status_format
to display something other than
.Dq Li bytes
and increment
.Va dpv_overall_read
accordingly
.Pq e.g., counting lines .
.Pp
When
.Fn dpv
is processing the current file, the
.Va length
and
.Va read
members of the
.Fn action
.Fa file
argument are used for calculating the display of mini progress bars
.Po
if enabled; see
.Va pbar_size
above
.Pc .
If the
.Va length
member of the current
.Fa file
is less than zero
.Pq indicating an unknown file length ,
a
.Xr humanize_number 3
version of the
.Va read
member is used instead of a traditional progress bar.
Otherwise a progress bar is calculated as percentage read to file length.
.Fn action
callback must maintain these member values for mini-progress bars.
.Pp
The
.Fn dpv_free
function performs
.Xr free 3
on private global variables initialized by
.Fn dpv .
.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 Dv DPV_DISPLAY_DIALOG
or
.Xr Xdialog 1
.Pq requires Dv DPV_DISPLAY_XDIALOG ;
default is either
.Ql dialog
.Pq for Dv DPV_DISPLAY_DIALOG
or
.Ql Xdialog
.Pq for Dv DPV_DISPLAY_XDIALOG .
.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 .
.It Ev msg_done Ev msg_fail Ev msg_pending
Internationalization strings for overriding the default English strings
.Ql Done ,
.Ql Fail ,
and
.Ql Pending
respectively.
To prevent their usage, explicitly set the
.Va msg_done ,
.Va msg_fail ,
and
.Va msg_pending
members of
.Fn dpv
.Fa config
argument to default macros
.Pq DPV_DONE_DEFAULT, DPV_FAIL_DEFAULT, and DPV_PENDING_DEFAULT
or desired values.
.El
.Sh FILES
.Bl -tag -width ".Pa $HOME/.dialogrc" -compact
.It Pa $HOME/.dialogrc
.El
.Sh SEE ALSO
.Xr dialog 1 ,
.Xr dialog 3 ,
.Xr Xdialog 1
.Sh HISTORY
The
.Nm
library first appeared in
.Fx 11.0 .
.Sh AUTHORS
.An Devin Teske Aq dteske@FreeBSD.org
.Sh BUGS
.Xr Xdialog 1 ,
when given both
.Ql Fl -title Ar title
.Po
see above
.Ql Va title
member of
.Va struct dpv_config
.Pc
and
.Ql Fl -backtitle Ar backtitle
.Po
see above
.Ql Va backtitle
member of
.Va struct dpv_config
.Pc ,
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 .
Embed escaped newlines within prompt text(s) to force line breaks.
.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 3
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 unsetenv 3
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.

721
lib/libdpv/dpv.c Normal file
View File

@ -0,0 +1,721 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <dialog.h>
#include <err.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string_m.h>
#include <unistd.h>
#include "dialog_util.h"
#include "dialogrc.h"
#include "dprompt.h"
#include "dpv.h"
#include "dpv_private.h"
#include "status.h"
#include "util.h"
/* Test Mechanics (Only used when dpv_config.options |= DPV_TEST_MODE) */
#define INCREMENT 1 /* Increment % per-pass test-mode */
#define XDIALOG_INCREMENT 15 /* different for slower Xdialog(1) */
static uint8_t increment = INCREMENT;
/* Debugging */
uint8_t debug = FALSE;
/* Data to process */
int dpv_interrupt = FALSE;
int dpv_abort = FALSE;
unsigned int dpv_nfiles = 0;
/* Data processing */
long long dpv_overall_read = 0;
static char pathbuf[PATH_MAX];
/* Extra display information */
uint8_t no_labels = FALSE; /* dpv_config.options & DPV_NO_LABELS */
uint8_t wide = FALSE; /* dpv_config.options & DPV_WIDE_MODE */
char *aprompt = NULL; /* dpv_config.aprompt */
char *msg_done = NULL; /* dpv_config.msg_done */
char *msg_fail = NULL; /* dpv_config.msg_fail */
char *msg_pending = NULL; /* dpv_config.msg_pending */
char *pprompt = NULL; /* dpv_config.pprompt */
/* Status-Line format for when using dialog(3) */
const char *status_format_custom = NULL;
char status_format_default[DPV_STATUS_FORMAT_MAX];
/*
* Takes a pointer to a dpv_config structure containing layout details and
* pointer to initial element in a linked-list of dpv_file_node structures,
* each presenting a file to process. Executes the `action' function passed-in
* as a member to the `config' structure argument.
*/
int
dpv(struct dpv_config *config, struct dpv_file_node *file_list)
{
char c;
uint8_t keep_going;
uint8_t nls = FALSE; /* See dialog_prompt_nlstate() */
uint8_t no_overrun = FALSE;
uint8_t pprompt_nls = FALSE; /* See dialog_prompt_nlstate() */
uint8_t shrink_label_size = FALSE;
mode_t mask;
uint16_t options;
char *cp;
char *fc;
char *last;
char *name;
char *output;
const char *status_fmt;
const char *path_fmt;
enum dpv_display display_type;
enum dpv_output output_type;
enum dpv_status status;
int (*action)(struct dpv_file_node *file, int out);
int backslash;
int dialog_last_update = 0;
int dialog_old_nthfile = 0;
int dialog_old_seconds = -1;
int dialog_out = STDOUT_FILENO;
int dialog_update_usec = 0;
int dialog_updates_per_second;
int files_left;
int max_cols;
int nthfile = 0;
int output_out;
int overall = 0;
int pct;
int res;
int seconds;
int status_last_update = 0;
int status_old_nthfile = 0;
int status_old_seconds = -1;
int status_update_usec = 0;
int status_updates_per_second;
pid_t output_pid;
pid_t pid;
size_t len;
struct dpv_file_node *curfile;
struct dpv_file_node *first_file;
struct dpv_file_node *list_head;
struct timeval now;
struct timeval start;
char init_prompt[PROMPT_MAX + 1] = "";
/* Initialize globals to default values */
aprompt = NULL;
pprompt = NULL;
options = 0;
action = NULL;
backtitle = NULL;
debug = FALSE;
dialog_test = FALSE;
dialog_updates_per_second = DIALOG_UPDATES_PER_SEC;
display_limit = DISPLAY_LIMIT_DEFAULT;
display_type = DPV_DISPLAY_LIBDIALOG;
label_size = LABEL_SIZE_DEFAULT;
msg_done = NULL;
msg_fail = NULL;
msg_pending = NULL;
no_labels = FALSE;
output = NULL;
output_type = DPV_OUTPUT_NONE;
pbar_size = PBAR_SIZE_DEFAULT;
status_format_custom = NULL;
status_updates_per_second = STATUS_UPDATES_PER_SEC;
title = NULL;
wide = FALSE;
/* Process config options (overriding defaults) */
if (config != NULL) {
if (config->aprompt != NULL) {
if (aprompt == NULL) {
aprompt = malloc(DPV_APROMPT_MAX);
if (aprompt == NULL)
return (-1);
}
snprintf(aprompt, DPV_APROMPT_MAX, "%s",
config->aprompt);
}
if (config->pprompt != NULL) {
if (pprompt == NULL) {
pprompt = malloc(DPV_PPROMPT_MAX + 2);
/* +2 is for implicit "\n" appended later */
if (pprompt == NULL)
return (-1);
}
snprintf(pprompt, DPV_APROMPT_MAX, "%s",
config->pprompt);
}
options = config->options;
action = config->action;
backtitle = config->backtitle;
debug = config->debug;
dialog_test = ((options & DPV_TEST_MODE) != 0);
dialog_updates_per_second = config->dialog_updates_per_second;
display_limit = config->display_limit;
display_type = config->display_type;
label_size = config->label_size;
msg_done = (char *)config->msg_done;
msg_fail = (char *)config->msg_fail;
msg_pending = (char *)config->msg_pending;
no_labels = ((options & DPV_NO_LABELS) != 0);
no_overrun = ((options & DPV_NO_OVERRUN) != 0);
output = config->output;
output_type = config->output_type;
pbar_size = config->pbar_size;
status_updates_per_second = config->status_updates_per_second;
title = config->title;
wide = ((options & DPV_WIDE_MODE) != 0);
/* Enforce some minimums (pedantic) */
if (display_limit < -1)
display_limit = -1;
if (label_size < -1)
label_size = -1;
if (pbar_size < -1)
pbar_size = -1;
/* For the mini-pbar, -1 means hide, zero is invalid unless
* only one file is given */
if (pbar_size == 0) {
if (file_list == NULL || file_list->next == NULL)
pbar_size = -1;
else
pbar_size = PBAR_SIZE_DEFAULT;
}
/* For the label, -1 means auto-size, zero is invalid unless
* specifically requested through the use of options flag */
if (label_size == 0 && no_labels == FALSE)
label_size = LABEL_SIZE_DEFAULT;
/* Status update should not be zero */
if (status_updates_per_second == 0)
status_updates_per_second = STATUS_UPDATES_PER_SEC;
} /* config != NULL */
/* Process the type of display we've been requested to produce */
switch (display_type) {
case DPV_DISPLAY_STDOUT:
debug = TRUE;
use_color = FALSE;
use_dialog = FALSE;
use_libdialog = FALSE;
use_xdialog = FALSE;
break;
case DPV_DISPLAY_DIALOG:
use_color = TRUE;
use_dialog = TRUE;
use_libdialog = FALSE;
use_xdialog = FALSE;
break;
case DPV_DISPLAY_XDIALOG:
snprintf(dialog, PATH_MAX, XDIALOG);
use_color = FALSE;
use_dialog = FALSE;
use_libdialog = FALSE;
use_xdialog = TRUE;
break;
default:
use_color = TRUE;
use_dialog = FALSE;
use_libdialog = TRUE;
use_xdialog = FALSE;
break;
} /* display_type */
/* Enforce additional minimums that require knowing our display type */
if (dialog_updates_per_second == 0)
dialog_updates_per_second = use_xdialog ?
XDIALOG_UPDATES_PER_SEC : DIALOG_UPDATES_PER_SEC;
/* Allow forceful override of use_color */
if (config != NULL && (config->options & DPV_USE_COLOR) != 0)
use_color = TRUE;
/* Count the number of files in provided list of dpv_file_node's */
if (use_dialog && pprompt != NULL && *pprompt != '\0')
pprompt_nls = dialog_prompt_nlstate(pprompt);
max_cols = dialog_maxcols();
if (label_size == -1)
shrink_label_size = TRUE;
/* Process file arguments */
for (curfile = file_list; curfile != NULL; curfile = curfile->next) {
dpv_nfiles++;
/* dialog(3) only expands literal newlines */
if (use_libdialog) strexpandnl(curfile->name);
/* Optionally calculate label size for file */
if (shrink_label_size) {
nls = FALSE;
name = curfile->name;
if (curfile == file_list)
nls = pprompt_nls;
last = (char *)dialog_prompt_lastline(name, nls);
if (use_dialog) {
c = *last;
*last = '\0';
nls = dialog_prompt_nlstate(name);
*last = c;
}
len = dialog_prompt_longestline(last, nls);
if ((int)len > (label_size - 3)) {
if (label_size > 0)
label_size += 3;
label_size = len;
/* Room for ellipsis (unless NULL) */
if (label_size > 0)
label_size += 3;
}
if (max_cols > 0 && label_size > (max_cols - pbar_size
- 9))
label_size = max_cols - pbar_size - 9;
}
if (debug)
warnx("label=[%s] path=[%s] size=%lli",
curfile->name, curfile->path, curfile->length);
} /* file_list */
/* Optionally process the contents of DIALOGRC (~/.dialogrc) */
if (use_dialog) {
res = parse_dialogrc();
if (debug && res == 0) {
warnx("Successfully read `%s' config file", DIALOGRC);
warnx("use_shadow = %i (Boolean)", use_shadow);
warnx("use_colors = %i (Boolean)", use_colors);
warnx("gauge_color=[%s] (FBH)", gauge_color);
}
} else if (use_libdialog) {
init_dialog(stdin, stdout);
use_shadow = dialog_state.use_shadow;
use_colors = dialog_state.use_colors;
gauge_color[0] = 48 + dlg_color_table[GAUGE_ATTR].fg;
gauge_color[1] = 48 + dlg_color_table[GAUGE_ATTR].bg;
gauge_color[2] = dlg_color_table[GAUGE_ATTR].hilite ?
'b' : 'B';
gauge_color[3] = '\0';
end_dialog();
if (debug) {
warnx("Finished initializing dialog(3) library");
warnx("use_shadow = %i (Boolean)", use_shadow);
warnx("use_colors = %i (Boolean)", use_colors);
warnx("gauge_color=[%s] (FBH)", gauge_color);
}
}
/* Enable mini progress bar automatically for stdin streams if unable
* to calculate progress (missing `lines:' syntax). */
if (dpv_nfiles <= 1 && file_list != NULL && file_list->length < 0 &&
!dialog_test)
pbar_size = PBAR_SIZE_DEFAULT;
/* If $USE_COLOR is set and non-NULL enable color; otherwise disable */
if ((cp = getenv(ENV_USE_COLOR)) != 0)
use_color = *cp != '\0' ? 1 : 0;
/* Print error and return `-1' if not given at least one name */
if (dpv_nfiles == 0) {
warnx("%s: no labels provided", __func__);
return (-1);
} else if (debug)
warnx("%s: %u label%s provided", __func__, dpv_nfiles,
dpv_nfiles == 1 ? "" : "s");
/* If only one file and pbar size is zero, default to `-1' */
if (dpv_nfiles <= 1 && pbar_size == 0)
pbar_size = -1;
/* Print some debugging information */
if (debug) {
warnx("%s: %s(%i) max rows x cols = %i x %i",
__func__, use_xdialog ? XDIALOG : DIALOG,
use_libdialog ? 3 : 1, dialog_maxrows(),
dialog_maxcols());
}
/* Xdialog(1) updates a lot slower than dialog(1) */
if (dialog_test && use_xdialog)
increment = XDIALOG_INCREMENT;
/* Always add implicit newline to pprompt (when specified) */
if (pprompt != NULL && *pprompt != '\0') {
len = strlen(pprompt);
/*
* NOTE: pprompt = malloc(PPROMPT_MAX + 2)
* NOTE: (see getopt(2) section above for pprompt allocation)
*/
pprompt[len++] = '\\';
pprompt[len++] = 'n';
pprompt[len++] = '\0';
}
/* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
if (use_xdialog && pprompt != NULL) {
/* Replace `\n' with `\n\\n\n' in pprompt */
len = strlen(pprompt);
len += strcount(pprompt, "\\n") * 2;
if (len > DPV_PPROMPT_MAX)
errx(EXIT_FAILURE, "%s: Oops, pprompt buffer overflow "
"(%zu > %i)", __func__, len, DPV_PPROMPT_MAX);
if (replaceall(pprompt, "\\n", "\n\\n\n") < 0)
err(EXIT_FAILURE, "%s: replaceall()", __func__);
}
/* libdialog requires literal newlines */
else if (use_libdialog && pprompt != NULL)
strexpandnl(pprompt);
/* Xdialog(1) requires newlines (a) escaped and (b) in triplicate */
if (use_xdialog && aprompt != NULL) {
/* Replace `\n' with `\n\\n\n' in aprompt */
len = strlen(aprompt);
len += strcount(aprompt, "\\n") * 2;
if (len > DPV_APROMPT_MAX)
errx(EXIT_FAILURE, "%s: Oops, aprompt buffer overflow "
" (%zu > %i)", __func__, len, DPV_APROMPT_MAX);
if (replaceall(aprompt, "\\n", "\n\\n\n") < 0)
err(EXIT_FAILURE, "%s: replaceall()", __func__);
}
/* libdialog requires literal newlines */
else if (use_libdialog && aprompt != NULL)
strexpandnl(aprompt);
/*
* Warn user about an obscure dialog(1) bug (neither Xdialog(1) nor
* libdialog are affected) in the `--gauge' widget. If the first non-
* whitespace letter of "{new_prompt}" in "XXX\n{new_prompt}\nXXX\n"
* is a number, the number can sometimes be mistaken for a percentage
* to the overall progressbar. Other nasty side-effects such as the
* entire prompt not displaying or displaying improperly are caused by
* this bug too.
*
* NOTE: When we can use color, we have a work-around... prefix the
* output with `\Zn' (used to terminate ANSI and reset to normal).
*/
if (use_dialog && !use_color) {
backslash = 0;
/* First, check pprompt (falls through if NULL) */
fc = pprompt;
while (fc != NULL && *fc != '\0') {
if (*fc == '\n') /* leading literal newline OK */
break;
if (!isspace(*fc) && *fc != '\\' && backslash == 0)
break;
else if (backslash > 0 && *fc != 'n')
break;
else if (*fc == '\\') {
backslash++;
if (backslash > 2)
break; /* we're safe */
}
fc++;
}
/* First non-whitespace character that dialog(1) will see */
if (fc != NULL && *fc >= '0' && *fc <= '9')
warnx("%s: WARNING! text argument to `-p' begins with "
"a number (not recommended)", __func__);
else if (fc > pprompt)
warnx("%s: WARNING! text argument to `-p' begins with "
"whitespace (not recommended)", __func__);
/*
* If no pprompt or pprompt is all whitespace, check the first
* file name provided to make sure it is alright too.
*/
if ((pprompt == NULL || *fc == '\0') && file_list != NULL) {
first_file = file_list;
fc = first_file->name;
while (fc != NULL && *fc != '\0' && isspace(*fc))
fc++;
/* First non-whitespace char that dialog(1) will see */
if (fc != NULL && *fc >= '0' && *fc <= '9')
warnx("%s: WARNING! File name `%s' begins "
"with a number (use `-p text' for safety)",
__func__, first_file->name);
}
}
dprompt_init(file_list);
/* Reads: label_size pbar_size pprompt aprompt dpv_nfiles */
/* Inits: dheight and dwidth */
if (!debug) {
/* Internally create the initial `--gauge' prompt text */
dprompt_recreate(file_list, (struct dpv_file_node *)NULL, 0);
/* Spawn [X]dialog(1) `--gauge', returning pipe descriptor */
if (use_libdialog) {
status_printf("");
dprompt_libprint(pprompt, aprompt, 0);
} else {
dprompt_sprint(init_prompt, pprompt, aprompt);
dialog_out = dialog_spawn_gauge(init_prompt, &pid);
dprompt_dprint(dialog_out, pprompt, aprompt, 0);
}
} /* !debug */
/* Seed the random(3) generator */
if (dialog_test)
srandom(0xf1eeface);
/* Set default/custom status line format */
if (dpv_nfiles > 1) {
snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s",
DPV_STATUS_MANY);
status_format_custom = config->status_many;
} else {
snprintf(status_format_default, DPV_STATUS_FORMAT_MAX, "%s",
DPV_STATUS_SOLO);
status_format_custom = config->status_solo;
}
/* Add test mode identifier to default status line if enabled */
if (dialog_test && (strlen(status_format_default) + 12) <
DPV_STATUS_FORMAT_MAX)
strcat(status_format_default, " [TEST MODE]");
/* Verify custom status format */
status_fmt = fmtcheck(status_format_custom, status_format_default);
if (status_format_custom != NULL &&
status_fmt == status_format_default) {
warnx("WARNING! Invalid status_format configuration `%s'",
status_format_custom);
warnx("Default status_format `%s'", status_format_default);
}
/* Record when we started (used to prevent updating too quickly) */
(void)gettimeofday(&start, (struct timezone *)NULL);
/* Calculate number of microseconds in-between sub-second updates */
if (status_updates_per_second != 0)
status_update_usec = 1000000 / status_updates_per_second;
if (dialog_updates_per_second != 0)
dialog_update_usec = 1000000 / dialog_updates_per_second;
/*
* Process the file list [serially] (one for each argument passed)
*/
files_left = dpv_nfiles;
list_head = file_list;
for (curfile = file_list; curfile != NULL; curfile = curfile->next) {
keep_going = TRUE;
output_out = -1;
pct = 0;
nthfile++;
files_left--;
if (dpv_interrupt)
break;
if (dialog_test)
pct = 0 - increment;
/* Attempt to spawn output program for this file */
if (!dialog_test && output != NULL) {
mask = umask(0022);
(void)umask(mask);
switch (output_type) {
case DPV_OUTPUT_SHELL:
output_out = shell_spawn_pipecmd(output,
curfile->name, &output_pid);
break;
case DPV_OUTPUT_FILE:
path_fmt = fmtcheck(output, "%s");
if (path_fmt == output)
len = snprintf(pathbuf,
PATH_MAX, output, curfile->name);
else
len = snprintf(pathbuf,
PATH_MAX, "%s", output);
if (len >= PATH_MAX) {
warnx("%s:%d:%s: pathbuf[%u] too small"
"to hold output argument",
__FILE__, __LINE__, __func__,
PATH_MAX);
return (-1);
}
if ((output_out = open(pathbuf,
O_CREAT|O_WRONLY, DEFFILEMODE & ~mask))
< 0) {
warn("%s", pathbuf);
return (-1);
}
break;
default:
break;
}
}
while (!dpv_interrupt && keep_going) {
if (dialog_test) {
usleep(50000);
pct += increment;
dpv_overall_read +=
(int)(random() / 512 / dpv_nfiles);
/* 512 limits fake readout to Megabytes */
} else if (action != NULL)
pct = action(curfile, output_out);
if (no_overrun || dialog_test)
keep_going = (pct < 100);
else {
status = curfile->status;
keep_going = (status == DPV_STATUS_RUNNING);
}
/* Get current time and calculate seconds elapsed */
gettimeofday(&now, (struct timezone *)NULL);
now.tv_sec = now.tv_sec - start.tv_sec;
now.tv_usec = now.tv_usec - start.tv_usec;
if (now.tv_usec < 0)
now.tv_sec--, now.tv_usec += 1000000;
seconds = now.tv_sec + (now.tv_usec / 1000000.0);
/* Update dialog (be it dialog(3), dialog(1), etc.) */
if ((dialog_updates_per_second != 0 &&
(
seconds != dialog_old_seconds ||
now.tv_usec - dialog_last_update >=
dialog_update_usec ||
nthfile != dialog_old_nthfile
)) || pct == 100
) {
/* Calculate overall progress (rounding up) */
overall = (100 * nthfile - 100 + pct) /
dpv_nfiles;
if (((100 * nthfile - 100 + pct) * 10 /
dpv_nfiles % 100) > 50)
overall++;
dprompt_recreate(list_head, curfile, pct);
if (use_libdialog && !debug) {
/* Update dialog(3) widget */
dprompt_libprint(pprompt, aprompt,
overall);
} else {
/* stdout, dialog(1), or Xdialog(1) */
dprompt_dprint(dialog_out, pprompt,
aprompt, overall);
fsync(dialog_out);
}
dialog_old_seconds = seconds;
dialog_old_nthfile = nthfile;
dialog_last_update = now.tv_usec;
}
/* Update the status line */
if ((use_libdialog && !debug) &&
status_updates_per_second != 0 &&
(
keep_going != TRUE ||
seconds != status_old_seconds ||
now.tv_usec - status_last_update >=
status_update_usec ||
nthfile != status_old_nthfile
)
) {
status_printf(status_fmt, dpv_overall_read,
(dpv_overall_read / (seconds == 0 ? 1 :
seconds) * 1.0),
1, /* XXX until we add parallelism XXX */
files_left);
status_old_seconds = seconds;
status_old_nthfile = nthfile;
status_last_update = now.tv_usec;
}
}
if (!dialog_test && output_out >= 0) {
close(output_out);
waitpid(output_pid, (int *)NULL, 0);
}
if (dpv_abort)
break;
/* Advance head of list when we hit the max display lines */
if (display_limit > 0 && nthfile % display_limit == 0)
list_head = curfile->next;
}
if (!debug) {
if (use_libdialog)
end_dialog();
else {
close(dialog_out);
waitpid(pid, (int *)NULL, 0);
}
if (!dpv_interrupt)
printf("\n");
} else
warnx("%s: %lli lines read", __func__, dpv_overall_read);
if (dpv_interrupt || dpv_abort)
return (-1);
else
return (0);
}
/*
* Free allocated items initialized by dpv()
*/
void
dpv_free(void)
{
dialogrc_free();
dprompt_free();
dialog_maxsize_free();
if (aprompt != NULL) {
free(aprompt);
aprompt = NULL;
}
if (pprompt != NULL) {
free(pprompt);
pprompt = NULL;
}
status_free();
}

161
lib/libdpv/dpv.h Normal file
View File

@ -0,0 +1,161 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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_H_
#define _DPV_H_
#include <sys/types.h>
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* Data to process */
extern long long dpv_overall_read;
/* Interrupt flag */
extern int dpv_interrupt; /* Set to TRUE in interrupt handler */
extern int dpv_abort; /* Set to true in callback to abort */
/*
* Display types for use with display_type member of dpv_config structure
*/
enum dpv_display {
DPV_DISPLAY_LIBDIALOG = 0, /* Display using dialog(3) (default) */
DPV_DISPLAY_STDOUT, /* Display on stdout */
DPV_DISPLAY_DIALOG, /* Display using spawned dialog(1) */
DPV_DISPLAY_XDIALOG, /* Display using spawned Xdialog(1) */
};
/*
* Output types for use with output_type member of dpv_config structure
*/
enum dpv_output {
DPV_OUTPUT_NONE = 0, /* No output (default) */
DPV_OUTPUT_FILE, /* Read `output' member as file path */
DPV_OUTPUT_SHELL, /* Read `output' member as shell cmd */
};
/*
* Activity types for use with status member of dpv_file_node structure.
* If you set a status other than DPV_STATUS_RUNNING on the current file in the
* action callback of dpv_config structure, you'll end callbacks for that
* dpv_file_node.
*/
enum dpv_status {
DPV_STATUS_RUNNING = 0, /* Running (default) */
DPV_STATUS_DONE, /* Completed */
DPV_STATUS_FAILED, /* Oops, something went wrong */
};
/*
* Anatomy of file option; pass an array of these as dpv() file_list argument
* terminated with a NULL pointer.
*/
struct dpv_file_node {
enum dpv_status status; /* status of read operation */
char *msg; /* display instead of "Done/Fail" */
char *name; /* name of file to read */
char *path; /* path to file */
long long length; /* expected size */
long long read; /* number units read (e.g., bytes) */
struct dpv_file_node *next; /* pointer to next (end with NULL) */
};
/*
* Anatomy of config option to pass as dpv() config argument
*/
struct dpv_config {
enum dpv_display display_type; /* Display (default TYPE_LIBDIALOG) */
enum dpv_output output_type; /* Output (default TYPE_NONE) */
int debug; /* Enable debugging output on stderr */
int display_limit; /* Files per `page'. Default -1 */
int label_size; /* Label size. Default 28 */
int pbar_size; /* Mini-progress size. See dpv(3) */
int dialog_updates_per_second; /* Progress updates/s. Default 16 */
int status_updates_per_second; /* dialog(3) status updates/second.
* Default 2 */
uint16_t options; /* Special options. Default 0 */
char *title; /* widget title */
char *backtitle; /* Widget backtitle */
char *aprompt; /* Prompt append. Default NULL */
char *pprompt; /* Prompt prefix. Default NULL */
char *msg_done; /* Progress text. Default `Done' */
char *msg_fail; /* Progress text. Default `Fail' */
char *msg_pending; /* Progress text. Default `Pending' */
char *output; /* Output format string; see dpv(3) */
const char *status_solo; /* dialog(3) solo-status format.
* Default DPV_STATUS_SOLO */
const char *status_many; /* dialog(3) many-status format.
* Default DPV_STATUS_MANY */
/*
* Function pointer; action to perform data transfer
*/
int (*action)(struct dpv_file_node *file, int out);
};
/*
* Macros for dpv() options bitmask argument
*/
#define DPV_TEST_MODE 0x0001 /* Test mode (fake reading data) */
#define DPV_WIDE_MODE 0x0002 /* prefix/append bump dialog width */
#define DPV_NO_LABELS 0x0004 /* Hide file_node.name labels */
#define DPV_USE_COLOR 0x0008 /* Override to force color output */
#define DPV_NO_OVERRUN 0x0010 /* Stop transfers when they hit 100% */
/*
* Limits (modify with extreme care)
*/
#define DPV_APROMPT_MAX 4096 /* Buffer size for `-a text' */
#define DPV_DISPLAY_LIMIT 10 /* Max file progress lines */
#define DPV_PPROMPT_MAX 4096 /* Buffer size for `-p text' */
#define DPV_STATUS_FORMAT_MAX 80 /* Buffer size for `-u format' */
/*
* Extra display information
*/
#define DPV_STATUS_SOLO "%'10lli bytes read @ %'9.1f bytes/sec."
#define DPV_STATUS_MANY (DPV_STATUS_SOLO " [%i/%i busy/wait]")
/*
* Strings
*/
#define DPV_DONE_DEFAULT "Done"
#define DPV_FAIL_DEFAULT "Fail"
#define DPV_PENDING_DEFAULT "Pending"
__BEGIN_DECLS
void dpv_free(void);
int dpv(struct dpv_config *_config, struct dpv_file_node *_file_list);
__END_DECLS
#endif /* !_DPV_H_ */

66
lib/libdpv/dpv_private.h Normal file
View File

@ -0,0 +1,66 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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_PRIVATE_H_
#define _DPV_PRIVATE_H_
#include <sys/types.h>
/* Debugging */
extern uint8_t debug;
/* Data to process */
extern unsigned int dpv_nfiles;
/* Extra display information */
extern uint8_t no_labels;
extern uint8_t wide;
extern char *msg_done, *msg_fail, *msg_pending;
extern char *pprompt, *aprompt;
extern const char status_format[];
/* Defaults */
#define DIALOG_UPDATES_PER_SEC 16
#define XDIALOG_UPDATES_PER_SEC 4
#define DISPLAY_LIMIT_DEFAULT 0 /* Auto-calculate */
#define LABEL_SIZE_DEFAULT 28
#define PBAR_SIZE_DEFAULT 17
#define STATUS_UPDATES_PER_SEC 2
/* states for dprompt_add_files() of dprompt.c */
enum dprompt_state {
DPROMPT_NONE = 0, /* Default */
DPROMPT_PENDING, /* Pending */
DPROMPT_PBAR, /* Progress bar */
DPROMPT_END_STATE, /* Done/Fail */
DPROMPT_DETAILS, /* dpv_file_node->read */
DPROMPT_CUSTOM_MSG, /* dpv_file_node->msg */
DPROMPT_MINIMAL, /* whitespace */
};
#endif /* !_DPV_PRIVATE_H_ */

111
lib/libdpv/status.c Normal file
View File

@ -0,0 +1,111 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <curses.h>
#include <dialog.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "dialog_util.h"
#include "status.h"
/* static globals */
static char *status_buf = NULL;
static int status_bufsize = -1;
static int status_row;
static int status_width;
/*
* Print a `one-liner' status message at the bottom of the screen. Messages are
* trimmed to fit within the console length (ANSI coloring not accounted for).
*/
void
status_printf(const char *fmt, ...)
{
int n, attrs;
chtype color = dlg_color_pair(dlg_color_table[BUTTON_ACTIVE_ATTR].fg,
dlg_color_table[SCREEN_ATTR].bg) | A_BOLD;
va_list args;
status_row = tty_maxrows() - 1;
status_width = tty_maxcols();
/* NULL is a special convention meaning "erase the old stuff" */
if (fmt == NULL) {
move(status_row, 0);
clrtoeol();
return;
}
/* Resize buffer if terminal width is greater */
if ((status_width + 1) > status_bufsize) {
status_buf = realloc(status_buf, status_width + 1);
if (status_buf == NULL) {
status_bufsize = -1;
return;
}
status_bufsize = status_width + 1;
}
/* Print the message within a space-filled buffer */
memset(status_buf, ' ', status_width);
va_start(args, fmt);
n = vsnprintf(status_buf, status_width + 1, fmt, args);
va_end(args);
/* If vsnprintf(3) produced less bytes than the maximum, change the
* implicitly-added NUL-terminator into a space and terminate at max */
if (n < status_width) {
status_buf[n] = ' ';
status_buf[status_width] = '\0';
}
/* Print text in screen bg, button active fg, and bold */
attrs = getattrs(stdscr);
attrset(color);
mvaddstr(status_row, 0, status_buf);
attrset(attrs);
/* Seat the cursor over the last character at absolute lower-right */
move(status_row, status_width - 1);
refresh();
}
/*
* Free allocated items initialized by status_printf()
*/
void
status_free(void)
{
if (status_buf != NULL) {
free(status_buf);
status_buf = NULL;
}
}

43
lib/libdpv/status.h Normal file
View File

@ -0,0 +1,43 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _STATUS_H_
#define _STATUS_H_
#include <sys/cdefs.h>
/* dialog(3) dlg_color_table[] attributes */
#define SCREEN_ATTR 0 /* entry used for status line bg */
#define BUTTON_ACTIVE_ATTR 5 /* entry used for status line fg */
__BEGIN_DECLS
void status_free(void);
void status_printf(const char *_format, ...);
__END_DECLS
#endif /* !_STATUS_H_ */

107
lib/libdpv/util.c Normal file
View File

@ -0,0 +1,107 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <err.h>
#include <limits.h>
#include <spawn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "util.h"
extern char **environ;
static char cmdbuf[CMDBUFMAX] = "";
static char shellcmd[PATH_MAX] = PATH_SHELL;
static char *shellcmd_argv[6] = {
shellcmd,
__DECONST(char *, "-c"),
cmdbuf,
__DECONST(char *, "--"),
shellcmd,
NULL,
};
/*
* Spawn a sh(1) command. Writes the resulting process ID to the pid_t pointed
* at by `pid'. Returns a file descriptor (int) suitable for writing data to
* the spawned command (data written to file descriptor is seen as standard-in
* by the spawned sh(1) command). Returns `-1' if unable to spawn command.
*
* If cmd contains a single "%s" sequence, replace it with label if non-NULL.
*/
int
shell_spawn_pipecmd(const char *cmd, const char *label, pid_t *pid)
{
int error;
int len;
posix_spawn_file_actions_t action;
#if SHELL_SPAWN_DEBUG
unsigned int i;
#endif
int stdin_pipe[2] = { -1, -1 };
/* Populate argument array */
if (label != NULL && fmtcheck(cmd, "%s") == cmd)
len = snprintf(cmdbuf, CMDBUFMAX, cmd, label);
else
len = snprintf(cmdbuf, CMDBUFMAX, "%s", cmd);
if (len >= CMDBUFMAX) {
warnx("%s:%d:%s: cmdbuf[%u] too small to hold cmd argument",
__FILE__, __LINE__, __func__, CMDBUFMAX);
return (-1);
}
/* Open a pipe to communicate with [X]dialog(1) */
if (pipe(stdin_pipe) < 0)
err(EXIT_FAILURE, "%s: pipe(2)", __func__);
/* Fork sh(1) process */
#if SHELL_SPAWN_DEBUG
fprintf(stderr, "%s: spawning `", __func__);
for (i = 0; shellcmd_argv[i] != NULL; i++) {
if (i == 0)
fprintf(stderr, "%s", shellcmd_argv[i]);
else if (i == 2)
fprintf(stderr, " '%s'", shellcmd_argv[i]);
else
fprintf(stderr, " %s", shellcmd_argv[i]);
}
fprintf(stderr, "'\n");
#endif
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO);
posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
error = posix_spawnp(pid, shellcmd, &action,
(const posix_spawnattr_t *)NULL, shellcmd_argv, environ);
if (error != 0)
err(EXIT_FAILURE, "%s: posix_spawnp(3)", __func__);
return stdin_pipe[1];
}

50
lib/libdpv/util.h Normal file
View File

@ -0,0 +1,50 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _UTIL_H_
#define _UTIL_H_
#include <sys/types.h>
#include <paths.h>
#define SHELL_SPAWN_DEBUG 0 /* Debug spawning of sh(1) commands */
#ifdef _PATH_BSHELL
#define PATH_SHELL _PATH_BSHELL
#else
#define PATH_SHELL "/bin/sh"
#endif
#define CMDBUFMAX 4096
__BEGIN_DECLS
int shell_spawn_pipecmd(const char *_cmd, const char *_label, pid_t *_pid);
__END_DECLS
#endif /* !_UTIL_H_ */

21
lib/libfigpar/Makefile Normal file
View File

@ -0,0 +1,21 @@
# $FreeBSD$
LIB= figpar
SHLIB_MAJOR= 0
INCS= figpar.h string_m.h
MAN= figpar.3
MLINKS= figpar.3 get_config_option.3 \
figpar.3 parse_config.3 \
figpar.3 replaceall.3 \
figpar.3 strcount.3 \
figpar.3 strexpand.3 \
figpar.3 strexpandnl.3 \
figpar.3 strtolower.3
CFLAGS+= -I${.CURDIR}
SRCS= figpar.c string_m.c
WARNS?= 6
.include <bsd.lib.mk>

251
lib/libfigpar/figpar.3 Normal file
View File

@ -0,0 +1,251 @@
.\" Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
.\" 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 Oct 24, 2014
.Dt FIGPAR 3
.Os
.Sh NAME
.Nm figpar ,
.Nm parse_config ,
.Nm get_config_option
.Nd configuration file parsing library
.Sh LIBRARY
.Lb libfigpar
.Sh SYNOPSIS
.In figpar.h
.Ft int
.Fo parse_config
.Fa "struct fp_config options[], const char *path"
.Fa "int \*[lp]*unknown\*[rp]\*[lp]struct fp_config *option, uint32_t line"
.Fa "char *directive, char *value\*[rp], uint8_t processing_options"
.Fc
.Ft "struct fp_config *"
.Fo get_config_option
.Fa "struct fp_config options[], const char *directive"
.Fc
.In string_m.h
.Ft int
.Fo replaceall
.Fa "char *source, const char *find, const char *replace"
.Fc
.Ft unsigned int
.Fo strcount
.Fa "const char *source, const char *find"
.Fc
.Ft void
.Fo strexpand
.Fa "char *source"
.Fc
.Ft void
.Fo strexpandnl
.Fa "char *source"
.Fc
.Ft void
.Fo strtolower
.Fa "char *source"
.Fc
.Sh DESCRIPTION
The
.Nm
library provides a light-weight, portable framework for parsing configuration
files.
The library uses
.Xr open 2 ,
.Xr read 2 ,
and
.Xr lseek 2
within the file pointed to by
.Fa path
to find directives and values which are then made available to the application.
.Pp
Due to the fact that configuration files may have basic syntax differences,
the library does not attempt to impose any structure on the data but instead
provides raw data to a set of callback functions.
These callback functions can in-turn initiate abort through their return value,
allowing custom syntax validation during parsing.
.Pp
Configuration directives, types, and callback functions are provided through
data structures defined in
.In figpar.h :
.Bd -literal -offset indent
struct fp_config {
enum fp_cfgtype type; /* value type */
const char *directive; /* keyword */
union fp_cfgvalue value; /* value */
/* Pointer to function used when directive is found */
int (*action)(struct fp_config *option, uint32_t line,
char *directive, char *value);
};
enum fp_cfgtype {
FP_TYPE_NONE = 0x0000, /* for directives with no value */
FP_TYPE_BOOL = 0x0001, /* boolean */
FP_TYPE_INT = 0x0002, /* signed 32 bit integer */
FP_TYPE_UINT = 0x0004, /* unsigned 32 bit integer */
FP_TYPE_STR = 0x0008, /* string pointer */
FP_TYPE_STRARRAY = 0x0010, /* string array pointer */
FP_TYPE_DATA1 = 0x0020, /* void data type-1 (whatever) */
FP_TYPE_DATA2 = 0x0040, /* void data type-2 (whatever) */
FP_TYPE_DATA3 = 0x0080, /* void data type-3 (whatever) */
FP_TYPE_RESERVED1 = 0x0100, /* reserved data type-1 (future) */
FP_TYPE_RESERVED2 = 0x0200, /* reserved data type-2 (future) */
FP_TYPE_RESERVED3 = 0x0400, /* reserved data type-3 (future) */
};
union fp_cfgvalue {
void *data; /* Pointer to NUL-terminated string */
char *str; /* Pointer to NUL-terminated string */
char **strarray; /* Pointer to an array of strings */
int32_t num; /* Signed 32-bit integer value */
uint32_t u_num; /* Unsigned 32-bit integer value */
uint32_t boolean:1; /* Boolean integer value (0 or 1) */
};
.Ed
.Pp
The
.Fa processing_options
argument to
.Fn parse_config
is a mask of bit fields which indicate various
processing options.
The possible flags are as follows:
.Bl -tag -width FP_BREAK_ON_SEMICOLON
.It Dv FP_BREAK_ON_EQUALS
An equals sign
.Pq Ql Li =
is normally considered part of the directive.
This flag enables terminating the directive at the equals sign.
Also makes equals sign optional and transient.
.It Dv FP_BREAK_ON_SEMICOLON
A semicolon
.Pq Ql Li \;
is normally considered part of the value.
This flag enables terminating the value at the semicolon.
Also allows multiple statements on a single line separated by semicolon.
.It Dv FP_CASE_SENSITIVE
Normally directives are matched case insensitively using
.Xr fnmatch 3 .
This flag enables directive matching to be case sensitive.
.It Dv FP_REQUIRE_EQUALS
If a directive is not followed by an equals, processing is aborted.
.It Dv FP_STRICT_EQUALS
Equals must be part of the directive to be considered a delimiter between
directive and value.
.El
.Pp
The
.Fa options
struct array pointer can be NULL and every directive will invoke the
.Fn unknown
function argument.
.Pp
The directive for each fp_config item in the
.Fn parse_config
options argument is matched against each parsed directive using
.Xr fnmatch 3
until a match is found.
If a match is found, the
.Fn action
function for that fp_config directive is invoked with the line number,
directive, and value.
Otherwise if no match, the
.Fn unknown
function is invoked
.Pq with the same arguments .
.Pp
If either
.Fa action
or
.Fa unknown
return non-zero,
.Fn parse_config
aborts reading the file and returns the error value to its caller.
.Pp
.Fn get_config_option
traverses the options-array and returns the option that matches via
.Xr strcmp 3 ,
or if no match a pointer to a static dummy struct is returned
.Pq whose values are all zero or NULL .
.Pp
The use of
.Fa "struct fp_config"
is entirely optional as-is the use of
.Fa "enum fp_cfgtype"
or
.Fa "union fp_cfgvalue" .
For example, you could choose to pass a NULL pointer to
.Fn parse_config
for the first argument and then provide a simple
.Fa unknown
function based on
.Xr queue 3
that populates a singly-linked list of your own struct containing the
.Fa directive
and
.Fa value .
.Pp
In addition, the following miscellaneous string manipulation routines are
provided by
.In string_m.h :
.Bl -tag -width strexpandnl()
.It Fn replaceall
Replace all occurrences of
.Fa find
in
.Fa source
with
.Fa replace .
.It Fn strcount
Count the number of occurrences of one string that appear in the
.Fa source
string.
Return value is the total count.
An example use would be if you need to know how large a block of memory needs
to be for a
.Fn replaceall
series.
.It Fn strexpand
Expand escape sequences in a buffer pointed to by
.Fa source .
.It Fn strexpandnl
Expand only the escaped newlines in a buffer pointed to by
.Fa source .
.It Fn strtolower
Convert a string to lower case.
.El
.Sh SEE ALSO
.Xr queue 3
.Sh HISTORY
The
.Nm
library first appeared in
.Fx 11.0 .
.Sh AUTHORS
.An Devin Teske Aq dteske@FreeBSD.org
.Sh BUGS
This is the first implementation of the library,
and the interface may be subject to refinement.

469
lib/libfigpar/figpar.c Normal file
View File

@ -0,0 +1,469 @@
/*-
* Copyright (c) 2002-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <fnmatch.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "figpar.h"
#include "string_m.h"
struct fp_config fp_dummy_config = {0, NULL, {0}, NULL};
/*
* Search for config option (struct fp_config) in the array of config options,
* returning the struct whose directive matches the given parameter. If no
* match is found, a pointer to the static dummy array (above) is returned.
*
* This is to eliminate dependency on the index position of an item in the
* array, since the index position is more apt to be changed as code grows.
*/
struct fp_config *
get_config_option(struct fp_config options[], const char *directive)
{
uint32_t n;
/* Check arguments */
if (options == NULL || directive == NULL)
return (&fp_dummy_config);
/* Loop through the array, return the index of the first match */
for (n = 0; options[n].directive != NULL; n++)
if (strcmp(options[n].directive, directive) == 0)
return (&(options[n]));
/* Re-initialize the dummy variable in case it was written to */
fp_dummy_config.directive = NULL;
fp_dummy_config.type = 0;
fp_dummy_config.action = NULL;
fp_dummy_config.value.u_num = 0;
return (&fp_dummy_config);
}
/*
* Parse the configuration file at `path' and execute the `action' call-back
* functions for any directives defined by the array of config options (first
* argument).
*
* For unknown directives that are encountered, you can optionally pass a
* call-back function for the third argument to be called for unknowns.
*
* Returns zero on success; otherwise returns -1 and errno should be consulted.
*/
int
parse_config(struct fp_config options[], const char *path,
int (*unknown)(struct fp_config *option, uint32_t line, char *directive,
char *value), uint16_t processing_options)
{
uint8_t bequals;
uint8_t bsemicolon;
uint8_t case_sensitive;
uint8_t comment = 0;
uint8_t end;
uint8_t found;
uint8_t have_equals = 0;
uint8_t quote;
uint8_t require_equals;
uint8_t strict_equals;
char p[2];
char *directive;
char *t;
char *value;
int error;
int fd;
ssize_t r = 1;
uint32_t dsize;
uint32_t line = 1;
uint32_t n;
uint32_t vsize;
uint32_t x;
off_t charpos;
off_t curpos;
char rpath[PATH_MAX];
/* Sanity check: if no options and no unknown function, return */
if (options == NULL && unknown == NULL)
return (-1);
/* Processing options */
bequals = (processing_options & FP_BREAK_ON_EQUALS) == 0 ? 0 : 1;
bsemicolon = (processing_options & FP_BREAK_ON_SEMICOLON) == 0 ? 0 : 1;
case_sensitive = (processing_options & FP_CASE_SENSITIVE) == 0 ? 0 : 1;
require_equals = (processing_options & FP_REQUIRE_EQUALS) == 0 ? 0 : 1;
strict_equals = (processing_options & FP_STRICT_EQUALS) == 0 ? 0 : 1;
/* Initialize strings */
directive = value = 0;
vsize = dsize = 0;
/* Resolve the file path */
if (realpath(path, rpath) == 0)
return (-1);
/* Open the file */
if ((fd = open(rpath, O_RDONLY)) < 0)
return (-1);
/* Read the file until EOF */
while (r != 0) {
r = read(fd, p, 1);
/* skip to the beginning of a directive */
while (r != 0 && (isspace(*p) || *p == '#' || comment ||
(bsemicolon && *p == ';'))) {
if (*p == '#')
comment = 1;
else if (*p == '\n') {
comment = 0;
line++;
}
r = read(fd, p, 1);
}
/* Test for EOF; if EOF then no directive was found */
if (r == 0) {
close(fd);
return (0);
}
/* Get the current offset */
curpos = lseek(fd, 0, SEEK_CUR) - 1;
if (curpos == -1) {
close(fd);
return (-1);
}
/* Find the length of the directive */
for (n = 0; r != 0; n++) {
if (isspace(*p))
break;
if (bequals && *p == '=') {
have_equals = 1;
break;
}
if (bsemicolon && *p == ';')
break;
r = read(fd, p, 1);
}
/* Test for EOF, if EOF then no directive was found */
if (n == 0 && r == 0) {
close(fd);
return (0);
}
/* Go back to the beginning of the directive */
error = (int)lseek(fd, curpos, SEEK_SET);
if (error == (curpos - 1)) {
close(fd);
return (-1);
}
/* Allocate and read the directive into memory */
if (n > dsize) {
if ((directive = realloc(directive, n + 1)) == NULL) {
close(fd);
return (-1);
}
dsize = n;
}
r = read(fd, directive, n);
/* Advance beyond the equals sign if appropriate/desired */
if (bequals && *p == '=') {
if (lseek(fd, 1, SEEK_CUR) != -1)
r = read(fd, p, 1);
if (strict_equals && isspace(*p))
*p = '\n';
}
/* Terminate the string */
directive[n] = '\0';
/* Convert directive to lower case before comparison */
if (!case_sensitive)
strtolower(directive);
/* Move to what may be the start of the value */
if (!(bsemicolon && *p == ';') &&
!(strict_equals && *p == '=')) {
while (r != 0 && isspace(*p) && *p != '\n')
r = read(fd, p, 1);
}
/* An equals sign may have stopped us, should we eat it? */
if (r != 0 && bequals && *p == '=' && !strict_equals) {
have_equals = 1;
r = read(fd, p, 1);
while (r != 0 && isspace(*p) && *p != '\n')
r = read(fd, p, 1);
}
/* If no value, allocate a dummy value and jump to action */
if (r == 0 || *p == '\n' || *p == '#' ||
(bsemicolon && *p == ';')) {
/* Initialize the value if not already done */
if (value == NULL && (value = malloc(1)) == NULL) {
close(fd);
return (-1);
}
value[0] = '\0';
goto call_function;
}
/* Get the current offset */
curpos = lseek(fd, 0, SEEK_CUR) - 1;
if (curpos == -1) {
close(fd);
return (-1);
}
/* Find the end of the value */
quote = 0;
end = 0;
while (r != 0 && end == 0) {
/* Advance to the next character if we know we can */
if (*p != '\"' && *p != '#' && *p != '\n' &&
(!bsemicolon || *p != ';')) {
r = read(fd, p, 1);
continue;
}
/*
* If we get this far, we've hit an end-key
*/
/* Get the current offset */
charpos = lseek(fd, 0, SEEK_CUR) - 1;
if (charpos == -1) {
close(fd);
return (-1);
}
/*
* Go back so we can read the character before the key
* to check if the character is escaped (which means we
* should continue).
*/
error = (int)lseek(fd, -2, SEEK_CUR);
if (error == -3) {
close(fd);
return (-1);
}
r = read(fd, p, 1);
/*
* Count how many backslashes there are (an odd number
* means the key is escaped, even means otherwise).
*/
for (n = 1; *p == '\\'; n++) {
/* Move back another offset to read */
error = (int)lseek(fd, -2, SEEK_CUR);
if (error == -3) {
close(fd);
return (-1);
}
r = read(fd, p, 1);
}
/* Move offset back to the key and read it */
error = (int)lseek(fd, charpos, SEEK_SET);
if (error == (charpos - 1)) {
close(fd);
return (-1);
}
r = read(fd, p, 1);
/*
* If an even number of backslashes was counted meaning
* key is not escaped, we should evaluate what to do.
*/
if ((n & 1) == 1) {
switch (*p) {
case '\"':
/*
* Flag current sequence of characters
* to follow as being quoted (hashes
* are not considered comments).
*/
quote = !quote;
break;
case '#':
/*
* If we aren't in a quoted series, we
* just hit an inline comment and have
* found the end of the value.
*/
if (!quote)
end = 1;
break;
case '\n':
/*
* Newline characters must always be
* escaped, whether inside a quoted
* series or not, otherwise they
* terminate the value.
*/
end = 1;
case ';':
if (!quote && bsemicolon)
end = 1;
break;
}
} else if (*p == '\n')
/* Escaped newline character. increment */
line++;
/* Advance to the next character */
r = read(fd, p, 1);
}
/* Get the current offset */
charpos = lseek(fd, 0, SEEK_CUR) - 1;
if (charpos == -1) {
close(fd);
return (-1);
}
/* Get the length of the value */
n = (uint32_t)(charpos - curpos);
if (r != 0) /* more to read, but don't read ending key */
n--;
/* Move offset back to the beginning of the value */
error = (int)lseek(fd, curpos, SEEK_SET);
if (error == (curpos - 1)) {
close(fd);
return (-1);
}
/* Allocate and read the value into memory */
if (n > vsize) {
if ((value = realloc(value, n + 1)) == NULL) {
close(fd);
return (-1);
}
vsize = n;
}
r = read(fd, value, n);
/* Terminate the string */
value[n] = '\0';
/* Cut trailing whitespace off by termination */
t = value + n;
while (isspace(*--t))
*t = '\0';
/* Escape the escaped quotes (replaceall is in string_m.c) */
x = strcount(value, "\\\""); /* in string_m.c */
if (x != 0 && (n + x) > vsize) {
if ((value = realloc(value, n + x + 1)) == NULL) {
close(fd);
return (-1);
}
vsize = n + x;
}
if (replaceall(value, "\\\"", "\\\\\"") < 0) {
/* Replace operation failed for some unknown reason */
close(fd);
return (-1);
}
/* Remove all new line characters */
if (replaceall(value, "\\\n", "") < 0) {
/* Replace operation failed for some unknown reason */
close(fd);
return (-1);
}
/* Resolve escape sequences */
strexpand(value); /* in string_m.c */
call_function:
/* Abort if we're seeking only assignments */
if (require_equals && !have_equals)
return (-1);
found = have_equals = 0; /* reset */
/* If there are no options defined, call unknown and loop */
if (options == NULL && unknown != NULL) {
error = unknown(NULL, line, directive, value);
if (error != 0) {
close(fd);
return (error);
}
continue;
}
/* Loop through the array looking for a match for the value */
for (n = 0; options[n].directive != NULL; n++) {
error = fnmatch(options[n].directive, directive,
FNM_NOESCAPE);
if (error == 0) {
found = 1;
/* Call function for array index item */
if (options[n].action != NULL) {
error = options[n].action(
&options[n],
line, directive, value);
if (error != 0) {
close(fd);
return (error);
}
}
} else if (error != FNM_NOMATCH) {
/* An error has occurred */
close(fd);
return (-1);
}
}
if (!found && unknown != NULL) {
/*
* No match was found for the value we read from the
* file; call function designated for unknown values.
*/
error = unknown(NULL, line, directive, value);
if (error != 0) {
close(fd);
return (error);
}
}
}
close(fd);
return (0);
}

99
lib/libfigpar/figpar.h Normal file
View File

@ -0,0 +1,99 @@
/*-
* Copyright (c) 2002-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _FIGPAR_H_
#define _FIGPAR_H_
#include <sys/types.h>
/*
* Union for storing various types of data in a single common container.
*/
union fp_cfgvalue {
void *data; /* Pointer to NUL-terminated string */
char *str; /* Pointer to NUL-terminated string */
char **strarray; /* Pointer to an array of strings */
int32_t num; /* Signed 32-bit integer value */
uint32_t u_num; /* Unsigned 32-bit integer value */
uint32_t boolean:1; /* Boolean integer value (0 or 1) */
};
/*
* Option types (based on above cfgvalue union)
*/
enum fp_cfgtype {
FP_TYPE_NONE = 0x0000, /* for directives with no value */
FP_TYPE_BOOL = 0x0001, /* boolean */
FP_TYPE_INT = 0x0002, /* signed 32 bit integer */
FP_TYPE_UINT = 0x0004, /* unsigned 32 bit integer */
FP_TYPE_STR = 0x0008, /* string pointer */
FP_TYPE_STRARRAY = 0x0010, /* string array pointer */
FP_TYPE_DATA1 = 0x0020, /* void data type-1 (whatever) */
FP_TYPE_DATA2 = 0x0040, /* void data type-2 (whatever) */
FP_TYPE_DATA3 = 0x0080, /* void data type-3 (whatever) */
FP_TYPE_RESERVED1 = 0x0100, /* reserved data type-1 (future) */
FP_TYPE_RESERVED2 = 0x0200, /* reserved data type-2 (future) */
FP_TYPE_RESERVED3 = 0x0400, /* reserved data type-3 (future) */
};
/*
* Options to parse_config() for processing_options bitmask
*/
#define FP_BREAK_ON_EQUALS 0x0001 /* stop reading directive at `=' */
#define FP_BREAK_ON_SEMICOLON 0x0002 /* `;' starts a new line */
#define FP_CASE_SENSITIVE 0x0004 /* directives are case sensitive */
#define FP_REQUIRE_EQUALS 0x0008 /* assignment directives only */
#define FP_STRICT_EQUALS 0x0010 /* `=' must be part of directive */
/*
* Anatomy of a config file option
*/
struct fp_config {
enum fp_cfgtype type; /* Option value type */
const char *directive; /* config file keyword */
union fp_cfgvalue value; /* NB: set by action */
/*
* Function pointer; action to be taken when the directive is found
*/
int (*action)(struct fp_config *option, uint32_t line, char *directive,
char *value);
};
extern struct fp_config fp_dummy_config;
__BEGIN_DECLS
int parse_config(struct fp_config _options[],
const char *_path,
int (*_unknown)(struct fp_config *_option,
uint32_t _line, char *_directive, char *_value),
uint16_t _processing_options);
struct fp_config *get_config_option(struct fp_config _options[],
const char *_directive);
__END_DECLS
#endif /* _FIGPAR_H_ */

309
lib/libfigpar/string_m.c Normal file
View File

@ -0,0 +1,309 @@
/*-
* Copyright (c) 2001-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "string_m.h"
/*
* Counts the number of occurrences of one string that appear in the source
* string. Return value is the total count.
*
* An example use would be if you need to know how large a block of memory
* needs to be for a replaceall() series.
*/
unsigned int
strcount(const char *source, const char *find)
{
const char *p = source;
size_t flen;
unsigned int n = 0;
/* Both parameters are required */
if (source == NULL || find == NULL)
return (0);
/* Cache the length of find element */
flen = strlen(find);
if (strlen(source) == 0 || flen == 0)
return (0);
/* Loop until the end of the string */
while (*p != '\0') {
if (strncmp(p, find, flen) == 0) { /* found an instance */
p += flen;
n++;
} else
p++;
}
return (n);
}
/*
* Replaces all occurrences of `find' in `source' with `replace'.
*
* You should not pass a string constant as the first parameter, it needs to be
* a pointer to an allocated block of memory. The block of memory that source
* points to should be large enough to hold the result. If the length of the
* replacement string is greater than the length of the find string, the result
* will be larger than the original source string. To allocate enough space for
* the result, use the function strcount() declared above to determine the
* number of occurrences and how much larger the block size needs to be.
*
* If source is not large enough, the application will crash. The return value
* is the length (in bytes) of the result.
*
* When an error occurs, -1 is returned and the global variable errno is set
* accordingly. Returns zero on success.
*/
int
replaceall(char *source, const char *find, const char *replace)
{
char *p;
char *t;
char *temp;
size_t flen;
size_t rlen;
size_t slen;
uint32_t n = 0;
errno = 0; /* reset global error number */
/* Check that we have non-null parameters */
if (source == NULL)
return (0);
if (find == NULL)
return (strlen(source));
/* Cache the length of the strings */
slen = strlen(source);
flen = strlen(find);
rlen = replace ? strlen(replace) : 0;
/* Cases where no replacements need to be made */
if (slen == 0 || flen == 0 || slen < flen)
return (slen);
/* If replace is longer than find, we'll need to create a temp copy */
if (rlen > flen) {
temp = malloc(slen + 1);
if (errno != 0) /* could not allocate memory */
return (-1);
strcpy(temp, source);
} else
temp = source;
/* Reconstruct the string with the replacements */
p = source; t = temp; /* position elements */
while (*t != '\0') {
if (strncmp(t, find, flen) == 0) {
/* found an occurrence */
for (n = 0; replace && replace[n]; n++)
*p++ = replace[n];
t += flen;
} else
*p++ = *t++; /* copy character and increment */
}
/* Terminate the string */
*p = '\0';
/* Free the temporary allocated memory */
if (temp != source)
free(temp);
/* Return the length of the completed string */
return (strlen(source));
}
/*
* Expands escape sequences in a buffer pointed to by `source'. This function
* steps through each character, and converts escape sequences such as "\n",
* "\r", "\t" and others into their respective meanings.
*
* You should not pass a string constant or literal to this function or the
* program will likely segmentation fault when it tries to modify the data.
*
* The string length will either shorten or stay the same depending on whether
* any escape sequences were converted but the amount of memory allocated does
* not change.
*
* Interpreted sequences are:
*
* \0NNN character with octal value NNN (0 to 3 digits)
* \N character with octal value N (0 thru 7)
* \a alert (BEL)
* \b backslash
* \f form feed
* \n new line
* \r carriage return
* \t horizontal tab
* \v vertical tab
* \xNN byte with hexadecimal value NN (1 to 2 digits)
*
* All other sequences are unescaped (ie. '\"' and '\#').
*/
void strexpand(char *source)
{
uint8_t c;
char *chr;
char *pos;
char d[4];
/* Initialize position elements */
pos = chr = source;
/* Loop until we hit the end of the string */
while (*pos != '\0') {
if (*chr != '\\') {
*pos = *chr; /* copy character to current offset */
pos++;
chr++;
continue;
}
/* Replace the backslash with the correct character */
switch (*++chr) {
case 'a': *pos = '\a'; break; /* bell/alert (BEL) */
case 'b': *pos = '\b'; break; /* backspace */
case 'f': *pos = '\f'; break; /* form feed */
case 'n': *pos = '\n'; break; /* new line */
case 'r': *pos = '\r'; break; /* carriage return */
case 't': *pos = '\t'; break; /* horizontal tab */
case 'v': *pos = '\v'; break; /* vertical tab */
case 'x': /* hex value (1 to 2 digits)(\xNN) */
d[2] = '\0'; /* pre-terminate the string */
/* verify next two characters are hex */
d[0] = isxdigit(*(chr+1)) ? *++chr : '\0';
if (d[0] != '\0')
d[1] = isxdigit(*(chr+1)) ? *++chr : '\0';
/* convert the characters to decimal */
c = (uint8_t)strtoul(d, 0, 16);
/* assign the converted value */
*pos = (c != 0 || d[0] == '0') ? c : *++chr;
break;
case '0': /* octal value (0 to 3 digits)(\0NNN) */
d[3] = '\0'; /* pre-terminate the string */
/* verify next three characters are octal */
d[0] = (isdigit(*(chr+1)) && *(chr+1) < '8') ?
*++chr : '\0';
if (d[0] != '\0')
d[1] = (isdigit(*(chr+1)) && *(chr+1) < '8') ?
*++chr : '\0';
if (d[1] != '\0')
d[2] = (isdigit(*(chr+1)) && *(chr+1) < '8') ?
*++chr : '\0';
/* convert the characters to decimal */
c = (uint8_t)strtoul(d, 0, 8);
/* assign the converted value */
*pos = c;
break;
default: /* single octal (\0..7) or unknown sequence */
if (isdigit(*chr) && *chr < '8') {
d[0] = *chr;
d[1] = '\0';
*pos = (uint8_t)strtoul(d, 0, 8);
} else
*pos = *chr;
}
/* Increment to next offset, possible next escape sequence */
pos++;
chr++;
}
}
/*
* Expand only the escaped newlines in a buffer pointed to by `source'. This
* function steps through each character, and converts the "\n" sequence into
* a literal newline and the "\\n" sequence into "\n".
*
* You should not pass a string constant or literal to this function or the
* program will likely segmentation fault when it tries to modify the data.
*
* The string length will either shorten or stay the same depending on whether
* any escaped newlines were converted but the amount of memory allocated does
* not change.
*/
void strexpandnl(char *source)
{
uint8_t backslash = 0;
char *cp1;
char *cp2;
/* Replace '\n' with literal in dprompt */
cp1 = cp2 = source;
while (*cp2 != '\0') {
*cp1 = *cp2;
if (*cp2 == '\\')
backslash++;
else if (*cp2 != 'n')
backslash = 0;
else if (backslash > 0) {
*(--cp1) = (backslash & 1) == 1 ? '\n' : 'n';
backslash = 0;
}
cp1++;
cp2++;
}
*cp1 = *cp2;
}
/*
* Convert a string to lower case. You should not pass a string constant to
* this function. Only pass pointers to allocated memory with null terminated
* string data.
*/
void
strtolower(char *source)
{
char *p = source;
if (source == NULL)
return;
while (*p != '\0') {
*p = tolower(*p);
p++; /* would have just used `*p++' but gcc 3.x warns */
}
}

43
lib/libfigpar/string_m.h Normal file
View File

@ -0,0 +1,43 @@
/*-
* Copyright (c) 2001-2014 Devin Teske <dteske@FreeBSD.org>
* 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 _STRING_M_H_
#define _STRING_M_H_
#include <sys/cdefs.h>
__BEGIN_DECLS
void strexpand(char *_source);
void strexpandnl(char *_source);
void strtolower(char *_source);
int replaceall(char *_source, const char *_find,
const char *_replace);
unsigned int strcount(const char *_source, const char *_find);
__END_DECLS
#endif /* !_STRING_M_H_ */

View File

@ -43,12 +43,14 @@ LIBDEVINFO?= ${DESTDIR}${LIBDIR}/libdevinfo.a
LIBDEVSTAT?= ${DESTDIR}${LIBDIR}/libdevstat.a
LIBDIALOG?= ${DESTDIR}${LIBDIR}/libdialog.a
LIBDNS?= ${DESTDIR}${LIBDIR}/libdns.a
LIBDPV?= ${DESTDIR}${LIBDIR}/libdpv.a
LIBDTRACE?= ${DESTDIR}${LIBDIR}/libdtrace.a
LIBDWARF?= ${DESTDIR}${LIBDIR}/libdwarf.a
LIBEDIT?= ${DESTDIR}${LIBDIR}/libedit.a
LIBELF?= ${DESTDIR}${LIBDIR}/libelf.a
LIBEXECINFO?= ${DESTDIR}${LIBDIR}/libexecinfo.a
LIBFETCH?= ${DESTDIR}${LIBDIR}/libfetch.a
LIBFIGPAR?= ${DESTDIR}${LIBDIR}/libfigpar.a
LIBFL?= "don't use LIBFL, use LIBL"
LIBFORM?= ${DESTDIR}${LIBDIR}/libform.a
LIBG2C?= ${DESTDIR}${LIBDIR}/libg2c.a

View File

@ -58,7 +58,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 1100043 /* Master, propagated to newvers */
#define __FreeBSD_version 1100044 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,

View File

@ -36,6 +36,7 @@ SUBDIR= alias \
ctlstat \
cut \
dirname \
dpv \
du \
ee \
elf2aout \

12
usr.bin/dpv/Makefile Normal file
View File

@ -0,0 +1,12 @@
# $FreeBSD$
PROG= dpv
CFLAGS+= -I${.CURDIR}
DPADD+= ${LIBDPV} ${LIBDIALOG} ${LIBFIGPAR} ${LIBNCURSES} ${LIBUTIL}
LDADD+= -ldpv -ldialog -lfigpar -lncurses -lutil
WARNS?= 6
.include <bsd.prog.mk>

430
usr.bin/dpv/dpv.1 Normal file
View File

@ -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

541
usr.bin/dpv/dpv.c Normal file
View File

@ -0,0 +1,541 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/stat.h>
#include <sys/types.h>
#define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */
#include <dialog.h>
#include <dpv.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string_m.h>
#include <unistd.h>
#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);
}
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)
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);
}

68
usr.bin/dpv/dpv_util.h Normal file
View File

@ -0,0 +1,68 @@
/*-
* Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org>
* 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_ */