/*
* regex: regular expression pattern matcher
*
* (c) 1999-2011 IOhannes m zmölnig, forum::für::umläute, institute of electronic music and acoustics (iem)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "zexy.h"
#ifdef HAVE_REGEX_H
# include
# include
# include
#endif
# define NUM_REGMATCHES 10
/*
* regex : see whether a regular expression matches the given symbol
*/
/* ------------------------- regex ------------------------------- */
/* match a regular expression against a string */
static t_class *regex_class;
typedef struct _regex {
t_object x_obj;
#ifdef HAVE_REGEX_H
char *x_regexstring; /* the uncompiled regular expression */
int x_regexstringlength;
regex_t *x_regexp;
int x_matchnum;
int x_flags; /* flags for the regex-compiler; REG_EXTENDED is always enabled */
#endif
t_outlet*x_outResult;
t_outlet*x_outDetails;
t_outlet*x_outNumDetails;
} t_regex;
#ifdef HAVE_REGEX_H
static char*regex_l2s(int *reslen, t_symbol*s, int argc, t_atom*argv)
{
char *result = 0;
int pos=0, i=0;
t_atom*ap;
int length=0;
if(reslen) {
*reslen=length;
}
/* 1st get the length of the symbol */
if(s) {
length+=strlen(s->s_name);
} else {
length-=1;
}
length+=argc;
i=argc;
ap=argv;
while(i--) {
char buffer[MAXPDSTRING];
int len=0;
if(A_SYMBOL==ap->a_type) {
len=strlen(ap->a_w.w_symbol->s_name);
} else {
atom_string(ap, buffer, MAXPDSTRING);
len=strlen(buffer);
}
length+=len;
ap++;
}
if(length<=0) {
return(0);
}
result = (char*)getbytes((length+1)*sizeof(char));
if (s) {
char *buf = s->s_name;
strcpy(result+pos, buf);
pos+=strlen(buf);
if(i) {
strcpy(result+pos, " ");
pos += 1;
}
}
ap=argv;
i=argc;
while(i--) {
if(A_SYMBOL==ap->a_type) {
strcpy(result+pos, ap->a_w.w_symbol->s_name);
pos+= strlen(ap->a_w.w_symbol->s_name);
} else {
char buffer[MAXPDSTRING];
atom_string(ap, buffer, MAXPDSTRING);
strcpy(result+pos, buffer);
pos += strlen(buffer);
}
ap++;
if(i) {
strcpy(result+pos, " ");
pos++;
}
}
result[length]=0;
if(reslen) {
*reslen=length;
}
return result;
}
static void regex_compile(t_regex *x)
{
int flags = x->x_flags;
flags |= REG_EXTENDED;
if(0==x->x_regexstring || 0==x->x_regexstringlength) {
pd_error(x, "[regex]: no regular expression given");
return;
}
if(x->x_regexp) {
regfree(x->x_regexp);
freebytes(x->x_regexp, sizeof(t_regex));
x->x_regexp=0;
}
x->x_regexp=(regex_t*)getbytes(sizeof(t_regex));
if(regcomp(x->x_regexp, x->x_regexstring, flags)) {
pd_error(x, "[regex]: invalid regular expression: %s", x->x_regexstring);
if(x->x_regexp) {
freebytes(x->x_regexp, sizeof(t_regex));
}
x->x_regexp=0;
}
}
#endif
static void regex_case(t_regex *x, t_float f)
{
#if HAVE_REGEX_H
if(f>0.f) {
x->x_flags |= REG_ICASE;
} else {
x->x_flags ^= REG_ICASE;
}
regex_compile(x);
#endif
}
static void regex_regex(t_regex *x, t_symbol*s, int argc, t_atom*argv)
{
#ifdef HAVE_REGEX_H
char*result=0;
int length=0;
result=regex_l2s(&length, 0, argc, argv);
if(0==result || 0==length) {
pd_error(x, "[regex]: no regular expression given");
return;
}
if(x->x_regexstring) {
freebytes(x->x_regexstring, x->x_regexstringlength);
x->x_regexstring=0;
x->x_regexstringlength=0;
}
x->x_regexstring=result;
x->x_regexstringlength=length;
regex_compile(x);
#endif
}
/* compare the given list as string with the precompiled regex */
static void regex_symbol(t_regex *x, t_symbol *s, int argc, t_atom*argv)
{
#ifdef HAVE_REGEX_H
char*teststring=0;
int length=0;
int num_matches=x->x_matchnum;
regmatch_t*match=(regmatch_t*)getbytes(sizeof(regmatch_t)*num_matches);
t_atom*ap=(t_atom*)getbytes(sizeof(t_atom)*(3*num_matches));
int err=0;
if(!x->x_regexp) {
pd_error(x, "[regex]: no regular expression!");
goto cleanup;
}
teststring=regex_l2s(&length, 0, argc, argv);
if(!teststring||!length) {
pd_error(x, "[regex]: cannot evaluate string");
goto cleanup;
}
/* do the actual comparing against the regex */
err=regexec(x->x_regexp, teststring, num_matches, match, 0);
if(teststring) {
freebytes(teststring, length);
teststring=0;
}
if(err) { /* NO match */
if(match) {
freebytes(match, sizeof(regmatch_t)*num_matches);
match=0;
}
outlet_float(x->x_outResult, 0.f);
} else { /* match! */
int num_results=0;
int i=0;
t_atom*ap2=ap;
for(i=0; i0 && (match[i].rm_so==match[i-1].rm_so)
&& (match[i].rm_eo==match[i-1].rm_eo)) {
/* duplicate matches */
} else {
SETFLOAT(ap2+0, (t_float)i);
SETFLOAT(ap2+1, (t_float)match[i].rm_so);
SETFLOAT(ap2+2, (t_float)match[i].rm_eo);
ap2+=3;
num_results++;
}
}
}
if(match) {
freebytes(match, sizeof(regmatch_t)*num_matches);
match=0;
}
outlet_float(x->x_outNumDetails, (t_float)num_results);
for(i=0; ix_outDetails, gensym("list"), 3, ap+(i*3));
}
outlet_float(x->x_outResult, 1.f);
}
cleanup:
if(teststring) {
freebytes(teststring, length);
}
if(match) {
freebytes(match, sizeof(regmatch_t)*num_matches);
}
if(ap) {
freebytes(ap, sizeof(t_atom)*(1+2*num_matches));
}
#endif
}
static void *regex_new(t_symbol *s, int argc, t_atom*argv)
{
t_regex *x = (t_regex *)pd_new(regex_class);
inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("symbol"), gensym("regex"));
x->x_outResult=outlet_new(&x->x_obj, 0);
x->x_outDetails=outlet_new(&x->x_obj, gensym("list"));
x->x_outNumDetails=outlet_new(&x->x_obj, gensym("float"));
#ifdef HAVE_REGEX_H
x->x_flags=0;
x->x_regexstring=0;
x->x_regexstringlength=0;
x->x_regexp=0;
x->x_matchnum=NUM_REGMATCHES;
if(argc) {
regex_regex(x, gensym(""), argc, argv);
} else {
t_atom a;
SETSYMBOL(&a, gensym(".*"));
regex_regex(x, 0, 1, &a);
}
#else
error("[regex] non-functional: compiled without regex-support!");
#endif
return (x);
}
static void regex_free(t_regex *x)
{
#ifdef HAVE_REGEX_H
if(x->x_regexstring) {
freebytes(x->x_regexstring, x->x_regexstringlength);
x->x_regexstring=0;
x->x_regexstringlength=0;
}
if(x->x_regexp) {
regfree(x->x_regexp);
freebytes(x->x_regexp, sizeof(t_regex));
x->x_regexp=0;
}
#endif
}
static void regex_help(t_regex*x)
{
post("\n"HEARTSYMBOL" regex\t\t:: test the input whether it matches a regular expression");
}
void regex_setup(void)
{
regex_class = class_new(gensym("regex"), (t_newmethod)regex_new,
(t_method)regex_free, sizeof(t_regex), 0, A_GIMME, 0);
class_addlist (regex_class, regex_symbol);
class_addmethod(regex_class, (t_method)regex_regex, gensym("regex"),
A_GIMME, 0);
class_addmethod(regex_class, (t_method)regex_case, gensym("case"), A_FLOAT,
0);
class_addmethod(regex_class, (t_method)regex_help, gensym("help"), A_NULL);
zexy_register("regex");
}