chiark / gitweb /
util: unify line caching and column caching
[elogind.git] / src / shared / util.c
index be94515d9d8740e852a8fa56ca461676d2456bd6..ef30cb2dab15b2dfade9be5dde7d7e101fa9f31a 100644 (file)
@@ -56,6 +56,7 @@
 #include <sys/mman.h>
 #include <sys/vfs.h>
 #include <linux/magic.h>
+#include <limits.h>
 
 #include "macro.h"
 #include "util.h"
@@ -71,6 +72,9 @@
 int saved_argc = 0;
 char **saved_argv = NULL;
 
+static volatile unsigned cached_columns = 0;
+static volatile unsigned cached_lines = 0;
+
 size_t page_size(void) {
         static __thread size_t pgsz = 0;
         long r;
@@ -140,6 +144,10 @@ dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
+        if (ts->tv_sec == (time_t) -1 &&
+            ts->tv_nsec == (long) -1)
+                return (usec_t) -1;
+
         return
                 (usec_t) ts->tv_sec * USEC_PER_SEC +
                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
@@ -148,6 +156,12 @@ usec_t timespec_load(const struct timespec *ts) {
 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
         assert(ts);
 
+        if (u == (usec_t) -1) {
+                ts->tv_sec = (time_t) -1;
+                ts->tv_nsec = (long) -1;
+                return ts;
+        }
+
         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
         ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
 
@@ -157,6 +171,10 @@ struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
 usec_t timeval_load(const struct timeval *tv) {
         assert(tv);
 
+        if (tv->tv_sec == (time_t) -1 &&
+            tv->tv_usec == (suseconds_t) -1)
+                return (usec_t) -1;
+
         return
                 (usec_t) tv->tv_sec * USEC_PER_SEC +
                 (usec_t) tv->tv_usec;
@@ -165,6 +183,12 @@ usec_t timeval_load(const struct timeval *tv) {
 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
         assert(tv);
 
+        if (u == (usec_t) -1) {
+                tv->tv_sec = (time_t) -1;
+                tv->tv_usec = (suseconds_t) -1;
+                return tv;
+        }
+
         tv->tv_sec = (time_t) (u / USEC_PER_SEC);
         tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
 
@@ -1686,7 +1710,8 @@ char *xescape(const char *s, const char *bad) {
          * chars, in \xFF style escaping. May be reversed with
          * cunescape. */
 
-        if (!(r = new(char, strlen(s)*4+1)))
+        r = new(char, strlen(s) * 4 + 1);
+        if (!r)
                 return NULL;
 
         for (f = s, t = r; *f; f++) {
@@ -1935,7 +1960,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
 
         sec = (time_t) (t / USEC_PER_SEC);
 
-        if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0)
+        if (strftime(buf, l, "%a, %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
                 return NULL;
 
         return buf;
@@ -2151,28 +2176,25 @@ int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
 }
 
 int ask(char *ret, const char *replies, const char *text, ...) {
-        bool on_tty;
 
         assert(ret);
         assert(replies);
         assert(text);
 
-        on_tty = isatty(STDOUT_FILENO);
-
         for (;;) {
                 va_list ap;
                 char c;
                 int r;
                 bool need_nl = true;
 
-                if (on_tty)
+                if (on_tty())
                         fputs(ANSI_HIGHLIGHT_ON, stdout);
 
                 va_start(ap, text);
                 vprintf(text, ap);
                 va_end(ap);
 
-                if (on_tty)
+                if (on_tty())
                         fputs(ANSI_HIGHLIGHT_OFF, stdout);
 
                 fflush(stdout);
@@ -2729,16 +2751,31 @@ int parse_usec(const char *t, usec_t *usec) {
                 const char *suffix;
                 usec_t usec;
         } table[] = {
+                { "seconds", USEC_PER_SEC },
+                { "second", USEC_PER_SEC },
                 { "sec", USEC_PER_SEC },
                 { "s", USEC_PER_SEC },
+                { "minutes", USEC_PER_MINUTE },
+                { "minute", USEC_PER_MINUTE },
                 { "min", USEC_PER_MINUTE },
+                { "months", USEC_PER_MONTH },
+                { "month", USEC_PER_MONTH },
+                { "msec", USEC_PER_MSEC },
+                { "ms", USEC_PER_MSEC },
+                { "m", USEC_PER_MINUTE },
+                { "hours", USEC_PER_HOUR },
+                { "hour", USEC_PER_HOUR },
                 { "hr", USEC_PER_HOUR },
                 { "h", USEC_PER_HOUR },
+                { "days", USEC_PER_DAY },
+                { "day", USEC_PER_DAY },
                 { "d", USEC_PER_DAY },
+                { "weeks", USEC_PER_WEEK },
+                { "week", USEC_PER_WEEK },
                 { "w", USEC_PER_WEEK },
-                { "msec", USEC_PER_MSEC },
-                { "ms", USEC_PER_MSEC },
-                { "m", USEC_PER_MINUTE },
+                { "years", USEC_PER_YEAR },
+                { "year", USEC_PER_YEAR },
+                { "y", USEC_PER_YEAR },
                 { "usec", 1ULL },
                 { "us", 1ULL },
                 { "", USEC_PER_SEC }, /* default is sec */
@@ -2792,16 +2829,31 @@ int parse_nsec(const char *t, nsec_t *nsec) {
                 const char *suffix;
                 nsec_t nsec;
         } table[] = {
+                { "seconds", NSEC_PER_SEC },
+                { "second", NSEC_PER_SEC },
                 { "sec", NSEC_PER_SEC },
                 { "s", NSEC_PER_SEC },
+                { "minutes", NSEC_PER_MINUTE },
+                { "minute", NSEC_PER_MINUTE },
                 { "min", NSEC_PER_MINUTE },
+                { "months", NSEC_PER_MONTH },
+                { "month", NSEC_PER_MONTH },
+                { "msec", NSEC_PER_MSEC },
+                { "ms", NSEC_PER_MSEC },
+                { "m", NSEC_PER_MINUTE },
+                { "hours", NSEC_PER_HOUR },
+                { "hour", NSEC_PER_HOUR },
                 { "hr", NSEC_PER_HOUR },
                 { "h", NSEC_PER_HOUR },
+                { "days", NSEC_PER_DAY },
+                { "day", NSEC_PER_DAY },
                 { "d", NSEC_PER_DAY },
+                { "weeks", NSEC_PER_WEEK },
+                { "week", NSEC_PER_WEEK },
                 { "w", NSEC_PER_WEEK },
-                { "msec", NSEC_PER_MSEC },
-                { "ms", NSEC_PER_MSEC },
-                { "m", NSEC_PER_MINUTE },
+                { "years", NSEC_PER_YEAR },
+                { "year", NSEC_PER_YEAR },
+                { "y", NSEC_PER_YEAR },
                 { "usec", NSEC_PER_USEC },
                 { "us", NSEC_PER_USEC },
                 { "nsec", 1ULL },
@@ -3739,41 +3791,26 @@ int fd_columns(int fd) {
         return ws.ws_col;
 }
 
-static unsigned columns_cached(bool cached) {
-        static __thread int parsed_columns = 0, env_columns = -1;
+unsigned columns(void) {
         const char *e;
+        unsigned c;
 
-        if (_likely_(parsed_columns > 0 && cached))
-                return parsed_columns;
-
-        if (_unlikely_(env_columns == -1)) {
-                e = getenv("COLUMNS");
-                if (e)
-                        env_columns = atoi(e);
-                else
-                        env_columns = 0;
-        }
-
-        if (env_columns > 0) {
-                parsed_columns = env_columns;
-                return parsed_columns;
-        }
-
-        if (parsed_columns <= 0 || !cached)
-                parsed_columns = fd_columns(STDOUT_FILENO);
+        if (_likely_(cached_columns > 0))
+                return cached_columns;
 
-        if (parsed_columns <= 0)
-                parsed_columns = 80;
+        c = 0;
+        e = getenv("COLUMNS");
+        if (e)
+                safe_atou(e, &c);
 
-        return parsed_columns;
-}
+        if (c <= 0)
+                c = fd_columns(STDOUT_FILENO);
 
-unsigned columns(void) {
-        return columns_cached(true);
-}
+        if (c <= 0)
+                c = 80;
 
-unsigned columns_uncached(void) {
-        return columns_cached(false);
+        cached_columns = c;
+        return c;
 }
 
 int fd_lines(int fd) {
@@ -3790,23 +3827,40 @@ int fd_lines(int fd) {
 }
 
 unsigned lines(void) {
-        static __thread int parsed_lines = 0;
         const char *e;
+        unsigned l;
 
-        if (_likely_(parsed_lines > 0))
-                return parsed_lines;
+        if (_likely_(cached_lines > 0))
+                return cached_lines;
 
+        l = 0;
         e = getenv("LINES");
         if (e)
-                parsed_lines = atoi(e);
+                safe_atou(e, &l);
 
-        if (parsed_lines <= 0)
-                parsed_lines = fd_lines(STDOUT_FILENO);
+        if (l <= 0)
+                l = fd_lines(STDOUT_FILENO);
 
-        if (parsed_lines <= 0)
-                parsed_lines = 25;
+        if (l <= 0)
+                l = 24;
 
-        return parsed_lines;
+        cached_lines = l;
+        return cached_lines;
+}
+
+/* intended to be used as a SIGWINCH sighandler */
+void columns_lines_cache_reset(int signum) {
+        cached_columns = 0;
+        cached_lines = 0;
+}
+
+bool on_tty(void) {
+        static int cached_on_tty = -1;
+
+        if (_unlikely_(cached_on_tty < 0))
+                cached_on_tty = isatty(STDOUT_FILENO) > 0;
+
+        return cached_on_tty;
 }
 
 int running_in_chroot(void) {
@@ -5024,8 +5078,10 @@ char *strjoin(const char *x, ...) {
                                 break;
 
                         n = strlen(t);
-                        if (n > ((size_t) -1) - l)
+                        if (n > ((size_t) -1) - l) {
+                                va_end(ap);
                                 return NULL;
+                        }
 
                         l += n;
                 }
@@ -5841,3 +5897,172 @@ void closedirp(DIR **d) {
 void umaskp(mode_t *u) {
         umask(*u);
 }
+
+bool filename_is_safe(const char *p) {
+
+        if (isempty(p))
+                return false;
+
+        if (strchr(p, '/'))
+                return false;
+
+        if (streq(p, "."))
+                return false;
+
+        if (streq(p, ".."))
+                return false;
+
+        if (strlen(p) > FILENAME_MAX)
+                return false;
+
+        return true;
+}
+
+bool string_is_safe(const char *p) {
+        const char *t;
+
+        assert(p);
+
+        for (t = p; *t; t++) {
+                if (*t > 0 && *t < ' ')
+                        return false;
+
+                if (strchr("\\\"\'", *t))
+                        return false;
+        }
+
+        return true;
+}
+
+int parse_timestamp(const char *t, usec_t *usec) {
+        const char *k;
+        struct tm tm, copy;
+        time_t x;
+        usec_t plus = 0, minus = 0, ret;
+        int r;
+
+        /*
+         * Allowed syntaxes:
+         *
+         *   2012-09-22 16:34:22
+         *   2012-09-22 16:34     (seconds will be set to 0)
+         *   2012-09-22           (time will be set to 00:00:00)
+         *   16:34:22             (date will be set to today)
+         *   16:34                (date will be set to today, seconds to 0)
+         *   now
+         *   yesterday            (time is set to 00:00:00)
+         *   today                (time is set to 00:00:00)
+         *   tomorrow             (time is set to 00:00:00)
+         *   +5min
+         *   -5days
+         *
+         */
+
+        assert(t);
+        assert(usec);
+
+        x = time(NULL);
+        assert_se(localtime_r(&x, &tm));
+
+        if (streq(t, "now"))
+                goto finish;
+
+        else if (streq(t, "today")) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "yesterday")) {
+                tm.tm_mday --;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (streq(t, "tomorrow")) {
+                tm.tm_mday ++;
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+
+        } else if (t[0] == '+') {
+
+                r = parse_usec(t+1, &plus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        } else if (t[0] == '-') {
+
+                r = parse_usec(t+1, &minus);
+                if (r < 0)
+                        return r;
+
+                goto finish;
+        }
+
+        copy = tm;
+        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%Y-%m-%d", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
+                goto finish;
+        }
+
+        tm = copy;
+        k = strptime(t, "%H:%M:%S", &tm);
+        if (k && *k == 0)
+                goto finish;
+
+        tm = copy;
+        k = strptime(t, "%H:%M", &tm);
+        if (k && *k == 0) {
+                tm.tm_sec = 0;
+                goto finish;
+        }
+
+        return -EINVAL;
+
+finish:
+        x = mktime(&tm);
+        if (x == (time_t) -1)
+                return -EINVAL;
+
+        ret = (usec_t) x * USEC_PER_SEC;
+
+        ret += plus;
+        if (ret > minus)
+                ret -= minus;
+        else
+                ret = 0;
+
+        *usec = ret;
+
+        return 0;
+}