1
|
/*__________________________________________________________________________
detox á extract value/content from tag-structured symbol
Copyright (C) 2003 - 2006 jan schacher
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, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
revised tree output 20040712
initial build 200300502
pd-port 20060506
____________________________________________________________________________*/
#include "m_pd.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <locale.h>
#define VERSION "1.2"
#define CLIP(x,a,b) (x)=(x)<(a)?(a):(x)>(b)?(b):(x)
typedef struct _detox
{
t_object ob;
t_atom t_tree[257], t_attrpair[2];
t_atom t_comp, t_blip;
long t_treecount;
short t_debug;
short t_mode;
void *s_outlet, *s_outlet2, *s_outlet3, *s_outlet4;
} t_detox;
t_symbol *ps_nothing;
void *detox_class;
void *detox_new(t_symbol *s, long argc, t_atom *argv);
void detox_free(t_detox *x);
void detox_reset(t_detox *x);
void detox_debug(t_detox *x, float f);
void detox_mode(t_detox *x, float f);
void detox_anything(t_detox *x, t_symbol *s, long argc, t_atom *argv);
long clip(long in, long min, long max);
void version(t_detox *x);
void detox_setup(void)
{
detox_class = class_new(gensym("detox"), (t_newmethod)detox_new,
(t_method)detox_free, sizeof(t_detox), 0, A_GIMME, 0);
class_addsymbol(detox_class, (t_method)detox_anything);
// class_addmethod(detox_class, (t_method)detox_anything, gensym("anything"), A_GIMME,0);
class_addmethod(detox_class, (t_method)detox_debug, gensym("debug"), A_FLOAT, 0);
class_addmethod(detox_class, (t_method)detox_mode, gensym("mode"), A_FLOAT, 0);
class_addmethod(detox_class, (t_method)detox_reset, gensym("reset"), 0);
class_addmethod(detox_class, (t_method)version, gensym("version"), 0);
post(". detox . jasch . "__DATE__" ");
ps_nothing = gensym("");
}
void *detox_new(t_symbol *s, long argc, t_atom *argv)
{
t_detox *x = (t_detox *)pd_new(detox_class);
x->s_outlet = outlet_new(&x->ob, NULL);
x->s_outlet2 = outlet_new(&x->ob, NULL);
x->s_outlet3 = outlet_new(&x->ob, NULL);
x->s_outlet4 = outlet_new(&x->ob, gensym("float"));
x->t_treecount = 0;
x->t_debug = 0;
if((argc >= 1)&&(argv[0].a_type == A_FLOAT)){
if(argv[0].a_w.w_float == 0){
x->t_mode = 0;
}else{
x->t_mode = 1;
}
}else{
x->t_mode = 1;
}
x->t_debug = 0;
if(x->t_debug) post("mode is %ld", x->t_mode);
return (x);
}
void detox_reset(t_detox *x)
{
short i;
x->t_treecount = 0;
for(i=0;i<256; i++) x->t_tree[i].a_w.w_symbol = ps_nothing;
}
void detox_debug(t_detox *x, float f)
{
if(f == 0.0){
x->t_debug = 0;
}else if(f != 0){
x->t_debug = 1;
}
}
void detox_mode(t_detox *x, float f)
{
if(f == 0.0){
x->t_mode = 0;
}else if(f != 0){
x->t_mode = 1;
}
}
void detox_anything(t_detox *x, t_symbol *s, long argc, t_atom *argv)
{
t_atom *outlist, *comp, *attrpair;
char local[1024], local2[1024];
char temp[1024];
char tempstring[2];
char *ptr, *ptr2, *ptr3, *ptr4, *ptr5;
long i, j, k, last, len, len2, tempcount = 0, pos = 0;
short tagtype = 0; // 0 = closed, 1 = open, 2 = closing
short outsize = 0;
long quotetype = 0;
short attrpresent = 0;
i = 0;
strcpy(tempstring, " ");
outlist = x->t_tree;
attrpair = x->t_attrpair;
x->t_attrpair[0].a_type = A_SYMBOL;
x->t_attrpair[1].a_type = A_SYMBOL;
if(s->s_name == "debug"){
x->t_debug = CLIP(argv[0].a_w.w_float, 0, 1);
return;
}
strcpy(local2, s->s_name);
last = strlen(local2); // test for tag
if(x->t_debug) post("input is %s", s->s_name);
k=0;
while(isspace(local2[k])){ // remove whitespace/tab/cr/linefeed
k++;
}
if(x->t_debug) post("k is %ld, last is %ld", k, last);
for(i=k, j=0; i<last; i++, j++){ // remove whitespace/tab/cr/linefeed
local[j] = local2[i];
}
local[j] = 0; // terminate string
if((local[0] != '<') || (local[j-1] != '>')){
tagtype = 0; // not a well formed tag
if(x->t_debug) post("tagtype 0");
goto content;
}
if((local[1] == '?') || (local[1] == '!')){
tagtype = 4; // this might be a metatag
if(x->t_debug) post("tagtype 4");
goto content;
}
ptr = strchr(local, '/'); // is it a closing tag ?
if(x->t_debug) post("ptr contains %s", s->s_name);
if(ptr == NULL){ // we have new open tag
tagtype = 2;
if(x->t_debug) post("tagtype 2");
}else if((local[1] == '/')){ // is it at beginning
tagtype = 3;
if(x->t_debug) post("tagtype 3");
}else if(local[1] != '/'){ // is it elsewhere
if(x->t_debug) post("tagtype 1");
tagtype = 1;
}
switch(tagtype){
case 1: // closed tag :: add temporarily to tree
ptr = strchr(local, '>');
ptr2 = strchr(local, ' ');
if(ptr2 != NULL) *ptr2 = 0;
if(ptr != NULL){
*ptr = 0;
}else{
return;
}
len = strlen(local);
ptr5 = (char *)memmove((local + 0),(local + 1), len-1);
local[len-1] = 0;
ptr5 = local;
x->t_blip.a_type = A_SYMBOL;
x->t_blip.a_w.w_symbol = gensym(ptr5);
tempcount = 1;
break;
case 2: // opening tag :: add to tree, increment count
ptr2 = strchr(local, ' ');
if(ptr2 != NULL){
*ptr2 = 0;
if(x->t_debug) post("inside opening tag w/ whitespace, %s", local);
}else{
local[strlen(local) - 1] = 0;
// local[strlen(ptr2) -1] = 0;
if(x->t_debug) post("inside opening tag no whitespace, %s", local);
// return;
}
len = strlen(local);
ptr5 = (char *)memmove((local + 0),(local + 1), len-1);
if(x->t_debug) post("ptr5 is %s", ptr5);
local[len-1] = 0;
ptr5 = local;
x->t_tree[x->t_treecount].a_type = A_SYMBOL;
x->t_tree[x->t_treecount].a_w.w_symbol = gensym(ptr5);
x->t_treecount++;
x->t_treecount = clip(x->t_treecount, 0, 255);
break;
case 3: // closing tag :: remove from tree, decrement count
ptr2 = strchr(local, '>');
if(ptr2 != NULL){
*ptr2 = 0;
}else{
return;
}
len = strlen(local);
ptr5 = (char *)memmove((local + 0),(local + 2), len-2);
local[len-2] = 0;
ptr5 = local;
x->t_comp.a_type = A_SYMBOL;
x->t_comp.a_w.w_symbol = gensym(ptr5);
if(x->t_comp.a_w.w_symbol->s_name == x->t_tree[x->t_treecount-1].a_w.w_symbol->s_name){
x->t_treecount -= 1;
x->t_treecount = clip(x->t_treecount, 0, 255);
}else{
outlet_float(x->s_outlet4, 3);
outlet_float(x->s_outlet3, -1);
return;
}
break;
}
content:
outlet_float(x->s_outlet4, tagtype);
if(tagtype == 0) return;
if(x->t_treecount > 0){
if(tempcount){
outsize = x->t_treecount + 1;
x->t_tree[outsize-1] = x->t_blip;
}else{
outsize = x->t_treecount;
}
outlet_list(x->s_outlet3, 0L, outsize, outlist); // ouput tree
}else if(x->t_treecount == 0){
if(tempcount){
outlet_anything(x->s_outlet3, x->t_blip.a_w.w_symbol, 0, NULL); // ouput tree
}else{
outlet_float(x->s_outlet3, 0); // ouput tree
}
}
strcpy(local, s->s_name);
ptr = strchr(local, '>'); // parse content between enclosing tags <tag>blah</tag>
if(ptr == NULL) goto onward;
*ptr = 0;
++ptr;
ptr2 = strchr(ptr, '<');
if(ptr2 == NULL) goto onward;
*ptr2 = 0;
outlet_anything(x->s_outlet2, gensym(ptr), 0, NULL);
onward:
strcpy(local, s->s_name); // get full buffer again
switch(x->t_mode){
case 0: // the old way of attribute parsing (mode 0)
if(x->t_debug) post("content: before strtok %s", local);
ptr = strtok(local,tempstring);
while (ptr != NULL){
ptr3 = strchr(ptr, '"');
if(ptr3 != NULL){
*ptr3 = 0;
++ptr3;
ptr4 = strchr(ptr3, '"');
*ptr4 = 0;
outlet_anything(x->s_outlet, gensym(ptr3), 0, NULL);
if(x->t_debug) post("content: after strtok %s", ptr);
}
ptr = strtok(NULL, tempstring);
}
break;
case 1: // the new way of attribute parsing (mode 1)
ptr = local;
if(strcspn(ptr, "=\"\'") == strlen(ptr)) break; // no attr-chars found, bail ouit
ptr = strchr(local, 32); // find first whitespace
if(ptr == NULL) break; // no whitespace found bail
attr_loop:
while((ptr[0] == ' ') && (ptr != NULL)) ptr++; // find next non-whitespace char
ptr2 = ptr;
while(!((ptr2[0] == ' ')||(ptr2[0] == '=')) && (ptr2 != NULL)) ptr2++; // find next non-white or not equal-sign
len = strlen(ptr) - strlen(ptr2);
strncpy(temp, ptr, len); // copy into temp-buffer
temp[len] = 0; // terminate temp_buffer
attrpair[0].a_type = A_SYMBOL;
attrpair[0].a_w.w_symbol = gensym(temp); // put into output symbol
ptr = ptr2;
while(!((ptr[0] == '\'') || (ptr[0] == '\"')) && (ptr != NULL)){ // move until single or double quote
ptr++;
quotetype = ptr[0];
}
ptr2 = ++ptr;
while((ptr2[0] != quotetype) && (ptr2 != NULL)) ptr2++;
len = strlen(ptr) - strlen(ptr2);
strncpy(temp, ptr, len);
temp[len] = 0;
attrpair[1].a_type = A_SYMBOL;
attrpair[1].a_w.w_symbol = gensym(temp);
outlet_anything(x->s_outlet, x->t_attrpair[0].a_w.w_symbol, 1, &x->t_attrpair[1]);
ptr = ++ptr2;
while((ptr[0] == ' ')&&(ptr != NULL)){ ptr++; } // find next non-whitespace char
if((ptr[0] == '/') || (ptr[0] == '>') || (ptr[0] == '?') || (ptr == NULL)){ // if char is "/" or ">" or "?" bail out
break;
}else{
goto attr_loop;
}
break;
}
}
void detox_free(t_detox *x)
{
// notify_free((t_object *)x);
}
long clip(long in, long min, long max)
{
return in < min? min: (in > max? max : in);
}
void version(t_detox *x)
{
post("á detox á jasch - version " VERSION" compiled "__DATE__" "__TIME__ );
}
|