aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCameron Katri <me@cameronkatri.com>2021-03-31 08:49:50 -0400
committerCameron Katri <me@cameronkatri.com>2021-03-31 08:49:50 -0400
commita3225ce583b7138c5c939a1f91a6998d1cf77e4e (patch)
tree10772ce7d8eb8f98694dd9798e5ef226228a6120
parente7528f8ba630dfe4454d7668a9ee02589c00dfa6 (diff)
parent8d6d26bea0b0a02b1ec25e099f1c8878d5a1211a (diff)
downloadbsd-progress-debian-debian.tar.gz
bsd-progress-debian-debian.tar.zst
bsd-progress-debian-debian.zip
Update upstream source from tag 'upstream/9.99.81'debian/9.99.81-1debian-debian
Update to upstream version '9.99.81' with Debian dir 88c446da6f32af343e2e61ac4d27334f0f0437ec
-rw-r--r--.gitignore2
-rw-r--r--Makefile31
-rw-r--r--include/progressbar.h91
-rw-r--r--include/tzfile.h174
-rw-r--r--progress.1147
-rw-r--r--progress.c291
-rw-r--r--progressbar.c424
-rw-r--r--strsuftoll.c237
8 files changed, 1397 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..769578e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.o
+progress
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..cc9c47d
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+# $NetBSD: Makefile,v 1.2 2003/01/22 02:56:30 lukem Exp $
+
+PROG= progress
+SRCS= progress.c progressbar.c strsuftoll.c
+
+
+CPPFLAGS +=-Iinclude -DSTANDALONE_PROGRESS
+UNAME ?= $(shell uname)
+PREFIX ?= /usr
+PROG_PREFIX ?= bsd-
+
+ifeq ($(UNAME), Linux)
+ifeq ($(shell pkg-config libbsd-overlay && echo 1),1)
+ CPPFLAGS+=$(shell pkg-config libbsd-overlay --cflags)
+ LDFLAGS+=$(shell pkg-config libbsd-overlay --libs)
+endif # ($(shell pkg-config libbsd-overlay && echo 1),1)
+endif # ($(UNAME), Linux)
+
+all: $(PROG)
+
+$(PROG): $(SRCS:%.c=%.o)
+ $(CC) $(LDFLAGS) -o $@ $^
+
+install: $(PROG) progress.1
+ install -Dm755 $(PROG) $(DESTDIR)$(PREFIX)/bin/$(PROG_PREFIX)$(PROG)
+ install -Dm644 progress.1 $(DESTDIR)$(PREFIX)/share/man/man1/$(PROG_PREFIX)progress.1
+
+clean:
+ rm -f *.o $(PROG)
+
+.PHONY: all install clean
diff --git a/include/progressbar.h b/include/progressbar.h
new file mode 100644
index 0000000..daf382b
--- /dev/null
+++ b/include/progressbar.h
@@ -0,0 +1,91 @@
+/* $NetBSD: progressbar.h,v 1.9 2021/01/06 04:43:14 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1996-2021 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#ifndef STANDALONE_PROGRESS
+#include <setjmp.h>
+#endif /* !STANDALONE_PROGRESS */
+
+#ifndef GLOBAL
+#define GLOBAL extern
+#endif
+
+
+#define STALLTIME 5 /* # of seconds of no xfer before "stalling" */
+
+typedef void (*sigfunc)(int);
+
+
+GLOBAL FILE *ttyout; /* stdout, or stderr if retrieving to stdout */
+
+GLOBAL int progress; /* display transfer progress bar */
+GLOBAL int ttywidth; /* width of tty */
+
+GLOBAL off_t bytes; /* current # of bytes read */
+GLOBAL off_t filesize; /* size of file being transferred */
+GLOBAL off_t restart_point; /* offset to restart transfer */
+GLOBAL char *prefix; /* Text written left of progress bar */
+
+
+#ifndef STANDALONE_PROGRESS
+GLOBAL int fromatty; /* input is from a terminal */
+GLOBAL int verbose; /* print messages coming back from server */
+GLOBAL int quit_time; /* maximum time to wait if stalled */
+
+GLOBAL const char *direction; /* direction transfer is occurring */
+
+GLOBAL sigjmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+#endif /* !STANDALONE_PROGRESS */
+
+int foregroundproc(void);
+void alarmtimer(int);
+void progressmeter(int);
+sigfunc xsignal(int, sigfunc);
+
+#ifndef STANDALONE_PROGRESS
+void psummary(int);
+void ptransfer(int);
+#endif /* !STANDALONE_PROGRESS */
+
+#ifdef NO_LONG_LONG
+# define LLF "%ld"
+# define LLFP(x) "%" x "ld"
+# define LLT long
+# define ULLF "%lu"
+# define ULLFP(x) "%" x "lu"
+# define ULLT unsigned long
+#else
+# define LLF "%lld"
+# define LLFP(x) "%" x "lld"
+# define LLT long long
+# define ULLF "%llu"
+# define ULLFP(x) "%" x "llu"
+# define ULLT unsigned long long
+#endif
diff --git a/include/tzfile.h b/include/tzfile.h
new file mode 100644
index 0000000..5c7a222
--- /dev/null
+++ b/include/tzfile.h
@@ -0,0 +1,174 @@
+/* $NetBSD: tzfile.h,v 1.10 2019/07/03 15:49:21 christos Exp $ */
+
+#ifndef _TZFILE_H_
+#define _TZFILE_H_
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson.
+*/
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** Information about time zone files.
+*/
+
+#ifndef TZDIR /* Time zone object file directory */
+#define TZDIR "/usr/share/zoneinfo"
+#endif /* !defined TZDIR */
+
+#ifndef TZDEFAULT
+#define TZDEFAULT "/etc/localtime"
+#endif /* !defined TZDEFAULT */
+
+#ifndef TZDEFRULES
+#define TZDEFRULES "posixrules"
+#endif /* !defined TZDEFRULES */
+
+
+/* See Internet RFC 8536 for more details about the following format. */
+
+/*
+** Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_version[1]; /* '\0' or '2' or '3' as of 2013 */
+ char tzh_reserved[15]; /* reserved; must be zero */
+ char tzh_ttisutcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+** . . .followed by. . .
+**
+** tzh_timecnt (char [4])s coded transition times a la time(2)
+** tzh_timecnt (unsigned char)s types of local time starting at above
+** tzh_typecnt repetitions of
+** one (char [4]) coded UT offset in seconds
+** one (unsigned char) used to set tm_isdst
+** one (unsigned char) that's an abbreviation list index
+** tzh_charcnt (char)s '\0'-terminated zone abbreviations
+** tzh_leapcnt repetitions of
+** one (char [4]) coded leap second transition times
+** one (char [4]) total correction after above
+** tzh_ttisstdcnt (char)s indexed by type; if 1, transition
+** time is standard time, if 0,
+** transition time is local (wall clock)
+** time; if absent, transition times are
+** assumed to be local time
+** tzh_ttisutcnt (char)s indexed by type; if 1, transition
+** time is UT, if 0, transition time is
+** local time; if absent, transition
+** times are assumed to be local time.
+** When this is 1, the corresponding
+** std/wall indicator must also be 1.
+*/
+
+/*
+** If tzh_version is '2' or greater, the above is followed by a second instance
+** of tzhead and a second instance of the data in which each coded transition
+** time uses 8 rather than 4 chars,
+** then a POSIX-TZ-environment-variable-style string for use in handling
+** instants after the last transition time stored in the file
+** (with nothing between the newlines if there is no POSIX representation for
+** such instants).
+**
+** If tz_version is '3' or greater, the above is extended as follows.
+** First, the POSIX TZ string's hour offset may range from -167
+** through 167 as compared to the POSIX-required 0 through 24.
+** Second, its DST start time may be January 1 at 00:00 and its stop
+** time December 31 at 24:00 plus the difference between DST and
+** standard time, indicating DST all year.
+*/
+
+/*
+** In the current implementation, "tzset()" refuses to deal with files that
+** exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+#define TZ_MAX_TIMES 2000
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+/* This must be at least 17 for Europe/Samara and Europe/Vilnius. */
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#define SECSPERMIN 60
+#define MINSPERHOUR 60
+#define HOURSPERDAY 24
+#define DAYSPERWEEK 7
+#define DAYSPERNYEAR 365
+#define DAYSPERLYEAR 366
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY)
+#define MONSPERYEAR 12
+
+#define TM_SUNDAY 0
+#define TM_MONDAY 1
+#define TM_TUESDAY 2
+#define TM_WEDNESDAY 3
+#define TM_THURSDAY 4
+#define TM_FRIDAY 5
+#define TM_SATURDAY 6
+
+#define TM_JANUARY 0
+#define TM_FEBRUARY 1
+#define TM_MARCH 2
+#define TM_APRIL 3
+#define TM_MAY 4
+#define TM_JUNE 5
+#define TM_JULY 6
+#define TM_AUGUST 7
+#define TM_SEPTEMBER 8
+#define TM_OCTOBER 9
+#define TM_NOVEMBER 10
+#define TM_DECEMBER 11
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+#define EPOCH_WDAY TM_THURSDAY
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
+
+/*
+** Since everything in isleap is modulo 400 (or a factor of 400), we know that
+** isleap(y) == isleap(y % 400)
+** and so
+** isleap(a + b) == isleap((a + b) % 400)
+** or
+** isleap(a + b) == isleap(a % 400 + b % 400)
+** This is true even if % means modulo rather than Fortran remainder
+** (which is allowed by C89 but not C99).
+** We use this to avoid addition overflow problems.
+*/
+
+#define isleap_sum(a, b) isleap((a) % 400 + (b) % 400)
+
+#endif /* !defined _TZFILE_H_ */
diff --git a/progress.1 b/progress.1
new file mode 100644
index 0000000..028bda9
--- /dev/null
+++ b/progress.1
@@ -0,0 +1,147 @@
+.\" $NetBSD: progress.1,v 1.15 2014/04/13 01:45:34 snj Exp $
+.\"
+.\" Copyright (c) 2003-2007 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by John Hawkinson.
+.\"
+.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.\"
+.Dd June 6, 2007
+.Dt PROGRESS 1
+.Os
+.Sh NAME
+.Nm progress
+.Nd feed input to a command, displaying a progress bar
+.Sh SYNOPSIS
+.Nm
+.Op Fl ez
+.Op Fl b Ar buffersize
+.Op Fl f Ar file
+.Op Fl l Ar length
+.Op Fl p Ar prefix
+.Ar cmd
+.Op Ar args ...
+.Sh DESCRIPTION
+The
+.Nm
+utility opens a pipe to
+.Ar cmd
+and feeds an input stream into it, while displaying a progress bar to
+standard output.
+If no filename is specified,
+.Nm
+reads from standard input.
+Where feasible,
+.Nm
+.Xr fstat 2 Ns s
+the input to determine the length, so a time estimate can be calculated.
+.Pp
+If no length is specified or determined,
+.Nm
+simply displays a count of the data and the data rate.
+.Pp
+The options are as follows:
+.Bl -tag -width XlXlengthXX
+.It Fl b Ar buffersize
+Read in buffers of the specified size (default 64k).
+An optional suffix (per
+.Xr strsuftoll 3 )
+may be given.
+.It Fl e
+Display progress to standard error instead of standard output.
+.It Fl f Ar file
+Read from the specified
+.Ar file
+instead of standard input.
+.It Fl l Ar length
+Use the specified length for the time estimate, rather than attempting to
+.Xr fstat 2
+the input.
+An optional suffix (per
+.Xr strsuftoll 3 )
+may be given.
+.It Fl p Ar prefix
+Print the given
+.Dq prefix
+text before (left of) the progress bar.
+.It Fl z
+Filter the input through
+.Xr gunzip 1 .
+If
+.Fl f
+is specified, calculate the length using
+.Ic gzip -l .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The command
+.Dl progress -zf file.tar.gz tar xf -
+will extract the
+.Pa file.tar.gz
+displaying the progress bar as time passes:
+.Bd -literal
+ 0% | | 0 0.00 KiB/s --:-- ETA
+ 40% |******** | 273 KiB 271.95 KiB/s 00:01 ETA
+ 81% |*********************** | 553 KiB 274.61 KiB/s 00:00 ETA
+100% |*******************************| 680 KiB 264.59 KiB/s 00:00 ETA
+.Ed
+.Pp
+If it is preferred to monitor the progress of the decompression
+process (unlikely), then
+.Dl progress -f file.tar.gz tar zxf -
+could be used.
+.Pp
+The command
+.Dl dd if=/dev/rwd0d ibs=64k | \e
+.Dl progress -l 120g dd of=/dev/rwd1d obs=64k
+will copy the 120 GiB disk
+.Sy wd0
+.Pa ( /dev/rwd0d )
+to
+.Sy wd1
+.Pa ( /dev/rwd1d ) ,
+displaying a progress bar during the operation.
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr strsuftoll 3
+.Sh HISTORY
+.Nm
+first appeared in
+.Nx 1.6.1 .
+The dynamic progress bar display code is part of
+.Xr ftp 1 .
+.Sh AUTHORS
+.Nm
+was written by
+.An John Hawkinson
+.Aq jhawk@NetBSD.org .
+.Xr ftp 1 Ns 's
+dynamic progress bar was written by Luke Mewburn.
+.Sh BUGS
+Since the progress bar is displayed asynchronously, it may be
+difficult to read some error messages, both those produced by the
+pipeline, as well as those produced by
+.Nm
+itself.
diff --git a/progress.c b/progress.c
new file mode 100644
index 0000000..a921ac9
--- /dev/null
+++ b/progress.c
@@ -0,0 +1,291 @@
+/* $NetBSD: progress.c,v 1.23 2021/01/07 12:02:52 lukem Exp $ */
+
+/*-
+ * Copyright (c) 2003 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by John Hawkinson.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+
+#define _GNU_SOURCE
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: progress.c,v 1.23 2021/01/07 12:02:52 lukem Exp $");
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+ /* LONGLONG */
+long long strsuftoll(const char *, const char *, long long, long long);
+ /* LONGLONG */
+long long strsuftollx(const char *, const char *, long long, long long,
+ char *, size_t);
+
+#define GLOBAL /* force GLOBAL decls in progressbar.h to be
+ * declared */
+#include "progressbar.h"
+
+static void broken_pipe(int unused);
+static void usage(void);
+
+static void
+broken_pipe(int unused)
+{
+ signal(SIGPIPE, SIG_DFL);
+ progressmeter(1);
+ kill(getpid(), SIGPIPE);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-ez] [-b buffersize] [-f file] [-l length]\n"
+ " %*.s [-p prefix] cmd [args...]\n",
+ getprogname(), (int) strlen(getprogname()), "");
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *fb_buf;
+ char *infile = NULL;
+ pid_t pid = 0, gzippid = 0, deadpid;
+ int ch, fd, outpipe[2];
+ int ws, gzipstat, cmdstat;
+ int eflag = 0, lflag = 0, zflag = 0;
+ ssize_t nr, nw, off;
+ size_t buffersize;
+ struct stat statb;
+ struct winsize wins;
+
+ setprogname(argv[0]);
+
+ /* defaults: Read from stdin, 0 filesize (no completion estimate) */
+ fd = STDIN_FILENO;
+ filesize = 0;
+ buffersize = 64 * 1024;
+ prefix = NULL;
+
+ while ((ch = getopt(argc, argv, "b:ef:l:p:z")) != -1)
+ switch (ch) {
+ case 'b':
+ buffersize = (size_t) strsuftoll("buffer size", optarg,
+ 0, SSIZE_MAX);
+ break;
+ case 'e':
+ eflag++;
+ break;
+ case 'f':
+ infile = optarg;
+ break;
+ case 'l':
+ lflag++;
+ filesize = strsuftoll("input size", optarg, 0,
+ LLONG_MAX);
+ break;
+ case 'p':
+ prefix = optarg;
+ break;
+ case 'z':
+ zflag++;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if (infile && (fd = open(infile, O_RDONLY, 0)) < 0)
+ err(1, "%s", infile);
+
+ /* stat() to get the filesize unless overridden, or -z */
+ if (!zflag && !lflag && (fstat(fd, &statb) == 0)) {
+ if (S_ISFIFO(statb.st_mode)) {
+ /* stat(2) on pipe may return only the
+ * first few bytes with more coming.
+ * Don't trust!
+ */
+ } else {
+ filesize = statb.st_size;
+ }
+ }
+
+ /* gzip -l the file if we have the name and -z is given */
+ if (zflag && !lflag && infile != NULL) {
+ FILE *gzipsizepipe;
+ char buf[256], *cp, *cmd;
+
+ /*
+ * Read second word of last line of gzip -l output. Looks like:
+ * % gzip -l ../etc.tgz
+ * compressed uncompressed ratio uncompressed_name
+ * 119737 696320 82.8% ../etc.tar
+ */
+
+ asprintf(&cmd, "gzip -l %s", infile);
+ if ((gzipsizepipe = popen(cmd, "r")) == NULL)
+ err(1, "reading compressed file length");
+ for (; fgets(buf, 256, gzipsizepipe) != NULL;)
+ continue;
+ strtoimax(buf, &cp, 10);
+ filesize = strtoimax(cp, NULL, 10);
+ if (pclose(gzipsizepipe) < 0)
+ err(1, "closing compressed file length pipe");
+ free(cmd);
+ }
+ /* Pipe input through gzip -dc if -z is given */
+ if (zflag) {
+ int gzippipe[2];
+
+ if (pipe(gzippipe) < 0)
+ err(1, "gzip pipe");
+ gzippid = fork();
+ if (gzippid < 0)
+ err(1, "fork for gzip");
+
+ if (gzippid) {
+ /* parent */
+ dup2(gzippipe[0], fd);
+ close(gzippipe[0]);
+ close(gzippipe[1]);
+ } else {
+ dup2(gzippipe[1], STDOUT_FILENO);
+ dup2(fd, STDIN_FILENO);
+ close(gzippipe[0]);
+ close(gzippipe[1]);
+ if (execlp("gzip", "gzip", "-dc", NULL))
+ err(1, "exec()ing gzip");
+ }
+ }
+
+ /* Initialize progressbar.c's global state */
+ bytes = 0;
+ progress = 1;
+ ttyout = eflag ? stderr : stdout;
+
+ if (ioctl(fileno(ttyout), TIOCGWINSZ, &wins) == -1)
+ ttywidth = 80;
+ else
+ ttywidth = wins.ws_col;
+
+ fb_buf = malloc(buffersize);
+ if (fb_buf == NULL)
+ err(1, "malloc for buffersize");
+
+ if (pipe(outpipe) < 0)
+ err(1, "output pipe");
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork for output pipe");
+
+ if (pid == 0) {
+ /* child */
+ dup2(outpipe[0], STDIN_FILENO);
+ close(outpipe[0]);
+ close(outpipe[1]);
+ execvp(argv[0], argv);
+ err(1, "could not exec %s", argv[0]);
+ }
+ close(outpipe[0]);
+
+ signal(SIGPIPE, broken_pipe);
+ progressmeter(-1);
+
+ while (1) {
+ do {
+ nr = read(fd, fb_buf, buffersize);
+ } while (nr < 0 && errno == EINTR);
+ if (nr <= 0)
+ break;
+ for (off = 0; nr; nr -= nw, off += nw, bytes += nw)
+ if ((nw = write(outpipe[1], fb_buf + off,
+ (size_t) nr)) < 0) {
+ if (errno == EINTR) {
+ nw = 0;
+ continue;
+ }
+ progressmeter(1);
+ err(1, "writing %u bytes to output pipe",
+ (unsigned) nr);
+ }
+ }
+ close(outpipe[1]);
+
+ gzipstat = 0;
+ cmdstat = 0;
+ while (pid || gzippid) {
+ deadpid = wait(&ws);
+ /*
+ * We need to exit with an error if the command (or gzip)
+ * exited abnormally.
+ * Unfortunately we can't generate a true 'exited by signal'
+ * error without sending the signal to ourselves :-(
+ */
+ ws = WIFSIGNALED(ws) ? WTERMSIG(ws) : WEXITSTATUS(ws);
+
+ if (deadpid != -1 && errno == EINTR)
+ continue;
+ if (deadpid == pid) {
+ pid = 0;
+ cmdstat = ws;
+ continue;
+ }
+ if (deadpid == gzippid) {
+ gzippid = 0;
+ gzipstat = ws;
+ continue;
+ }
+ break;
+ }
+
+ progressmeter(1);
+ signal(SIGPIPE, SIG_DFL);
+
+ free(fb_buf);
+
+ exit(cmdstat ? cmdstat : gzipstat);
+}
diff --git a/progressbar.c b/progressbar.c
new file mode 100644
index 0000000..85f1ecb
--- /dev/null
+++ b/progressbar.c
@@ -0,0 +1,424 @@
+/* $NetBSD: progressbar.c,v 1.24 2021/01/06 04:43:14 lukem Exp $ */
+
+/*-
+ * Copyright (c) 1997-2021 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: progressbar.c,v 1.24 2021/01/06 04:43:14 lukem Exp $");
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Misc support routines
+ */
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <tzfile.h>
+#include <unistd.h>
+
+#include "progressbar.h"
+
+#if !defined(NO_PROGRESS)
+/*
+ * return non-zero if we're the current foreground process
+ */
+int
+foregroundproc(void)
+{
+ static pid_t pgrp = -1;
+
+ if (pgrp == -1)
+ pgrp = getpgrp();
+
+ return (tcgetpgrp(fileno(ttyout)) == pgrp);
+}
+#endif /* !defined(NO_PROGRESS) */
+
+
+static void updateprogressmeter(int);
+
+/*
+ * SIGALRM handler to update the progress meter
+ */
+static void
+updateprogressmeter(int dummy)
+{
+ int oerrno = errno;
+
+ progressmeter(0);
+ errno = oerrno;
+}
+
+/*
+ * List of order of magnitude suffixes, per IEC 60027-2.
+ */
+#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
+static const char * const suffixes[] = {
+ "", /* 2^0 (byte) */
+ "KiB", /* 2^10 Kibibyte */
+ "MiB", /* 2^20 Mebibyte */
+ "GiB", /* 2^30 Gibibyte */
+ "TiB", /* 2^40 Tebibyte */
+ "PiB", /* 2^50 Pebibyte */
+ "EiB", /* 2^60 Exbibyte */
+#if 0
+ /* The following are not necessary for signed 64-bit off_t */
+ "ZiB", /* 2^70 Zebibyte */
+ "YiB", /* 2^80 Yobibyte */
+#endif
+};
+#define NSUFFIXES (int)(sizeof(suffixes) / sizeof(suffixes[0]))
+#endif
+
+/*
+ * Display a transfer progress bar if progress is non-zero.
+ * SIGALRM is hijacked for use by this function.
+ * - Before the transfer, set filesize to size of file (or -1 if unknown),
+ * and call with flag = -1. This starts the once per second timer,
+ * and a call to updateprogressmeter() upon SIGALRM.
+ * - During the transfer, updateprogressmeter will call progressmeter
+ * with flag = 0
+ * - After the transfer, call with flag = 1
+ */
+static struct timeval start;
+static struct timeval lastupdate;
+
+#define BUFLEFT (sizeof(buf) - len)
+
+void
+progressmeter(int flag)
+{
+ static off_t lastsize;
+ off_t cursize;
+ struct timeval now, wait;
+#ifndef NO_PROGRESS
+ struct timeval td;
+ off_t abbrevsize, bytespersec;
+ double elapsed;
+ int ratio, i, remaining, barlength;
+
+ /*
+ * Work variables for progress bar.
+ *
+ * XXX: if the format of the progress bar changes
+ * (especially the number of characters in the
+ * `static' portion of it), be sure to update
+ * these appropriately.
+ */
+#endif
+#if !defined(NO_PROGRESS) || !defined(STANDALONE_PROGRESS)
+ size_t len;
+ char buf[256]; /* workspace for progress bar */
+#endif
+#ifndef NO_PROGRESS
+#define BAROVERHEAD 45 /* non `*' portion of progress bar */
+ /*
+ * stars should contain at least
+ * sizeof(buf) - BAROVERHEAD entries
+ */
+ static const char stars[] =
+"*****************************************************************************"
+"*****************************************************************************"
+"*****************************************************************************";
+
+#endif
+
+ if (flag == -1) {
+ (void)gettimeofday(&start, NULL);
+ lastupdate = start;
+ lastsize = restart_point;
+ }
+
+ (void)gettimeofday(&now, NULL);
+ cursize = bytes + restart_point;
+ timersub(&now, &lastupdate, &wait);
+ if (cursize > lastsize) {
+ lastupdate = now;
+ lastsize = cursize;
+ wait.tv_sec = 0;
+ } else {
+#ifndef STANDALONE_PROGRESS
+ if (quit_time > 0 && wait.tv_sec > quit_time) {
+ len = snprintf(buf, sizeof(buf), "\r\n%s: "
+ "transfer aborted because stalled for %lu sec.\r\n",
+ getprogname(), (unsigned long)wait.tv_sec);
+ (void)write(fileno(ttyout), buf, len);
+ alarmtimer(0);
+ (void)xsignal(SIGALRM, SIG_DFL);
+ siglongjmp(toplevel, 1);
+ }
+#endif /* !STANDALONE_PROGRESS */
+ }
+ /*
+ * Always set the handler even if we are not the foreground process.
+ */
+#ifdef STANDALONE_PROGRESS
+ if (progress) {
+#else
+ if (quit_time > 0 || progress) {
+#endif /* !STANDALONE_PROGRESS */
+ if (flag == -1) {
+ (void)xsignal(SIGALRM, updateprogressmeter);
+ alarmtimer(1); /* set alarm timer for 1 Hz */
+ } else if (flag == 1) {
+ alarmtimer(0);
+ (void)xsignal(SIGALRM, SIG_DFL);
+ }
+ }
+#ifndef NO_PROGRESS
+ if (!progress)
+ return;
+ len = 0;
+
+ /*
+ * print progress bar only if we are foreground process.
+ */
+ if (! foregroundproc())
+ return;
+
+ len += snprintf(buf + len, BUFLEFT, "\r");
+ if (prefix)
+ len += snprintf(buf + len, BUFLEFT, "%s", prefix);
+ if (filesize > 0) {
+ ratio = (int)((double)cursize * 100.0 / (double)filesize);
+ ratio = MAX(ratio, 0);
+ ratio = MIN(ratio, 100);
+ len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio);
+
+ /*
+ * calculate the length of the `*' bar, ensuring that
+ * the number of stars won't exceed the buffer size
+ */
+ barlength = MIN((int)(sizeof(buf) - 1), ttywidth) - BAROVERHEAD;
+ if (prefix)
+ barlength -= (int)strlen(prefix);
+ if (barlength > 0) {
+ i = barlength * ratio / 100;
+ len += snprintf(buf + len, BUFLEFT,
+ "|%.*s%*s|", i, stars, (int)(barlength - i), "");
+ }
+ }
+
+ abbrevsize = cursize;
+ for (i = 0; abbrevsize >= 100000 && i < NSUFFIXES; i++)
+ abbrevsize >>= 10;
+ if (i == NSUFFIXES)
+ i--;
+ len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %-3s ",
+ (LLT)abbrevsize,
+ suffixes[i]);
+
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+
+ bytespersec = 0;
+ if (bytes > 0) {
+ bytespersec = bytes;
+ if (elapsed > 0.0)
+ bytespersec /= elapsed;
+ }
+ for (i = 1; bytespersec >= 1024000 && i < NSUFFIXES; i++)
+ bytespersec >>= 10;
+ len += snprintf(buf + len, BUFLEFT,
+ " " LLFP("3") ".%02d %.2sB/s ",
+ (LLT)(bytespersec / 1024),
+ (int)((bytespersec % 1024) * 100 / 1024),
+ suffixes[i]);
+
+ if (filesize > 0) {
+ if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
+ len += snprintf(buf + len, BUFLEFT, " --:-- ETA");
+ } else if (wait.tv_sec >= STALLTIME) {
+ len += snprintf(buf + len, BUFLEFT, " - stalled -");
+ } else {
+ remaining = (int)
+ ((filesize - restart_point) / (bytes / elapsed) -
+ elapsed);
+ if (remaining >= 100 * SECSPERHOUR)
+ len += snprintf(buf + len, BUFLEFT,
+ " --:-- ETA");
+ else {
+ i = remaining / SECSPERHOUR;
+ if (i)
+ len += snprintf(buf + len, BUFLEFT,
+ "%2d:", i);
+ else
+ len += snprintf(buf + len, BUFLEFT,
+ " ");
+ i = remaining % SECSPERHOUR;
+ len += snprintf(buf + len, BUFLEFT,
+ "%02d:%02d ETA", i / 60, i % 60);
+ }
+ }
+ }
+ if (flag == 1)
+ len += snprintf(buf + len, BUFLEFT, "\n");
+ (void)write(fileno(ttyout), buf, len);
+
+#endif /* !NO_PROGRESS */
+}
+
+#ifndef STANDALONE_PROGRESS
+/*
+ * Display transfer statistics.
+ * Requires start to be initialised by progressmeter(-1),
+ * direction to be defined by xfer routines, and filesize and bytes
+ * to be updated by xfer routines
+ * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
+ * instead of ttyout.
+ */
+void
+ptransfer(int siginfo)
+{
+ struct timeval now, td, wait;
+ double elapsed;
+ off_t bytespersec;
+ int remaining, hh, i;
+ size_t len;
+
+ char buf[256]; /* Work variable for transfer status. */
+
+ if (!verbose && !progress && !siginfo)
+ return;
+
+ (void)gettimeofday(&now, NULL);
+ timersub(&now, &start, &td);
+ elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
+ bytespersec = 0;
+ if (bytes > 0) {
+ bytespersec = bytes;
+ if (elapsed > 0.0)
+ bytespersec /= elapsed;
+ }
+ len = 0;
+ len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ",
+ (LLT)bytes, bytes == 1 ? "" : "s", direction);
+ remaining = (int)elapsed;
+ if (remaining > SECSPERDAY) {
+ int days;
+
+ days = remaining / SECSPERDAY;
+ remaining %= SECSPERDAY;
+ len += snprintf(buf + len, BUFLEFT,
+ "%d day%s ", days, days == 1 ? "" : "s");
+ }
+ hh = remaining / SECSPERHOUR;
+ remaining %= SECSPERHOUR;
+ if (hh)
+ len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
+ len += snprintf(buf + len, BUFLEFT,
+ "%02d:%02d ", remaining / 60, remaining % 60);
+
+ for (i = 1; bytespersec >= 1024000 && i < NSUFFIXES; i++)
+ bytespersec >>= 10;
+ if (i == NSUFFIXES)
+ i--;
+ len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %.2sB/s)",
+ (LLT)(bytespersec / 1024),
+ (int)((bytespersec % 1024) * 100 / 1024),
+ suffixes[i]);
+
+ if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
+ && bytes + restart_point <= filesize) {
+ remaining = (int)((filesize - restart_point) /
+ (bytes / elapsed) - elapsed);
+ hh = remaining / SECSPERHOUR;
+ remaining %= SECSPERHOUR;
+ len += snprintf(buf + len, BUFLEFT, " ETA: ");
+ if (hh)
+ len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
+ len += snprintf(buf + len, BUFLEFT, "%02d:%02d",
+ remaining / 60, remaining % 60);
+ timersub(&now, &lastupdate, &wait);
+ if (wait.tv_sec >= STALLTIME)
+ len += snprintf(buf + len, BUFLEFT, " (stalled)");
+ }
+ len += snprintf(buf + len, BUFLEFT, "\n");
+ (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len);
+}
+
+/*
+ * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
+ */
+void
+psummary(int notused)
+{
+ int oerrno = errno;
+
+ if (bytes > 0) {
+ if (fromatty)
+ write(fileno(ttyout), "\n", 1);
+ ptransfer(1);
+ }
+ errno = oerrno;
+}
+#endif /* !STANDALONE_PROGRESS */
+
+
+/*
+ * Set the SIGALRM interval timer for wait seconds, 0 to disable.
+ */
+void
+alarmtimer(int wait)
+{
+ struct itimerval itv;
+
+ itv.it_value.tv_sec = wait;
+ itv.it_value.tv_usec = 0;
+ itv.it_interval = itv.it_value;
+ setitimer(ITIMER_REAL, &itv, NULL);
+}
+
+/*
+ * Install a non-restartable POSIX signal handler.
+ */
+sigfunc
+xsignal(int sig, sigfunc func)
+{
+ struct sigaction act, oact;
+ act.sa_handler = func;
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+#if defined(SA_INTERRUPT) /* SunOS 4.x */
+ act.sa_flags = SA_INTERRUPT;
+#endif
+ if (sigaction(sig, &act, &oact) < 0)
+ return (SIG_ERR);
+ return (oact.sa_handler);
+}
diff --git a/strsuftoll.c b/strsuftoll.c
new file mode 100644
index 0000000..973df9d
--- /dev/null
+++ b/strsuftoll.c
@@ -0,0 +1,237 @@
+/* $NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $ */
+/*-
+ * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+ */
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#if defined(LIBC_SCCS) && !defined(lint)
+__RCSID("$NetBSD: strsuftoll.c,v 1.9 2011/10/22 22:08:47 christos Exp $");
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+ /* LONGLONG */
+long long strsuftoll(const char *, const char *, long long, long long);
+ /* LONGLONG */
+long long strsuftollx(const char *, const char *, long long, long long,
+ char *, size_t);
+
+/*
+ * Convert an expression of the following forms to a (u)int64_t.
+ * 1) A positive decimal number.
+ * 2) A positive decimal number followed by a b (mult by 512).
+ * 3) A positive decimal number followed by a k (mult by 1024).
+ * 4) A positive decimal number followed by a m (mult by 1048576).
+ * 5) A positive decimal number followed by a g (mult by 1073741824).
+ * 6) A positive decimal number followed by a t (mult by 1099511627776).
+ * 7) A positive decimal number followed by a w (mult by sizeof int)
+ * 8) Two or more positive decimal numbers (with/without k,b or w).
+ * separated by x (also * for backwards compatibility), specifying
+ * the product of the indicated values.
+ * Returns the result upon successful conversion, or exits with an
+ * appropriate error.
+ *
+ */
+/* LONGLONG */
+long long
+strsuftoll(const char *desc, const char *val,
+ long long min, long long max)
+{
+ long long result;
+ char errbuf[100];
+
+ result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
+ if (*errbuf != '\0')
+ errx(EXIT_FAILURE, "%s", errbuf);
+ return result;
+}
+
+/*
+ * As strsuftoll(), but returns the error message into the provided buffer
+ * rather than exiting with it.
+ */
+/* LONGLONG */
+static long long
+__strsuftollx(const char *desc, const char *val,
+ long long min, long long max, char *ebuf, size_t ebuflen, size_t depth)
+{
+ long long num, t;
+ char *expr;
+
+ (void)(desc != NULL);
+ (void)(val != NULL);
+ (void)(ebuf != NULL);
+
+ if (depth > 16) {
+ snprintf(ebuf, ebuflen, "%s: Recursion limit exceeded", desc);
+ return 0;
+ }
+
+ while (isspace((unsigned char)*val)) /* Skip leading space */
+ val++;
+
+ errno = 0;
+ num = strtoll(val, &expr, 10);
+ if (errno == ERANGE)
+ goto erange; /* Overflow */
+
+ if (expr == val) /* No digits */
+ goto badnum;
+
+ switch (*expr) {
+ case 'b':
+ t = num;
+ num *= 512; /* 1 block */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'k':
+ t = num;
+ num *= 1024; /* 1 kibibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'm':
+ t = num;
+ num *= 1048576; /* 1 mebibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'g':
+ t = num;
+ num *= 1073741824; /* 1 gibibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 't':
+ t = num;
+ num *= 1099511627776LL; /* 1 tebibyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'w':
+ t = num;
+ num *= sizeof(int); /* 1 word */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ }
+
+ switch (*expr) {
+ case '\0':
+ break;
+ case '*': /* Backward compatible */
+ case 'x':
+ t = num;
+ num *= __strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen,
+ depth + 1);
+ if (*ebuf != '\0')
+ return 0;
+ if (t > num) {
+ erange:
+ errno = ERANGE;
+ snprintf(ebuf, ebuflen, "%s: %s", desc, strerror(errno));
+ return 0;
+ }
+ break;
+ default:
+ badnum:
+ snprintf(ebuf, ebuflen, "%s `%s': illegal number", desc, val);
+ return 0;
+ }
+ if (num < min) {
+ /* LONGLONG */
+ snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
+ desc, (long long)num, (long long)min);
+ return 0;
+ }
+ if (num > max) {
+ /* LONGLONG */
+ snprintf(ebuf, ebuflen, "%s %lld is greater than %lld.",
+ desc, (long long)num, (long long)max);
+ return 0;
+ }
+ *ebuf = '\0';
+ return num;
+}
+
+long long
+strsuftollx(const char *desc, const char *val,
+ long long min, long long max, char *ebuf, size_t ebuflen)
+{
+ return __strsuftollx(desc, val, min, max, ebuf, ebuflen, 0);
+}