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.

343 lines
9.0KB

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