logo

utils-std

Collection of commonly available Unix tools git clone https://anongit.hacktivis.me/git/utils-std.git

consent.c (3223B)


  1. // utils-std: Collection of commonly available Unix tools
  2. // SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
  3. // SPDX-License-Identifier: MPL-2.0
  4. #define _POSIX_C_SOURCE 200809L
  5. #include "./consent.h"
  6. #include <assert.h>
  7. #include <errno.h>
  8. #include <langinfo.h> // nl_langinfo
  9. #include <regex.h> // regcomp
  10. #include <stdarg.h> // va_list
  11. #include <stdio.h> // fprintf, getline,
  12. #include <stdlib.h> // free
  13. #include <string.h> // strerror
  14. #include <unistd.h> // isatty, write
  15. // Needs to be executed after setlocale
  16. regex_t consent_yesexpr_r;
  17. regex_t consent_noexpr_r;
  18. void
  19. consent_init(void)
  20. {
  21. char *yesexpr = nl_langinfo(YESEXPR);
  22. int yesexpr_ret = regcomp(&consent_yesexpr_r, yesexpr, REG_EXTENDED | REG_NOSUB);
  23. if(yesexpr_ret != 0)
  24. {
  25. char errstr[64] = "";
  26. regerror(yesexpr_ret, &consent_yesexpr_r, errstr, 100);
  27. fprintf(
  28. stderr, "%s: Got errorneous yesexpr regex /%s/ from locale: %s\n", argv0, yesexpr, errstr);
  29. errno = 0;
  30. fprintf(stderr, "%s: Fallbacking to /^[Yy]/\n", argv0);
  31. yesexpr_ret = regcomp(&consent_yesexpr_r, "^[Yy]", REG_EXTENDED | REG_NOSUB);
  32. assert(yesexpr_ret == 0);
  33. }
  34. assert(errno == 0);
  35. char *noexpr = nl_langinfo(NOEXPR);
  36. int noexpr_ret = regcomp(&consent_noexpr_r, noexpr, REG_EXTENDED | REG_NOSUB);
  37. if(noexpr_ret != 0)
  38. {
  39. char errstr[64] = "";
  40. regerror(noexpr_ret, &consent_noexpr_r, errstr, 100);
  41. fprintf(
  42. stderr, "%s: Got errorneous noexpr regex /%s/ from locale: %s\n", argv0, noexpr, errstr);
  43. errno = 0;
  44. fprintf(stderr, "%s: Fallbacking to /^[Nn]/\n", argv0);
  45. noexpr_ret = regcomp(&consent_noexpr_r, "^[Nn]", REG_EXTENDED | REG_NOSUB);
  46. assert(noexpr_ret == 0);
  47. }
  48. assert(errno == 0);
  49. }
  50. void
  51. consent_finish(void)
  52. {
  53. regfree(&consent_yesexpr_r);
  54. regfree(&consent_noexpr_r);
  55. }
  56. // Consent therefore defaults to no
  57. bool
  58. consentf(const char *restrict fmt, ...)
  59. {
  60. bool result = false;
  61. char *line = NULL;
  62. size_t len = 0;
  63. va_list ap;
  64. assert(errno == 0);
  65. va_start(ap, fmt);
  66. int ret = vfprintf(stderr, fmt, ap);
  67. va_end(ap);
  68. if(ret < 0)
  69. {
  70. fprintf(stderr, "%s: Failed to print user prompt: %s\n", argv0, strerror(errno));
  71. errno = 0;
  72. goto end;
  73. }
  74. assert(errno == 0);
  75. ssize_t nread = getline(&line, &len, stdin);
  76. if(nread < 0)
  77. {
  78. fprintf(stderr, "\n%s: Failed getting user entry via getline: %s\n", argv0, strerror(errno));
  79. errno = 0;
  80. goto end;
  81. }
  82. if(nread == 0)
  83. {
  84. fprintf(stderr, "%s: Got empty response, considering it false\n", argv0);
  85. errno = 0;
  86. goto end;
  87. }
  88. // Doesn't echoes if not a TTY
  89. if(!isatty(0)) write(2, line, nread);
  90. // isatty changes errno if not a TTY *sigh*
  91. errno = 0;
  92. if(line[nread - 1] == '\n') line[--nread] = 0;
  93. if(line[0] == 0 || line[0] == '\n' || line[0] == '\r')
  94. {
  95. fprintf(stderr, "%s: Got empty response, considering it false\n", argv0);
  96. goto end;
  97. }
  98. if(regexec(&consent_yesexpr_r, line, 0, 0, 0) == 0)
  99. {
  100. result = true;
  101. goto end;
  102. }
  103. if(regexec(&consent_noexpr_r, line, 0, 0, 0) == 0)
  104. {
  105. result = false;
  106. goto end;
  107. }
  108. fprintf(stderr,
  109. "%s: User entry \"%s\" neither affirmative nor negative, considering it false\n",
  110. argv0,
  111. line);
  112. end:
  113. if(len != 0) free(line);
  114. return result;
  115. }