diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2022-04-29 02:05:28 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2022-04-29 02:05:28 -0700 |
commit | fe971e32ce519474a8095bb6aa3a7c8253858a6a (patch) | |
tree | a7f69429c96516039cae44708028c9e66634003a /pw.c | |
parent | 2fdac8f23f3082d71f93381cd644befb76832542 (diff) | |
download | pw-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.c | 106 |
1 files changed, 100 insertions, 6 deletions
@@ -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 |