consent.c (3326B)
- // utils-std: Collection of commonly available Unix tools
- // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
- // SPDX-License-Identifier: MPL-2.0
- #define _POSIX_C_SOURCE 200809L
- #include "./consent.h"
- #include <assert.h>
- #include <errno.h>
- #include <langinfo.h> // nl_langinfo
- #include <regex.h> // regcomp
- #include <stdarg.h> // va_list
- #include <stdio.h> // fprintf, getline,
- #include <stdlib.h> // free
- #include <string.h> // strerror
- #include <unistd.h> // isatty, write
- // Needs to be executed after setlocale
- regex_t consent_yesexpr_r;
- regex_t consent_noexpr_r;
- void
- consent_init(void)
- {
- char *yesexpr = nl_langinfo(YESEXPR);
- int yesexpr_ret = regcomp(&consent_yesexpr_r, yesexpr, REG_EXTENDED | REG_NOSUB);
- if(yesexpr_ret != 0)
- {
- char errstr[64] = "";
- regerror(yesexpr_ret, &consent_yesexpr_r, errstr, 100);
- fprintf(stderr,
- "%s: warning: Got erroneous yesexpr regex /%s/ from locale: %s\n",
- argv0,
- yesexpr,
- errstr);
- errno = 0;
- fprintf(stderr, "%s: Fallbacking to /^[Yy]/\n", argv0);
- yesexpr_ret = regcomp(&consent_yesexpr_r, "^[Yy]", REG_EXTENDED | REG_NOSUB);
- assert(yesexpr_ret == 0);
- }
- assert(errno == 0);
- char *noexpr = nl_langinfo(NOEXPR);
- int noexpr_ret = regcomp(&consent_noexpr_r, noexpr, REG_EXTENDED | REG_NOSUB);
- if(noexpr_ret != 0)
- {
- char errstr[64] = "";
- regerror(noexpr_ret, &consent_noexpr_r, errstr, 100);
- fprintf(stderr,
- "%s: warning: Got erroneous noexpr regex /%s/ from locale: %s\n",
- argv0,
- noexpr,
- errstr);
- errno = 0;
- fprintf(stderr, "%s: Fallbacking to /^[Nn]/\n", argv0);
- noexpr_ret = regcomp(&consent_noexpr_r, "^[Nn]", REG_EXTENDED | REG_NOSUB);
- assert(noexpr_ret == 0);
- }
- assert(errno == 0);
- }
- void
- consent_finish(void)
- {
- regfree(&consent_yesexpr_r);
- regfree(&consent_noexpr_r);
- }
- // Consent therefore defaults to no
- bool
- consentf(const char *restrict fmt, ...)
- {
- bool result = false;
- char *line = NULL;
- size_t len = 0;
- va_list ap;
- assert(errno == 0);
- va_start(ap, fmt);
- int ret = vfprintf(stderr, fmt, ap);
- va_end(ap);
- if(ret < 0)
- {
- fprintf(stderr, "%s: error: Failed to print user prompt: %s\n", argv0, strerror(errno));
- errno = 0;
- goto end;
- }
- assert(errno == 0);
- ssize_t nread = getline(&line, &len, stdin);
- if(nread < 0)
- {
- fprintf(
- stderr, "\n%s: error: Failed getting user entry via getline: %s\n", argv0, strerror(errno));
- errno = 0;
- goto end;
- }
- if(nread == 0)
- {
- fprintf(stderr, "%s: Got empty response, considering it false\n", argv0);
- errno = 0;
- goto end;
- }
- // Doesn't echoes if not a TTY
- if(!isatty(0)) write(2, line, nread);
- // isatty changes errno if not a TTY *sigh*
- errno = 0;
- if(line[nread - 1] == '\n') line[--nread] = 0;
- if(line[0] == 0 || line[0] == '\n' || line[0] == '\r')
- {
- fprintf(stderr, "%s: Got empty response, considering it false\n", argv0);
- goto end;
- }
- if(regexec(&consent_yesexpr_r, line, 0, 0, 0) == 0)
- {
- result = true;
- goto end;
- }
- if(regexec(&consent_noexpr_r, line, 0, 0, 0) == 0)
- {
- result = false;
- goto end;
- }
- fprintf(stderr,
- "%s: User entry \"%s\" neither affirmative nor negative, considering it false\n",
- argv0,
- line);
- end:
- if(len != 0) free(line);
- return result;
- }