logo

utils-std

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

consent.c (3242B)


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