aboutsummaryrefslogtreecommitdiffstats
path: root/pw.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2022-04-29 02:05:28 -0700
committerKaz Kylheku <kaz@kylheku.com>2022-04-29 02:05:28 -0700
commitfe971e32ce519474a8095bb6aa3a7c8253858a6a (patch)
treea7f69429c96516039cae44708028c9e66634003a /pw.c
parent2fdac8f23f3082d71f93381cd644befb76832542 (diff)
downloadpw-fe971e32ce519474a8095bb6aa3a7c8253858a6a.tar.gz
pw-fe971e32ce519474a8095bb6aa3a7c8253858a6a.tar.bz2
pw-fe971e32ce519474a8095bb6aa3a7c8253858a6a.zip
Command line: improve robustness of conversions.
Diffstat (limited to 'pw.c')
-rw-r--r--pw.c106
1 files changed, 100 insertions, 6 deletions
diff --git a/pw.c b/pw.c
index 342e510..6a4b5b2 100644
--- a/pw.c
+++ b/pw.c
@@ -5,6 +5,7 @@
#include <string.h>
#include <limits.h>
#include <stdarg.h>
+#include <math.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/poll.h>
@@ -22,6 +23,13 @@
#define ESC 27
#define DEL 127
+#ifdef __GNUC__
+#define printf_attr(fmtpos, vargpos) __attribute__ ((format (printf, \
+ fmtpos, vargpos)))
+#else
+#define printf_attr(fmtpos, vargpos)
+#endif
+
enum status_flags {
stat_dirty = 1, // display needs refresh
stat_eof = 2, // end of data reached
@@ -49,6 +57,9 @@ typedef struct dstr {
#define dstr_of(str) ((dstr *) ((str) - sizeof (dstr)))
+static int poll_interval = 1000;
+static int long_interval = 10000;
+
static char **snapshot;
static int snaplines;
static char *trigpat;
@@ -71,6 +82,7 @@ static void panic(const char *fmt, ...)
abort();
}
+printf_attr(1, 2)
static void error(const char *fmt, ...)
{
va_list vl;
@@ -130,6 +142,25 @@ static char *dsdup(char *str)
return copy;
}
+printf_attr(1, 2)
+static char *dsdupf(char *fmt, ...)
+{
+ size_t len = 256, needed;
+ char *out = dsgrow(0, len);
+
+ for (;;) {
+ va_list vl;
+ va_start (vl, fmt);
+ needed = vsnprintf(out, len + 1, fmt, vl);
+ va_end (vl);
+ if (needed <= len)
+ break;
+ len = needed;
+ }
+
+ return dsgrow(out, needed);
+}
+
static char *addch(char *line, int ch)
{
size_t len = line ? dslen(line) : 0;
@@ -289,6 +320,60 @@ static void redraw(char **circbuf, int nlines, int hpos,
drawstatus(columns, stat, cmd);
}
+static int getzp(const char *str, char **err)
+{
+ char *endp;
+ long val = strtol(str, &endp, 10);
+
+ if (endp == str) {
+ *err = dsdup("number expected");
+ return -1;
+ }
+
+ if (val <= 0) {
+ *err = dsdup("positive value required");
+ return -1;
+ }
+
+ if (val >= INT_MAX) {
+ *err = dsdup("unreasonably large value");
+ return -1;
+ }
+
+ return val;
+}
+
+static int getms(const char *str, char **err)
+{
+ errno = 0;
+ char *endp;
+ double sec = strtod(str, &endp);
+
+ if (endp == str) {
+ *err = dsdup("number expected");
+ return -1;
+ }
+
+ if ((sec == 0 || sec == HUGE_VAL) && errno != 0) {
+ *err = dsdup("unreasonable real value");
+ return -1;
+ }
+
+ if (sec < 0) {
+ *err = dsdup("positive value required");
+ return -1;
+ }
+
+ double msec = sec * 1000;
+
+ if (msec > (double) INT_MAX) {
+ *err = dsdupf("maximum interval is %f", INT_MAX / 1000.0);
+ return -1;
+ }
+
+ return msec;
+}
+
static void execute(char *cmd, unsigned *pstat)
{
char *arg = cmd + 2 + strspn(cmd + 2, " \t");
@@ -414,8 +499,6 @@ int main(int argc, char **argv)
char *line = 0;
FILE *tty = fopen("/dev/tty", "r+");
int maxlines = 15, nlines = 0;
- int poll_interval = 1000;
- int long_interval = 10000;
int opt;
int fd = fileno(stdin);
int ttyfd = tty ? fileno(tty) : -1;
@@ -440,13 +523,24 @@ int main(int argc, char **argv)
while ((opt = getopt(argc, argv, "n:i:l:d")) != -1) {
switch (opt) {
case 'n':
- maxlines = atoi(optarg);
+ {
+ char *err;
+ if ((maxlines = getzp(optarg, &err)) < 0) {
+ error("-%c option: %s\n", opt, err);
+ return EXIT_FAILURE;
+ }
+ }
break;
case 'i': case 'l':
{
- double interval = atof(optarg) * 1000;
- if (interval > (double) INT_MAX)
- error("maximum interval is %f\n", INT_MAX / 1000.0);
+ char *err;
+ int interval = getms(optarg, &err);
+
+ if (interval < 0) {
+ error("-%c option: %s\n", opt, err);
+ return EXIT_FAILURE;
+ }
+
if (opt == 'i')
poll_interval = interval;
else