Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

347 lines
9.3KB

  1. #include <rtosc/rtosc.h>
  2. #include <ctype.h>
  3. #include <assert.h>
  4. #include <string.h>
  5. #include <strings.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. static bool rtosc_match_number(const char **pattern, const char **msg)
  9. {
  10. //Verify both hold digits
  11. if(!isdigit(**pattern) || !isdigit(**msg))
  12. return false;
  13. //Read in both numeric values
  14. unsigned max = atoi(*pattern);
  15. unsigned val = atoi(*msg);
  16. ////Advance pointers
  17. while(isdigit(**pattern))++*pattern;
  18. while(isdigit(**msg))++*msg;
  19. //Match iff msg number is strictly less than pattern
  20. return val < max;
  21. }
  22. // pattern = /previous/{A,B,C,D,E}/after
  23. // ^
  24. // message = /previous/C/after
  25. // ^
  26. const char *rtosc_match_options(const char *pattern, const char **msg)
  27. {
  28. const char *preserve = *msg;
  29. assert(*pattern == '{');
  30. pattern++;
  31. retry:
  32. while(1) {
  33. //Check for special characters
  34. if(*pattern == ',' || *pattern == '}') {
  35. goto advance_until_end;
  36. } else if((*pattern == **msg)) { //verbatim compare
  37. if(**msg)
  38. ++pattern, ++*msg;
  39. else
  40. goto try_next;
  41. } else
  42. goto try_next;
  43. }
  44. advance_until_end:
  45. while(*pattern && *pattern != '}') pattern++;
  46. if(*pattern == '}')
  47. pattern++;
  48. return pattern;
  49. try_next:
  50. *msg = preserve;
  51. while(*pattern && *pattern != '}' && *pattern != ',') pattern++;
  52. if(*pattern == ',') {
  53. pattern++;
  54. goto retry;
  55. }
  56. return NULL;
  57. }
  58. const char *rtosc_match_path(const char *pattern,
  59. const char *msg, const char** path_end)
  60. {
  61. if(!path_end)
  62. path_end = &msg; // writing *path_end = msg later will have no effect
  63. while(1) {
  64. //Check for special characters
  65. if(*pattern == ':' && !*msg)
  66. return *path_end = msg, pattern;
  67. else if(*pattern == '{') {
  68. pattern = rtosc_match_options(pattern, &msg);
  69. if(!pattern)
  70. return NULL;
  71. } else if(*pattern == '*') {
  72. //advance message and pattern to '/' or ':' and '\0'
  73. while(*pattern && *pattern != '/' && *pattern != ':')
  74. pattern++;
  75. if(*pattern == '/' || *pattern == ':')
  76. while(*msg && *msg != '/')
  77. msg++;
  78. } else if(*pattern == '/' && *msg == '/') {
  79. ++pattern;
  80. ++msg;
  81. if(*pattern == '\0' || *pattern == ':')
  82. return *path_end = msg, pattern;
  83. } else if(*pattern == '#') {
  84. ++pattern;
  85. if(!rtosc_match_number(&pattern, &msg))
  86. return NULL;
  87. } else if((*pattern == *msg)) { //verbatim compare
  88. if(*msg)
  89. ++pattern, ++msg;
  90. else
  91. return *path_end = msg, pattern;
  92. } else
  93. return NULL;
  94. }
  95. }
  96. //Match the arg string or fail
  97. static bool rtosc_match_args(const char *pattern, const char *msg)
  98. {
  99. //match anything if now arg restriction is present (ie the ':')
  100. if(*pattern++ != ':')
  101. return true;
  102. const char *arg_str = rtosc_argument_string(msg);
  103. bool arg_match = *pattern || *pattern == *arg_str;
  104. while(*pattern && *pattern != ':')
  105. arg_match &= (*pattern++==*arg_str++);
  106. if(*pattern==':') {
  107. if(arg_match && !*arg_str)
  108. return true;
  109. else
  110. return rtosc_match_args(pattern, msg); //retry
  111. }
  112. return arg_match;
  113. }
  114. bool rtosc_match(const char *pattern,
  115. const char *msg, const char** path_end)
  116. {
  117. const char *arg_pattern = rtosc_match_path(pattern, msg, path_end);
  118. if(!arg_pattern)
  119. return false;
  120. else if(*arg_pattern == ':')
  121. return rtosc_match_args(arg_pattern, msg);
  122. return true;
  123. }
  124. /*
  125. * Special characters from the specification:
  126. * ' ' space 32
  127. * # number sign 35
  128. * * asterisk 42
  129. * , comma 44
  130. * / forward slash 47
  131. * ? question mark 63
  132. * [ open bracket 91
  133. * ] close bracket 93
  134. * { open curly brace 123
  135. * } close curly brace 125
  136. */
  137. #if 0
  138. QUOTE FROM OSC 1.0 SPEC
  139. '?' in the OSC Address Pattern matches any single character
  140. '*' in the OSC Address Pattern matches any sequence of zero or more characters
  141. A string of characters in square brackets (e.g., "[string]") in the OSC Address Pattern matches any character in the string.
  142. Inside square brackets, the minus sign (-) and exclamation point (!) have special meanings:
  143. two characters separated by a minus sign indicate the range of characters between the given two
  144. in ASCII collating sequence. (A minus sign at the end of the string has no special meaning.)
  145. An exclamation point at the beginning of a bracketed string negates the sense of the list,
  146. meaning that the list matches any character not in the list.
  147. (An exclamation point anywhere besides the first character after the open bracket has no special meaning.)
  148. A comma-separated list of strings enclosed in curly braces (e.g., "{foo,bar}") in the OSC Address Pattern
  149. matches any of the strings in the list.
  150. #endif
  151. //for literal string X
  152. //for X+?+[] Y
  153. //for Y+single{} Z
  154. //for numeric string N
  155. //assume a is of the form X
  156. //assume b is a pattern of the form:
  157. //* (1)
  158. //Y (2)
  159. //Y* (3)
  160. //*X* (4)
  161. //Z (5)
  162. //Z* (6)
  163. //Y#N (7)
  164. #define RTOSC_MATCH_ALL 1
  165. #define RTOSC_MATCH_CHAR 2
  166. #define RTOSC_MATCH_PARTIAL_CHAR 3
  167. #define RTOSC_MATCH_SUBSTRING 4
  168. #define RTOSC_MATCH_OPTIONS 5
  169. #define RTOSC_MATCH_PARTIAL_OPTIONS 6
  170. #define RTOSC_MATCH_ENUMERATED 7
  171. static bool is_charwise(uint8_t c)
  172. {
  173. return c<=0x7f && c != ' ' && c != '#' &&
  174. c != '/' && c != '{' && c != '}';
  175. }
  176. int rtosc_subpath_pat_type(const char *pattern)
  177. {
  178. int charwise_only = 1;
  179. const char *last_star = strrchr(pattern, '*');
  180. const char *pound = strchr(pattern, '#');
  181. if(!strcmp("*", pattern))
  182. return RTOSC_MATCH_ALL;
  183. for(const char *p = pattern;*p;++p)
  184. charwise_only &= is_charwise(*p);
  185. if(charwise_only && !last_star)
  186. return RTOSC_MATCH_CHAR;
  187. if(pound)
  188. return RTOSC_MATCH_ENUMERATED;
  189. return 2;
  190. }
  191. static bool rtosc_match_char(const char **path, const char **pattern)
  192. {
  193. //printf("rtosc_match_char('%s','%s')\n", *path, *pattern);
  194. if(**path == **pattern && **path) {
  195. ++*path;
  196. ++*pattern;
  197. return true;
  198. } else if(**pattern == '?' && *path) {
  199. ++*path;
  200. ++*pattern;
  201. return true;
  202. } else if(**pattern == '[') {
  203. bool matched = false;
  204. bool negation = false;
  205. char last_range = '\0';
  206. char to_match = **path;
  207. ++*pattern;
  208. if(**pattern == '!') {
  209. negation = true;
  210. ++*pattern;
  211. }
  212. while(**pattern && **pattern != ']') {
  213. last_range = **pattern;
  214. if(**pattern == to_match) {
  215. matched = true;
  216. } else if(**pattern == '-') {//range
  217. ++*pattern;
  218. char range_high = **pattern;
  219. if(range_high == ']' || !range_high)
  220. break;
  221. if(to_match <= range_high && to_match >= last_range)
  222. matched = true;
  223. }
  224. ++*pattern;
  225. }
  226. if(**pattern == ']')
  227. ++*pattern;
  228. ++*path;
  229. return negation ^ matched;
  230. }
  231. return false;
  232. }
  233. bool rtosc_match_partial(const char *a, const char *b)
  234. {
  235. //assume a is of the form X
  236. //assume b is a pattern of the form: (1..6)
  237. //This is done to avoid backtracking of any kind
  238. //This is an OSC serialization library, not a regex
  239. //implementation
  240. char patternbuf[256];
  241. (void) patternbuf;
  242. int type = rtosc_subpath_pat_type(b);
  243. if(type == RTOSC_MATCH_ALL)
  244. return true;
  245. else if(type == RTOSC_MATCH_CHAR || type == RTOSC_MATCH_PARTIAL_CHAR) {
  246. while(rtosc_match_char(&a,&b));
  247. if(!*a && !*b)
  248. return true;
  249. else if(*a && *b=='*' && b[1] == '\0')
  250. return true;
  251. else
  252. return false;
  253. } else if(type == 4) {
  254. //extract substring
  255. const char *sub=NULL;
  256. return strstr(a,sub);
  257. } else if(type == RTOSC_MATCH_OPTIONS || type == 6) {
  258. return false;
  259. } else if(type == RTOSC_MATCH_ENUMERATED) {
  260. while(rtosc_match_char(&a,&b));
  261. if(*a && *b=='#' && b[1] != '\0')
  262. return atoi(a) < atoi(b+1);
  263. return false;
  264. } else
  265. return 0;
  266. assert(false);
  267. }
  268. int rtosc_matchable_path(const char *pattern)
  269. {
  270. (void) pattern;
  271. return 0;
  272. }
  273. int chunk_path(const char *a, int b, const char *c)
  274. {
  275. (void) a;
  276. (void) b;
  277. (void) c;
  278. return 0;
  279. }
  280. void advance_path(const char **a)
  281. {
  282. (void) a;
  283. }
  284. bool rtosc_match_full_path(const char *pattern, const char *message)
  285. {
  286. assert(false && "This API is a WIP");
  287. char subpattern[256];
  288. char submessage[256];
  289. const char *p = pattern;
  290. const char *m = message;
  291. step:
  292. if(*p != *m)
  293. return 0;
  294. if(chunk_path(subpattern, sizeof(subpattern), p))
  295. return 0;
  296. if(chunk_path(submessage, sizeof(submessage), m))
  297. return 0;
  298. advance_path(&p);
  299. advance_path(&m);
  300. if(*p == 0 && *m == 0)
  301. return 1;
  302. else
  303. goto step;
  304. }