Index: usr.sbin/bsdinstall/distextract/Makefile =================================================================== --- usr.sbin/bsdinstall/distextract/Makefile (revision 270723) +++ usr.sbin/bsdinstall/distextract/Makefile (working copy) @@ -2,8 +2,8 @@ BINDIR= /usr/libexec/bsdinstall PROG= distextract -DPADD= ${LIBARCHIVE} ${LIBNCURSESW} ${LIBDIALOG} ${LIBM} -LDADD= -larchive -lncursesw -ldialog -lm +DPADD= ${LIBARCHIVE} ${LIBDPV} ${LIBDIALOG} ${LIBFIGAR} ${LIBM} ${LIBNCURSESW} +LDADD= -larchive -ldpv -ldialog -lfigpar -lncursesw -lm WARNS?= 6 MAN= Index: usr.sbin/bsdinstall/distextract/distextract.c =================================================================== --- usr.sbin/bsdinstall/distextract/distextract.c (revision 270723) +++ usr.sbin/bsdinstall/distextract/distextract.c (working copy) @@ -1,5 +1,6 @@ /*- * Copyright (c) 2011 Nathan Whitehorn + * Copyright (c) 2014 Devin Teske * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,215 +28,309 @@ */ #include -#include +#include +#include +#include +#include +#include #include #include -#include -#include +#include +#include +#include +#include -static int extract_files(int nfiles, const char **files); +/* Data to process */ +static char *distdir = NULL; +static struct archive *archive = NULL; +static struct dpv_file_node *file_list = NULL; +/* Data processing */ +static int extract_flags = 0; + +/* Extra display information */ +static char backtitle[] = "FreeBSD Installer"; +static char title[] = "Archive Extraction"; +static char pprompt[] = "Extracting distribution files...\n"; +static char aprompt[] = "\n Overall Progress:"; + +/* Function prototypes for private functions (see style(9)) */ +static int count_archive_files(const char *file); +static int extract(struct dpv_file_node *file, int out); +void sig_int(int sig); + +#if __FreeBSD_version <= 1000008 /* r232154: bump for libarchive update */ +#define archive_read_support_filter_all(x) \ + archive_read_support_compression_all(x) +#endif + +#define _errx(...) (end_dialog(), errx(__VA_ARGS__)) + int main(void) { - char *diststring; - const char **dists; - int i, retval, ndists = 0; + int ndists = 0; + size_t config_size = sizeof(struct dpv_config); + size_t file_node_size = sizeof(struct dpv_file_node); + char *distributions, *chrootdir; + struct dpv_config *config; + struct dpv_file_node *curfile = file_list; + struct sigaction act; - if (getenv("DISTRIBUTIONS") == NULL) { - fprintf(stderr, "DISTRIBUTIONS variable is not set\n"); - return (1); - } + if ((distributions = getenv("DISTRIBUTIONS")) == NULL) + errx(EXIT_FAILURE, "DISTRIBUTIONS variable is not set"); + if ((distdir = getenv("BSDINSTALL_DISTDIR")) == NULL) + distdir = __DECONST(char *, ""); - diststring = strdup(getenv("DISTRIBUTIONS")); - for (i = 0; diststring[i] != 0; i++) - if (isspace(diststring[i]) && !isspace(diststring[i+1])) - ndists++; - ndists++; /* Last one */ + /* Initialize dialog(3) */ + init_dialog(stdin, stdout); + dialog_vars.backtitle = backtitle; + dlg_put_backtitle(); - dists = calloc(ndists, sizeof(const char *)); - if (dists == NULL) { - fprintf(stderr, "Out of memory!\n"); - free(diststring); - return (1); + dialog_msgbox("", + "Checking distribution archives.\nPlease wait...", 4, 35, FALSE); + + /* + * Parse $DISTRIBUTIONS into dpv(3) linked-list + */ + while (*distributions != '\0') + { + size_t span = strcspn(distributions, "\t\n\v\f\r "); + + if (span < 1) { /* currently on whitespace */ + distributions++; + continue; + } + ndists++; + + /* Allocate a new struct for the distribution */ + if (curfile == NULL) { + if ((curfile = calloc(1, file_node_size)) == NULL) + _errx(EXIT_FAILURE, "Out of memory?!"); + file_list = curfile; + } else { + if ((curfile->next = calloc(1, file_node_size)) + == NULL) _errx(EXIT_FAILURE, "Out of memory?!"); + curfile = curfile->next; + } + + /* Set path */ + if ((curfile->path = malloc(span + 1)) == NULL) + _errx(EXIT_FAILURE, "Out of memory?!"); + snprintf(curfile->path, span + 1, "%s", distributions); + curfile->path[span] = '\0'; + + /* Set display name */ + curfile->name = strrchr(curfile->path, '/'); + if (curfile->name == NULL) curfile->name = curfile->path; + + /* Set initial length in bytes (-1 == unknown) */ + curfile->length = count_archive_files(curfile->path); + if (curfile->length < 0) { + end_dialog(); + return EXIT_FAILURE; + } + + distributions += span; } - for (i = 0; i < ndists; i++) - dists[i] = strsep(&diststring, " \t"); + /* Optionally chdir(2) into $BSDINSTALL_CHROOT */ + chrootdir = getenv("BSDINSTALL_CHROOT"); + if (chrootdir != NULL && chdir(chrootdir) != 0) + { + char error[PATH_MAX + 512]; - init_dialog(stdin, stdout); - dialog_vars.backtitle = __DECONST(char *, "FreeBSD Installer"); - dlg_put_backtitle(); + snprintf(error, sizeof(error), + "Could could change to directory %s: %s\n", + chrootdir, strerror(errno)); + dialog_msgbox("Error", error, 0, 0, TRUE); - if (chdir(getenv("BSDINSTALL_CHROOT")) != 0) { - char error[512]; - sprintf(error, "Could could change to directory %s: %s\n", - getenv("BSDINSTALL_DISTDIR"), strerror(errno)); - dialog_msgbox("Error", error, 0, 0, TRUE); end_dialog(); - return (1); + return EXIT_FAILURE; } - retval = extract_files(ndists, dists); + /* + * Set cleanup routine for Ctrl-C action + */ + act.sa_handler = sig_int; + sigaction(SIGINT, &act, 0); - end_dialog(); + /* + * Hand off to dpv(3) + */ - free(diststring); - free(dists); + if ((config = calloc(1, config_size)) == NULL) + _errx(EXIT_FAILURE, "Out of memory?!"); - return (retval); + config->backtitle = backtitle; + config->title = title; + config->pprompt = pprompt; + config->aprompt = aprompt; + config->options |= DPV_WIDE_MODE; + config->label_size = -1; + config->action = extract; + config->status_solo = + "%10lli files read @ %'9.1f files/sec."; + config->status_many = + "%10lli files read @ %'9.1f files/sec. [%i/%i busy/wait]"; + + extract_flags |= ARCHIVE_EXTRACT_TIME; + extract_flags |= ARCHIVE_EXTRACT_OWNER; + extract_flags |= ARCHIVE_EXTRACT_PERM; + extract_flags |= ARCHIVE_EXTRACT_ACL; + extract_flags |= ARCHIVE_EXTRACT_XATTR; + extract_flags |= ARCHIVE_EXTRACT_FFLAGS; + + end_dialog(); + return dpv(config, file_list); } +void +sig_int(int sig __unused) +{ + dpv_interrupt = TRUE; +} + +/* + * Returns number of files in archive file. Parses $BSDINSTALL_DISTDIR/MANIFEST + * if it exists, otherwise uses archive(3) to read the archive file. + */ static int -count_files(const char *file) +count_archive_files(const char *file) { - struct archive *archive; + int count, rv; + char path[PATH_MAX]; + char errormsg[512]; + static FILE *manifest = NULL; struct archive_entry *entry; - static FILE *manifest = NULL; - char path[MAXPATHLEN]; - char errormsg[512]; - int file_count, err; if (manifest == NULL) { - sprintf(path, "%s/MANIFEST", getenv("BSDINSTALL_DISTDIR")); + snprintf(path, PATH_MAX, "%s/MANIFEST", distdir); manifest = fopen(path, "r"); } - if (manifest != NULL) { + if (manifest != NULL) + { char line[512]; - char *tok1, *tok2; rewind(manifest); - while (fgets(line, sizeof(line), manifest) != NULL) { - tok2 = line; - tok1 = strsep(&tok2, "\t"); - if (tok1 == NULL || strcmp(tok1, file) != 0) + while (fgets(line, sizeof(line), manifest) != NULL) + { + char *p = &line[0]; + size_t span = strcspn(p, "\t") ; + + if (span < 1 || strncmp(p, file, span) != 0) continue; /* - * We're at the right manifest line. The file count is - * in the third element + * We're at the right manifest line. + * The file count is in the third element */ - tok1 = strsep(&tok2, "\t"); - tok1 = strsep(&tok2, "\t"); - if (tok1 != NULL) - return atoi(tok1); + span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); + span = strcspn(p += span + (*p != '\0' ? 1 : 0), "\t"); + if (span > 0) { + count = (int)strtol(p, (char **)NULL, 10); + if (count == 0 && errno == EINVAL) + continue; + return count; + } } } - /* Either we didn't have a manifest, or this archive wasn't there */ - archive = archive_read_new(); + /* + * Either no manifest, or manifest didn't mention this archive. + * Use archive(3) to read the archive, counting files within. + */ + if ((archive = archive_read_new()) == NULL) { + snprintf(errormsg, sizeof(errormsg), + "Error: %s\n", archive_error_string(NULL)); + dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); + return -1; + } archive_read_support_format_all(archive); archive_read_support_filter_all(archive); - sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), file); - err = archive_read_open_filename(archive, path, 4096); - if (err != ARCHIVE_OK) { + snprintf(path, PATH_MAX, "%s/%s", distdir, file); + rv = archive_read_open_filename(archive, path, 4096); + if (rv != ARCHIVE_OK) { snprintf(errormsg, sizeof(errormsg), "Error while extracting %s: %s\n", file, archive_error_string(archive)); dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); - return (-1); + archive = NULL; + return -1; } - file_count = 0; + count = 0; while (archive_read_next_header(archive, &entry) == ARCHIVE_OK) - file_count++; + count++; + archive_read_free(archive); + archive = NULL; - return (file_count); + return count; } static int -extract_files(int nfiles, const char **files) +extract(struct dpv_file_node *file, int out __unused) { - const char *items[nfiles*2]; char path[PATH_MAX]; - int archive_files[nfiles]; - int total_files, current_files, archive_file; - struct archive *archive; struct archive_entry *entry; char errormsg[512]; - char status[8]; - int i, err, progress, last_progress; + int rv; - err = 0; - progress = 0; - - /* Make the transfer list for dialog */ - for (i = 0; i < nfiles; i++) { - items[i*2] = strrchr(files[i], '/'); - if (items[i*2] != NULL) - items[i*2]++; - else - items[i*2] = files[i]; - items[i*2 + 1] = "Pending"; - } - - dialog_msgbox("", - "Checking distribution archives.\nPlease wait...", 0, 0, FALSE); - - /* Count all the files */ - total_files = 0; - for (i = 0; i < nfiles; i++) { - archive_files[i] = count_files(files[i]); - if (archive_files[i] < 0) - return (-1); - total_files += archive_files[i]; - } - - current_files = 0; - - for (i = 0; i < nfiles; i++) { - archive = archive_read_new(); + /* Open the archive if necessary */ + if (archive == NULL) { + if ((archive = archive_read_new()) == NULL) { + snprintf(errormsg, sizeof(errormsg), + "Error: %s\n", archive_error_string(NULL)); + dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); + dpv_abort = 1; + return -1; + } archive_read_support_format_all(archive); archive_read_support_filter_all(archive); - sprintf(path, "%s/%s", getenv("BSDINSTALL_DISTDIR"), files[i]); - err = archive_read_open_filename(archive, path, 4096); - - items[i*2 + 1] = "In Progress"; - archive_file = 0; - - while ((err = archive_read_next_header(archive, &entry)) == - ARCHIVE_OK) { - last_progress = progress; - progress = (current_files*100)/total_files; - - sprintf(status, "-%d", - (archive_file*100)/archive_files[i]); - items[i*2 + 1] = status; - - if (progress > last_progress) - dialog_mixedgauge("Archive Extraction", - "Extracting distribution files...", 0, 0, - progress, nfiles, - __DECONST(char **, items)); - - err = archive_read_extract(archive, entry, - ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_OWNER | - ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL | - ARCHIVE_EXTRACT_XATTR | ARCHIVE_EXTRACT_FFLAGS); - - if (err != ARCHIVE_OK) - break; - - archive_file++; - current_files++; - } - - items[i*2 + 1] = "Done"; - - if (err != ARCHIVE_EOF) { + snprintf(path, PATH_MAX, "%s/%s", distdir, file->path); + rv = archive_read_open_filename(archive, path, 4096); + if (rv != 0) { snprintf(errormsg, sizeof(errormsg), - "Error while extracting %s: %s\n", items[i*2], + "Error opening %s: %s\n", file->name, archive_error_string(archive)); - items[i*2 + 1] = "Failed"; - dialog_msgbox("Extract Error", errormsg, 0, 0, - TRUE); - return (err); + dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); + file->status = DPV_STATUS_FAILED; + dpv_abort = 1; + return -1; } + } + /* Read the next archive header */ + rv = archive_read_next_header(archive, &entry); + + /* If that went well, perform the extraction */ + if (rv == ARCHIVE_OK) + rv = archive_read_extract(archive, entry, extract_flags); + + /* Test for either EOF or error */ + if (rv == ARCHIVE_EOF) { archive_read_free(archive); + archive = NULL; + file->status = DPV_STATUS_DONE; + return 100; + } else if (rv != ARCHIVE_OK) { + snprintf(errormsg, sizeof(errormsg), + "Error while extracting %s: %s\n", file->name, + archive_error_string(archive)); + dialog_msgbox("Extract Error", errormsg, 0, 0, TRUE); + file->status = DPV_STATUS_FAILED; + dpv_abort = 1; + return -1; } - return (0); + overall_read++; + file->read++; + + /* Calculate [overall] percentage of completion (if possible) */ + if (file->length >= 0) + return (file->read * 100 / file->length); + else + return -1; } Index: share/mk/bsd.libnames.mk =================================================================== --- share/mk/bsd.libnames.mk (revision 270723) +++ share/mk/bsd.libnames.mk (working copy) @@ -43,6 +43,7 @@ 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 @@ -49,6 +50,7 @@ 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 Index: lib/Makefile =================================================================== --- lib/Makefile (revision 270723) +++ lib/Makefile (working copy) @@ -42,11 +42,13 @@ SUBDIR= ${SUBDIR_ORDERED} \ libcrypt \ libdevinfo \ libdevstat \ + libdpv \ libdwarf \ libedit \ libexecinfo \ libexpat \ libfetch \ + libfigpar \ libgeom \ ${_libgpib} \ ${_libgssapi} \ Index: lib/libdpv/Makefile =================================================================== --- lib/libdpv/Makefile (revision 0) +++ lib/libdpv/Makefile (working copy) @@ -0,0 +1,16 @@ +# $FreeBSD$ + +LIB= dpv +SHLIB_MAJOR= 1 +INCS= dpv.h +MAN= dpv.3 +MLINKS= dpv.3 dpv_free.3 + +CFLAGS+= -I${.CURDIR} +LDFLAGS+= -ldialog -lfigpar -lncurses -lutil + +SRCS= dialog_util.c dialogrc.c dprompt.c dpv.c status.c util.c + +WARNS?= 6 + +.include Property changes on: lib/libdpv/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dialog_util.c =================================================================== --- lib/libdpv/dialog_util.c (revision 0) +++ lib/libdpv/dialog_util.c (working copy) @@ -0,0 +1,592 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ +int dheight = 0; +int dwidth = 0; +char *title = NULL; +char *backtitle = NULL; + +/* TTY/Screen characteristics */ +static struct winsize *maxsize = NULL; + +/* Function prototypes for private functions (see style(9)) */ +void tty_maxsize_update(void); +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. + */ +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. + */ +void +x11_maxsize_update(void) +{ + FILE *f = NULL; + char *cp, *rows, *cols; + char cmdbuf[LINE_MAX], 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) +{ + unsigned int n = 0; + int height, width, error, stdin_pipe[2] = { -1, -1 }; + char *cp, *dargv[13], dummy_init[2] = ""; + posix_spawn_file_actions_t action; + + /* 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 + { + unsigned int i; + + 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 = 1; /* See declaration comment */ + } else if (*cp == '\n') { + if (!nls) nlines++; + nls = 0; /* 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() */ + int n = 0, longest = 0; + const char *p = prompt; + + /* `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 = 0; /* 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 = 1; /* 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) +{ + char *lastline, *p; + uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */ + + 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 = 0; /* See declaration comment */ + } + } + /* dialog(3) does not expand escaped newlines */ + if (use_libdialog) { + p++; + continue; + } + if (*p == '\\' && *(p + 1) != '\0' && *(++p) == 'n') { + nls = 1; /* 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() */ + int wlines = 0, n = 0; + char *p = prompt; + + /* `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 = 0; /* 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 = 1; /* 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). + */ + char *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) */ +} Property changes on: lib/libdpv/dialog_util.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dialog_util.h =================================================================== --- lib/libdpv/dialog_util.h (revision 0) +++ lib/libdpv/dialog_util.h (working copy) @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DIALOG_UTIL_H_ +#define _DIALOG_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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 int dheight, dwidth; +extern char *title, *backtitle; + +/* Function prototypes */ +int dialog_maxcols(void); +int dialog_maxrows(void); +char * dialog_prompt_lastline(char *prompt, uint8_t nlstate); +unsigned int dialog_prompt_longestline(const char *prompt, uint8_t nlstate); +unsigned int dialog_prompt_numlines(const char *prompt, uint8_t nlstate); +uint8_t dialog_prompt_nlstate(const char *prompt); +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() + +#ifdef __cplusplus +} +#endif + +#endif /* !_DIALOG_UTIL_H_ */ Property changes on: lib/libdpv/dialog_util.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libdpv/dialogrc.c =================================================================== --- lib/libdpv/dialogrc.c (revision 0) +++ lib/libdpv/dialogrc.c (working copy) @@ -0,0 +1,326 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dialogrc.h" + +#define STR_BUFSIZE 255 + +/* dialog(1) `.dialogrc' characteristics */ +char separator[STR_BUFSIZE] = ""; +uint8_t use_shadow = 1; +uint8_t use_colors = 1; +char gauge_color[STR_BUFSIZE] = "47b"; /* (BLUE,WHITE,ON) */ + +/* Function prototypes for private functions (see style(9)) */ +int setattr(struct config *, uint32_t, char *, char *); +int setbool(struct config *, uint32_t, char *, char *); +int setnum(struct config *, uint32_t, char *, char *); +int setstr(struct config *, uint32_t, char *, char *); + +/* + * Anatomy of DIALOGRC (~/.dialogrc by default) + * NOTE: Must appear after private function prototypes (above) + * NB: Brace-initialization of union requires cast to *first* member of union + */ +static struct config dialogrc_config[] = { + /* TYPE Directive DEFAULT HANDLER */ + {TYPE_INT, "aspect", {(void *)0}, &setnum}, + {TYPE_STR, "separate_widget", {separator}, &setstr}, + {TYPE_INT, "tab_len", {(void *)0}, &setnum}, + {TYPE_BOOL, "visit_items", {(void *)0}, &setbool}, + {TYPE_BOOL, "use_shadow", {(void *)1}, &setbool}, + {TYPE_BOOL, "use_colors", {(void *)1}, &setbool}, + {TYPE_STR, "screen_color", {NULL}, &setattr}, + {TYPE_STR, "shadow_color", {NULL}, &setattr}, + {TYPE_STR, "dialog_color", {NULL}, &setattr}, + {TYPE_STR, "title_color", {NULL}, &setattr}, + {TYPE_STR, "border_color", {NULL}, &setattr}, + {TYPE_STR, "button_active_color", {NULL}, &setattr}, + {TYPE_STR, "button_inactive_color", {NULL}, &setattr}, + {TYPE_STR, "button_key_active_color", {NULL}, &setattr}, + {TYPE_STR, "button_key_inactive_color", {NULL}, &setattr}, + {TYPE_STR, "button_label_active_color", {NULL}, &setattr}, + {TYPE_STR, "button_label_inactive_color", {NULL}, &setattr}, + {TYPE_STR, "inputbox_color", {NULL}, &setattr}, + {TYPE_STR, "inputbox_border_color", {NULL}, &setattr}, + {TYPE_STR, "searchbox_color", {NULL}, &setattr}, + {TYPE_STR, "searchbox_title_color", {NULL}, &setattr}, + {TYPE_STR, "searchbox_border_color", {NULL}, &setattr}, + {TYPE_STR, "position_indicator_color", {NULL}, &setattr}, + {TYPE_STR, "menubox_color", {NULL}, &setattr}, + {TYPE_STR, "menubox_border_color", {NULL}, &setattr}, + {TYPE_STR, "item_color", {NULL}, &setattr}, + {TYPE_STR, "item_selected_color", {NULL}, &setattr}, + {TYPE_STR, "tag_color", {NULL}, &setattr}, + {TYPE_STR, "tag_selected_color", {NULL}, &setattr}, + {TYPE_STR, "tag_key_color", {NULL}, &setattr}, + {TYPE_STR, "tag_key_selected_color", {NULL}, &setattr}, + {TYPE_STR, "check_color", {NULL}, &setattr}, + {TYPE_STR, "check_selected_color", {NULL}, &setattr}, + {TYPE_STR, "uarrow_color", {NULL}, &setattr}, + {TYPE_STR, "darrow_color", {NULL}, &setattr}, + {TYPE_STR, "itemhelp_color", {NULL}, &setattr}, + {TYPE_STR, "form_active_text_color", {NULL}, &setattr}, + {TYPE_STR, "form_text_color", {NULL}, &setattr}, + {TYPE_STR, "form_item_readonly_color", {NULL}, &setattr}, + {TYPE_STR, "gauge_color", {gauge_color}, &setattr}, + {0, NULL, {0}, NULL} +}; + +/* + * figpar call-back for interpreting value as .dialogrc `Attribute' + */ +int +setattr(struct config *option, uint32_t line __unused, + char *directive __unused, char *value) +{ + size_t len; + char *cp = value, 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 != '(') + { + char *val; + + /* 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' + */ +int +setbool(struct config *option, uint32_t line __unused, + char *directive __unused, char *value) +{ + if (option == NULL) { + warnx("%s:%d:%s: Missing callback parameter", __FILE__, + __LINE__, __func__); + return -1; /* Abort processing */ + } + + /* Assume ON, check for OFF (case-insensitive) */ + option->value.boolean = 1; + strtolower(value); + if (strcmp(value, "off") == 0) option->value.boolean = 0; + + return 0; +} + +/* + * figpar call-back for interpreting value as .dialogrc `Number' + */ +int +setnum(struct config *option, uint32_t line __unused, char *directive __unused, + char *value) +{ + if (option == NULL) { + warnx("%s:%d:%s: Missing callback parameter", __FILE__, + __LINE__, __func__); + return -1; /* Abort processing */ + } + + /* Convert the string to a 32-bit signed integer */ + option->value.num = (int32_t)strtol(value, (char **)NULL, 10); + + return 0; +} + +/* + * figpar call-back for interpreting value as .dialogrc `String' + */ +int +setstr(struct config *option, uint32_t line __unused, char *directive __unused, + char *value) +{ + size_t len; + + if (option == NULL) { + warnx("%s:%d:%s: Missing callback parameter", __FILE__, + __LINE__, __func__); + return -1; /* Abort processing */ + } + + /* Allocate memory for the data if not already done */ + if (option->value.str == NULL) { + if ((option->value.str = malloc(STR_BUFSIZE)) == NULL) + return -1; + } + + /* Trim leading quote */ + if (*value == '"') value++; + + /* Write the data into the buffer */ + snprintf(option->value.str, STR_BUFSIZE, "%s", value); + + /* Trim trailing quote */ + len = strlen(option->value.str); + if (option->value.str[len - 1] == '"') + option->value.str[len - 1] = '\0'; + + return 0; +} + +/* + * Parse (in order of preference) $DIALOGRC or `$HOME/.dialogrc'. Returns zero + * on success, -1 on failure (and errno should be consulted). + */ +int +parse_dialogrc(void) +{ + int res; + char *cp, 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) + { + size_t len; + + /* Copy $HOME into buffer and append trailing `/' if missing */ + snprintf(path, PATH_MAX, "%s", cp); + len = strlen(path); + cp = path + len; + if (len > 0 && len < (PATH_MAX - 1) && *(cp - 1) != '/') { + *cp++ = '/'; *cp = '\0'; len++; + } + + /* If we still have room, shove in the name of rc file */ + if (len < (PATH_MAX - 1)) + snprintf(cp, PATH_MAX - len, "%s", DIALOGRC); + } else { + /* Like dialog(1), don't process a file if $HOME is unset */ + errno = ENOENT; + return -1; + } + + /* Process file (either $DIALOGRC if set, or `$HOME/.dialogrc') */ + res = parse_config(dialogrc_config, path, NULL, BREAK_ON_EQUALS); + + /* Set some globals based on what we parsed */ + use_shadow = dialogrc_config_option("use_shadow")->value.boolean; + use_colors = dialogrc_config_option("use_colors")->value.boolean; + snprintf(gauge_color, STR_BUFSIZE, "%s", + dialogrc_config_option("gauge_color")->value.str); + + return res; +} + +/* + * Return a pointer to the `.dialogrc' config option specific to `directive' or + * static dummy_config (full of NULLs) if none found (see get_config_option(3); + * part of figpar(3)). + */ +struct config * +dialogrc_config_option(const char *directive) +{ + return get_config_option(dialogrc_config, directive); +} Property changes on: lib/libdpv/dialogrc.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dialogrc.h =================================================================== --- lib/libdpv/dialogrc.h (revision 0) +++ lib/libdpv/dialogrc.h (working copy) @@ -0,0 +1,60 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DIALOGRC_H_ +#define _DIALOGRC_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* 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[]; + +/* Function prototypes */ +struct config * dialogrc_config_option(const char *directive); +int parse_dialogrc(void); + +#ifdef __cplusplus +} +#endif + +#endif /* !_DIALOGRC_H_ */ Property changes on: lib/libdpv/dialogrc.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dprompt.c =================================================================== --- lib/libdpv/dprompt.c (revision 0) +++ lib/libdpv/dprompt.c (working copy) @@ -0,0 +1,731 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#define _BSD_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ +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 done_size, done_lsize, done_rsize; +static int fail_size, fail_lsize, fail_rsize; +static int msg_size, msg_lsize, msg_rsize; +static int pend_size, pend_lsize, pend_rsize; +static int pct_lsize, pct_rsize; +static int gauge_percent = 0; +static char *done = NULL; +static char *fail = NULL; +static char *pend = NULL; +static char msg[PROMPT_MAX + 1]; +#define SPIN_SIZE 4 +static char spin[SPIN_SIZE + 1] = "/-\\|"; +static char *spin_cp = spin; +static void *gauge = NULL; + +/* Function prototypes for private functions (see style(9)) */ +char spin_char(void); +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. + */ +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 max_cols, max_rows, 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--) + { + int nthfile = 0, nlines = 0; + + fheight = (int)dpv_nfiles > display_limit ? + (unsigned int)display_limit : dpv_nfiles; + for (curfile = file_list; curfile != NULL; + curfile = curfile->next) + { + nthfile++; + nlines += dialog_prompt_numlines(curfile->name, nls); + if ((nthfile % display_limit) == 0) { + if (nlines > fheight) fheight = nlines; + nlines = nthfile = 0; + } + } + if (nlines > fheight) fheight = nlines; + 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) + { + int len; + + 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?!"); + 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?!"); + 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?!"); + 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. + */ +int +dprompt_add_files(struct dpv_file_node *file_list, + struct dpv_file_node *curfile, int pct) +{ + uint8_t after_curfile = ( curfile ? FALSE : TRUE ); + uint8_t nls = 0; + int nlines = 0, nthfile = 0; + enum dprompt_state dstate; + char bold_code = 'b'; /* default: enabled */ + char color_code = '4'; /* default: blue */ + char flabel[FLABEL_MAX + 1]; + char pbar[pbar_size + 16]; /* +15 for optional color */ + struct dpv_file_node *fp; + + /* 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) + { + int pwidth; + char pbar_cap[sizeof(pbar)]; + char pbar_fill[sizeof(pbar)]; + + /* 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) + { + int flabel_size = label_size; + int estext_lsize, estext_size, estext_rsize; + char *lastline, *name = fp->name; + const char *format, *bg_code, *estext; + + + 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) + { + char 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) + { + const char *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) + { + char *cp; + + /* + * 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) */ + { + char human[32]; + int lsize, rsize, hlen; + + 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 < (msg_size = strlen(msg))) { + msg_lsize = msg_rsize = 0; + *(msg + pbar_size) = '\0'; + msg_size = pbar_size; + } else { + msg_lsize = (pbar_size - msg_size) / 2; + msg_rsize = msg_lsize; + if ((msg_rsize + msg_size + msg_lsize) + != pbar_size) msg_rsize++; + } + if (use_color) + dprompt_add(format, bold_code, bg_code, + msg_lsize, "", msg, msg_rsize, ""); + else + dprompt_add(format, msg_lsize, "", msg, + msg_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 */ + 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) +{ + /* + * 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) + { + size_t len; + + /* 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); +} Property changes on: lib/libdpv/dprompt.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dprompt.h =================================================================== --- lib/libdpv/dprompt.h (revision 0) +++ lib/libdpv/dprompt.h (working copy) @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DPROMPT_H_ +#define _DPROMPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#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; + +/* Function prototypes */ +int dprompt_add(const char *format, ...); +void dprompt_clear(void); +void dprompt_dprint(int fd, const char *prefix, const char *append, + int overall); +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_sprint(char * restrict str, const char *prefix, + const char *append); + +#ifdef __cplusplus +} +#endif + +#endif /* !_DPROMPT_H_ */ Property changes on: lib/libdpv/dprompt.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libdpv/dpv.3 =================================================================== --- lib/libdpv/dpv.3 (revision 0) +++ lib/libdpv/dpv.3 (working copy) @@ -0,0 +1,483 @@ +.\" 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 Aug 30, 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 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 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 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 . Property changes on: lib/libdpv/dpv.3 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dpv.c =================================================================== --- lib/libdpv/dpv.c (revision 0) +++ lib/libdpv/dpv.c (working copy) @@ -0,0 +1,701 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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 *pprompt = NULL; /* dpv_config.pprompt */ +char *msg_done = NULL; +char *msg_fail = NULL; +char *msg_pending = NULL; + +/* Status-Line format for when using dialog(3) */ +char status_format_default[DPV_STATUS_FORMAT_MAX]; +const char *status_format_custom = NULL; + +/* + * 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) +{ + uint8_t no_overrun = FALSE; + uint8_t pprompt_nls = 0; /* See dialog_prompt_nlstate() */ + uint8_t shrink_label_size = FALSE; + uint16_t options; + enum dpv_display display_type; + enum dpv_output output_type; + int (*action)(struct dpv_file_node *file, int out); + int dialog_out = STDOUT_FILENO; + int dialog_updates_per_second, dialog_update_usec = 0; + int files_left; + int max_cols; + int nthfile = 0, overall = 0; + int seconds, status_old_seconds = -1, dialog_old_seconds = -1; + int status_last_update = 0, dialog_last_update = 0; + int status_old_nthfile = 0, dialog_old_nthfile = 0; + int status_updates_per_second, status_update_usec = 0; + char *cp, *output; + const char *status_fmt, *path_fmt; + pid_t pid; + size_t len; + struct dpv_file_node *list_head, *curfile; + struct timeval start, now; + + /* 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) + { + int nls = 0; /* See dialog_prompt_nlstate() */ + char *last, *name = curfile->name; + + if (curfile == file_list) nls = pprompt_nls; + last = (char *)dialog_prompt_lastline(name, nls); + if (use_dialog) { + char 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) { + int res; + 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) + { + int backslash = 0; + char *fc; + + /* 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) + { + struct dpv_file_node *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) + { + char init_prompt[PROMPT_MAX + 1] = ""; + + /* 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 */ + dprompt_sprint(init_prompt, pprompt, aprompt); + if (use_libdialog) { + status_printf(""); + dprompt_libprint(pprompt, aprompt, 0); + } else { + 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) */ + 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) + { + uint8_t keep_going = TRUE; + int output_out = -1; + int pct = 0; + pid_t output_pid; + + 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) + { + mode_t 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) + { + enum dpv_status status; + + if (dialog_test) { + usleep(50000); + pct += increment; + overall_read += + (int)(random() / 512 / dpv_nfiles); + } 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, overall_read, + (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__, overall_read); + + if (dpv_interrupt || dpv_abort) + return -1; + else + return 0; +} + +/* + * Free allocated items initialized by dpv() + */ +void +dpv_free(void) +{ + if (aprompt != NULL) free(aprompt); + if (pprompt != NULL) free(pprompt); +} Property changes on: lib/libdpv/dpv.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/dpv.h =================================================================== --- lib/libdpv/dpv.h (revision 0) +++ lib/libdpv/dpv.h (working copy) @@ -0,0 +1,168 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DPV_H_ +#define _DPV_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* Data to process */ +extern long long 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" + +/* Function prototypes */ +int dpv(struct dpv_config *config, struct dpv_file_node *file_list); +void dpv_free(void); + +#ifdef __cplusplus +} +#endif + +#endif /* !_DPV_H_ */ Property changes on: lib/libdpv/dpv.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libdpv/dpv_private.h =================================================================== --- lib/libdpv/dpv_private.h (revision 0) +++ lib/libdpv/dpv_private.h (working copy) @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DPV_PRIVATE_H_ +#define _DPV_PRIVATE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* 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 */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* !_DPV_PRIVATE_H_ */ Property changes on: lib/libdpv/dpv_private.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/status.c =================================================================== --- lib/libdpv/status.c (revision 0) +++ lib/libdpv/status.c (working copy) @@ -0,0 +1,98 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include "dialog_util.h" +#include "status.h" + +/* static globals */ +static char *status_buf; +static int status_bufsize = -1; +static int status_row, 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, ...) +{ + va_list args; + int n, attrs; + chtype color = dlg_color_pair(dlg_color_table[BUTTON_ACTIVE_ATTR].fg, + dlg_color_table[SCREEN_ATTR].bg) | A_BOLD; + + 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(); +} Property changes on: lib/libdpv/status.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/status.h =================================================================== --- lib/libdpv/status.h (revision 0) +++ lib/libdpv/status.h (working copy) @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* 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 */ + +/* Function prototypes */ +void status_printf(const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* !_STATUS_H_ */ Property changes on: lib/libdpv/status.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libdpv/util.c =================================================================== --- lib/libdpv/util.c (revision 0) +++ lib/libdpv/util.c (working copy) @@ -0,0 +1,112 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#include "util.h" + +extern char **environ; +static char shellcmd[PATH_MAX] = PATH_SHELL; +static char cmdbuf[CMDBUFMAX] = ""; + +/* + * 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) +{ + unsigned int n = 0; + int len, error, stdin_pipe[2] = { -1, -1 }; + char *dargv[6]; + posix_spawn_file_actions_t action; + + /* Populate argument array */ + dargv[n++] = shellcmd; + if ((dargv[n] = malloc(3)) == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + sprintf(dargv[n++], "-c"); + 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; + } + dargv[n++] = cmdbuf; + if ((dargv[n] = malloc(3)) == NULL) + errx(EXIT_FAILURE, "Out of memory?!"); + sprintf(dargv[n++], "--"); + dargv[n++] = shellcmd; + dargv[n] = NULL; + + /* 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 + { + unsigned int i; + + fprintf(stderr, "%s: spawning `", __func__); + for (i = 0; i < n; i++) + { + if (i == 0) + fprintf(stderr, "%s", dargv[i]); + else if (i == 2) + 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, shellcmd, &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]; +} Property changes on: lib/libdpv/util.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libdpv/util.h =================================================================== --- lib/libdpv/util.h (revision 0) +++ lib/libdpv/util.h (working copy) @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#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 + +/* Function prototypes */ +int shell_spawn_pipecmd(const char *cmd, const char *label, pid_t *pid); + +#ifdef __cplusplus +} +#endif + +#endif /* !_UTIL_H_ */ Property changes on: lib/libdpv/util.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: lib/libfigpar/Makefile =================================================================== --- lib/libfigpar/Makefile (revision 0) +++ lib/libfigpar/Makefile (working copy) @@ -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 Property changes on: lib/libfigpar/Makefile ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libfigpar/figpar.3 =================================================================== --- lib/libfigpar/figpar.3 (revision 0) +++ lib/libfigpar/figpar.3 (working copy) @@ -0,0 +1,251 @@ +.\" 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 Aug 25, 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 config options[], const char *path" +.Fa "int \*[lp]*unknown\*[rp]\*[lp]struct config *option, uint32_t line" +.Fa "char *directive, char *value\*[rp], uint8_t processing_options" +.Fc +.Ft "struct config *" +.Fo get_config_option +.Fa "struct 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 config { + enum cfgtype type; /* value type */ + const char *directive; /* keyword */ + union cfgvalue value; /* value */ + + /* Pointer to function used when directive is found */ + int (*action)(struct config *option, uint32_t line, + char *directive, char *value); +}; + +enum cfgtype { + TYPE_NONE = 0x0000, /* for directives with no value */ + TYPE_BOOL = 0x0001, /* boolean */ + TYPE_INT = 0x0002, /* signed 32 bit integer */ + TYPE_UINT = 0x0004, /* unsigned 32 bit integer */ + TYPE_STR = 0x0008, /* string pointer */ + TYPE_STRARRAY = 0x0010, /* string array pointer */ + TYPE_DATA1 = 0x0020, /* void data type-1 (for whatever) */ + TYPE_DATA2 = 0x0040, /* void data type-2 (for whatever) */ + TYPE_DATA3 = 0x0080, /* void data type-3 (for whatever) */ + TYPE_RESERVED1 = 0x0100, /* reserved data type-1 (for future) */ + TYPE_RESERVED2 = 0x0200, /* reserved data type-2 (for future) */ + TYPE_RESERVED3 = 0x0400, /* reserved data type-3 (for future) */ +}; + +union 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 BREAK_ON_SEMICOLON +.It Dv 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 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 CASE_SENSITIVE +Normally directives are matched case insensitively using +.Xr fnmatch 3 . +This flag enables directive matching to be case sensitive. +.It Dv REQUIRE_EQUALS +If a directive is not followed by an equals, processing is aborted. +.It Dv 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 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 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 config" +is entirely optional as-is the use of +.Fa "enum cfgtype" +or +.Fa "union 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 still subject to refinement. Property changes on: lib/libfigpar/figpar.3 ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libfigpar/figpar.c =================================================================== --- lib/libfigpar/figpar.c (revision 0) +++ lib/libfigpar/figpar.c (working copy) @@ -0,0 +1,403 @@ +/*- + * Copyright (c) 2002-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "figpar.h" +#include "string_m.h" + +struct config dummy_config = {0, NULL, {0}, NULL}; + +/* + * Search for config option (struct 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 config * +get_config_option(struct config options[], const char *directive) +{ + uint32_t n; + + /* Check arguments */ + if (options == NULL || directive == NULL) return &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 */ + dummy_config.directive = NULL; + dummy_config.type = 0; + dummy_config.value.u_num = 0; + dummy_config.action = NULL; + + return &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 config options[], const char *path, + int (*unknown)(struct config *option, uint32_t line, char *directive, + char *value), uint16_t processing_options) +{ + uint8_t bequals, bsemicolon, case_sensitive; + uint8_t require_equals, strict_equals; + uint8_t comment = 0, have_equals = 0, quote, end, found; + int fd, error; + off_t curpos, charpos; + ssize_t r = 1; + char rpath[PATH_MAX], p[2], *t; + char *directive, *value; + uint32_t dsize, vsize, n, x, line = 1; + + /* Sanity check: if no options and no unknown function, return */ + if (options == NULL && unknown == NULL) return -1; + + /* Processing options */ + bequals = ((processing_options & BREAK_ON_EQUALS) == 0 ? 0 : 1); + bsemicolon = ((processing_options & BREAK_ON_SEMICOLON) == 0 ? 0 : 1); + case_sensitive = ((processing_options & CASE_SENSITIVE) == 0 ? 0 : 1); + require_equals = ((processing_options & REQUIRE_EQUALS) == 0 ? 0 : 1); + strict_equals = ((processing_options & 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) + { + 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 = charpos - curpos; + if (r != 0) n--; /* more to read, but don't read ending key */ + + /* 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; +} Property changes on: lib/libfigpar/figpar.c ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libfigpar/figpar.h =================================================================== --- lib/libfigpar/figpar.h (revision 0) +++ lib/libfigpar/figpar.h (working copy) @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 2002-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _FIGPAR_H_ +#define _FIGPAR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* + * Union for storing various types of data in a single common container. + */ +union 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 cfgtype { + TYPE_NONE = 0x0000, /* for directives with no value */ + TYPE_BOOL = 0x0001, /* boolean */ + TYPE_INT = 0x0002, /* signed 32 bit integer */ + TYPE_UINT = 0x0004, /* unsigned 32 bit integer */ + TYPE_STR = 0x0008, /* string pointer */ + TYPE_STRARRAY = 0x0010, /* string array pointer */ + TYPE_DATA1 = 0x0020, /* void data type-1 (for whatever) */ + TYPE_DATA2 = 0x0040, /* void data type-2 (for whatever) */ + TYPE_DATA3 = 0x0080, /* void data type-3 (for whatever) */ + TYPE_RESERVED1 = 0x0100, /* reserved data type-1 (for future) */ + TYPE_RESERVED2 = 0x0200, /* reserved data type-2 (for future) */ + TYPE_RESERVED3 = 0x0400, /* reserved data type-3 (for future) */ +}; + +/* + * Options to parse_config() for processing_options bitmask + */ +#define BREAK_ON_EQUALS 0x0001 /* stop reading directive at `=' */ +#define BREAK_ON_SEMICOLON 0x0002 /* `;' starts a new line */ +#define CASE_SENSITIVE 0x0004 /* directives are case sensitive */ +#define REQUIRE_EQUALS 0x0008 /* assignment directives only */ +#define STRICT_EQUALS 0x0010 /* `=' must be part of directive */ + +/* + * Anatomy of a config file option + */ +struct config { + enum cfgtype type; /* Type of value that the option is */ + const char *directive; /* Keyword to locate in config file */ + union cfgvalue value; /* Default (overwrite by action) */ + + /* + * Function pointer; action to be taken when the directive is found + */ + int (*action)(struct config *option, uint32_t line, char *directive, + char *value); +}; +extern struct config dummy_config; + +/* Function prototypes */ +struct config * get_config_option(struct config options[], + const char *directive); +int parse_config(struct config options[], const char *path, + int (*unknown)(struct config *option, uint32_t line, + char *directive, char *value), + uint16_t processing_options); + +#ifdef __cplusplus +} +#endif + +#endif /* _FIGPAR_H_ */ Property changes on: lib/libfigpar/figpar.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libfigpar/string_m.c =================================================================== --- lib/libfigpar/string_m.c (revision 0) +++ lib/libfigpar/string_m.c (working copy) @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 2001-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include + +#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) +{ + unsigned int n = 0; + size_t flen; + const char *p = source; + + /* 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) { + p += flen; n++; /* found an instance */ + } 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) +{ + size_t slen, flen, rlen; + uint32_t n = 0; + char *p, *t, *temp; + + 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) return -1; /* could not allocate memory */ + 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 *pos, *chr, 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, *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 ? '\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 */ + } +} Property changes on: lib/libfigpar/string_m.c ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: lib/libfigpar/string_m.h =================================================================== --- lib/libfigpar/string_m.h (revision 0) +++ lib/libfigpar/string_m.h (working copy) @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2001-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _STRING_M_H_ +#define _STRING_M_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Function prototypes */ +int replaceall(char *source, const char *find, + const char *replace); +unsigned int strcount(const char *source, const char *find); +void strexpand(char *source); +void strexpandnl(char *source); +void strtolower(char *source); + +#ifdef __cplusplus +} +#endif + +#endif /* !_STRING_M_H_ */ Property changes on: lib/libfigpar/string_m.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: usr.bin/Makefile =================================================================== --- usr.bin/Makefile (revision 270723) +++ usr.bin/Makefile (working copy) @@ -36,6 +36,7 @@ SUBDIR= alias \ ctlstat \ cut \ dirname \ + dpv \ du \ ee \ elf2aout \ Index: usr.bin/dpv/Makefile =================================================================== --- usr.bin/dpv/Makefile (revision 0) +++ usr.bin/dpv/Makefile (working copy) @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PROG= dpv + +CFLAGS+= -I${.CURDIR} + +DPADD+= ${LIBDPV} ${LIBDIALOG} ${LIBFIGPAR} ${LIBNCURSES} ${LIBUTIL} +LDADD+= -ldpv -ldialog -lfigpar -lncurses -lutil + +WARNS?= 6 + +.include Property changes on: usr.bin/dpv/Makefile ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: usr.bin/dpv/dpv.1 =================================================================== --- usr.bin/dpv/dpv.1 (revision 0) +++ usr.bin/dpv/dpv.1 (working copy) @@ -0,0 +1,399 @@ +.\" 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 Aug 30, 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 . +.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 Property changes on: usr.bin/dpv/dpv.1 ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Index: usr.bin/dpv/dpv.c =================================================================== --- usr.bin/dpv/dpv.c (revision 0) +++ usr.bin/dpv/dpv.c (working copy) @@ -0,0 +1,549 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#define _BSD_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dpv_util.h" + +/* Debugging */ +static uint8_t debug = FALSE; + +/* Data to process */ +static unsigned int nfiles = 0; +static struct dpv_file_node *file_list = NULL; + +/* Data processing */ +static uint8_t no_overrun = FALSE; +static uint8_t line_mode = FALSE; +static int output_type = DPV_OUTPUT_NONE; +static int fd = -1; +static size_t bsize; +static char *buf = NULL; +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 for private functions (see style(9)) */ +void usage(void); +void sig_int(int sig); +int operate_common(struct dpv_file_node *file, int out); +int operate_on_bytes(struct dpv_file_node *file, int out); +int operate_on_lines(struct dpv_file_node *file, int out); + +int +operate_common(struct dpv_file_node *file, int out) +{ + /* 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) + { + struct stat sb; + + 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; +} + +int +operate_on_bytes(struct dpv_file_node *file, int out) +{ + ssize_t r, w; + + if (operate_common(file, out) < 0) return -1; + + /* [Re-]Fill the buffer */ + if ((r = read(fd, buf, bsize)) <= 0) + { + if (fd != STDIN_FILENO) close(fd); + fd = -1; + file->status = DPV_STATUS_DONE; + return 100; + } + + /* [Re-]Dump the buffer */ + if (out >= 0) + { + if ((w = write(out, buf, r)) < 0) { + end_dialog(); + err(EXIT_FAILURE, "output"); + } + fsync(out); + } + + overall_read += r; + file->read += r; + + /* Calculate percentage of completion (if possible) */ + if (file->length >= 0) + { + int 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; +} + +int +operate_on_lines(struct dpv_file_node *file, int out) +{ + char *p; + ssize_t r, w; + + if (operate_common(file, out) < 0) return -1; + + /* [Re-]Fill the buffer */ + if ((r = read(fd, buf, bsize)) <= 0) + { + if (fd != STDIN_FILENO) close(fd); + fd = -1; + file->status = DPV_STATUS_DONE; + return 100; + } + buf[r] = '\0'; + + /* [Re-]Dump the buffer */ + if (out >= 0) + { + if ((w = write(out, buf, r)) < 0) { + end_dialog(); + err(EXIT_FAILURE, "output"); + } + fsync(out); + } + + /* Process the buffer for number of lines */ + for (p = buf; p != NULL && *p != '\0';) + if ((p = strchr(p, '\n')) != NULL) + overall_read++, p++, file->read++; + + /* Calculate percentage of completion (if possible) */ + if (file->length >= 0) + { + int 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[]) +{ + struct dpv_config *config; + struct dpv_file_node *curfile; + size_t config_size = sizeof(struct dpv_config); + size_t file_node_size = sizeof(struct dpv_file_node); + int ch; + int n = 0; + 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; + 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) */ + default: /* unhandled argument (based on switch) */ + usage(); + } + } + argc -= optind; + argv += optind; + + /* Process remaining arguments as list of names to display */ + for (curfile = file_list; n < argc; n++) + { + char dummy; + + 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]; + } + + if (!multiple) break; + } + + /* Display usage and exit if not given at least one name */ + if (nfiles == 0) { + warnx("no labels provided"); + usage(); + } + + /* + * 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(); + exit(EXIT_SUCCESS); +} + +/* + * Interrupt handler to indicate we received a Ctrl-C interrupt. + */ +void +sig_int(int sig __unused) +{ + dpv_interrupt = TRUE; +} + +/* + * Print short usage statement to stderr and exit with error status. + */ +void +usage(void) +{ + + if (debug) exit(EXIT_FAILURE); /* No need for usage */ + + fprintf(stderr, "Usage: %s [options] lines:label\n", pgm); + fprintf(stderr, " %s [options] -m lines1:label1 path1 " + "[lines2: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); +} Property changes on: usr.bin/dpv/dpv.c ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Index: usr.bin/dpv/dpv_util.h =================================================================== --- usr.bin/dpv/dpv_util.h (revision 0) +++ usr.bin/dpv/dpv_util.h (working copy) @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 2013-2014 Devin Teske + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DPV_UTIL_H_ +#define _DPV_UTIL_H_ + +/* Limits */ +#define BUFSIZE_MAX (2 * 1024 * 1024) + /* Buffer size for read(2) input */ +#ifndef MAXPHYS +#define MAXPHYS (128 * 1024) + /* max raw I/O transfer size */ +#endif + +/* + * Memory strategry threshold, in pages: if physmem is larger than this, + * use a large buffer. + */ +#define PHYSPAGES_THRESHOLD (32 * 1024) + +/* + * Small (default) buffer size in bytes. It's inefficient for this to be + * smaller than MAXPHYS. + */ +#define BUFSIZE_SMALL (MAXPHYS) + +/* + * Math macros + */ +#undef MIN +#define MIN(x,y) ((x) < (y) ? (x) : (y)) +#undef MAX +#define MAX(x,y) ((x) > (y) ? (x) : (y)) + +/* + * Extra display information + */ +#define BYTE_STATUS_SOLO "%'10lli bytes read @ %'9.1f bytes/sec." +#define BYTE_STATUS_MANY (BYTE_STATUS_SOLO " [%i/%i busy/wait]") +#define LINE_STATUS_SOLO "%'10lli lines read @ %'9.1f lines/sec." +#define LINE_STATUS_MANY (LINE_STATUS_SOLO " [%i/%i busy/wait]") + +#endif /* !_DPV_UTIL_H_ */ Property changes on: usr.bin/dpv/dpv_util.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property