diff options
author | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
---|---|---|
committer | Peter D'Hoye <peter.dhoye@gmail.com> | 2009-05-22 21:58:48 +0000 |
commit | 513389b4c1bc8afe4b2dc9947c534bfeb105e3da (patch) | |
tree | 10e673b35651ac567fed2eda0c679c7ade64cbc6 /apps/plugins/pdbox/PDa/extra/OSCroute.c | |
parent | 95fa7f6a2ef466444fbe3fe87efc6d5db6b77b36 (diff) | |
download | rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.tar.gz rockbox-513389b4c1bc8afe4b2dc9947c534bfeb105e3da.zip |
Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin project of Wincent Balin. Stripped some non-sourcefiles and added a rockbox readme that needs a bit more info from Wincent. Is added to CATEGORIES and viewers, but not yet to SUBDIRS (ie doesn't build yet)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21044 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins/pdbox/PDa/extra/OSCroute.c')
-rw-r--r-- | apps/plugins/pdbox/PDa/extra/OSCroute.c | 1204 |
1 files changed, 1204 insertions, 0 deletions
diff --git a/apps/plugins/pdbox/PDa/extra/OSCroute.c b/apps/plugins/pdbox/PDa/extra/OSCroute.c new file mode 100644 index 0000000000..437d34dc68 --- /dev/null +++ b/apps/plugins/pdbox/PDa/extra/OSCroute.c | |||
@@ -0,0 +1,1204 @@ | |||
1 | /* | ||
2 | Written by Adrian Freed, The Center for New Music and Audio Technologies, | ||
3 | University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04 | ||
4 | The Regents of the University of California (Regents). | ||
5 | |||
6 | Permission to use, copy, modify, distribute, and distribute modified versions | ||
7 | of this software and its documentation without fee and without a signed | ||
8 | licensing agreement, is hereby granted, provided that the above copyright | ||
9 | notice, this paragraph and the following two paragraphs appear in all copies, | ||
10 | modifications, and distributions. | ||
11 | |||
12 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | ||
13 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING | ||
14 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS | ||
15 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
16 | |||
17 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
18 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
19 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED | ||
20 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE | ||
21 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | ||
22 | |||
23 | |||
24 | The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl | ||
25 | */ | ||
26 | |||
27 | /* OSC-route.c | ||
28 | Max object for OSC-style dispatching | ||
29 | |||
30 | To-do: | ||
31 | |||
32 | Match a pattern against a pattern? | ||
33 | Declare outlet types / distinguish leaf nodes from other children | ||
34 | More sophisticated (2-pass?) allmessages scheme | ||
35 | set message? | ||
36 | |||
37 | |||
38 | pd | ||
39 | ------------- | ||
40 | -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 | ||
41 | |||
42 | |||
43 | */ | ||
44 | |||
45 | #ifdef WIN32 | ||
46 | #include <stdlib.h> | ||
47 | #include <string.h> | ||
48 | #endif | ||
49 | #ifdef __APPLE__ | ||
50 | #include <stdio.h> | ||
51 | #endif | ||
52 | #ifdef UNIX | ||
53 | #include <stdio.h> | ||
54 | #endif | ||
55 | |||
56 | /* structure definition of your object */ | ||
57 | |||
58 | #define MAX_NUM 20 | ||
59 | #define OSC_ROUTE_VERSION "1.05" | ||
60 | #define OSCWarning(x...) post(x) | ||
61 | |||
62 | /* the required include files */ | ||
63 | #include "m_pd.h" | ||
64 | |||
65 | |||
66 | #ifndef TRUE | ||
67 | typedef int Boolean; | ||
68 | #define TRUE 1 | ||
69 | #define FALSE 0 | ||
70 | #endif | ||
71 | |||
72 | |||
73 | /* Fixed byte width types */ | ||
74 | typedef int int4; /* 4 byte int */ | ||
75 | |||
76 | Boolean PatternMatch (const char *pattern, const char *test); | ||
77 | |||
78 | |||
79 | |||
80 | /* Version 1.04: Allows #1 thru #9 as typed-in arguments | ||
81 | Version 1.05: Allows "list" messages as well as "message" messages. | ||
82 | */ | ||
83 | |||
84 | static t_class *OSCroute_class; | ||
85 | |||
86 | typedef struct _OSCroute | ||
87 | { | ||
88 | t_object x_obj; // required header | ||
89 | t_int x_num; // Number of address prefixes we store | ||
90 | t_int x_complainmode; // Do we print a message if no match? | ||
91 | t_int x_sendmode; // use pd internal sends instead of outlets | ||
92 | char *x_prefixes[MAX_NUM]; | ||
93 | void *x_outlets[MAX_NUM+1]; | ||
94 | } t_OSCroute; | ||
95 | |||
96 | t_symbol *ps_list, *ps_complain, *ps_emptySymbol; | ||
97 | |||
98 | /* prototypes */ | ||
99 | |||
100 | void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
101 | void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
102 | void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
103 | /* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */ | ||
104 | void *OSCroute_new(t_symbol *s, int argc, t_atom *argv); | ||
105 | void OSCroute_version (t_OSCroute *x); | ||
106 | /* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */ | ||
107 | /* char *dstString); */ | ||
108 | void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
109 | |||
110 | static char *NextSlashOrNull(char *p); | ||
111 | static void StrCopyUntilSlash(char *target, const char *source); | ||
112 | |||
113 | |||
114 | // free | ||
115 | static void OSCroute_free(t_OSCroute *x) | ||
116 | { | ||
117 | // freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec)); | ||
118 | } | ||
119 | |||
120 | /* initialization routine */ | ||
121 | |||
122 | // setup | ||
123 | #ifdef WIN32 | ||
124 | OSC_API void OSCroute_setup(void) { | ||
125 | #else | ||
126 | void OSCroute_setup(void) { | ||
127 | #endif | ||
128 | OSCroute_class = class_new(gensym("OSCroute"), (t_newmethod)OSCroute_new, | ||
129 | (t_method)OSCroute_free,sizeof(t_OSCroute), 0, A_GIMME, 0); | ||
130 | class_addlist(OSCroute_class, OSCroute_list); | ||
131 | class_addanything(OSCroute_class, OSCroute_anything); | ||
132 | class_addmethod(OSCroute_class, (t_method)OSCroute_version, gensym("version"), A_NULL, 0, 0); | ||
133 | class_sethelpsymbol(OSCroute_class, gensym("OSCroute-help.pd")); | ||
134 | |||
135 | /* | ||
136 | class_addmethod(OSCroute_class, (t_method)OSCroute_connect, | ||
137 | gensym("connect"), A_SYMBOL, A_FLOAT, 0); | ||
138 | class_addmethod(OSCroute_class, (t_method)OSCroute_disconnect, | ||
139 | gensym("disconnect"), 0); | ||
140 | class_addmethod(OSCroute_class, (t_method)OSCroute_send, gensym("send"), | ||
141 | A_GIMME, 0); | ||
142 | */ | ||
143 | /* ps_list = gensym("list"); */ | ||
144 | /* ps_complain = gensym("complain"); */ | ||
145 | ps_emptySymbol = gensym(""); | ||
146 | |||
147 | post("OSCroute object version " OSC_ROUTE_VERSION " by Matt Wright. pd: jdl Win32 raf."); | ||
148 | post("OSCroute Copyright © 1999 Regents of the University of California. All Rights Reserved."); | ||
149 | } | ||
150 | |||
151 | |||
152 | |||
153 | /* instance creation routine */ | ||
154 | |||
155 | void *OSCroute_new(t_symbol *s, int argc, t_atom *argv) | ||
156 | { | ||
157 | |||
158 | t_OSCroute *x = (t_OSCroute *)pd_new(OSCroute_class); // get memory for a new object & initialize | ||
159 | |||
160 | int i; //{{raf}} n not used | ||
161 | |||
162 | // EnterCallback(); | ||
163 | |||
164 | if (argc > MAX_NUM) { | ||
165 | post("* OSC-route: too many arguments: %ld (max %ld)", argc, MAX_NUM); | ||
166 | // ExitCallback(); | ||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | x->x_complainmode = 0; | ||
171 | x->x_num = 0; | ||
172 | for (i = 0; i < argc; ++i) { | ||
173 | if (argv[i].a_type == A_SYMBOL) { | ||
174 | if (argv[i].a_w.w_symbol->s_name[0] == '/') { | ||
175 | /* Now that's a nice prefix */ | ||
176 | x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name; | ||
177 | ++(x->x_num); | ||
178 | } else if (argv[i].a_w.w_symbol->s_name[0] == '#' && | ||
179 | argv[i].a_w.w_symbol->s_name[1] >= '1' && | ||
180 | argv[i].a_w.w_symbol->s_name[1] <= '9') { | ||
181 | /* The Max programmer is trying to make a patch that will be | ||
182 | a subpatch with arguments. We have to make an outlet for this | ||
183 | argument. */ | ||
184 | x->x_prefixes[i] = "dummy"; | ||
185 | ++(x->x_num); | ||
186 | } else { | ||
187 | /* Maybe this is an option we support */ | ||
188 | |||
189 | /* if (argv[i].a_w.w_sym == ps_complain) { */ | ||
190 | /* x->x_complainmode = 1; */ | ||
191 | /* } else { */ | ||
192 | /* post("* OSC-route: Unrecognized argument %s", argv[i].a_w.w_sym->s_name); */ | ||
193 | /* } */ | ||
194 | |||
195 | } | ||
196 | |||
197 | // no LONG | ||
198 | |||
199 | /* } else if (argv[i].a_type == A_FLOAD) { */ | ||
200 | /* // Convert to a numeral. Max ints are -2147483648 to 2147483647 */ | ||
201 | /* char *string = getbytes(12); */ | ||
202 | /* // I can't be bothered to plug this 12 byte memory leak */ | ||
203 | /* if (string == 0) { */ | ||
204 | /* post("* OSC-route: out of memory!"); */ | ||
205 | /* // ExitCallback(); */ | ||
206 | /* return 0; */ | ||
207 | /* } */ | ||
208 | /* sprintf(string, "%d", argv[i].a_w.w_long); */ | ||
209 | /* x->x_prefixes[i] = string; */ | ||
210 | /* ++(x->x_num); */ | ||
211 | |||
212 | } else if (argv[i].a_type == A_FLOAT) { | ||
213 | post("* OSC-route: float arguments are not OK."); | ||
214 | // ExitCallback(); | ||
215 | return 0; | ||
216 | } else { | ||
217 | post("* OSC-route: unrecognized argument type!"); | ||
218 | // ExitCallback(); | ||
219 | return 0; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | |||
224 | /* Have to create the outlets in reverse order */ | ||
225 | /* well, not in pd ? */ | ||
226 | // for (i = x->x_num-1; i >= 0; --i) { | ||
227 | // for (i = 0; i <= x->x_num-1; i++) { | ||
228 | for (i = 0; i <= x->x_num; i++) { | ||
229 | // x->x_outlets[i] = listout(x); | ||
230 | x->x_outlets[i] = outlet_new(&x->x_obj, &s_list); | ||
231 | } | ||
232 | |||
233 | // ExitCallback(); | ||
234 | return (x); | ||
235 | } | ||
236 | |||
237 | |||
238 | void OSCroute_version (t_OSCroute *x) { | ||
239 | // EnterCallback(); | ||
240 | post("OSCroute Version " OSC_ROUTE_VERSION | ||
241 | ", by Matt Wright. pd jdl, win32: raf.\nOSCroute Compiled " __TIME__ " " __DATE__); | ||
242 | // ExitCallback(); | ||
243 | } | ||
244 | |||
245 | /* I don't know why these aren't defined in some Max #include file. */ | ||
246 | #define ASSIST_INLET 1 | ||
247 | #define ASSIST_OUTLET 2 | ||
248 | |||
249 | void OSCroute_assist (t_OSCroute *x, void *box, long msg, long arg, | ||
250 | char *dstString) { | ||
251 | // EnterCallback(); | ||
252 | |||
253 | if (msg==ASSIST_INLET) { | ||
254 | sprintf(dstString, "Incoming OSC messages"); | ||
255 | } else if (msg==ASSIST_OUTLET) { | ||
256 | if (arg < 0 || arg >= x->x_num) { | ||
257 | post("* OSCroute_assist: No outlet corresponds to arg %ld!", arg); | ||
258 | } else { | ||
259 | sprintf(dstString, "subaddress + args for prefix %s", x->x_prefixes[arg]); | ||
260 | } | ||
261 | } else { | ||
262 | post("* OSCroute_assist: unrecognized message %ld", msg); | ||
263 | } | ||
264 | |||
265 | // ExitCallback(); | ||
266 | } | ||
267 | |||
268 | void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
269 | // EnterCallback(); | ||
270 | if (argc > 0 && argv[0].a_type == A_SYMBOL) { | ||
271 | /* Ignore the fact that this is a "list" */ | ||
272 | OSCroute_doanything(x, argv[0].a_w.w_symbol, argc-1, argv+1); | ||
273 | } else { | ||
274 | // post("* OSC-route: invalid list beginning with a number"); | ||
275 | // output on unmatched outlet jdl 20020908 | ||
276 | if (argv[0].a_type == A_FLOAT) { | ||
277 | outlet_float(x->x_outlets[x->x_num], argv[0].a_w.w_float); | ||
278 | } else { | ||
279 | post("* OSC-route: unrecognized atom type!"); | ||
280 | } | ||
281 | } | ||
282 | // ExitCallback(); | ||
283 | } | ||
284 | |||
285 | |||
286 | void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
287 | // EnterCallback(); | ||
288 | OSCroute_doanything(x, s, argc, argv); | ||
289 | // ExitCallback(); | ||
290 | } | ||
291 | |||
292 | |||
293 | |||
294 | |||
295 | void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
296 | char *pattern, *nextSlash; | ||
297 | int i; | ||
298 | int matchedAnything; | ||
299 | // post("*** OSCroute_anything(s %s, argc %ld)", s->s_name, (long) argc); | ||
300 | |||
301 | pattern = s->s_name; | ||
302 | if (pattern[0] != '/') { | ||
303 | post("* OSC-route: invalid message pattern %s does not begin with /", s->s_name); | ||
304 | outlet_anything(x->x_outlets[x->x_num], s, argc, argv); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | matchedAnything = 0; | ||
309 | |||
310 | nextSlash = NextSlashOrNull(pattern+1); | ||
311 | if (*nextSlash == '\0') { | ||
312 | /* last level of the address, so we'll output the argument list */ | ||
313 | |||
314 | |||
315 | #ifdef NULL_IS_DIFFERENT_FROM_BANG | ||
316 | if (argc==0) { | ||
317 | post("* OSC-route: why are you matching one level pattern %s with no args?", | ||
318 | pattern); | ||
319 | return; | ||
320 | } | ||
321 | #endif | ||
322 | |||
323 | for (i = 0; i < x->x_num; ++i) { | ||
324 | if (PatternMatch(pattern+1, x->x_prefixes[i]+1)) { | ||
325 | ++matchedAnything; | ||
326 | |||
327 | // I hate stupid Max lists with a special first element | ||
328 | if (argc == 0) { | ||
329 | outlet_bang(x->x_outlets[i]); | ||
330 | } else if (argv[0].a_type == A_SYMBOL) { | ||
331 | // Promote the symbol that was argv[0] to the special symbol | ||
332 | outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1); | ||
333 | } else if (argc > 1) { | ||
334 | // Multiple arguments starting with a number, so naturally we have | ||
335 | // to use a special function to output this "list", since it's what | ||
336 | // Max originally meant by "list". | ||
337 | outlet_list(x->x_outlets[i], 0L, argc, argv); | ||
338 | } else { | ||
339 | // There was only one argument, and it was a number, so we output it | ||
340 | // not as a list | ||
341 | /* if (argv[0].a_type == A_LONG) { */ | ||
342 | |||
343 | /* outlet_int(x->x_outlets[i], argv[0].a_w.w_long); */ | ||
344 | // } else | ||
345 | if (argv[0].a_type == A_FLOAT) { | ||
346 | |||
347 | outlet_float(x->x_outlets[i], argv[0].a_w.w_float); | ||
348 | } else { | ||
349 | post("* OSC-route: unrecognized atom type!"); | ||
350 | } | ||
351 | } | ||
352 | } | ||
353 | } | ||
354 | } else { | ||
355 | /* There's more address after this part, so our output list will begin with | ||
356 | the next slash. */ | ||
357 | t_symbol *restOfPattern = 0; /* avoid the gensym unless we have to output */ | ||
358 | char patternBegin[1000]; | ||
359 | |||
360 | |||
361 | /* Get the first level of the incoming pattern to match against all our prefixes */ | ||
362 | StrCopyUntilSlash(patternBegin, pattern+1); | ||
363 | |||
364 | for (i = 0; i < x->x_num; ++i) { | ||
365 | if (PatternMatch(patternBegin, x->x_prefixes[i]+1)) { | ||
366 | ++matchedAnything; | ||
367 | if (restOfPattern == 0) { | ||
368 | restOfPattern = gensym(nextSlash); | ||
369 | } | ||
370 | outlet_anything(x->x_outlets[i], restOfPattern, argc, argv); | ||
371 | } | ||
372 | } | ||
373 | } | ||
374 | |||
375 | if (x->x_complainmode) { | ||
376 | if (!matchedAnything) { | ||
377 | post("* OSC-route: pattern %s did not match any prefixes", pattern); | ||
378 | } | ||
379 | } | ||
380 | |||
381 | // output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908 | ||
382 | if (!matchedAnything) { | ||
383 | outlet_anything(x->x_outlets[x->x_num], s, argc, argv); | ||
384 | } | ||
385 | |||
386 | |||
387 | } | ||
388 | |||
389 | static char *NextSlashOrNull(char *p) { | ||
390 | while (*p != '/' && *p != '\0') { | ||
391 | p++; | ||
392 | } | ||
393 | return p; | ||
394 | } | ||
395 | |||
396 | static void StrCopyUntilSlash(char *target, const char *source) { | ||
397 | while (*source != '/' && *source != '\0') { | ||
398 | *target = *source; | ||
399 | ++target; | ||
400 | ++source; | ||
401 | } | ||
402 | *target = 0; | ||
403 | } | ||
404 | |||
405 | static int MyStrCopy(char *target, const char *source) { | ||
406 | int i = 0; | ||
407 | while (*source != '\0') { | ||
408 | *target = *source; | ||
409 | ++target; | ||
410 | ++source; | ||
411 | ++i; | ||
412 | } | ||
413 | *target = 0; | ||
414 | return i; | ||
415 | } | ||
416 | |||
417 | |||
418 | |||
419 | void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
420 | int i; | ||
421 | t_symbol *prefixSymbol = 0; | ||
422 | char prefixBuf[1000]; | ||
423 | char *endOfPrefix; | ||
424 | t_atom a[1]; | ||
425 | |||
426 | if (argc >= 1 && argv[0].a_type == A_SYMBOL) { | ||
427 | prefixSymbol = argv[0].a_w.w_symbol; | ||
428 | endOfPrefix = prefixBuf + MyStrCopy(prefixBuf, | ||
429 | prefixSymbol->s_name); | ||
430 | } else { | ||
431 | prefixSymbol = ps_emptySymbol; | ||
432 | prefixBuf[0] = '\0'; | ||
433 | endOfPrefix = prefixBuf; | ||
434 | } | ||
435 | |||
436 | |||
437 | for (i = 0; i < x->x_num; ++i) { | ||
438 | post("OSC: %s%s", prefixSymbol->s_name, x->x_prefixes[i]); | ||
439 | MyStrCopy(endOfPrefix, x->x_prefixes[i]); | ||
440 | SETSYMBOL(a, gensym(prefixBuf)); | ||
441 | outlet_anything(x->x_outlets[i], s, 1, a); | ||
442 | } | ||
443 | } | ||
444 | |||
445 | |||
446 | /* --------------------------------------------------- */ | ||
447 | |||
448 | |||
449 | |||
450 | static const char *theWholePattern; /* Just for warning messages */ | ||
451 | |||
452 | static Boolean MatchBrackets (const char *pattern, const char *test); | ||
453 | static Boolean MatchList (const char *pattern, const char *test); | ||
454 | |||
455 | Boolean PatternMatch (const char * pattern, const char * test) { | ||
456 | theWholePattern = pattern; | ||
457 | |||
458 | if (pattern == 0 || pattern[0] == 0) { | ||
459 | return test[0] == 0; | ||
460 | } | ||
461 | |||
462 | if (test[0] == 0) { | ||
463 | if (pattern[0] == '*') | ||
464 | return PatternMatch (pattern+1,test); | ||
465 | else | ||
466 | return FALSE; | ||
467 | } | ||
468 | |||
469 | switch (pattern[0]) { | ||
470 | case 0 : return test[0] == 0; | ||
471 | case '?' : return PatternMatch (pattern + 1, test + 1); | ||
472 | case '*' : | ||
473 | if (PatternMatch (pattern+1, test)) { | ||
474 | return TRUE; | ||
475 | } else { | ||
476 | return PatternMatch (pattern, test+1); | ||
477 | } | ||
478 | case ']' : | ||
479 | case '}' : | ||
480 | OSCWarning("Spurious %c in pattern \".../%s/...\"",pattern[0], theWholePattern); | ||
481 | return FALSE; | ||
482 | case '[' : | ||
483 | return MatchBrackets (pattern,test); | ||
484 | case '{' : | ||
485 | return MatchList (pattern,test); | ||
486 | case '\\' : | ||
487 | if (pattern[1] == 0) { | ||
488 | return test[0] == 0; | ||
489 | } else if (pattern[1] == test[0]) { | ||
490 | return PatternMatch (pattern+2,test+1); | ||
491 | } else { | ||
492 | return FALSE; | ||
493 | } | ||
494 | default : | ||
495 | if (pattern[0] == test[0]) { | ||
496 | return PatternMatch (pattern+1,test+1); | ||
497 | } else { | ||
498 | return FALSE; | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | |||
504 | /* we know that pattern[0] == '[' and test[0] != 0 */ | ||
505 | |||
506 | static Boolean MatchBrackets (const char *pattern, const char *test) { | ||
507 | Boolean result; | ||
508 | Boolean negated = FALSE; | ||
509 | const char *p = pattern; | ||
510 | |||
511 | if (pattern[1] == 0) { | ||
512 | OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern); | ||
513 | return FALSE; | ||
514 | } | ||
515 | |||
516 | if (pattern[1] == '!') { | ||
517 | negated = TRUE; | ||
518 | p++; | ||
519 | } | ||
520 | |||
521 | while (*p != ']') { | ||
522 | if (*p == 0) { | ||
523 | OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern); | ||
524 | return FALSE; | ||
525 | } | ||
526 | if (p[1] == '-' && p[2] != 0) { | ||
527 | if (test[0] >= p[0] && test[0] <= p[2]) { | ||
528 | result = !negated; | ||
529 | goto advance; | ||
530 | } | ||
531 | } | ||
532 | if (p[0] == test[0]) { | ||
533 | result = !negated; | ||
534 | goto advance; | ||
535 | } | ||
536 | p++; | ||
537 | } | ||
538 | |||
539 | result = negated; | ||
540 | |||
541 | advance: | ||
542 | |||
543 | if (!result) | ||
544 | return FALSE; | ||
545 | |||
546 | while (*p != ']') { | ||
547 | if (*p == 0) { | ||
548 | OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern); | ||
549 | return FALSE; | ||
550 | } | ||
551 | p++; | ||
552 | } | ||
553 | |||
554 | return PatternMatch (p+1,test+1); | ||
555 | } | ||
556 | |||
557 | static Boolean MatchList (const char *pattern, const char *test) { | ||
558 | |||
559 | const char *restOfPattern, *tp = test; | ||
560 | |||
561 | |||
562 | for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++) { | ||
563 | if (*restOfPattern == 0) { | ||
564 | OSCWarning("Unterminated { in pattern \".../%s/...\"", theWholePattern); | ||
565 | return FALSE; | ||
566 | } | ||
567 | } | ||
568 | |||
569 | restOfPattern++; /* skip close curly brace */ | ||
570 | |||
571 | |||
572 | pattern++; /* skip open curly brace */ | ||
573 | |||
574 | while (1) { | ||
575 | |||
576 | if (*pattern == ',') { | ||
577 | if (PatternMatch (restOfPattern, tp)) { | ||
578 | return TRUE; | ||
579 | } else { | ||
580 | tp = test; | ||
581 | ++pattern; | ||
582 | } | ||
583 | } else if (*pattern == '}') { | ||
584 | return PatternMatch (restOfPattern, tp); | ||
585 | } else if (*pattern == *tp) { | ||
586 | ++pattern; | ||
587 | ++tp; | ||
588 | } else { | ||
589 | tp = test; | ||
590 | while (*pattern != ',' && *pattern != '}') { | ||
591 | pattern++; | ||
592 | } | ||
593 | if (*pattern == ',') { | ||
594 | pattern++; | ||
595 | } | ||
596 | } | ||
597 | } | ||
598 | |||
599 | } | ||
600 | |||
601 | |||
602 | |||
603 | /* | ||
604 | Written by Adrian Freed, The Center for New Music and Audio Technologies, | ||
605 | University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04 | ||
606 | The Regents of the University of California (Regents). | ||
607 | |||
608 | Permission to use, copy, modify, distribute, and distribute modified versions | ||
609 | of this software and its documentation without fee and without a signed | ||
610 | licensing agreement, is hereby granted, provided that the above copyright | ||
611 | notice, this paragraph and the following two paragraphs appear in all copies, | ||
612 | modifications, and distributions. | ||
613 | |||
614 | IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, | ||
615 | SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING | ||
616 | OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS | ||
617 | BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
618 | |||
619 | REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | ||
620 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
621 | PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED | ||
622 | HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE | ||
623 | MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | ||
624 | |||
625 | |||
626 | The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl | ||
627 | */ | ||
628 | |||
629 | /* OSC-route.c | ||
630 | Max object for OSC-style dispatching | ||
631 | |||
632 | To-do: | ||
633 | |||
634 | Match a pattern against a pattern? | ||
635 | Declare outlet types / distinguish leaf nodes from other children | ||
636 | More sophisticated (2-pass?) allmessages scheme | ||
637 | set message? | ||
638 | |||
639 | |||
640 | pd | ||
641 | ------------- | ||
642 | -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 | ||
643 | |||
644 | |||
645 | */ | ||
646 | |||
647 | #ifdef WIN32 | ||
648 | #include <stdlib.h> | ||
649 | #include <string.h> | ||
650 | #endif | ||
651 | #ifdef __APPLE__ | ||
652 | #include <stdio.h> | ||
653 | #endif | ||
654 | #ifdef UNIX | ||
655 | #include <stdio.h> | ||
656 | #endif | ||
657 | |||
658 | /* structure definition of your object */ | ||
659 | |||
660 | #define MAX_NUM 20 | ||
661 | #define OSC_ROUTE_VERSION "1.05" | ||
662 | #define OSCWarning(x...) post(x) | ||
663 | |||
664 | /* the required include files */ | ||
665 | #include "m_pd.h" | ||
666 | |||
667 | |||
668 | #ifndef TRUE | ||
669 | typedef int Boolean; | ||
670 | #define TRUE 1 | ||
671 | #define FALSE 0 | ||
672 | #endif | ||
673 | |||
674 | |||
675 | /* Fixed byte width types */ | ||
676 | typedef int int4; /* 4 byte int */ | ||
677 | |||
678 | Boolean PatternMatch (const char *pattern, const char *test); | ||
679 | |||
680 | |||
681 | |||
682 | /* Version 1.04: Allows #1 thru #9 as typed-in arguments | ||
683 | Version 1.05: Allows "list" messages as well as "message" messages. | ||
684 | */ | ||
685 | |||
686 | static t_class *OSCroute_class; | ||
687 | |||
688 | typedef struct _OSCroute | ||
689 | { | ||
690 | t_object x_obj; // required header | ||
691 | t_int x_num; // Number of address prefixes we store | ||
692 | t_int x_complainmode; // Do we print a message if no match? | ||
693 | t_int x_sendmode; // use pd internal sends instead of outlets | ||
694 | char *x_prefixes[MAX_NUM]; | ||
695 | void *x_outlets[MAX_NUM+1]; | ||
696 | } t_OSCroute; | ||
697 | |||
698 | t_symbol *ps_list, *ps_complain, *ps_emptySymbol; | ||
699 | |||
700 | /* prototypes */ | ||
701 | |||
702 | void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
703 | void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
704 | void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
705 | /* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */ | ||
706 | void *OSCroute_new(t_symbol *s, int argc, t_atom *argv); | ||
707 | void OSCroute_version (t_OSCroute *x); | ||
708 | /* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */ | ||
709 | /* char *dstString); */ | ||
710 | void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); | ||
711 | |||
712 | static char *NextSlashOrNull(char *p); | ||
713 | static void StrCopyUntilSlash(char *target, const char *source); | ||
714 | |||
715 | |||
716 | // free | ||
717 | static void OSCroute_free(t_OSCroute *x) | ||
718 | { | ||
719 | // freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec)); | ||
720 | } | ||
721 | |||
722 | /* initialization routine */ | ||
723 | |||
724 | // setup | ||
725 | #ifdef WIN32 | ||
726 | OSC_API void OSCroute_setup(void) { | ||
727 | #else | ||
728 | void OSCroute_setup(void) { | ||
729 | #endif | ||
730 | OSCroute_class = class_new(gensym("OSCroute"), (t_newmethod)OSCroute_new, | ||
731 | (t_method)OSCroute_free,sizeof(t_OSCroute), 0, A_GIMME, 0); | ||
732 | class_addlist(OSCroute_class, OSCroute_list); | ||
733 | class_addanything(OSCroute_class, OSCroute_anything); | ||
734 | class_addmethod(OSCroute_class, (t_method)OSCroute_version, gensym("version"), A_NULL, 0, 0); | ||
735 | class_sethelpsymbol(OSCroute_class, gensym("OSCroute-help.pd")); | ||
736 | |||
737 | /* | ||
738 | class_addmethod(OSCroute_class, (t_method)OSCroute_connect, | ||
739 | gensym("connect"), A_SYMBOL, A_FLOAT, 0); | ||
740 | class_addmethod(OSCroute_class, (t_method)OSCroute_disconnect, | ||
741 | gensym("disconnect"), 0); | ||
742 | class_addmethod(OSCroute_class, (t_method)OSCroute_send, gensym("send"), | ||
743 | A_GIMME, 0); | ||
744 | */ | ||
745 | /* ps_list = gensym("list"); */ | ||
746 | /* ps_complain = gensym("complain"); */ | ||
747 | ps_emptySymbol = gensym(""); | ||
748 | |||
749 | post("OSCroute object version " OSC_ROUTE_VERSION " by Matt Wright. pd: jdl Win32 raf."); | ||
750 | post("OSCroute Copyright © 1999 Regents of the University of California. All Rights Reserved."); | ||
751 | } | ||
752 | |||
753 | |||
754 | |||
755 | /* instance creation routine */ | ||
756 | |||
757 | void *OSCroute_new(t_symbol *s, int argc, t_atom *argv) | ||
758 | { | ||
759 | |||
760 | t_OSCroute *x = (t_OSCroute *)pd_new(OSCroute_class); // get memory for a new object & initialize | ||
761 | |||
762 | int i; //{{raf}} n not used | ||
763 | |||
764 | // EnterCallback(); | ||
765 | |||
766 | if (argc > MAX_NUM) { | ||
767 | post("* OSC-route: too many arguments: %ld (max %ld)", argc, MAX_NUM); | ||
768 | // ExitCallback(); | ||
769 | return 0; | ||
770 | } | ||
771 | |||
772 | x->x_complainmode = 0; | ||
773 | x->x_num = 0; | ||
774 | for (i = 0; i < argc; ++i) { | ||
775 | if (argv[i].a_type == A_SYMBOL) { | ||
776 | if (argv[i].a_w.w_symbol->s_name[0] == '/') { | ||
777 | /* Now that's a nice prefix */ | ||
778 | x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name; | ||
779 | ++(x->x_num); | ||
780 | } else if (argv[i].a_w.w_symbol->s_name[0] == '#' && | ||
781 | argv[i].a_w.w_symbol->s_name[1] >= '1' && | ||
782 | argv[i].a_w.w_symbol->s_name[1] <= '9') { | ||
783 | /* The Max programmer is trying to make a patch that will be | ||
784 | a subpatch with arguments. We have to make an outlet for this | ||
785 | argument. */ | ||
786 | x->x_prefixes[i] = "dummy"; | ||
787 | ++(x->x_num); | ||
788 | } else { | ||
789 | /* Maybe this is an option we support */ | ||
790 | |||
791 | /* if (argv[i].a_w.w_sym == ps_complain) { */ | ||
792 | /* x->x_complainmode = 1; */ | ||
793 | /* } else { */ | ||
794 | /* post("* OSC-route: Unrecognized argument %s", argv[i].a_w.w_sym->s_name); */ | ||
795 | /* } */ | ||
796 | |||
797 | } | ||
798 | |||
799 | // no LONG | ||
800 | |||
801 | /* } else if (argv[i].a_type == A_FLOAD) { */ | ||
802 | /* // Convert to a numeral. Max ints are -2147483648 to 2147483647 */ | ||
803 | /* char *string = getbytes(12); */ | ||
804 | /* // I can't be bothered to plug this 12 byte memory leak */ | ||
805 | /* if (string == 0) { */ | ||
806 | /* post("* OSC-route: out of memory!"); */ | ||
807 | /* // ExitCallback(); */ | ||
808 | /* return 0; */ | ||
809 | /* } */ | ||
810 | /* sprintf(string, "%d", argv[i].a_w.w_long); */ | ||
811 | /* x->x_prefixes[i] = string; */ | ||
812 | /* ++(x->x_num); */ | ||
813 | |||
814 | } else if (argv[i].a_type == A_FLOAT) { | ||
815 | post("* OSC-route: float arguments are not OK."); | ||
816 | // ExitCallback(); | ||
817 | return 0; | ||
818 | } else { | ||
819 | post("* OSC-route: unrecognized argument type!"); | ||
820 | // ExitCallback(); | ||
821 | return 0; | ||
822 | } | ||
823 | } | ||
824 | |||
825 | |||
826 | /* Have to create the outlets in reverse order */ | ||
827 | /* well, not in pd ? */ | ||
828 | // for (i = x->x_num-1; i >= 0; --i) { | ||
829 | // for (i = 0; i <= x->x_num-1; i++) { | ||
830 | for (i = 0; i <= x->x_num; i++) { | ||
831 | // x->x_outlets[i] = listout(x); | ||
832 | x->x_outlets[i] = outlet_new(&x->x_obj, &s_list); | ||
833 | } | ||
834 | |||
835 | // ExitCallback(); | ||
836 | return (x); | ||
837 | } | ||
838 | |||
839 | |||
840 | void OSCroute_version (t_OSCroute *x) { | ||
841 | // EnterCallback(); | ||
842 | post("OSCroute Version " OSC_ROUTE_VERSION | ||
843 | ", by Matt Wright. pd jdl, win32: raf.\nOSCroute Compiled " __TIME__ " " __DATE__); | ||
844 | // ExitCallback(); | ||
845 | } | ||
846 | |||
847 | /* I don't know why these aren't defined in some Max #include file. */ | ||
848 | #define ASSIST_INLET 1 | ||
849 | #define ASSIST_OUTLET 2 | ||
850 | |||
851 | void OSCroute_assist (t_OSCroute *x, void *box, long msg, long arg, | ||
852 | char *dstString) { | ||
853 | // EnterCallback(); | ||
854 | |||
855 | if (msg==ASSIST_INLET) { | ||
856 | sprintf(dstString, "Incoming OSC messages"); | ||
857 | } else if (msg==ASSIST_OUTLET) { | ||
858 | if (arg < 0 || arg >= x->x_num) { | ||
859 | post("* OSCroute_assist: No outlet corresponds to arg %ld!", arg); | ||
860 | } else { | ||
861 | sprintf(dstString, "subaddress + args for prefix %s", x->x_prefixes[arg]); | ||
862 | } | ||
863 | } else { | ||
864 | post("* OSCroute_assist: unrecognized message %ld", msg); | ||
865 | } | ||
866 | |||
867 | // ExitCallback(); | ||
868 | } | ||
869 | |||
870 | void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
871 | // EnterCallback(); | ||
872 | if (argc > 0 && argv[0].a_type == A_SYMBOL) { | ||
873 | /* Ignore the fact that this is a "list" */ | ||
874 | OSCroute_doanything(x, argv[0].a_w.w_symbol, argc-1, argv+1); | ||
875 | } else { | ||
876 | // post("* OSC-route: invalid list beginning with a number"); | ||
877 | // output on unmatched outlet jdl 20020908 | ||
878 | if (argv[0].a_type == A_FLOAT) { | ||
879 | outlet_float(x->x_outlets[x->x_num], argv[0].a_w.w_float); | ||
880 | } else { | ||
881 | post("* OSC-route: unrecognized atom type!"); | ||
882 | } | ||
883 | } | ||
884 | // ExitCallback(); | ||
885 | } | ||
886 | |||
887 | |||
888 | void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
889 | // EnterCallback(); | ||
890 | OSCroute_doanything(x, s, argc, argv); | ||
891 | // ExitCallback(); | ||
892 | } | ||
893 | |||
894 | |||
895 | |||
896 | |||
897 | void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
898 | char *pattern, *nextSlash; | ||
899 | int i; | ||
900 | int matchedAnything; | ||
901 | // post("*** OSCroute_anything(s %s, argc %ld)", s->s_name, (long) argc); | ||
902 | |||
903 | pattern = s->s_name; | ||
904 | if (pattern[0] != '/') { | ||
905 | post("* OSC-route: invalid message pattern %s does not begin with /", s->s_name); | ||
906 | outlet_anything(x->x_outlets[x->x_num], s, argc, argv); | ||
907 | return; | ||
908 | } | ||
909 | |||
910 | matchedAnything = 0; | ||
911 | |||
912 | nextSlash = NextSlashOrNull(pattern+1); | ||
913 | if (*nextSlash == '\0') { | ||
914 | /* last level of the address, so we'll output the argument list */ | ||
915 | |||
916 | |||
917 | #ifdef NULL_IS_DIFFERENT_FROM_BANG | ||
918 | if (argc==0) { | ||
919 | post("* OSC-route: why are you matching one level pattern %s with no args?", | ||
920 | pattern); | ||
921 | return; | ||
922 | } | ||
923 | #endif | ||
924 | |||
925 | for (i = 0; i < x->x_num; ++i) { | ||
926 | if (PatternMatch(pattern+1, x->x_prefixes[i]+1)) { | ||
927 | ++matchedAnything; | ||
928 | |||
929 | // I hate stupid Max lists with a special first element | ||
930 | if (argc == 0) { | ||
931 | outlet_bang(x->x_outlets[i]); | ||
932 | } else if (argv[0].a_type == A_SYMBOL) { | ||
933 | // Promote the symbol that was argv[0] to the special symbol | ||
934 | outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1); | ||
935 | } else if (argc > 1) { | ||
936 | // Multiple arguments starting with a number, so naturally we have | ||
937 | // to use a special function to output this "list", since it's what | ||
938 | // Max originally meant by "list". | ||
939 | outlet_list(x->x_outlets[i], 0L, argc, argv); | ||
940 | } else { | ||
941 | // There was only one argument, and it was a number, so we output it | ||
942 | // not as a list | ||
943 | /* if (argv[0].a_type == A_LONG) { */ | ||
944 | |||
945 | /* outlet_int(x->x_outlets[i], argv[0].a_w.w_long); */ | ||
946 | // } else | ||
947 | if (argv[0].a_type == A_FLOAT) { | ||
948 | |||
949 | outlet_float(x->x_outlets[i], argv[0].a_w.w_float); | ||
950 | } else { | ||
951 | post("* OSC-route: unrecognized atom type!"); | ||
952 | } | ||
953 | } | ||
954 | } | ||
955 | } | ||
956 | } else { | ||
957 | /* There's more address after this part, so our output list will begin with | ||
958 | the next slash. */ | ||
959 | t_symbol *restOfPattern = 0; /* avoid the gensym unless we have to output */ | ||
960 | char patternBegin[1000]; | ||
961 | |||
962 | |||
963 | /* Get the first level of the incoming pattern to match against all our prefixes */ | ||
964 | StrCopyUntilSlash(patternBegin, pattern+1); | ||
965 | |||
966 | for (i = 0; i < x->x_num; ++i) { | ||
967 | if (PatternMatch(patternBegin, x->x_prefixes[i]+1)) { | ||
968 | ++matchedAnything; | ||
969 | if (restOfPattern == 0) { | ||
970 | restOfPattern = gensym(nextSlash); | ||
971 | } | ||
972 | outlet_anything(x->x_outlets[i], restOfPattern, argc, argv); | ||
973 | } | ||
974 | } | ||
975 | } | ||
976 | |||
977 | if (x->x_complainmode) { | ||
978 | if (!matchedAnything) { | ||
979 | post("* OSC-route: pattern %s did not match any prefixes", pattern); | ||
980 | } | ||
981 | } | ||
982 | |||
983 | // output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908 | ||
984 | if (!matchedAnything) { | ||
985 | outlet_anything(x->x_outlets[x->x_num], s, argc, argv); | ||
986 | } | ||
987 | |||
988 | |||
989 | } | ||
990 | |||
991 | static char *NextSlashOrNull(char *p) { | ||
992 | while (*p != '/' && *p != '\0') { | ||
993 | p++; | ||
994 | } | ||
995 | return p; | ||
996 | } | ||
997 | |||
998 | static void StrCopyUntilSlash(char *target, const char *source) { | ||
999 | while (*source != '/' && *source != '\0') { | ||
1000 | *target = *source; | ||
1001 | ++target; | ||
1002 | ++source; | ||
1003 | } | ||
1004 | *target = 0; | ||
1005 | } | ||
1006 | |||
1007 | static int MyStrCopy(char *target, const char *source) { | ||
1008 | int i = 0; | ||
1009 | while (*source != '\0') { | ||
1010 | *target = *source; | ||
1011 | ++target; | ||
1012 | ++source; | ||
1013 | ++i; | ||
1014 | } | ||
1015 | *target = 0; | ||
1016 | return i; | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | |||
1021 | void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { | ||
1022 | int i; | ||
1023 | t_symbol *prefixSymbol = 0; | ||
1024 | char prefixBuf[1000]; | ||
1025 | char *endOfPrefix; | ||
1026 | t_atom a[1]; | ||
1027 | |||
1028 | if (argc >= 1 && argv[0].a_type == A_SYMBOL) { | ||
1029 | prefixSymbol = argv[0].a_w.w_symbol; | ||
1030 | endOfPrefix = prefixBuf + MyStrCopy(prefixBuf, | ||
1031 | prefixSymbol->s_name); | ||
1032 | } else { | ||
1033 | prefixSymbol = ps_emptySymbol; | ||
1034 | prefixBuf[0] = '\0'; | ||
1035 | endOfPrefix = prefixBuf; | ||
1036 | } | ||
1037 | |||
1038 | |||
1039 | for (i = 0; i < x->x_num; ++i) { | ||
1040 | post("OSC: %s%s", prefixSymbol->s_name, x->x_prefixes[i]); | ||
1041 | MyStrCopy(endOfPrefix, x->x_prefixes[i]); | ||
1042 | SETSYMBOL(a, gensym(prefixBuf)); | ||
1043 | outlet_anything(x->x_outlets[i], s, 1, a); | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | |||
1048 | /* --------------------------------------------------- */ | ||
1049 | |||
1050 | |||
1051 | |||
1052 | static const char *theWholePattern; /* Just for warning messages */ | ||
1053 | |||
1054 | static Boolean MatchBrackets (const char *pattern, const char *test); | ||
1055 | static Boolean MatchList (const char *pattern, const char *test); | ||
1056 | |||
1057 | Boolean PatternMatch (const char * pattern, const char * test) { | ||
1058 | theWholePattern = pattern; | ||
1059 | |||
1060 | if (pattern == 0 || pattern[0] == 0) { | ||
1061 | return test[0] == 0; | ||
1062 | } | ||
1063 | |||
1064 | if (test[0] == 0) { | ||
1065 | if (pattern[0] == '*') | ||
1066 | return PatternMatch (pattern+1,test); | ||
1067 | else | ||
1068 | return FALSE; | ||
1069 | } | ||
1070 | |||
1071 | switch (pattern[0]) { | ||
1072 | case 0 : return test[0] == 0; | ||
1073 | case '?' : return PatternMatch (pattern + 1, test + 1); | ||
1074 | case '*' : | ||
1075 | if (PatternMatch (pattern+1, test)) { | ||
1076 | return TRUE; | ||
1077 | } else { | ||
1078 | return PatternMatch (pattern, test+1); | ||
1079 | } | ||
1080 | case ']' : | ||
1081 | case '}' : | ||
1082 | OSCWarning("Spurious %c in pattern \".../%s/...\"",pattern[0], theWholePattern); | ||
1083 | return FALSE; | ||
1084 | case '[' : | ||
1085 | return MatchBrackets (pattern,test); | ||
1086 | case '{' : | ||
1087 | return MatchList (pattern,test); | ||
1088 | case '\\' : | ||
1089 | if (pattern[1] == 0) { | ||
1090 | return test[0] == 0; | ||
1091 | } else if (pattern[1] == test[0]) { | ||
1092 | return PatternMatch (pattern+2,test+1); | ||
1093 | } else { | ||
1094 | return FALSE; | ||
1095 | } | ||
1096 | default : | ||
1097 | if (pattern[0] == test[0]) { | ||
1098 | return PatternMatch (pattern+1,test+1); | ||
1099 | } else { | ||
1100 | return FALSE; | ||
1101 | } | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | |||
1106 | /* we know that pattern[0] == '[' and test[0] != 0 */ | ||
1107 | |||
1108 | static Boolean MatchBrackets (const char *pattern, const char *test) { | ||
1109 | Boolean result; | ||
1110 | Boolean negated = FALSE; | ||
1111 | const char *p = pattern; | ||
1112 | |||
1113 | if (pattern[1] == 0) { | ||
1114 | OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern); | ||
1115 | return FALSE; | ||
1116 | } | ||
1117 | |||
1118 | if (pattern[1] == '!') { | ||
1119 | negated = TRUE; | ||
1120 | p++; | ||
1121 | } | ||
1122 | |||
1123 | while (*p != ']') { | ||
1124 | if (*p == 0) { | ||
1125 | OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern); | ||
1126 | return FALSE; | ||
1127 | } | ||
1128 | if (p[1] == '-' && p[2] != 0) { | ||
1129 | if (test[0] >= p[0] && test[0] <= p[2]) { | ||
1130 | result = !negated; | ||
1131 | goto advance; | ||
1132 | } | ||
1133 | } | ||
1134 | if (p[0] == test[0]) { | ||
1135 | result = !negated; | ||
1136 | goto advance; | ||
1137 | } | ||
1138 | p++; | ||
1139 | } | ||
1140 | |||
1141 | result = negated; | ||
1142 | |||
1143 | advance: | ||
1144 | |||
1145 | if (!result) | ||
1146 | return FALSE; | ||
1147 | |||
1148 | while (*p != ']') { | ||
1149 | if (*p == 0) { | ||
1150 | OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern); | ||
1151 | return FALSE; | ||
1152 | } | ||
1153 | p++; | ||
1154 | } | ||
1155 | |||
1156 | return PatternMatch (p+1,test+1); | ||
1157 | } | ||
1158 | |||
1159 | static Boolean MatchList (const char *pattern, const char *test) { | ||
1160 | |||
1161 | const char *restOfPattern, *tp = test; | ||
1162 | |||
1163 | |||
1164 | for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++) { | ||
1165 | if (*restOfPattern == 0) { | ||
1166 | OSCWarning("Unterminated { in pattern \".../%s/...\"", theWholePattern); | ||
1167 | return FALSE; | ||
1168 | } | ||
1169 | } | ||
1170 | |||
1171 | restOfPattern++; /* skip close curly brace */ | ||
1172 | |||
1173 | |||
1174 | pattern++; /* skip open curly brace */ | ||
1175 | |||
1176 | while (1) { | ||
1177 | |||
1178 | if (*pattern == ',') { | ||
1179 | if (PatternMatch (restOfPattern, tp)) { | ||
1180 | return TRUE; | ||
1181 | } else { | ||
1182 | tp = test; | ||
1183 | ++pattern; | ||
1184 | } | ||
1185 | } else if (*pattern == '}') { | ||
1186 | return PatternMatch (restOfPattern, tp); | ||
1187 | } else if (*pattern == *tp) { | ||
1188 | ++pattern; | ||
1189 | ++tp; | ||
1190 | } else { | ||
1191 | tp = test; | ||
1192 | while (*pattern != ',' && *pattern != '}') { | ||
1193 | pattern++; | ||
1194 | } | ||
1195 | if (*pattern == ',') { | ||
1196 | pattern++; | ||
1197 | } | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | } | ||
1202 | |||
1203 | |||
1204 | |||