logo

utils-std

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

consent.c (3326B)


  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(stderr,
  28. "%s: warning: Got erroneous yesexpr regex /%s/ from locale: %s\n",
  29. argv0,
  30. yesexpr,
  31. errstr);
  32. errno = 0;
  33. fprintf(stderr, "%s: Fallbacking to /^[Yy]/\n", argv0);
  34. yesexpr_ret = regcomp(&consent_yesexpr_r, "^[Yy]", REG_EXTENDED | REG_NOSUB);
  35. assert(yesexpr_ret == 0);
  36. }
  37. assert(errno == 0);
  38. char *noexpr = nl_langinfo(NOEXPR);
  39. int noexpr_ret = regcomp(&consent_noexpr_r, noexpr, REG_EXTENDED | REG_NOSUB);
  40. if(noexpr_ret != 0)
  41. {
  42. char errstr[64] = "";
  43. regerror(noexpr_ret, &consent_noexpr_r, errstr, 100);
  44. fprintf(stderr,
  45. "%s: warning: Got erroneous noexpr regex /%s/ from locale: %s\n",
  46. argv0,
  47. noexpr,
  48. errstr);
  49. errno = 0;
  50. fprintf(stderr, "%s: Fallbacking to /^[Nn]/\n", argv0);
  51. noexpr_ret = regcomp(&consent_noexpr_r, "^[Nn]", REG_EXTENDED | REG_NOSUB);
  52. assert(noexpr_ret == 0);
  53. }
  54. assert(errno == 0);
  55. }
  56. void
  57. consent_finish(void)
  58. {
  59. regfree(&consent_yesexpr_r);
  60. regfree(&consent_noexpr_r);
  61. }
  62. // Consent therefore defaults to no
  63. bool
  64. consentf(const char *restrict fmt, ...)
  65. {
  66. bool result = false;
  67. char *line = NULL;
  68. size_t len = 0;
  69. va_list ap;
  70. assert(errno == 0);
  71. va_start(ap, fmt);
  72. int ret = vfprintf(stderr, fmt, ap);
  73. va_end(ap);
  74. if(ret < 0)
  75. {
  76. fprintf(stderr, "%s: error: Failed to print user prompt: %s\n", argv0, strerror(errno));
  77. errno = 0;
  78. goto end;
  79. }
  80. assert(errno == 0);
  81. ssize_t nread = getline(&line, &len, stdin);
  82. if(nread < 0)
  83. {
  84. fprintf(
  85. stderr, "\n%s: error: Failed getting user entry via getline: %s\n", argv0, strerror(errno));
  86. errno = 0;
  87. goto end;
  88. }
  89. if(nread == 0)
  90. {
  91. fprintf(stderr, "%s: Got empty response, considering it false\n", argv0);
  92. errno = 0;
  93. goto end;
  94. }
  95. // Doesn't echoes if not a TTY
  96. if(!isatty(0)) write(2, line, nread);
  97. // isatty changes errno if not a TTY *sigh*
  98. errno = 0;
  99. if(line[nread - 1] == '\n') line[--nread] = 0;
  100. if(line[0] == 0 || line[0] == '\n' || line[0] == '\r')
  101. {
  102. fprintf(stderr, "%s: Got empty response, considering it false\n", argv0);
  103. goto end;
  104. }
  105. if(regexec(&consent_yesexpr_r, line, 0, 0, 0) == 0)
  106. {
  107. result = true;
  108. goto end;
  109. }
  110. if(regexec(&consent_noexpr_r, line, 0, 0, 0) == 0)
  111. {
  112. result = false;
  113. goto end;
  114. }
  115. fprintf(stderr,
  116. "%s: User entry \"%s\" neither affirmative nor negative, considering it false\n",
  117. argv0,
  118. line);
  119. end:
  120. if(len != 0) free(line);
  121. return result;
  122. }