aboutsummaryrefslogtreecommitdiff
path: root/matches.c
blob: 13c3b1d12634ff5926857102b567ac45b0400d0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/**
 * The [matches] class in Pure Data is an external to verify that a subject 
 * matches a Perl-compatible regular expression mask.
 * 
 * http://alexandre.quessy.net
 * 
 * @author Alexandre Quessy
 * @c GNU General Public License 2006
 */
 
#include "m_pd.h"
#include <string.h>
#include <stdlib.h>
#include <pcre.h>

#define CAPTUREVECTORSIZE 30 /* multiple of 3 */
#define MATCHES_ERROR_PREFIX "[matches]: "
#define MATCHES_FALSE_MASK "xxxxxxxxxxxxxxxxxxxxxx"
#define MATCHES_DECIMAL_PRECISION_SIZE_T 4

/** variables of the pd object */
typedef struct matches {
  t_object x_ob; /* contient inlets et outlets */
  t_outlet *x_outlet;
  pcre *regex; /* mask */
} t_matches;

/** functions */
char *_str_replace(const char search, const char replace, const char *subject);
int _isMatching(t_matches *x, char *text);
void _compile_pattern(t_matches *x, const char *pattern);


/** left inlet: subject */
void matches_symbol0(t_matches *x, t_symbol *arg) {
  int isOk =_isMatching(x, arg->s_name);
  outlet_float(x->x_outlet, isOk);
}

/* left inlet: subject (when a float) 
void matches_float0(t_matches *x, t_floatarg arg) {
  char *str_float = sprintf("%f0", arg);
  int isOk =_isMatching(x, str_float);
  outlet_float(x->x_outlet, isOk);
}
*/


/** right inlet : mask */
void matches_symbol1(t_matches *x, t_symbol *arg) {
  _compile_pattern(x, arg->s_name);
}

/* right inlet 
void matches_float1(t_matches *x, t_floatarg arg) {
  char *str_float = sprintf("%f0", arg);
  _compile_pattern(x, str_float);
}
*/
t_class *matches_class;

/** constructor */
void *matches_new(t_symbol *selector, int argc, t_atom *argv) {
  int is_valid = 0;
  t_matches *x = (t_matches *) pd_new(matches_class);
  
  /* set the mask */
  if (argc < 1) {
    post("%s No mask given as argument. Please supply one as message.\n", MATCHES_ERROR_PREFIX);
  } else {
    /* if (argv[0].a_type == A_SYMBOL) { */
      t_symbol *tmp = atom_getsymbol(&argv[0]);
      _compile_pattern(x, tmp->s_name);
      is_valid = 1;
      /*
    } else {
      post("%s Argument should be a symbol\n", MATCHES_ERROR_PREFIX);
    }
    */
  }
  if (is_valid == 0) {
    _compile_pattern(x, MATCHES_FALSE_MASK);
  }
  /* add inlets */
  inlet_new(&x->x_ob, &x->x_ob.ob_pd, gensym("symbol"), gensym("mask")); /* selecteur, renomme mask */
  
  /* add outlet */
  x->x_outlet = outlet_new(&x->x_ob, gensym("float"));
  return (void *)x;
}

/** setup */
void matches_setup(void) {
  matches_class = class_new(gensym("matches"), (t_newmethod) matches_new, 0, sizeof(t_matches), 0, A_GIMME, 0);
  class_addmethod(matches_class, (t_method) matches_symbol0, gensym("symbol"), A_SYMBOL, 0);
  class_addmethod(matches_class, (t_method) matches_symbol1, gensym("mask"), A_SYMBOL, 0);
  /* 
  class_addmethod(matches_class, (t_method) matches_float0, gensym("float"), A_FLOAT, 0);
  class_addmethod(matches_class, (t_method) matches_float1, gensym("float"), A_FLOAT, 0);
  */
}



/*

matju aalex: pour les inlets acceptant plusieurs sélecteurs, check gridflow/bridge/puredata.c
matju aalex: ça fait: inlet_new(self->bself, &p->ob_pd, 0,0);
matju aalex: avec un proxy inlet
aalex ???? MiS : http://www.tigerdirect.ca/applications/SearchTools/item-details.asp?EdpNo=1068181&CatId=107
aalex ah
aalex matju  : il me faut absolument un proxy-inlet, donc
matju aalex: dans cet exemple, self->bself est d'un soustype de t_object* et &p->ob_pd est un t_pd*
matju aalex: je crois que oui
matju aalex: &p->ob_pd est en fait la même chose que (t_pd *)p, si je ne m'abuse

*/



/* ############################### functions ###################### */




/** clone of the PHP function */
char *_str_replace(const char search, const char replace, const char *subject) {
  int i, len;
  char *result = strdup(subject);
  len = strlen(result);
  for (i = 0; i <= len; i++) {
    if(result[i] == search) { 
      result[i] = replace;
    }
  }
  return result;
}

/** 
 * returns 1 if matches, 0 if not 
 * TODO use stdbool.h, I think
 */
int _isMatching(t_matches *x, char *text) { /* 2nd is const */
  int capturevector[CAPTUREVECTORSIZE];
  int rc;
  int i;
  int result = 0;
  
  rc = pcre_exec(x->regex, NULL, text, (int) strlen(text), 0, 0, capturevector, CAPTUREVECTORSIZE);
  
  if (rc < 0) {
    result = 0;
  } else {
    result = 1;
  }
  
  /* ok, a partir d'ici c'est different de l'autre exemple, sauf qu'on avait mis des parentheses dans le pattern */
  if (rc == 0) {
    rc = CAPTUREVECTORSIZE / 3;
    post("ovector only has room for %d captured substrings\n", rc - 1);
  }
  for (i = 0; i < rc; i++) {
    char *substring_start = text + capturevector[2 * i]; /* ovector */
    int substring_length = capturevector[2 * i + 1] - capturevector[2 * i];
    post("%2d matching: %.*s", i, substring_length, substring_start);
  }
  /* I think that from here I should clean up my memory */
  
  return result;
}

/* ############## begin class matches ####################### */

/** 
 * private method to set and compile the mask 
 * TODO : use pcre_malloc() and pcre_free()
 */
void _compile_pattern(t_matches *x, const char *pattern) {
  pcre *regex;
  const char *regex_error; 
  int erroroffset;
  char *mask = _str_replace('`', '\\', pattern);
  
  regex = pcre_compile(mask, 0, &regex_error, &erroroffset, NULL);
  if (regex == NULL) {
    post("%s Compilation failed at offset %d : %s", MATCHES_ERROR_PREFIX, erroroffset, regex_error);
    regex = pcre_compile(MATCHES_FALSE_MASK, 0, &regex_error, &erroroffset, NULL); /* will always return false if invalid pattern */
  } else {
    post("%s New PCRE mask: %s", MATCHES_ERROR_PREFIX, mask);
  }
  /* free(x->regex); */
  x->regex = regex;
}