summaryrefslogtreecommitdiff
path: root/apps/plugins/pdbox/PDa/extra/OSCroute.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/pdbox/PDa/extra/OSCroute.c')
-rw-r--r--apps/plugins/pdbox/PDa/extra/OSCroute.c1204
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/*
2Written by Adrian Freed, The Center for New Music and Audio Technologies,
3University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04
4The Regents of the University of California (Regents).
5
6Permission to use, copy, modify, distribute, and distribute modified versions
7of this software and its documentation without fee and without a signed
8licensing agreement, is hereby granted, provided that the above copyright
9notice, this paragraph and the following two paragraphs appear in all copies,
10modifications, and distributions.
11
12IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
13SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
14OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
15BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
17REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
20HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
21MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
22
23
24The 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
67typedef int Boolean;
68#define TRUE 1
69#define FALSE 0
70#endif
71
72
73/* Fixed byte width types */
74typedef int int4; /* 4 byte int */
75
76Boolean 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
84static t_class *OSCroute_class;
85
86typedef 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
96t_symbol *ps_list, *ps_complain, *ps_emptySymbol;
97
98/* prototypes */
99
100void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
101void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
102void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
103/* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */
104void *OSCroute_new(t_symbol *s, int argc, t_atom *argv);
105void OSCroute_version (t_OSCroute *x);
106/* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */
107/* char *dstString); */
108void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
109
110static char *NextSlashOrNull(char *p);
111static void StrCopyUntilSlash(char *target, const char *source);
112
113
114// free
115static 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
126void 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
155void *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
238void 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
249void 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
268void 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
286void 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
295void 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
389static char *NextSlashOrNull(char *p) {
390 while (*p != '/' && *p != '\0') {
391 p++;
392 }
393 return p;
394}
395
396static 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
405static 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
419void 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
450static const char *theWholePattern; /* Just for warning messages */
451
452static Boolean MatchBrackets (const char *pattern, const char *test);
453static Boolean MatchList (const char *pattern, const char *test);
454
455Boolean 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
506static 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
541advance:
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
557static 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/*
604Written by Adrian Freed, The Center for New Music and Audio Technologies,
605University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04
606The Regents of the University of California (Regents).
607
608Permission to use, copy, modify, distribute, and distribute modified versions
609of this software and its documentation without fee and without a signed
610licensing agreement, is hereby granted, provided that the above copyright
611notice, this paragraph and the following two paragraphs appear in all copies,
612modifications, and distributions.
613
614IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
615SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
616OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
617BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
618
619REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
620THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
621PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
622HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
623MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
624
625
626The 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
669typedef int Boolean;
670#define TRUE 1
671#define FALSE 0
672#endif
673
674
675/* Fixed byte width types */
676typedef int int4; /* 4 byte int */
677
678Boolean 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
686static t_class *OSCroute_class;
687
688typedef 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
698t_symbol *ps_list, *ps_complain, *ps_emptySymbol;
699
700/* prototypes */
701
702void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
703void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
704void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
705/* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */
706void *OSCroute_new(t_symbol *s, int argc, t_atom *argv);
707void OSCroute_version (t_OSCroute *x);
708/* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */
709/* char *dstString); */
710void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv);
711
712static char *NextSlashOrNull(char *p);
713static void StrCopyUntilSlash(char *target, const char *source);
714
715
716// free
717static 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
728void 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
757void *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
840void 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
851void 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
870void 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
888void 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
897void 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
991static char *NextSlashOrNull(char *p) {
992 while (*p != '/' && *p != '\0') {
993 p++;
994 }
995 return p;
996}
997
998static 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
1007static 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
1021void 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
1052static const char *theWholePattern; /* Just for warning messages */
1053
1054static Boolean MatchBrackets (const char *pattern, const char *test);
1055static Boolean MatchList (const char *pattern, const char *test);
1056
1057Boolean 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
1108static 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
1143advance:
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
1159static 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