mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-27 17:52:43 +00:00
MFC r274116:
Add new libraries/utilities for data throughput visualization. dpv(3): dialog progress view library dpv(1): stream data from stdin or multiple paths with dialog progress view figpar(3): configuration file parsing library MFC r274120, r274121, r274123, r274124, r274144, r274146, r274159, r274192, r274203, r274209, r274226, r274270, and r274851: Fixes following r274116 Reviews: D714 Relnotes: New libdpv/libfigpar and dpv(1) utility Reviewed by: jelischer, shurd Discussed at: MeetBSD California 2014 Vendor/Dev Summit Discussed on: -current Thanks to: ngie, ian, jelischer, shurd, bapt
This commit is contained in:
parent
3500df8479
commit
b244a1adde
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/stable/10/; revision=275040
@ -1510,6 +1510,7 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \
|
||||
${_lib_atf} \
|
||||
lib/libbz2 ${_libcom_err} lib/libcrypt \
|
||||
lib/libelf lib/libexpat \
|
||||
lib/libfigpar \
|
||||
${_lib_libgssapi} ${_lib_libipx} \
|
||||
lib/libkiconv lib/libkvm lib/liblzma lib/libmd \
|
||||
lib/ncurses/ncurses lib/ncurses/ncursesw \
|
||||
@ -1520,7 +1521,8 @@ _prebuild_libs= ${_kerberos5_lib_libasn1} \
|
||||
${_cddl_lib_libzfs_core} \
|
||||
lib/libutil ${_lib_libypclnt} lib/libz lib/msun \
|
||||
${_secure_lib_libcrypto} ${_lib_libldns} \
|
||||
${_secure_lib_libssh} ${_secure_lib_libssl}
|
||||
${_secure_lib_libssh} ${_secure_lib_libssl} \
|
||||
gnu/lib/libdialog
|
||||
.if ${MK_GNUCXX} != no
|
||||
_prebuild_libs+= gnu/lib/libstdc++ gnu/lib/libsupc++
|
||||
gnu/lib/libstdc++__L: lib/msun__L
|
||||
@ -1637,6 +1639,8 @@ _lib_libypclnt= lib/libypclnt
|
||||
lib/libradius__L: lib/libmd__L
|
||||
.endif
|
||||
|
||||
gnu/lib/libdialog__L: lib/msun__L lib/ncurses/ncursesw__L
|
||||
|
||||
.for _lib in ${_prereq_libs}
|
||||
${_lib}__PL: .PHONY .MAKE
|
||||
.if exists(${.CURDIR}/${_lib})
|
||||
|
@ -13,6 +13,9 @@ SRCS= argv.c arrows.c buildlist.c buttons.c calendar.c checklist.c \
|
||||
INCS= dialog.h dlg_colors.h dlg_config.h dlg_keys.h
|
||||
MAN= dialog.3
|
||||
|
||||
DPADD= ${LIBNCURSESW} ${LIBM}
|
||||
LDADD= -lncursesw -lm
|
||||
|
||||
CFLAGS+= -I${.CURDIR} -I${DIALOG} -D_XOPEN_SOURCE_EXTENDED -DGCC_UNUSED=__unused
|
||||
.PATH: ${DIALOG}
|
||||
WARNS?= 1
|
||||
|
@ -39,12 +39,14 @@ SUBDIR= ${SUBDIR_ORDERED} \
|
||||
libcrypt \
|
||||
libdevinfo \
|
||||
libdevstat \
|
||||
libdpv \
|
||||
libdwarf \
|
||||
libedit \
|
||||
${_libefi} \
|
||||
libexecinfo \
|
||||
libexpat \
|
||||
libfetch \
|
||||
libfigpar \
|
||||
libgeom \
|
||||
${_libgpib} \
|
||||
${_libgssapi} \
|
||||
@ -121,7 +123,7 @@ SUBDIR_DEPEND_libc++= libcxxrt
|
||||
SUBDIR_DEPEND_libc= libcompiler_rt
|
||||
SUBDIR_DEPEND_libcam= libsbuf
|
||||
SUBDIR_DEPEND_libdevstat= libkvm
|
||||
SUBDIR_DEPEND_libdiaglog= ncurses
|
||||
SUBDIR_DEPEND_libdpv= libfigpar ncurses libutil
|
||||
SUBDIR_DEPEND_libedit= ncurses
|
||||
SUBDIR_DEPEND_libg++= msun
|
||||
SUBDIR_DEPEND_libgeom= libexpat libsbuf
|
||||
|
18
lib/libdpv/Makefile
Normal file
18
lib/libdpv/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
# $FreeBSD$
|
||||
|
||||
LIB= dpv
|
||||
SHLIB_MAJOR= 1
|
||||
INCS= dpv.h
|
||||
MAN= dpv.3
|
||||
MLINKS= dpv.3 dpv_free.3
|
||||
|
||||
DPADD= ${LIBDIALOG} ${LIBFIGPAR} ${LIBNCURSESW} ${LIBUTIL}
|
||||
LDADD= -ldialog -lfigpar -lncursesw -lutil
|
||||
|
||||
SRCS= dialog_util.c dialogrc.c dprompt.c dpv.c status.c util.c
|
||||
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
.include <bsd.lib.mk>
|
633
lib/libdpv/dialog_util.c
Normal file
633
lib/libdpv/dialog_util.c
Normal 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;
|
||||
static 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
73
lib/libdpv/dialog_util.h
Normal 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
359
lib/libdpv/dialogrc.c
Normal 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 fp_config *, uint32_t, char *, char *);
|
||||
static int setbool(struct fp_config *, uint32_t, char *, char *);
|
||||
static int setnum(struct fp_config *, uint32_t, char *, char *);
|
||||
static int setstr(struct fp_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 fp_config dialogrc_config[] = {
|
||||
/* TYPE Directive DEFAULT HANDLER */
|
||||
{FP_TYPE_INT, "aspect", {(void *)0}, &setnum},
|
||||
{FP_TYPE_STR, "separate_widget", {separator}, &setstr},
|
||||
{FP_TYPE_INT, "tab_len", {(void *)0}, &setnum},
|
||||
{FP_TYPE_BOOL, "visit_items", {(void *)0}, &setbool},
|
||||
{FP_TYPE_BOOL, "use_shadow", {(void *)1}, &setbool},
|
||||
{FP_TYPE_BOOL, "use_colors", {(void *)1}, &setbool},
|
||||
{FP_TYPE_STR, "screen_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "shadow_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "dialog_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "title_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "border_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "button_active_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "button_inactive_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "button_key_active_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "button_key_inactive_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "button_label_active_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "button_label_inactive_color",{NULL}, &setattr},
|
||||
{FP_TYPE_STR, "inputbox_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "inputbox_border_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "searchbox_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "searchbox_title_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "searchbox_border_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "position_indicator_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "menubox_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "menubox_border_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "item_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "item_selected_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "tag_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "tag_selected_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "tag_key_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "tag_key_selected_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "check_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "check_selected_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "uarrow_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "darrow_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "itemhelp_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "form_active_text_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "form_text_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "form_item_readonly_color", {NULL}, &setattr},
|
||||
{FP_TYPE_STR, "gauge_color", {gauge_color}, &setattr},
|
||||
{0, NULL, {0}, NULL}
|
||||
};
|
||||
|
||||
/*
|
||||
* figpar call-back for interpreting value as .dialogrc `Attribute'
|
||||
*/
|
||||
static int
|
||||
setattr(struct fp_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 fp_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 fp_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 fp_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, FP_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 fp_dummy_config (full of NULLs) if none found (see
|
||||
* get_config_option(3); part of figpar(3)).
|
||||
*/
|
||||
struct fp_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
56
lib/libdpv/dialogrc.h
Normal 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 fp_config *dialogrc_config_option(const char *_directive);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_DIALOGRC_H_ */
|
770
lib/libdpv/dprompt.c
Normal file
770
lib/libdpv/dprompt.c
Normal 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
59
lib/libdpv/dprompt.h
Normal 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
510
lib/libdpv/dpv.3
Normal 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
721
lib/libdpv/dpv.c
Normal 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) */
|
||||
static const char *status_format_custom = NULL;
|
||||
static 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
161
lib/libdpv/dpv.h
Normal 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
66
lib/libdpv/dpv_private.h
Normal 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
111
lib/libdpv/status.c
Normal 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
43
lib/libdpv/status.h
Normal 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
107
lib/libdpv/util.c
Normal 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
50
lib/libdpv/util.h
Normal 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
21
lib/libfigpar/Makefile
Normal 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
251
lib/libfigpar/figpar.3
Normal 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
469
lib/libfigpar/figpar.c
Normal 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
99
lib/libfigpar/figpar.h
Normal 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
309
lib/libfigpar/string_m.c
Normal 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
43
lib/libfigpar/string_m.h
Normal 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_ */
|
@ -42,12 +42,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
|
||||
|
@ -58,7 +58,7 @@
|
||||
* in the range 5 to 9.
|
||||
*/
|
||||
#undef __FreeBSD_version
|
||||
#define __FreeBSD_version 1001502 /* Master, propagated to newvers */
|
||||
#define __FreeBSD_version 1001503 /* Master, propagated to newvers */
|
||||
|
||||
/*
|
||||
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,
|
||||
|
@ -36,6 +36,7 @@ SUBDIR= alias \
|
||||
ctlstat \
|
||||
cut \
|
||||
dirname \
|
||||
dpv \
|
||||
du \
|
||||
ee \
|
||||
elf2aout \
|
||||
|
12
usr.bin/dpv/Makefile
Normal file
12
usr.bin/dpv/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= dpv
|
||||
|
||||
CFLAGS+= -I${.CURDIR}
|
||||
|
||||
DPADD= ${LIBDPV} ${LIBDIALOG} ${LIBFIGPAR} ${LIBNCURSESW} ${LIBUTIL} ${LIBM}
|
||||
LDADD= -ldpv -ldialog -lfigpar -lncursesw -lutil -lm
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
.include <bsd.prog.mk>
|
430
usr.bin/dpv/dpv.1
Normal file
430
usr.bin/dpv/dpv.1
Normal 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
541
usr.bin/dpv/dpv.c
Normal 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);
|
||||
}
|
||||
|
||||
dpv_overall_read += r;
|
||||
file->read += r;
|
||||
|
||||
/* Calculate percentage of completion (if possible) */
|
||||
if (file->length >= 0) {
|
||||
progress = (file->read * 100 / (file->length > 0 ?
|
||||
file->length : 1));
|
||||
|
||||
/* If no_overrun, do not return 100% until read >= length */
|
||||
if (no_overrun && progress == 100 && file->read < file->length)
|
||||
progress--;
|
||||
|
||||
return (progress);
|
||||
} else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int
|
||||
operate_on_lines(struct dpv_file_node *file, int out)
|
||||
{
|
||||
char *p;
|
||||
int progress;
|
||||
ssize_t r, w;
|
||||
|
||||
if (operate_common(file, out) < 0)
|
||||
return (-1);
|
||||
|
||||
/* [Re-]Fill the buffer */
|
||||
if ((r = read(fd, buf, bsize)) <= 0) {
|
||||
if (fd != STDIN_FILENO)
|
||||
close(fd);
|
||||
fd = -1;
|
||||
file->status = DPV_STATUS_DONE;
|
||||
return (100);
|
||||
}
|
||||
buf[r] = '\0';
|
||||
|
||||
/* [Re-]Dump the buffer */
|
||||
if (out >= 0) {
|
||||
if ((w = write(out, buf, r)) < 0) {
|
||||
end_dialog();
|
||||
err(EXIT_FAILURE, "output");
|
||||
}
|
||||
fsync(out);
|
||||
}
|
||||
|
||||
/* Process the buffer for number of lines */
|
||||
for (p = buf; p != NULL && *p != '\0';)
|
||||
if ((p = strchr(p, '\n')) != NULL)
|
||||
dpv_overall_read++, p++, file->read++;
|
||||
|
||||
/* Calculate percentage of completion (if possible) */
|
||||
if (file->length >= 0) {
|
||||
progress = (file->read * 100 / file->length);
|
||||
|
||||
/* If no_overrun, do not return 100% until read >= length */
|
||||
if (no_overrun && progress == 100 && file->read < file->length)
|
||||
progress--;
|
||||
|
||||
return (progress);
|
||||
} else
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Takes a list of names that are to correspond to input streams coming from
|
||||
* stdin or fifos and produces necessary config to drive dpv(3) `--gauge'
|
||||
* widget. If the `-d' flag is used, output is instead send to terminal
|
||||
* standard output (and the output can then be saved to a file, piped into
|
||||
* custom [X]dialog(1) invocation, or whatever.
|
||||
*/
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char dummy;
|
||||
int ch;
|
||||
int n = 0;
|
||||
size_t config_size = sizeof(struct dpv_config);
|
||||
size_t file_node_size = sizeof(struct dpv_file_node);
|
||||
struct dpv_config *config;
|
||||
struct dpv_file_node *curfile;
|
||||
struct sigaction act;
|
||||
|
||||
pgm = argv[0]; /* store a copy of invocation name */
|
||||
|
||||
/* Allocate config structure */
|
||||
if ((config = malloc(config_size)) == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
memset((void *)(config), '\0', config_size);
|
||||
|
||||
/*
|
||||
* Process command-line options
|
||||
*/
|
||||
while ((ch = getopt(argc, argv,
|
||||
"a:b:dDhi:I:lL:mn:No:p:P:t:TU:wx:X")) != -1) {
|
||||
switch(ch) {
|
||||
case 'a': /* additional message text to append */
|
||||
if (config->aprompt == NULL) {
|
||||
config->aprompt = malloc(DPV_APROMPT_MAX);
|
||||
if (config->aprompt == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
}
|
||||
snprintf(config->aprompt, DPV_APROMPT_MAX, "%s",
|
||||
optarg);
|
||||
break;
|
||||
case 'b': /* [X]dialog(1) backtitle */
|
||||
if (config->backtitle != NULL)
|
||||
free((char *)config->backtitle);
|
||||
config->backtitle = malloc(strlen(optarg) + 1);
|
||||
if (config->backtitle == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
*(config->backtitle) = '\0';
|
||||
strcat(config->backtitle, optarg);
|
||||
break;
|
||||
case 'd': /* debugging */
|
||||
debug = TRUE;
|
||||
config->debug = debug;
|
||||
break;
|
||||
case 'D': /* use dialog(1) instead of libdialog */
|
||||
config->display_type = DPV_DISPLAY_DIALOG;
|
||||
break;
|
||||
case 'h': /* help/usage */
|
||||
usage();
|
||||
break; /* NOTREACHED */
|
||||
case 'i': /* status line format string for single-file */
|
||||
config->status_solo = optarg;
|
||||
break;
|
||||
case 'I': /* status line format string for many-files */
|
||||
config->status_many = optarg;
|
||||
break;
|
||||
case 'l': /* Line mode */
|
||||
line_mode = TRUE;
|
||||
break;
|
||||
case 'L': /* custom label size */
|
||||
config->label_size =
|
||||
(int)strtol(optarg, (char **)NULL, 10);
|
||||
if (config->label_size == 0 && errno == EINVAL)
|
||||
errx(EXIT_FAILURE,
|
||||
"`-L' argument must be numeric");
|
||||
else if (config->label_size < -1)
|
||||
config->label_size = -1;
|
||||
break;
|
||||
case 'm': /* enable multiple file arguments */
|
||||
multiple = TRUE;
|
||||
break;
|
||||
case 'o': /* `-o path' for sending data-read to file */
|
||||
output_type = DPV_OUTPUT_FILE;
|
||||
config->output_type = DPV_OUTPUT_FILE;
|
||||
config->output = optarg;
|
||||
break;
|
||||
case 'n': /* custom number of files per `page' */
|
||||
config->display_limit =
|
||||
(int)strtol(optarg, (char **)NULL, 10);
|
||||
if (config->display_limit == 0 && errno == EINVAL)
|
||||
errx(EXIT_FAILURE,
|
||||
"`-n' argument must be numeric");
|
||||
else if (config->display_limit < 0)
|
||||
config->display_limit = -1;
|
||||
break;
|
||||
case 'N': /* No overrun (truncate reads of known-length) */
|
||||
no_overrun = TRUE;
|
||||
config->options |= DPV_NO_OVERRUN;
|
||||
break;
|
||||
case 'p': /* additional message text to use as prefix */
|
||||
if (config->pprompt == NULL) {
|
||||
config->pprompt = malloc(DPV_PPROMPT_MAX + 2);
|
||||
if (config->pprompt == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
/* +2 is for implicit "\n" appended later */
|
||||
}
|
||||
snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s",
|
||||
optarg);
|
||||
break;
|
||||
case 'P': /* custom size for mini-progressbar */
|
||||
config->pbar_size =
|
||||
(int)strtol(optarg, (char **)NULL, 10);
|
||||
if (config->pbar_size == 0 && errno == EINVAL)
|
||||
errx(EXIT_FAILURE,
|
||||
"`-P' argument must be numeric");
|
||||
else if (config->pbar_size < -1)
|
||||
config->pbar_size = -1;
|
||||
break;
|
||||
case 't': /* [X]dialog(1) title */
|
||||
if (config->title != NULL)
|
||||
free(config->title);
|
||||
config->title = malloc(strlen(optarg) + 1);
|
||||
if (config->title == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
*(config->title) = '\0';
|
||||
strcat(config->title, optarg);
|
||||
break;
|
||||
case 'T': /* test mode (don't read data, fake it) */
|
||||
config->options |= DPV_TEST_MODE;
|
||||
break;
|
||||
case 'U': /* updates per second */
|
||||
config->status_updates_per_second =
|
||||
(int)strtol(optarg, (char **)NULL, 10);
|
||||
if (config->status_updates_per_second == 0 &&
|
||||
errno == EINVAL)
|
||||
errx(EXIT_FAILURE,
|
||||
"`-U' argument must be numeric");
|
||||
break;
|
||||
case 'w': /* `-p' and `-a' widths bump [X]dialog(1) width */
|
||||
config->options |= DPV_WIDE_MODE;
|
||||
break;
|
||||
case 'x': /* `-x cmd' for sending data-read to sh(1) code */
|
||||
output_type = DPV_OUTPUT_SHELL;
|
||||
config->output_type = DPV_OUTPUT_SHELL;
|
||||
config->output = optarg;
|
||||
break;
|
||||
case 'X': /* X11 support through x11/xdialog */
|
||||
config->display_type = DPV_DISPLAY_XDIALOG;
|
||||
break;
|
||||
case '?': /* unknown argument (based on optstring) */
|
||||
/* FALLTHROUGH */
|
||||
default: /* unhandled argument (based on switch) */
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* Process remaining arguments as list of names to display */
|
||||
for (curfile = file_list; n < argc; n++) {
|
||||
nfiles++;
|
||||
|
||||
/* Allocate a new struct for the file argument */
|
||||
if (curfile == NULL) {
|
||||
if ((curfile = malloc(file_node_size)) == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
memset((void *)(curfile), '\0', file_node_size);
|
||||
file_list = curfile;
|
||||
} else {
|
||||
if ((curfile->next = malloc(file_node_size)) == NULL)
|
||||
errx(EXIT_FAILURE, "Out of memory?!");
|
||||
memset((void *)(curfile->next), '\0', file_node_size);
|
||||
curfile = curfile->next;
|
||||
}
|
||||
curfile->name = argv[n];
|
||||
|
||||
/* Read possible `lines:' prefix from label syntax */
|
||||
if (sscanf(curfile->name, "%lli:%c", &(curfile->length),
|
||||
&dummy) == 2)
|
||||
curfile->name = strchr(curfile->name, ':') + 1;
|
||||
else
|
||||
curfile->length = -1;
|
||||
|
||||
/* Read path argument if enabled */
|
||||
if (multiple) {
|
||||
if (++n >= argc)
|
||||
errx(EXIT_FAILURE, "Missing path argument "
|
||||
"for label number %i", nfiles);
|
||||
curfile->path = argv[n];
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Display usage and exit if not given at least one name */
|
||||
if (nfiles == 0) {
|
||||
warnx("no labels provided");
|
||||
usage();
|
||||
/* NOTREACHED */
|
||||
}
|
||||
|
||||
/*
|
||||
* Set cleanup routine for Ctrl-C action
|
||||
*/
|
||||
if (config->display_type == DPV_DISPLAY_LIBDIALOG) {
|
||||
act.sa_handler = sig_int;
|
||||
sigaction(SIGINT, &act, 0);
|
||||
}
|
||||
|
||||
/* Set status formats and action */
|
||||
if (line_mode) {
|
||||
config->status_solo = LINE_STATUS_SOLO;
|
||||
config->status_many = LINE_STATUS_SOLO;
|
||||
config->action = operate_on_lines;
|
||||
} else {
|
||||
config->status_solo = BYTE_STATUS_SOLO;
|
||||
config->status_many = BYTE_STATUS_SOLO;
|
||||
config->action = operate_on_bytes;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hand off to dpv(3)...
|
||||
*/
|
||||
if (dpv(config, file_list) != 0 && debug)
|
||||
warnx("dpv(3) returned error!?");
|
||||
|
||||
end_dialog();
|
||||
dpv_free();
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler to indicate we received a Ctrl-C interrupt.
|
||||
*/
|
||||
static void
|
||||
sig_int(int sig __unused)
|
||||
{
|
||||
dpv_interrupt = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print short usage statement to stderr and exit with error status.
|
||||
*/
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
|
||||
if (debug) /* No need for usage */
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
fprintf(stderr, "Usage: %s [options] bytes:label\n", pgm);
|
||||
fprintf(stderr, " %s [options] -m bytes1:label1 path1 "
|
||||
"[bytes2:label2 path2 ...]\n", pgm);
|
||||
fprintf(stderr, "OPTIONS:\n");
|
||||
#define OPTFMT "\t%-14s %s\n"
|
||||
fprintf(stderr, OPTFMT, "-a text",
|
||||
"Append text. Displayed below file progress indicators.");
|
||||
fprintf(stderr, OPTFMT, "-b backtitle",
|
||||
"String to be displayed on the backdrop, at top-left.");
|
||||
fprintf(stderr, OPTFMT, "-d",
|
||||
"Debug. Write to standard output instead of dialog.");
|
||||
fprintf(stderr, OPTFMT, "-D",
|
||||
"Use dialog(1) instead of dialog(3) [default].");
|
||||
fprintf(stderr, OPTFMT, "-h",
|
||||
"Produce this output on standard error and exit.");
|
||||
fprintf(stderr, OPTFMT, "-i format",
|
||||
"Customize status line format. See fdpv(1) for details.");
|
||||
fprintf(stderr, OPTFMT, "-I format",
|
||||
"Customize status line format. See fdpv(1) for details.");
|
||||
fprintf(stderr, OPTFMT, "-L size",
|
||||
"Label size. Must be a number greater than 0, or -1.");
|
||||
fprintf(stderr, OPTFMT, "-m",
|
||||
"Enable processing of multiple file argiments.");
|
||||
fprintf(stderr, OPTFMT, "-n num",
|
||||
"Display at-most num files per screen. Default is -1.");
|
||||
fprintf(stderr, OPTFMT, "-N",
|
||||
"No overrun. Stop reading input at stated length, if any.");
|
||||
fprintf(stderr, OPTFMT, "-o file",
|
||||
"Output data to file. First %s replaced with label text.");
|
||||
fprintf(stderr, OPTFMT, "-p text",
|
||||
"Prefix text. Displayed above file progress indicators.");
|
||||
fprintf(stderr, OPTFMT, "-P size",
|
||||
"Mini-progressbar size. Must be a number greater than 3.");
|
||||
fprintf(stderr, OPTFMT, "-t title",
|
||||
"Title string to be displayed at top of dialog(1) box.");
|
||||
fprintf(stderr, OPTFMT, "-T",
|
||||
"Test mode. Don't actually read any data, but fake it.");
|
||||
fprintf(stderr, OPTFMT, "-U num",
|
||||
"Update status line num times per-second. Default is 2.");
|
||||
fprintf(stderr, OPTFMT, "-w",
|
||||
"Wide. Width of `-p' and `-a' text bump dialog(1) width.");
|
||||
fprintf(stderr, OPTFMT, "-x cmd",
|
||||
"Send data to executed cmd. First %s replaced with label.");
|
||||
fprintf(stderr, OPTFMT, "-X",
|
||||
"X11. Use Xdialog(1) instead of dialog(1).");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
68
usr.bin/dpv/dpv_util.h
Normal file
68
usr.bin/dpv/dpv_util.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user