diff options
Diffstat (limited to '')
-rw-r--r-- | src/pattern.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/src/pattern.c b/src/pattern.c new file mode 100644 index 0000000..6912731 --- /dev/null +++ b/src/pattern.c @@ -0,0 +1,151 @@ +/** + * scrotty — Screenshot program for Linux's TTY + * + * Copyright © 2014, 2015 Mattias Andrée (maandree@member.fsf.org) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +#include "common.h" +#include "pattern.h" + + + +/** + * Duplicate all '%':s in a buffer until the first occurrence of a zero byte. + * + * @param buf The buffer. + * @param n The size of the buffer. + * @return -1 if the buffer is too small, otherwise. + * the new position of the first zero byte. + */ +static ssize_t +duplicate_percents (char *restrict buf, size_t n) +{ + size_t p = 0, pi, pc = 0, i; + + /* Count number of '%':s. */ + for (i = 0; buf[i]; i++) + if (buf[i] == '%') + pc++; + + /* Check whether the string will overflow. */ + if (i + pc > n) + return -1; + + /* Duplicate all '%':s. */ + for (pi = 0; pi < pc; pi++) + { + p = (size_t)(strchr (buf + p, '%') - buf); + memmove (buf + p + 1, buf + p, (i - (p - pi)) * sizeof (char)); + p += 2; + } + + return (ssize_t)(i + pi); +} + + +/** + * Parse and evaluate a --exec argument or filename pattern. + * + * If `path != NULL` than all non-escaped spaces in + * `pattern` will be stored as 255-bytes in `buf`. + * + * @param buf The output buffer. + * @param n The size of `buf`. + * @param pattern The pattern to evaluate. + * @param fbno The index of the framebuffer. + * @param width The width of the image/framebuffer. + * @param height The height of the image/framebuffer. + * @param path The filename of the saved image, `NULL` + * during the evaluation of the filename pattern. + * @return Zero on success, -1 on error. + */ +int +evaluate (char *restrict buf, size_t n, const char *restrict pattern, + int fbno, long width, long height, const char *restrict path) +{ +#define P(format, value) r = snprintf (buf + i, n - i, format "%zn", value, &j) + + size_t i = 0; + ssize_t j = 0; + int percent = 0, backslash = 0, dollar = 0, r; + char c; + char *fmt; + time_t t; + struct tm *tm; + + /* Expand '$' and '\'. */ + while ((i < n) && ((c = *pattern++))) + if (dollar) + { + dollar = 0; + if (path == NULL) + if ((c == 'f') || (c == 'n')) + continue; + if (c == 'i') P ("%i", fbno); + else if (c == 'f') P ("%s", path); + else if (c == 'n') P ("%s", strrchr (path, '/') ? (strrchr (path, '/') + 1) : path); + else if (c == 'p') P ("%ju", (uintmax_t)width * (uintmax_t)height); + else if (c == 'w') P ("%li", width); + else if (c == 'h') P ("%li", height); + else if (c == '$') r = 0, j = 1, buf[i] = '$'; + else if ((r < 0) || (j <= 0)) + return -1; + else if ((c == 'f') || (c == 'n')) + if (j = duplicate_percents (buf + i, n - i), j < 0) + goto enametoolong; + i += (size_t)j; + } + else if (backslash) buf[i++] = (c == 'n' ? '\n' : c), backslash = 0; + else if (percent) buf[i++] = c, percent = 0; + else if (c == '%') buf[i++] = c, percent = 1; + else if (c == '\\') backslash = 1; + else if (c == '$') dollar = 1; + else if (c == ' ') buf[i++] = path == NULL ? ' ' : (char)255; /* 255 is not valid in UTF-8. */ + else buf[i++] = c; + if (i >= n) + goto enametoolong; + buf[i] = '\0'; + + /* Check whether there are any '%' to expand. */ + if (strchr (buf, '%') == NULL) + return 0; + + /* Copy the buffer so we can reuse the buffer and use its old content for the format. */ + fmt = alloca ((strlen (buf) + 1) * sizeof (char)); + memcpy (fmt, buf, (strlen (buf) + 1) * sizeof (char)); + + /* Expand '%'. */ + t = time (NULL); + tm = localtime (&t); + if (tm == NULL) + goto fail; + +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + if (strftime (buf, n, fmt, tm) == 0) + goto enametoolong; /* No errors are defined for `strftime`. What else can we do? */ + + return 0; + + enametoolong: + return errno = ENAMETOOLONG, -1; + + fail: + return -1; + +#undef P +} + |