1*041394f3SDevin Teske /*- 2*041394f3SDevin Teske * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> 3*041394f3SDevin Teske * All rights reserved. 4*041394f3SDevin Teske * 5*041394f3SDevin Teske * Redistribution and use in source and binary forms, with or without 6*041394f3SDevin Teske * modification, are permitted provided that the following conditions 7*041394f3SDevin Teske * are met: 8*041394f3SDevin Teske * 1. Redistributions of source code must retain the above copyright 9*041394f3SDevin Teske * notice, this list of conditions and the following disclaimer. 10*041394f3SDevin Teske * 2. Redistributions in binary form must reproduce the above copyright 11*041394f3SDevin Teske * notice, this list of conditions and the following disclaimer in the 12*041394f3SDevin Teske * documentation and/or other materials provided with the distribution. 13*041394f3SDevin Teske * 14*041394f3SDevin Teske * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*041394f3SDevin Teske * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*041394f3SDevin Teske * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*041394f3SDevin Teske * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*041394f3SDevin Teske * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*041394f3SDevin Teske * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*041394f3SDevin Teske * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*041394f3SDevin Teske * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*041394f3SDevin Teske * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*041394f3SDevin Teske * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*041394f3SDevin Teske * SUCH DAMAGE. 25*041394f3SDevin Teske */ 26*041394f3SDevin Teske 27*041394f3SDevin Teske #include <sys/cdefs.h> 28*041394f3SDevin Teske __FBSDID("$FreeBSD$"); 29*041394f3SDevin Teske 30*041394f3SDevin Teske #include <sys/stat.h> 31*041394f3SDevin Teske #include <sys/types.h> 32*041394f3SDevin Teske 33*041394f3SDevin Teske #define _BSD_SOURCE /* to get dprintf() prototype in stdio.h below */ 34*041394f3SDevin Teske #include <dialog.h> 35*041394f3SDevin Teske #include <dpv.h> 36*041394f3SDevin Teske #include <err.h> 37*041394f3SDevin Teske #include <errno.h> 38*041394f3SDevin Teske #include <fcntl.h> 39*041394f3SDevin Teske #include <limits.h> 40*041394f3SDevin Teske #include <signal.h> 41*041394f3SDevin Teske #include <stdio.h> 42*041394f3SDevin Teske #include <stdlib.h> 43*041394f3SDevin Teske #include <string.h> 44*041394f3SDevin Teske #include <string_m.h> 45*041394f3SDevin Teske #include <unistd.h> 46*041394f3SDevin Teske 47*041394f3SDevin Teske #include "dpv_util.h" 48*041394f3SDevin Teske 49*041394f3SDevin Teske /* Debugging */ 50*041394f3SDevin Teske static uint8_t debug = FALSE; 51*041394f3SDevin Teske 52*041394f3SDevin Teske /* Data to process */ 53*041394f3SDevin Teske static struct dpv_file_node *file_list = NULL; 54*041394f3SDevin Teske static unsigned int nfiles = 0; 55*041394f3SDevin Teske 56*041394f3SDevin Teske /* Data processing */ 57*041394f3SDevin Teske static uint8_t line_mode = FALSE; 58*041394f3SDevin Teske static uint8_t no_overrun = FALSE; 59*041394f3SDevin Teske static char *buf = NULL; 60*041394f3SDevin Teske static int fd = -1; 61*041394f3SDevin Teske static int output_type = DPV_OUTPUT_NONE; 62*041394f3SDevin Teske static size_t bsize; 63*041394f3SDevin Teske static char rpath[PATH_MAX]; 64*041394f3SDevin Teske 65*041394f3SDevin Teske /* Extra display information */ 66*041394f3SDevin Teske static uint8_t multiple = FALSE; /* `-m' */ 67*041394f3SDevin Teske static char *pgm; /* set to argv[0] by main() */ 68*041394f3SDevin Teske 69*041394f3SDevin Teske /* Function prototypes */ 70*041394f3SDevin Teske static void sig_int(int sig); 71*041394f3SDevin Teske static void usage(void); 72*041394f3SDevin Teske int main(int argc, char *argv[]); 73*041394f3SDevin Teske static int operate_common(struct dpv_file_node *file, int out); 74*041394f3SDevin Teske static int operate_on_bytes(struct dpv_file_node *file, int out); 75*041394f3SDevin Teske static int operate_on_lines(struct dpv_file_node *file, int out); 76*041394f3SDevin Teske 77*041394f3SDevin Teske static int 78*041394f3SDevin Teske operate_common(struct dpv_file_node *file, int out) 79*041394f3SDevin Teske { 80*041394f3SDevin Teske struct stat sb; 81*041394f3SDevin Teske 82*041394f3SDevin Teske /* Open the file if necessary */ 83*041394f3SDevin Teske if (fd < 0) { 84*041394f3SDevin Teske if (multiple) { 85*041394f3SDevin Teske /* Resolve the file path and attempt to open it */ 86*041394f3SDevin Teske if (realpath(file->path, rpath) == 0 || 87*041394f3SDevin Teske (fd = open(rpath, O_RDONLY)) < 0) { 88*041394f3SDevin Teske warn("%s", file->path); 89*041394f3SDevin Teske file->status = DPV_STATUS_FAILED; 90*041394f3SDevin Teske return (-1); 91*041394f3SDevin Teske } 92*041394f3SDevin Teske } else { 93*041394f3SDevin Teske /* Assume stdin, but if that's a TTY instead use the 94*041394f3SDevin Teske * highest numbered file descriptor (obtained by 95*041394f3SDevin Teske * generating new fd and then decrementing). 96*041394f3SDevin Teske * 97*041394f3SDevin Teske * NB: /dev/stdin should always be open(2)'able 98*041394f3SDevin Teske */ 99*041394f3SDevin Teske fd = STDIN_FILENO; 100*041394f3SDevin Teske if (isatty(fd)) { 101*041394f3SDevin Teske fd = open("/dev/stdin", O_RDONLY); 102*041394f3SDevin Teske close(fd--); 103*041394f3SDevin Teske } 104*041394f3SDevin Teske 105*041394f3SDevin Teske /* This answer might be wrong, if dpv(3) has (by 106*041394f3SDevin Teske * request) opened an output file or pipe. If we 107*041394f3SDevin Teske * told dpv(3) to open a file, subtract one from 108*041394f3SDevin Teske * previous answer. If instead we told dpv(3) to 109*041394f3SDevin Teske * prepare a pipe output, subtract two. 110*041394f3SDevin Teske */ 111*041394f3SDevin Teske switch(output_type) { 112*041394f3SDevin Teske case DPV_OUTPUT_FILE: 113*041394f3SDevin Teske fd -= 1; 114*041394f3SDevin Teske break; 115*041394f3SDevin Teske case DPV_OUTPUT_SHELL: 116*041394f3SDevin Teske fd -= 2; 117*041394f3SDevin Teske break; 118*041394f3SDevin Teske } 119*041394f3SDevin Teske } 120*041394f3SDevin Teske } 121*041394f3SDevin Teske 122*041394f3SDevin Teske /* Allocate buffer if necessary */ 123*041394f3SDevin Teske if (buf == NULL) { 124*041394f3SDevin Teske /* Use output block size as buffer size if available */ 125*041394f3SDevin Teske if (out >= 0) { 126*041394f3SDevin Teske if (fstat(out, &sb) != 0) { 127*041394f3SDevin Teske warn("%i", out); 128*041394f3SDevin Teske file->status = DPV_STATUS_FAILED; 129*041394f3SDevin Teske return (-1); 130*041394f3SDevin Teske } 131*041394f3SDevin Teske if (S_ISREG(sb.st_mode)) { 132*041394f3SDevin Teske if (sysconf(_SC_PHYS_PAGES) > 133*041394f3SDevin Teske PHYSPAGES_THRESHOLD) 134*041394f3SDevin Teske bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 135*041394f3SDevin Teske else 136*041394f3SDevin Teske bsize = BUFSIZE_SMALL; 137*041394f3SDevin Teske } else 138*041394f3SDevin Teske bsize = MAX(sb.st_blksize, 139*041394f3SDevin Teske (blksize_t)sysconf(_SC_PAGESIZE)); 140*041394f3SDevin Teske } else 141*041394f3SDevin Teske bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8); 142*041394f3SDevin Teske 143*041394f3SDevin Teske /* Attempt to allocate */ 144*041394f3SDevin Teske if ((buf = malloc(bsize+1)) == NULL) { 145*041394f3SDevin Teske end_dialog(); 146*041394f3SDevin Teske err(EXIT_FAILURE, "Out of memory?!"); 147*041394f3SDevin Teske } 148*041394f3SDevin Teske } 149*041394f3SDevin Teske 150*041394f3SDevin Teske return (0); 151*041394f3SDevin Teske } 152*041394f3SDevin Teske 153*041394f3SDevin Teske static int 154*041394f3SDevin Teske operate_on_bytes(struct dpv_file_node *file, int out) 155*041394f3SDevin Teske { 156*041394f3SDevin Teske int progress; 157*041394f3SDevin Teske ssize_t r, w; 158*041394f3SDevin Teske 159*041394f3SDevin Teske if (operate_common(file, out) < 0) 160*041394f3SDevin Teske return (-1); 161*041394f3SDevin Teske 162*041394f3SDevin Teske /* [Re-]Fill the buffer */ 163*041394f3SDevin Teske if ((r = read(fd, buf, bsize)) <= 0) { 164*041394f3SDevin Teske if (fd != STDIN_FILENO) 165*041394f3SDevin Teske close(fd); 166*041394f3SDevin Teske fd = -1; 167*041394f3SDevin Teske file->status = DPV_STATUS_DONE; 168*041394f3SDevin Teske return (100); 169*041394f3SDevin Teske } 170*041394f3SDevin Teske 171*041394f3SDevin Teske /* [Re-]Dump the buffer */ 172*041394f3SDevin Teske if (out >= 0) { 173*041394f3SDevin Teske if ((w = write(out, buf, r)) < 0) { 174*041394f3SDevin Teske end_dialog(); 175*041394f3SDevin Teske err(EXIT_FAILURE, "output"); 176*041394f3SDevin Teske } 177*041394f3SDevin Teske fsync(out); 178*041394f3SDevin Teske } 179*041394f3SDevin Teske 180*041394f3SDevin Teske overall_read += r; 181*041394f3SDevin Teske file->read += r; 182*041394f3SDevin Teske 183*041394f3SDevin Teske /* Calculate percentage of completion (if possible) */ 184*041394f3SDevin Teske if (file->length >= 0) { 185*041394f3SDevin Teske progress = (file->read * 100 / (file->length > 0 ? 186*041394f3SDevin Teske file->length : 1)); 187*041394f3SDevin Teske 188*041394f3SDevin Teske /* If no_overrun, do not return 100% until read >= length */ 189*041394f3SDevin Teske if (no_overrun && progress == 100 && file->read < file->length) 190*041394f3SDevin Teske progress--; 191*041394f3SDevin Teske 192*041394f3SDevin Teske return (progress); 193*041394f3SDevin Teske } else 194*041394f3SDevin Teske return (-1); 195*041394f3SDevin Teske } 196*041394f3SDevin Teske 197*041394f3SDevin Teske static int 198*041394f3SDevin Teske operate_on_lines(struct dpv_file_node *file, int out) 199*041394f3SDevin Teske { 200*041394f3SDevin Teske char *p; 201*041394f3SDevin Teske int progress; 202*041394f3SDevin Teske ssize_t r, w; 203*041394f3SDevin Teske 204*041394f3SDevin Teske if (operate_common(file, out) < 0) 205*041394f3SDevin Teske return (-1); 206*041394f3SDevin Teske 207*041394f3SDevin Teske /* [Re-]Fill the buffer */ 208*041394f3SDevin Teske if ((r = read(fd, buf, bsize)) <= 0) { 209*041394f3SDevin Teske if (fd != STDIN_FILENO) 210*041394f3SDevin Teske close(fd); 211*041394f3SDevin Teske fd = -1; 212*041394f3SDevin Teske file->status = DPV_STATUS_DONE; 213*041394f3SDevin Teske return (100); 214*041394f3SDevin Teske } 215*041394f3SDevin Teske buf[r] = '\0'; 216*041394f3SDevin Teske 217*041394f3SDevin Teske /* [Re-]Dump the buffer */ 218*041394f3SDevin Teske if (out >= 0) { 219*041394f3SDevin Teske if ((w = write(out, buf, r)) < 0) { 220*041394f3SDevin Teske end_dialog(); 221*041394f3SDevin Teske err(EXIT_FAILURE, "output"); 222*041394f3SDevin Teske } 223*041394f3SDevin Teske fsync(out); 224*041394f3SDevin Teske } 225*041394f3SDevin Teske 226*041394f3SDevin Teske /* Process the buffer for number of lines */ 227*041394f3SDevin Teske for (p = buf; p != NULL && *p != '\0';) 228*041394f3SDevin Teske if ((p = strchr(p, '\n')) != NULL) 229*041394f3SDevin Teske overall_read++, p++, file->read++; 230*041394f3SDevin Teske 231*041394f3SDevin Teske /* Calculate percentage of completion (if possible) */ 232*041394f3SDevin Teske if (file->length >= 0) { 233*041394f3SDevin Teske progress = (file->read * 100 / file->length); 234*041394f3SDevin Teske 235*041394f3SDevin Teske /* If no_overrun, do not return 100% until read >= length */ 236*041394f3SDevin Teske if (no_overrun && progress == 100 && file->read < file->length) 237*041394f3SDevin Teske progress--; 238*041394f3SDevin Teske 239*041394f3SDevin Teske return (progress); 240*041394f3SDevin Teske } else 241*041394f3SDevin Teske return (-1); 242*041394f3SDevin Teske } 243*041394f3SDevin Teske 244*041394f3SDevin Teske /* 245*041394f3SDevin Teske * Takes a list of names that are to correspond to input streams coming from 246*041394f3SDevin Teske * stdin or fifos and produces necessary config to drive dpv(3) `--gauge' 247*041394f3SDevin Teske * widget. If the `-d' flag is used, output is instead send to terminal 248*041394f3SDevin Teske * standard output (and the output can then be saved to a file, piped into 249*041394f3SDevin Teske * custom [X]dialog(1) invocation, or whatever. 250*041394f3SDevin Teske */ 251*041394f3SDevin Teske int 252*041394f3SDevin Teske main(int argc, char *argv[]) 253*041394f3SDevin Teske { 254*041394f3SDevin Teske char dummy; 255*041394f3SDevin Teske int ch; 256*041394f3SDevin Teske int n = 0; 257*041394f3SDevin Teske size_t config_size = sizeof(struct dpv_config); 258*041394f3SDevin Teske size_t file_node_size = sizeof(struct dpv_file_node); 259*041394f3SDevin Teske struct dpv_config *config; 260*041394f3SDevin Teske struct dpv_file_node *curfile; 261*041394f3SDevin Teske struct sigaction act; 262*041394f3SDevin Teske 263*041394f3SDevin Teske pgm = argv[0]; /* store a copy of invocation name */ 264*041394f3SDevin Teske 265*041394f3SDevin Teske /* Allocate config structure */ 266*041394f3SDevin Teske if ((config = malloc(config_size)) == NULL) 267*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 268*041394f3SDevin Teske memset((void *)(config), '\0', config_size); 269*041394f3SDevin Teske 270*041394f3SDevin Teske /* 271*041394f3SDevin Teske * Process command-line options 272*041394f3SDevin Teske */ 273*041394f3SDevin Teske while ((ch = getopt(argc, argv, 274*041394f3SDevin Teske "a:b:dDhi:I:lL:mn:No:p:P:t:TU:wx:X")) != -1) { 275*041394f3SDevin Teske switch(ch) { 276*041394f3SDevin Teske case 'a': /* additional message text to append */ 277*041394f3SDevin Teske if (config->aprompt == NULL) { 278*041394f3SDevin Teske config->aprompt = malloc(DPV_APROMPT_MAX); 279*041394f3SDevin Teske if (config->aprompt == NULL) 280*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 281*041394f3SDevin Teske } 282*041394f3SDevin Teske snprintf(config->aprompt, DPV_APROMPT_MAX, "%s", 283*041394f3SDevin Teske optarg); 284*041394f3SDevin Teske break; 285*041394f3SDevin Teske case 'b': /* [X]dialog(1) backtitle */ 286*041394f3SDevin Teske if (config->backtitle != NULL) 287*041394f3SDevin Teske free((char *)config->backtitle); 288*041394f3SDevin Teske config->backtitle = malloc(strlen(optarg) + 1); 289*041394f3SDevin Teske if (config->backtitle == NULL) 290*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 291*041394f3SDevin Teske *(config->backtitle) = '\0'; 292*041394f3SDevin Teske strcat(config->backtitle, optarg); 293*041394f3SDevin Teske break; 294*041394f3SDevin Teske case 'd': /* debugging */ 295*041394f3SDevin Teske debug = TRUE; 296*041394f3SDevin Teske config->debug = debug; 297*041394f3SDevin Teske break; 298*041394f3SDevin Teske case 'D': /* use dialog(1) instead of libdialog */ 299*041394f3SDevin Teske config->display_type = DPV_DISPLAY_DIALOG; 300*041394f3SDevin Teske break; 301*041394f3SDevin Teske case 'h': /* help/usage */ 302*041394f3SDevin Teske usage(); 303*041394f3SDevin Teske break; /* NOTREACHED */ 304*041394f3SDevin Teske case 'i': /* status line format string for single-file */ 305*041394f3SDevin Teske config->status_solo = optarg; 306*041394f3SDevin Teske break; 307*041394f3SDevin Teske case 'I': /* status line format string for many-files */ 308*041394f3SDevin Teske config->status_many = optarg; 309*041394f3SDevin Teske break; 310*041394f3SDevin Teske case 'l': /* Line mode */ 311*041394f3SDevin Teske line_mode = TRUE; 312*041394f3SDevin Teske break; 313*041394f3SDevin Teske case 'L': /* custom label size */ 314*041394f3SDevin Teske config->label_size = 315*041394f3SDevin Teske (int)strtol(optarg, (char **)NULL, 10); 316*041394f3SDevin Teske if (config->label_size == 0 && errno == EINVAL) 317*041394f3SDevin Teske errx(EXIT_FAILURE, 318*041394f3SDevin Teske "`-L' argument must be numeric"); 319*041394f3SDevin Teske else if (config->label_size < -1) 320*041394f3SDevin Teske config->label_size = -1; 321*041394f3SDevin Teske break; 322*041394f3SDevin Teske case 'm': /* enable multiple file arguments */ 323*041394f3SDevin Teske multiple = TRUE; 324*041394f3SDevin Teske break; 325*041394f3SDevin Teske case 'o': /* `-o path' for sending data-read to file */ 326*041394f3SDevin Teske output_type = DPV_OUTPUT_FILE; 327*041394f3SDevin Teske config->output_type = DPV_OUTPUT_FILE; 328*041394f3SDevin Teske config->output = optarg; 329*041394f3SDevin Teske break; 330*041394f3SDevin Teske case 'n': /* custom number of files per `page' */ 331*041394f3SDevin Teske config->display_limit = 332*041394f3SDevin Teske (int)strtol(optarg, (char **)NULL, 10); 333*041394f3SDevin Teske if (config->display_limit == 0 && errno == EINVAL) 334*041394f3SDevin Teske errx(EXIT_FAILURE, 335*041394f3SDevin Teske "`-n' argument must be numeric"); 336*041394f3SDevin Teske else if (config->display_limit < 0) 337*041394f3SDevin Teske config->display_limit = -1; 338*041394f3SDevin Teske break; 339*041394f3SDevin Teske case 'N': /* No overrun (truncate reads of known-length) */ 340*041394f3SDevin Teske no_overrun = TRUE; 341*041394f3SDevin Teske config->options |= DPV_NO_OVERRUN; 342*041394f3SDevin Teske break; 343*041394f3SDevin Teske case 'p': /* additional message text to use as prefix */ 344*041394f3SDevin Teske if (config->pprompt == NULL) { 345*041394f3SDevin Teske config->pprompt = malloc(DPV_PPROMPT_MAX + 2); 346*041394f3SDevin Teske if (config->pprompt == NULL) 347*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 348*041394f3SDevin Teske /* +2 is for implicit "\n" appended later */ 349*041394f3SDevin Teske } 350*041394f3SDevin Teske snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s", 351*041394f3SDevin Teske optarg); 352*041394f3SDevin Teske break; 353*041394f3SDevin Teske case 'P': /* custom size for mini-progressbar */ 354*041394f3SDevin Teske config->pbar_size = 355*041394f3SDevin Teske (int)strtol(optarg, (char **)NULL, 10); 356*041394f3SDevin Teske if (config->pbar_size == 0 && errno == EINVAL) 357*041394f3SDevin Teske errx(EXIT_FAILURE, 358*041394f3SDevin Teske "`-P' argument must be numeric"); 359*041394f3SDevin Teske else if (config->pbar_size < -1) 360*041394f3SDevin Teske config->pbar_size = -1; 361*041394f3SDevin Teske break; 362*041394f3SDevin Teske case 't': /* [X]dialog(1) title */ 363*041394f3SDevin Teske if (config->title != NULL) 364*041394f3SDevin Teske free(config->title); 365*041394f3SDevin Teske config->title = malloc(strlen(optarg) + 1); 366*041394f3SDevin Teske if (config->title == NULL) 367*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 368*041394f3SDevin Teske *(config->title) = '\0'; 369*041394f3SDevin Teske strcat(config->title, optarg); 370*041394f3SDevin Teske break; 371*041394f3SDevin Teske case 'T': /* test mode (don't read data, fake it) */ 372*041394f3SDevin Teske config->options |= DPV_TEST_MODE; 373*041394f3SDevin Teske break; 374*041394f3SDevin Teske case 'U': /* updates per second */ 375*041394f3SDevin Teske config->status_updates_per_second = 376*041394f3SDevin Teske (int)strtol(optarg, (char **)NULL, 10); 377*041394f3SDevin Teske if (config->status_updates_per_second == 0 && 378*041394f3SDevin Teske errno == EINVAL) 379*041394f3SDevin Teske errx(EXIT_FAILURE, 380*041394f3SDevin Teske "`-U' argument must be numeric"); 381*041394f3SDevin Teske break; 382*041394f3SDevin Teske case 'w': /* `-p' and `-a' widths bump [X]dialog(1) width */ 383*041394f3SDevin Teske config->options |= DPV_WIDE_MODE; 384*041394f3SDevin Teske break; 385*041394f3SDevin Teske case 'x': /* `-x cmd' for sending data-read to sh(1) code */ 386*041394f3SDevin Teske output_type = DPV_OUTPUT_SHELL; 387*041394f3SDevin Teske config->output_type = DPV_OUTPUT_SHELL; 388*041394f3SDevin Teske config->output = optarg; 389*041394f3SDevin Teske break; 390*041394f3SDevin Teske case 'X': /* X11 support through x11/xdialog */ 391*041394f3SDevin Teske config->display_type = DPV_DISPLAY_XDIALOG; 392*041394f3SDevin Teske break; 393*041394f3SDevin Teske case '?': /* unknown argument (based on optstring) */ 394*041394f3SDevin Teske /* FALLTHROUGH */ 395*041394f3SDevin Teske default: /* unhandled argument (based on switch) */ 396*041394f3SDevin Teske usage(); 397*041394f3SDevin Teske /* NOTREACHED */ 398*041394f3SDevin Teske } 399*041394f3SDevin Teske } 400*041394f3SDevin Teske argc -= optind; 401*041394f3SDevin Teske argv += optind; 402*041394f3SDevin Teske 403*041394f3SDevin Teske /* Process remaining arguments as list of names to display */ 404*041394f3SDevin Teske for (curfile = file_list; n < argc; n++) { 405*041394f3SDevin Teske nfiles++; 406*041394f3SDevin Teske 407*041394f3SDevin Teske /* Allocate a new struct for the file argument */ 408*041394f3SDevin Teske if (curfile == NULL) { 409*041394f3SDevin Teske if ((curfile = malloc(file_node_size)) == NULL) 410*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 411*041394f3SDevin Teske memset((void *)(curfile), '\0', file_node_size); 412*041394f3SDevin Teske file_list = curfile; 413*041394f3SDevin Teske } else { 414*041394f3SDevin Teske if ((curfile->next = malloc(file_node_size)) == NULL) 415*041394f3SDevin Teske errx(EXIT_FAILURE, "Out of memory?!"); 416*041394f3SDevin Teske memset((void *)(curfile->next), '\0', file_node_size); 417*041394f3SDevin Teske curfile = curfile->next; 418*041394f3SDevin Teske } 419*041394f3SDevin Teske curfile->name = argv[n]; 420*041394f3SDevin Teske 421*041394f3SDevin Teske /* Read possible `lines:' prefix from label syntax */ 422*041394f3SDevin Teske if (sscanf(curfile->name, "%lli:%c", &(curfile->length), 423*041394f3SDevin Teske &dummy) == 2) 424*041394f3SDevin Teske curfile->name = strchr(curfile->name, ':') + 1; 425*041394f3SDevin Teske else 426*041394f3SDevin Teske curfile->length = -1; 427*041394f3SDevin Teske 428*041394f3SDevin Teske /* Read path argument if enabled */ 429*041394f3SDevin Teske if (multiple) { 430*041394f3SDevin Teske if (++n >= argc) 431*041394f3SDevin Teske errx(EXIT_FAILURE, "Missing path argument " 432*041394f3SDevin Teske "for label number %i", nfiles); 433*041394f3SDevin Teske curfile->path = argv[n]; 434*041394f3SDevin Teske } else 435*041394f3SDevin Teske break; 436*041394f3SDevin Teske } 437*041394f3SDevin Teske 438*041394f3SDevin Teske /* Display usage and exit if not given at least one name */ 439*041394f3SDevin Teske if (nfiles == 0) { 440*041394f3SDevin Teske warnx("no labels provided"); 441*041394f3SDevin Teske usage(); 442*041394f3SDevin Teske /* NOTREACHED */ 443*041394f3SDevin Teske } 444*041394f3SDevin Teske 445*041394f3SDevin Teske /* 446*041394f3SDevin Teske * Set cleanup routine for Ctrl-C action 447*041394f3SDevin Teske */ 448*041394f3SDevin Teske if (config->display_type == DPV_DISPLAY_LIBDIALOG) { 449*041394f3SDevin Teske act.sa_handler = sig_int; 450*041394f3SDevin Teske sigaction(SIGINT, &act, 0); 451*041394f3SDevin Teske } 452*041394f3SDevin Teske 453*041394f3SDevin Teske /* Set status formats and action */ 454*041394f3SDevin Teske if (line_mode) { 455*041394f3SDevin Teske config->status_solo = LINE_STATUS_SOLO; 456*041394f3SDevin Teske config->status_many = LINE_STATUS_SOLO; 457*041394f3SDevin Teske config->action = operate_on_lines; 458*041394f3SDevin Teske } else { 459*041394f3SDevin Teske config->status_solo = BYTE_STATUS_SOLO; 460*041394f3SDevin Teske config->status_many = BYTE_STATUS_SOLO; 461*041394f3SDevin Teske config->action = operate_on_bytes; 462*041394f3SDevin Teske } 463*041394f3SDevin Teske 464*041394f3SDevin Teske /* 465*041394f3SDevin Teske * Hand off to dpv(3)... 466*041394f3SDevin Teske */ 467*041394f3SDevin Teske if (dpv(config, file_list) != 0 && debug) 468*041394f3SDevin Teske warnx("dpv(3) returned error!?"); 469*041394f3SDevin Teske 470*041394f3SDevin Teske end_dialog(); 471*041394f3SDevin Teske dpv_free(); 472*041394f3SDevin Teske 473*041394f3SDevin Teske exit(EXIT_SUCCESS); 474*041394f3SDevin Teske } 475*041394f3SDevin Teske 476*041394f3SDevin Teske /* 477*041394f3SDevin Teske * Interrupt handler to indicate we received a Ctrl-C interrupt. 478*041394f3SDevin Teske */ 479*041394f3SDevin Teske static void 480*041394f3SDevin Teske sig_int(int sig __unused) 481*041394f3SDevin Teske { 482*041394f3SDevin Teske dpv_interrupt = TRUE; 483*041394f3SDevin Teske } 484*041394f3SDevin Teske 485*041394f3SDevin Teske /* 486*041394f3SDevin Teske * Print short usage statement to stderr and exit with error status. 487*041394f3SDevin Teske */ 488*041394f3SDevin Teske static void 489*041394f3SDevin Teske usage(void) 490*041394f3SDevin Teske { 491*041394f3SDevin Teske 492*041394f3SDevin Teske if (debug) /* No need for usage */ 493*041394f3SDevin Teske exit(EXIT_FAILURE); 494*041394f3SDevin Teske 495*041394f3SDevin Teske fprintf(stderr, "Usage: %s [options] bytes:label\n", pgm); 496*041394f3SDevin Teske fprintf(stderr, " %s [options] -m bytes1:label1 path1 " 497*041394f3SDevin Teske "[bytes2:label2 path2 ...]\n", pgm); 498*041394f3SDevin Teske fprintf(stderr, "OPTIONS:\n"); 499*041394f3SDevin Teske #define OPTFMT "\t%-14s %s\n" 500*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-a text", 501*041394f3SDevin Teske "Append text. Displayed below file progress indicators."); 502*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-b backtitle", 503*041394f3SDevin Teske "String to be displayed on the backdrop, at top-left."); 504*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-d", 505*041394f3SDevin Teske "Debug. Write to standard output instead of dialog."); 506*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-D", 507*041394f3SDevin Teske "Use dialog(1) instead of dialog(3) [default]."); 508*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-h", 509*041394f3SDevin Teske "Produce this output on standard error and exit."); 510*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-i format", 511*041394f3SDevin Teske "Customize status line format. See fdpv(1) for details."); 512*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-I format", 513*041394f3SDevin Teske "Customize status line format. See fdpv(1) for details."); 514*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-L size", 515*041394f3SDevin Teske "Label size. Must be a number greater than 0, or -1."); 516*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-m", 517*041394f3SDevin Teske "Enable processing of multiple file argiments."); 518*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-n num", 519*041394f3SDevin Teske "Display at-most num files per screen. Default is -1."); 520*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-N", 521*041394f3SDevin Teske "No overrun. Stop reading input at stated length, if any."); 522*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-o file", 523*041394f3SDevin Teske "Output data to file. First %s replaced with label text."); 524*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-p text", 525*041394f3SDevin Teske "Prefix text. Displayed above file progress indicators."); 526*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-P size", 527*041394f3SDevin Teske "Mini-progressbar size. Must be a number greater than 3."); 528*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-t title", 529*041394f3SDevin Teske "Title string to be displayed at top of dialog(1) box."); 530*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-T", 531*041394f3SDevin Teske "Test mode. Don't actually read any data, but fake it."); 532*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-U num", 533*041394f3SDevin Teske "Update status line num times per-second. Default is 2."); 534*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-w", 535*041394f3SDevin Teske "Wide. Width of `-p' and `-a' text bump dialog(1) width."); 536*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-x cmd", 537*041394f3SDevin Teske "Send data to executed cmd. First %s replaced with label."); 538*041394f3SDevin Teske fprintf(stderr, OPTFMT, "-X", 539*041394f3SDevin Teske "X11. Use Xdialog(1) instead of dialog(1)."); 540*041394f3SDevin Teske exit(EXIT_FAILURE); 541*041394f3SDevin Teske } 542