1
/******************************************************
3
* zexy - implementation file
5
* copyleft (c) IOhannes m zm�lnig
7
* 1999:forum::f�r::uml�ute:2004
9
* institute of electronic music and acoustics (iem)
11
******************************************************
13
* license: GNU General Public License v.2
15
******************************************************/
17
/* LATER: add a creation argument to specify the initial search mode
19
* LATER: bind a "name" to the [matchbox] so several objects can share the same entries
20
* if no name is given at creation time, the entries are local only
22
* even LATER: dynamically bind to several searchlists (via "set" message)
28
#define MATCHBOX_EXACT 0
29
#define MATCHBOX_OSC 1
32
# include <sys/types.h>
34
#define MATCHBOX_REGEX 2
43
* matchbox : see whether a regular expression matches the given symbol
46
/* ------------------------- matchbox ------------------------------- */
48
/* match the atoms of 2 lists */
50
static t_class *matchbox_class;
53
typedef struct _listlist {
56
struct _listlist *next;
60
typedef struct _matchbox
65
unsigned int x_numlists;
70
t_outlet*x_outNumResults;
74
/* ----------- here comes some infrastructure stuff -------------- */
77
static t_listlist* addlistlist(t_listlist*list, int argc, t_atom*argv) {
78
t_listlist*ll=(t_listlist*)getbytes(sizeof(t_listlist));
82
ll->argv=(t_atom*)getbytes(argc*sizeof(t_atom));
83
memcpy(ll->argv, argv, argc*sizeof(t_atom));
90
while(0!=lp->next)lp=lp->next;
96
/* delete the _next_ element from the list */
97
static t_listlist* deletelistnext(t_listlist*list) {
100
if(!list || !list->next)return list; /* nothing to delete */
104
if(ll->argv)freebytes(ll->argv, ll->argc*sizeof(t_atom));
109
freebytes(ll, sizeof(t_listlist));
113
/* delete the entire list of lists */
114
static void clearlistlist(t_listlist*list) {
115
if(!list)return; /* nothing to delete */
117
list=deletelistnext(list);
121
/* -------------- here comes the matching algorithms ----------- */
124
static int atommatch_exact(t_atom*pattern, t_atom*atom) {
125
if(pattern->a_type==atom->a_type) {
126
switch(pattern->a_type) {
128
return atom_getfloat(pattern)==atom_getfloat(atom);
130
return atom_getsymbol(pattern)==atom_getsymbol(atom);
132
return pattern==atom;
135
/* post("types don't match!"); */
142
#ifdef MATCHBOX_OSC /* OSC */
145
OSC pattern matching code was written by Matt Wright,
146
The Center for New Music and Audio Technologies,
147
University of California, Berkeley. Copyright (c) 1998,99,2000,01,02,03,04
148
The Regents of the University of California (Regents).
150
Permission to use, copy, modify, distribute, and distribute modified versions
151
of this software and its documentation without fee and without a signed
152
licensing agreement, is hereby granted, provided that the above copyright
153
notice, this paragraph and the following two paragraphs appear in all copies,
154
modifications, and distributions.
156
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
157
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
158
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
159
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
161
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
162
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
163
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
164
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
165
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
167
The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl
170
#define OSCWarning post
171
static int OSC_MatchBrackets (const char *pattern, const char *test, const char*theWholePattern);
172
static int OSC_MatchList (const char *pattern, const char *test, const char*theWholePattern);
174
static int OSC_PatternMatch (const char * pattern, const char * test, const char*theWholePattern) {
175
if (pattern == 0 || pattern[0] == 0) {
180
if (pattern[0] == '*')
181
return OSC_PatternMatch (pattern+1,test, theWholePattern);
186
switch (pattern[0]) {
187
case 0 : return test[0] == 0;
188
case '?' : return OSC_PatternMatch (pattern + 1, test + 1, theWholePattern);
190
if (OSC_PatternMatch (pattern+1, test, theWholePattern)) {
193
return OSC_PatternMatch (pattern, test+1, theWholePattern);
197
z_verbose(1, "[matchbox]: spurious %c in OSC-pattern \".../%s/...\"",pattern[0], theWholePattern);
200
return OSC_MatchBrackets (pattern,test, theWholePattern);
202
return OSC_MatchList (pattern,test, theWholePattern);
204
if (pattern[1] == 0) {
206
} else if (pattern[1] == test[0]) {
207
return OSC_PatternMatch (pattern+2,test+1, theWholePattern);
212
if (pattern[0] == test[0]) {
213
return OSC_PatternMatch (pattern+1,test+1, theWholePattern);
220
/* we know that pattern[0] == '[' and test[0] != 0 */
222
static int OSC_MatchBrackets (const char *pattern, const char *test, const char*theWholePattern) {
225
const char *p = pattern;
227
if (pattern[1] == 0) {
228
z_verbose(1, "[matchbox]: unterminated [ in OSC-pattern \".../%s/...\"", theWholePattern);
232
if (pattern[1] == '!') {
239
z_verbose(1, "[matchbox]: unterminated [ in OSC-pattern \".../%s/...\"", theWholePattern);
242
if (p[1] == '-' && p[2] != 0) {
243
if (test[0] >= p[0] && test[0] <= p[2]) {
248
if (p[0] == test[0]) {
264
z_verbose(1, "[matchbox]: unterminated [ in OSC-pattern \".../%s/...\"", theWholePattern);
270
return OSC_PatternMatch (p+1,test+1, theWholePattern);
273
static int OSC_MatchList (const char *pattern, const char *test, const char* theWholePattern) {
275
const char *restOfPattern, *tp = test;
277
for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++) {
278
if (*restOfPattern == 0) {
279
z_verbose(1, "[matchbox]: unterminated { in OSC-pattern \".../%s/...\"", theWholePattern);
284
restOfPattern++; /* skip close curly brace */
287
pattern++; /* skip open curly brace */
291
if (*pattern == ',') {
292
if (OSC_PatternMatch (restOfPattern, tp, theWholePattern)) {
298
} else if (*pattern == '}') {
299
return OSC_PatternMatch (restOfPattern, tp, theWholePattern);
300
} else if (*pattern == *tp) {
305
while (*pattern != ',' && *pattern != '}') {
308
if (*pattern == ',') {
315
static int atommatch_osc(t_atom*pattern, t_atom*test) {
318
int pattern_size=0, test_size=0;
322
if(pattern->a_type==A_SYMBOL) {
323
s_pattern=pattern->a_w.w_symbol->s_name;
325
pattern_size=sizeof(char)*MAXPDSTRING;
326
s_pattern=(char*)getbytes(pattern_size);
327
atom_string(pattern, s_pattern, pattern_size);
329
if(test->a_type==A_SYMBOL) {
330
s_test=test->a_w.w_symbol->s_name;
332
test_size=sizeof(char)*MAXPDSTRING;
333
s_test=(char*)getbytes(test_size);
334
atom_string(test, s_test, test_size);
338
result = OSC_PatternMatch(s_pattern, s_test, s_pattern);
341
freebytes(s_pattern, pattern_size);
342
s_pattern=0; pattern_size=0;
345
freebytes(s_test, test_size);
346
s_test=0; test_size=0;
355
#ifdef MATCHBOX_REGEX
356
static int atommatch_regex(regex_t*pattern, t_atom*test) {
361
if(0==pattern)return FALSE;
362
if(0==test) return FALSE;
364
if(test->a_type==A_SYMBOL) {
365
s_test=test->a_w.w_symbol->s_name;
367
test_size=sizeof(char)*MAXPDSTRING;
368
s_test=(char*)getbytes(test_size);
369
atom_string(test, s_test, test_size);
372
result=!(regexec(pattern, s_test, 0, 0, 0));
375
freebytes(s_test, test_size);
376
s_test=0; test_size=0;
382
static int listmatch_regex(int p_argc, regex_t**pattern, int t_argc, t_atom*test) {
383
/* match the patterns to the test */
389
if(FALSE==atommatch_regex(*pattern++, test++)) {
397
static t_listlist*matchlistlist_regex(unsigned int*numresults, t_listlist*searchlist, int p_argc, t_atom*p_argv, int flags, int delete_results) {
398
regex_t**regexpressions=0;
399
t_listlist*matchinglist=0, *sl;
405
/* 1st compile the patterns */
406
regexpressions=(regex_t**)getbytes(sizeof(regex_t*)*p_argc);
407
for(i=0; i<p_argc; i++) {
410
t_atom*pattern=p_argv+i;
411
if(pattern->a_type==A_SYMBOL) {
412
s_pattern=pattern->a_w.w_symbol->s_name;
414
pattern_size=sizeof(char)*MAXPDSTRING;
415
s_pattern=(char*)getbytes(pattern_size);
416
atom_string(pattern, s_pattern, pattern_size);
418
regexpressions[i]=(regex_t*)getbytes(sizeof(regex_t));
419
if(regcomp(regexpressions[i], s_pattern, flags)) {
420
z_verbose(1, "[matchbox]: invalid regular expression: %s", s_pattern);
421
if(regexpressions[i])freebytes(regexpressions[i], sizeof(regex_t));
425
freebytes(s_pattern, pattern_size);
426
s_pattern=0; pattern_size=0;
430
/* match the patterns to the tests */
431
if(FALSE==delete_results) {
432
for(sl=searchlist; 0!=sl; sl=sl->next) {
433
if(TRUE==listmatch_regex(p_argc, regexpressions, sl->argc, sl->argv)) {
434
matchinglist=addlistlist(matchinglist, sl->argc, sl->argv);
438
} else if (TRUE==delete_results) {
439
/* yummy: delete matching lists! */
440
t_listlist*lastgood=searchlist;
441
for(sl=searchlist; 0!=sl; sl=sl->next) {
442
if(TRUE==listmatch_regex(p_argc, regexpressions, sl->argc, sl->argv)) {
443
matchinglist=addlistlist(matchinglist, sl->argc, sl->argv);
446
sl=deletelistnext(lastgood);
453
/* clear the patterns */
454
for(i=0; i<p_argc; i++) {
455
if(regexpressions[i]){
456
regfree(regexpressions[i]);
457
freebytes(regexpressions[i], sizeof(regex_t));
460
freebytes(regexpressions, sizeof(regex_t*)*p_argc);
462
/* return the result */
467
#endif /* MATCHBOX_REGEX */
473
static int matchbox_atommatch(t_atom*pattern, t_atom*atom, int mode) {
476
case MATCHBOX_EXACT: return atommatch_exact(pattern, atom);
478
case MATCHBOX_OSC : return atommatch_osc(pattern, atom);
481
return atommatch_exact(pattern, atom);
484
static int matchlist(int argc_pattern, t_atom*argv_pattern,
485
int argc, t_atom*argv, int mode) {
488
if(argc!=argc_pattern)
491
for(i=0; i<argc; i++) {
492
if(0==matchbox_atommatch(argv_pattern+i, argv+i, mode))
499
static t_listlist*matchlistlist(unsigned int*numresults, t_listlist*searchlist, int p_argc, t_atom*p_argv, int mode, int delete_results) {
501
t_listlist*matchinglist=0, *sl;
503
/* extra handling of regex matching (because we want to compile only once */
504
#ifdef MATCHBOX_REGEX
505
if(MATCHBOX_REGEX==mode) {
506
matchinglist=matchlistlist_regex(&num, searchlist, p_argc, p_argv, 0, delete_results);
508
#endif /* MATCHBOX_REGEX */
509
/* normal matching */
510
if(FALSE==delete_results) {
511
for(sl=searchlist->next; 0!=sl; sl=sl->next) {
512
if(matchlist(p_argc, p_argv, sl->argc, sl->argv, mode)) {
513
matchinglist=addlistlist(matchinglist, sl->argc, sl->argv);
517
} else if (TRUE==delete_results) {
518
/* yummy: delete matching lists! */
519
t_listlist*lastgood=searchlist;
520
for(sl=searchlist->next; 0!=sl; sl=sl->next) {
521
if(matchlist(p_argc, p_argv, sl->argc, sl->argv, mode)) {
522
matchinglist=addlistlist(matchinglist, sl->argc, sl->argv);
525
sl=deletelistnext(lastgood);
538
static void matchbox_list(t_matchbox*x, t_symbol*s, int argc, t_atom*argv) {
539
unsigned int results=0;
541
t_listlist*resultlist=matchlistlist(&results, x->x_lists, argc, argv, mode, FALSE);
542
t_listlist*dummylist;
544
outlet_float(x->x_outNumResults, (t_float)results);
546
for(dummylist=resultlist; 0!=dummylist; dummylist=dummylist->next)
547
outlet_list(x->x_outResult, &s_list, dummylist->argc, dummylist->argv);
550
static void matchbox_add(t_matchbox*x, t_symbol*s, int argc, t_atom*argv) {
551
/* 1st match, whether we already have this entry */
552
if(matchlistlist(0, x->x_lists, argc, argv, MATCHBOX_EXACT, FALSE)) {
553
/* already there, skip the rest */
554
z_verbose(1, "[matchbox]: refusing to add already existing list to buffer...");
558
/* 2nd if this is a new entry, add it */
559
x->x_lists=addlistlist(x->x_lists, argc, argv);
563
static void matchbox_delete(t_matchbox*x, t_symbol*s, int argc, t_atom*argv) {
564
unsigned int results=0;
566
t_listlist*resultlist=matchlistlist(&results, x->x_lists, argc, argv, mode, TRUE);
567
t_listlist*dummylist;
568
t_symbol*delsym=gensym("deleted");
570
x->x_numlists-=results;
572
outlet_float(x->x_outNumResults, (t_float)results);
574
for(dummylist=resultlist; 0!=dummylist; dummylist=dummylist->next)
575
outlet_anything(x->x_outResult, delsym, dummylist->argc, dummylist->argv);
578
static void matchbox_dump(t_matchbox*x) {
581
if(0==x->x_lists || 0==x->x_lists->next){
582
outlet_float(x->x_outNumResults, 0);
586
outlet_float(x->x_outNumResults, x->x_numlists);
588
for(lp=x->x_lists->next; 0!=lp; lp=lp->next)
590
outlet_list(x->x_outResult, &s_list, lp->argc, lp->argv);
595
static void matchbox_clear(t_matchbox*x) {
596
clearlistlist(x->x_lists);
601
static void matchbox_mode(t_matchbox*x, t_symbol*s) {
603
x->x_mode=MATCHBOX_EXACT;
604
else if (gensym("OSC")==s) {
606
x->x_mode=MATCHBOX_OSC;
608
pd_error(x, "[matchbox] has been compiled without 'OSC' support; ignoring your request");
609
#endif /* MATCHBOX_OSC */
610
} else if(gensym("regex")==s) {
611
#ifdef MATCHBOX_REGEX
612
x->x_mode=MATCHBOX_REGEX;
614
pd_error(x, "[matchbox] has been compiled without 'regex' support; ignoring your request");
615
#endif /* MATCHBOX_REGEX */
617
pd_error(x, "mode '%s' is unknown, switching to 'exact' mode", s->s_name);
618
x->x_mode=MATCHBOX_EXACT;
622
static void *matchbox_new(t_symbol *s, int argc, t_atom*argv)
624
t_matchbox *x = (t_matchbox *)pd_new(matchbox_class);
626
inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("symbol"), gensym("add"));
628
x->x_outResult =outlet_new(&x->x_obj, gensym("list"));
629
x->x_outNumResults=outlet_new(&x->x_obj, &s_float);
632
x->x_lists=(t_listlist*)getbytes(sizeof(t_listlist));
638
x->x_mode = MATCHBOX_EXACT;
640
if(argc && argv->a_type==A_SYMBOL) {
641
matchbox_mode(x, atom_getsymbol(argv));
648
static void matchbox_free(t_matchbox *x)
651
freebytes(x->x_lists, sizeof(t_listlist));
655
static void matchbox_help(t_matchbox*x)
657
post("\n%c matchbox\t\t:: find a list in a pool of lists", HEARTSYMBOL);
660
void matchbox_setup(void)
663
post("matchbox: OSC-pattern matching code (c) Matt Wright, CNMAT");
664
#endif /* MATCHBOX_OSC */
667
matchbox_class = class_new(gensym("matchbox"), (t_newmethod)matchbox_new,
668
(t_method)matchbox_free, sizeof(t_matchbox), 0, A_GIMME, 0);
670
class_addlist (matchbox_class, matchbox_list);
672
class_addmethod(matchbox_class, (t_method)matchbox_add, gensym("add"), A_GIMME, 0);
673
class_addmethod(matchbox_class, (t_method)matchbox_delete, gensym("delete"), A_GIMME, 0);
674
class_addmethod(matchbox_class, (t_method)matchbox_clear, gensym("clear"), A_NULL, 0);
675
class_addmethod(matchbox_class, (t_method)matchbox_dump, gensym("dump"), A_NULL);
677
class_addmethod(matchbox_class, (t_method)matchbox_mode, gensym("mode"), A_SYMBOL, 0);
679
class_addmethod(matchbox_class, (t_method)matchbox_help, gensym("help"), A_NULL);
680
zexy_register("matchbox");