#include <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#define _BSD_SOURCE
#include <dialog.h>
#include <dpv.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string_m.h>
#include <unistd.h>
#include "dpv_util.h"
static uint8_t debug = FALSE;
static struct dpv_file_node *file_list = NULL;
static unsigned int nfiles = 0;
static uint8_t line_mode = FALSE;
static uint8_t no_overrun = FALSE;
static char *buf = NULL;
static int fd = -1;
static int output_type = DPV_OUTPUT_NONE;
static size_t bsize;
static char rpath[PATH_MAX];
static uint8_t multiple = FALSE;
static char *pgm;
static void sig_int(int sig);
static void usage(void);
int main(int argc, char *argv[]);
static int operate_common(struct dpv_file_node *file, int out);
static int operate_on_bytes(struct dpv_file_node *file, int out);
static int operate_on_lines(struct dpv_file_node *file, int out);
static int
operate_common(struct dpv_file_node *file, int out)
{
struct stat sb;
if (fd < 0) {
if (multiple) {
if (realpath(file->path, rpath) == 0 ||
(fd = open(rpath, O_RDONLY)) < 0) {
warn("%s", file->path);
file->status = DPV_STATUS_FAILED;
return (-1);
}
} else {
fd = STDIN_FILENO;
if (isatty(fd)) {
fd = open("/dev/stdin", O_RDONLY);
close(fd--);
}
switch(output_type) {
case DPV_OUTPUT_FILE:
fd -= 1;
break;
case DPV_OUTPUT_SHELL:
fd -= 2;
break;
}
}
}
if (buf == NULL) {
if (out >= 0) {
if (fstat(out, &sb) != 0) {
warn("%i", out);
file->status = DPV_STATUS_FAILED;
return (-1);
}
if (S_ISREG(sb.st_mode)) {
if (sysconf(_SC_PHYS_PAGES) >
PHYSPAGES_THRESHOLD)
bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
else
bsize = BUFSIZE_SMALL;
} else
bsize = MAX(sb.st_blksize,
(blksize_t)sysconf(_SC_PAGESIZE));
} else
bsize = MIN(BUFSIZE_MAX, MAXPHYS * 8);
if ((buf = malloc(bsize+1)) == NULL) {
end_dialog();
err(EXIT_FAILURE, "Out of memory?!");
}
}
return (0);
}
static int
operate_on_bytes(struct dpv_file_node *file, int out)
{
int progress;
ssize_t r, w;
if (operate_common(file, out) < 0)
return (-1);
if ((r = read(fd, buf, bsize)) <= 0) {
if (fd != STDIN_FILENO)
close(fd);
fd = -1;
file->status = DPV_STATUS_DONE;
return (100);
}
if (out >= 0) {
if ((w = write(out, buf, r)) < 0) {
end_dialog();
err(EXIT_FAILURE, "output");
}
fsync(out);
}
dpv_overall_read += r;
file->read += r;
if (file->length >= 0) {
progress = (file->read * 100 / (file->length > 0 ?
file->length : 1));
if (no_overrun && progress == 100 && file->read < file->length)
progress--;
return (progress);
} else
return (-1);
}
static int
operate_on_lines(struct dpv_file_node *file, int out)
{
char *p;
int progress;
ssize_t r, w;
if (operate_common(file, out) < 0)
return (-1);
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';
if (out >= 0) {
if ((w = write(out, buf, r)) < 0) {
end_dialog();
err(EXIT_FAILURE, "output");
}
fsync(out);
}
for (p = buf; p != NULL && *p != '\0';)
if ((p = strchr(p, '\n')) != NULL)
dpv_overall_read++, p++, file->read++;
if (file->length >= 0) {
progress = (file->read * 100 / file->length);
if (no_overrun && progress == 100 && file->read < file->length)
progress--;
return (progress);
} else
return (-1);
}
int
main(int argc, char *argv[])
{
char dummy;
int ch;
int n = 0;
size_t config_size = sizeof(struct dpv_config);
size_t file_node_size = sizeof(struct dpv_file_node);
struct dpv_config *config;
struct dpv_file_node *curfile;
struct sigaction act;
pgm = argv[0];
if ((config = malloc(config_size)) == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
memset((void *)(config), '\0', config_size);
while ((ch = getopt(argc, argv,
"a:b:dDhi:I:klL:mn:No:p:P:t:TU:wx:X")) != -1) {
switch(ch) {
case 'a':
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':
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':
debug = TRUE;
config->debug = debug;
break;
case 'D':
config->display_type = DPV_DISPLAY_DIALOG;
break;
case 'h':
usage();
break;
case 'i':
config->status_solo = optarg;
break;
case 'I':
config->status_many = optarg;
break;
case 'k':
config->keep_tite = TRUE;
break;
case 'l':
line_mode = TRUE;
break;
case 'L':
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':
multiple = TRUE;
break;
case 'o':
output_type = DPV_OUTPUT_FILE;
config->output_type = DPV_OUTPUT_FILE;
config->output = optarg;
break;
case 'n':
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 = TRUE;
config->options |= DPV_NO_OVERRUN;
break;
case 'p':
if (config->pprompt == NULL) {
config->pprompt = malloc(DPV_PPROMPT_MAX + 2);
if (config->pprompt == NULL)
errx(EXIT_FAILURE, "Out of memory?!");
}
snprintf(config->pprompt, DPV_PPROMPT_MAX, "%s",
optarg);
break;
case 'P':
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':
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':
config->options |= DPV_TEST_MODE;
break;
case 'U':
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':
config->options |= DPV_WIDE_MODE;
break;
case 'x':
output_type = DPV_OUTPUT_SHELL;
config->output_type = DPV_OUTPUT_SHELL;
config->output = optarg;
break;
case 'X':
config->display_type = DPV_DISPLAY_XDIALOG;
break;
case '?':
default:
usage();
}
}
argc -= optind;
argv += optind;
for (curfile = file_list; n < argc; n++) {
nfiles++;
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];
if (sscanf(curfile->name, "%lli:%c", &(curfile->length),
&dummy) == 2)
curfile->name = strchr(curfile->name, ':') + 1;
else
curfile->length = -1;
if (multiple) {
if (++n >= argc)
errx(EXIT_FAILURE, "Missing path argument "
"for label number %i", nfiles);
curfile->path = argv[n];
} else
break;
}
if (nfiles == 0) {
warnx("no labels provided");
usage();
}
if (config->display_type == DPV_DISPLAY_LIBDIALOG) {
act.sa_handler = sig_int;
sigaction(SIGINT, &act, 0);
}
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;
}
if (dpv(config, file_list) != 0 && debug)
warnx("dpv(3) returned error!?");
if (!config->keep_tite)
end_dialog();
dpv_free();
exit(EXIT_SUCCESS);
}
static void
sig_int(int sig __unused)
{
dpv_interrupt = TRUE;
}
static void
usage(void)
{
if (debug)
exit(EXIT_FAILURE);
fprintf(stderr, "Usage: %s [options] [bytes:]label\n", pgm);
fprintf(stderr, " %s [options] -m [bytes1:]label1 path1 "
"[[bytes2:]label2 path2 ...]\n", pgm);
fprintf(stderr, "OPTIONS:\n");
#define OPTFMT "\t%-14s %s\n"
fprintf(stderr, OPTFMT, "-a text",
"Append text. Displayed below file progress indicators.");
fprintf(stderr, OPTFMT, "-b backtitle",
"String to be displayed on the backdrop, at top-left.");
fprintf(stderr, OPTFMT, "-D",
"Use dialog(1) instead of dialog(3) [default].");
fprintf(stderr, OPTFMT, "-d",
"Debug. Write to standard output instead of dialog.");
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",
"No overrun. Stop reading input at stated length, if any.");
fprintf(stderr, OPTFMT, "-n num",
"Display at-most num files per screen. Default is -1.");
fprintf(stderr, OPTFMT, "-o file",
"Output data to file. First %s replaced with label text.");
fprintf(stderr, OPTFMT, "-P size",
"Mini-progressbar size. Must be a number greater than 3.");
fprintf(stderr, OPTFMT, "-p text",
"Prefix text. Displayed above file progress indicators.");
fprintf(stderr, OPTFMT, "-T",
"Test mode. Don't actually read any data, but fake it.");
fprintf(stderr, OPTFMT, "-t title",
"Title string to be displayed at top of dialog(1) box.");
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",
"X11. Use Xdialog(1) instead of dialog(1).");
fprintf(stderr, OPTFMT, "-x cmd",
"Send data to executed cmd. First %s replaced with label.");
exit(EXIT_FAILURE);
}