regex_match.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. /**
  2. * @file regex_match.c
  3. * @author Ambroz Bizjak <ambrop7@gmail.com>
  4. *
  5. * @section LICENSE
  6. *
  7. * This file is part of BadVPN.
  8. *
  9. * BadVPN is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2
  11. * as published by the Free Software Foundation.
  12. *
  13. * BadVPN is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program; if not, write to the Free Software Foundation, Inc.,
  20. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  21. *
  22. * @section DESCRIPTION
  23. *
  24. * Regular expression matching module.
  25. *
  26. * Synopsis: regex_match(string input, string regex)
  27. * Variables:
  28. * succeeded - "true" or "false", indicating whether input matched regex
  29. * matchN - for N=0,1,2,..., the matching data for the N-th subexpression
  30. * (match0 = whole match)
  31. */
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <regex.h>
  35. #include <misc/string_begins_with.h>
  36. #include <misc/parse_number.h>
  37. #include <ncd/NCDModule.h>
  38. #include <generated/blog_channel_ncd_regex_match.h>
  39. #define ModuleLog(i, ...) NCDModuleInst_Backend_Log((i), BLOG_CURRENT_CHANNEL, __VA_ARGS__)
  40. #define MAX_MATCHES 64
  41. struct instance {
  42. NCDModuleInst *i;
  43. char *input;
  44. int succeeded;
  45. int num_matches;
  46. regmatch_t matches[MAX_MATCHES];
  47. };
  48. static void func_new (NCDModuleInst *i)
  49. {
  50. // allocate instance
  51. struct instance *o = malloc(sizeof(*o));
  52. if (!o) {
  53. ModuleLog(i, BLOG_ERROR, "failed to allocate instance");
  54. goto fail0;
  55. }
  56. NCDModuleInst_Backend_SetUser(i, o);
  57. // init arguments
  58. o->i = i;
  59. // read arguments
  60. NCDValue *input_arg;
  61. NCDValue *regex_arg;
  62. if (!NCDValue_ListRead(o->i->args, 2, &input_arg, &regex_arg)) {
  63. ModuleLog(o->i, BLOG_ERROR, "wrong arity");
  64. goto fail1;
  65. }
  66. if (NCDValue_Type(input_arg) != NCDVALUE_STRING || NCDValue_Type(regex_arg) != NCDVALUE_STRING) {
  67. ModuleLog(o->i, BLOG_ERROR, "wrong type");
  68. goto fail1;
  69. }
  70. o->input = NCDValue_StringValue(input_arg);
  71. char *regex = NCDValue_StringValue(regex_arg);
  72. // compile regex
  73. regex_t preg;
  74. int ret;
  75. if ((ret = regcomp(&preg, regex, REG_EXTENDED)) != 0) {
  76. ModuleLog(o->i, BLOG_ERROR, "regcomp failed (error=%d)", ret);
  77. goto fail1;
  78. }
  79. // execute match
  80. o->succeeded = (regexec(&preg, o->input, MAX_MATCHES, o->matches, 0) == 0);
  81. // free regex
  82. regfree(&preg);
  83. // signal up
  84. NCDModuleInst_Backend_Event(o->i, NCDMODULE_EVENT_UP);
  85. return;
  86. fail1:
  87. free(o);
  88. fail0:
  89. NCDModuleInst_Backend_SetError(i);
  90. NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
  91. }
  92. static void func_die (void *vo)
  93. {
  94. struct instance *o = vo;
  95. NCDModuleInst *i = o->i;
  96. // free instance
  97. free(o);
  98. NCDModuleInst_Backend_Event(i, NCDMODULE_EVENT_DEAD);
  99. }
  100. static int func_getvar (void *vo, const char *name, NCDValue *out)
  101. {
  102. struct instance *o = vo;
  103. if (!strcmp(name, "succeeded")) {
  104. if (!NCDValue_InitString(out, (o->succeeded ? "true" : "false"))) {
  105. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  106. return 0;
  107. }
  108. return 1;
  109. }
  110. size_t pos;
  111. uintmax_t n;
  112. if ((pos = string_begins_with(name, "match")) && parse_unsigned_integer(name + pos, &n)) {
  113. if (o->succeeded && n < MAX_MATCHES && o->matches[n].rm_so >= 0) {
  114. regmatch_t *m = &o->matches[n];
  115. ASSERT(m->rm_so <= strlen(o->input))
  116. ASSERT(m->rm_eo >= m->rm_so)
  117. ASSERT(m->rm_eo <= strlen(o->input))
  118. size_t len = m->rm_eo - m->rm_so;
  119. char *str = malloc(len + 1);
  120. if (!str) {
  121. ModuleLog(o->i, BLOG_ERROR, "malloc failed");
  122. return 0;
  123. }
  124. memcpy(str, o->input + m->rm_so, len);
  125. str[len] = '\0';
  126. if (!NCDValue_InitString(out, str)) {
  127. ModuleLog(o->i, BLOG_ERROR, "NCDValue_InitCopy failed");
  128. free(str);
  129. return 0;
  130. }
  131. free(str);
  132. return 1;
  133. }
  134. }
  135. return 0;
  136. }
  137. static const struct NCDModule modules[] = {
  138. {
  139. .type = "regex_match",
  140. .func_new = func_new,
  141. .func_die = func_die,
  142. .func_getvar = func_getvar
  143. }, {
  144. .type = NULL
  145. }
  146. };
  147. const struct NCDModuleGroup ncdmodule_regex_match = {
  148. .modules = modules
  149. };