/**
* processor.c
*
* Copyright (C) 2003 - 2008 Bojan Smojver, Rexursive
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
*/
#include "private.h"
#include "scanner.h"
static apr_status_t regex_cleanup(void *data){
ap_regfree((ap_regex_t *)data);
return APR_SUCCESS;
}
static rxv_spin_node_t *node_copy(rxv_spin_node_t *node,
rxv_spin_extra_t *extra){
rxv_spin_txt_t *txt,*ctxt;
rxv_spin_ref_t *ref,*cref;
rxv_spin_for_t *loop,*cloop;
rxv_spin_if_t *cond,*ccond;
rxv_spin_node_t **next=NULL,*copy=NULL,*new=NULL,*first;
for(first=node;node;node=node->next){
switch(node->type){
case RXV_SPIN_TXT:
txt=(rxv_spin_txt_t *)node;
ctxt=apr_pcalloc(extra->pool,sizeof(*ctxt));
ctxt->node.type=txt->node.type;
ctxt->node.ttyp=txt->node.ttyp;
ctxt->length=txt->length;
ctxt->start=txt->start;
new=&ctxt->node;
break;
case RXV_SPIN_REF:
ref=(rxv_spin_ref_t *)node;
if(!(cref=apr_hash_get(extra->refs,&ref,sizeof(ref)))){
cref=apr_pcalloc(extra->pool,sizeof(*cref));
cref->node.type=ref->node.type;
cref->node.rtyp=ref->node.rtyp;
cref->name=ref->name;
if(ref->ref){
cref->ref=(rxv_spin_ref_t *)node_copy(&ref->ref->node,extra);
if(ref->ip==&ref->ref->index)
cref->ip=&cref->ref->index;
}
if(ref->ip==&ref->fixed)
cref->ip=&cref->fixed;
cref->fixed=ref->fixed;
}
new=&cref->node;
break;
case RXV_SPIN_FOR:
loop=(rxv_spin_for_t *)node;
cloop=apr_pcalloc(extra->pool,sizeof(*cloop));
cloop->node.type=loop->node.type;
cloop->ref=(rxv_spin_ref_t *)node_copy(&loop->ref->node,extra);
apr_hash_set(extra->refs,&loop->ref,sizeof(loop->ref),cloop->ref);
cloop->stmt=node_copy(loop->stmt,extra);
new=&cloop->node;
break;
case RXV_SPIN_IF:
cond=(rxv_spin_if_t *)node;
ccond=apr_pcalloc(extra->pool,sizeof(*ccond));
ccond->node.type=cond->node.type;
ccond->node.ityp=cond->node.ityp;
ccond->node.iopr=cond->node.iopr;
ccond->ref=(rxv_spin_ref_t *)node_copy(&cond->ref->node,extra);
switch(node->iopr){
case RXV_SPIN_OP_MATCH:
ccond->srg=cond->srg;
ccond->reg=apr_pcalloc(extra->pool,sizeof(*ccond->reg));
ap_regcomp(ccond->reg,ccond->srg,AP_REG_EXTENDED|AP_REG_NOSUB);
apr_pool_cleanup_register(extra->pool,ccond->reg,
regex_cleanup,apr_pool_cleanup_null);
break;
case RXV_SPIN_OP_EQ_LIT:
ccond->lit=cond->lit;
break;
case RXV_SPIN_OP_EQ_NUM:
ccond->num=cond->num;
break;
case RXV_SPIN_OP_EQ_REF:
ccond->rfr=(rxv_spin_ref_t *)node_copy(&cond->rfr->node,extra);
break;
case RXV_SPIN_OP_MODULO:
ccond->num=cond->num;
ccond->res=cond->res;
default:
break;
}
ccond->pos=node_copy(cond->pos,extra);
ccond->neg=node_copy(cond->neg,extra);
new=&ccond->node;
break;
default: /* should be impossible */
break;
}
if(copy)
*next=new;
else
copy=new;
next=&new->next;
if(node->next==first) break;
}
return copy;
}
apr_status_t rxv_spin_build(rxv_spin_extra_t *extra){
/* if there is a cached template, use it */
if(extra->cache){
/* nothing to do */
if(!extra->cache->root)
return APR_SUCCESS;
extra->scan=APR_BRIGADE_FIRST(extra->input);
extra->current=extra->current?extra->current->next:extra->cache->root;
if(extra->current->root){
extra->refs=apr_hash_make(extra->pool);
extra->root=node_copy(extra->current->root,extra);
if(!extra->current->next){
extra->eos=APR_BRIGADE_LAST(extra->input);
extra->input=NULL;
}
}
return APR_SUCCESS;
}
/* nothing to do */
if(APR_BRIGADE_EMPTY(extra->input))
return APR_SUCCESS;
/* we have to set up scanner */
if(!extra->scanner){
if(rxv_spin_yylex_init(&extra->scanner))
return apr_get_os_error();
rxv_spin_yyset_extra(extra,extra->scanner);
extra->scan=APR_BRIGADE_FIRST(extra->input);
/* make yywrap() read the buckets */
memset(extra->buf,0,2);
rxv_spin_yy_scan_buffer(extra->buf,2,extra->scanner);
}
if(rxv_spin_yyparse(extra->scanner)){
rxv_spin_yylex_destroy(extra->scanner);
extra->scanner=NULL;
return APR_EGENERAL;
}
/* bumped into EOF, maybe need to back up */
if(extra->eof){
if(extra->offset>extra->pos){
apr_bucket *b;
/* not at the beginning - split */
if(extra->pos>0){
if(apr_brigade_partition(extra->input,extra->pos,&b)!=APR_SUCCESS){
extra->error="cannot partition brigade";
rxv_spin_yylex_destroy(extra->scanner);
extra->scanner=NULL;
return APR_EGENERAL;
}
extra->split=apr_brigade_split(extra->input,b);
} else{
extra->split=extra->input;
}
extra->input=NULL;
} else if(extra->bpos>=extra->blen){
extra->input=NULL;
}
}
/* bumped into EOS, see if we're done */
if(extra->eos){
if(extra->offset>extra->pos){
rxv_spin_yylex_destroy(extra->scanner);
extra->scanner=NULL;
return APR_EGENERAL;
} else if(extra->bpos>=extra->blen){
extra->input=NULL;
}
}
if(!extra->input){
rxv_spin_yylex_destroy(extra->scanner);
extra->scanner=NULL;
}
return APR_SUCCESS;
}
static rxv_spin_data_t *ref_resolve(rxv_spin_ref_t *r,rxv_spin_ctx_t *ctx){
rxv_spin_data_t *data,*prev;
if(r->ref){
prev=r->ref->data;
data=ref_resolve(r->ref,ctx); /* recursively resolve to the top first */
if(data){
if(!r->data || data!=prev){
if(data->size==RXV_SPIN_ROWS){
apr_array_header_t *a;
a=apr_hash_get(data->cols,r->name,APR_HASH_KEY_STRING);
r->data=apr_is_empty_array(a)?NULL:(rxv_spin_data_t *)a->elts;
} else{
r->data=NULL;
}
}
} else{
r->data=NULL;
}
} else if(!r->data){
r->data=apr_hash_get(ctx->data->cols,r->name,APR_HASH_KEY_STRING);
}
if(r->data){
if(r->ip)
return r->data+*r->ip;
else
return r->data;
}
return NULL;
}
apr_status_t rxv_spin_render(rxv_spin_node_t *node,rxv_spin_extra_t *extra){
char *buf;
apr_size_t val;
unsigned long num;
apr_status_t rv;
apr_bucket_brigade *bb=extra->output;
apr_bucket *b;
rxv_spin_txt_t *txt;
rxv_spin_ref_t *ref,*rfr;
rxv_spin_for_t *loop;
rxv_spin_if_t *cond;
apr_pool_t *p=extra->pool;
rxv_spin_data_t *data,*rdta;
rxv_spin_node_t *first;
for(first=node;node;node=node->next){
switch(node->type){
case RXV_SPIN_TXT:
txt=(rxv_spin_txt_t *)node;
switch(node->ttyp){
case RXV_SPIN_TXT_BKT: /* we already have this in a bucket, copy */
apr_bucket_copy(txt->bucket,&b);
break;
case RXV_SPIN_TXT_TXT: /* filter, bucket from pooled memory */
b=apr_bucket_heap_create(txt->text,
txt->length,NULL,bb->bucket_alloc);
txt->bucket=b;
node->ttyp=RXV_SPIN_TXT_BKT;
break;
case RXV_SPIN_TXT_OFF: /* handler, copy mmap()-ed file bucket */
apr_bucket_copy(extra->scan,&b);
b->start=txt->start;
b->length=txt->length;
txt->bucket=b;
node->ttyp=RXV_SPIN_TXT_BKT;
break;
default:
break;
}
APR_BRIGADE_INSERT_TAIL(bb,b);
break;
case RXV_SPIN_REF:
ref=(rxv_spin_ref_t *)node;
data=ref_resolve(ref,extra->ctx);
/* skip rows, we cannot insert them into text */
if(data){
char *buf=NULL;
apr_size_t len=0;
/* prepare data and size for bucket */
switch(node->rtyp){
case RXV_SPIN_REF_REG:
if(data->size==RXV_SPIN_ROWS)
continue;
buf=data->data;
len=data->size;
break;
case RXV_SPIN_REF_SIZ:
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,rxv_spin_size(data));
len=strlen(buf);
break;
case RXV_SPIN_REF_IND:
if(ref->ip)
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,*ref->ip+1);
else
buf="0";
len=strlen(buf);
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
/* only replace valid stuff (i.e. size, index and singles) */
if(buf && len>0){
b=apr_bucket_heap_create(buf,len,NULL,extra->output->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb,b);
}
}
break;
case RXV_SPIN_FOR:
loop=(rxv_spin_for_t *)node;
ref=loop->ref;
data=ref_resolve(ref,extra->ctx);
if(data){
if(data->size==RXV_SPIN_ROWS){
if(data->cols){
apr_size_t size=rxv_spin_size(data);
for(ref->index=0;ref->indexindex++)
if((rv=rxv_spin_render(loop->stmt,extra))!=APR_SUCCESS)
return rv;
}
} else{
if(data->data)
if((rv=rxv_spin_render(loop->stmt,extra))!=APR_SUCCESS)
return rv;
}
}
break;
case RXV_SPIN_IF:
cond=(rxv_spin_if_t *)node;
ref=cond->ref;
rfr=cond->rfr;
data=ref_resolve(ref,extra->ctx);
if(data){
unsigned int truth;
switch(node->iopr){
case RXV_SPIN_OP_NONE:
switch(ref->node.rtyp){
case RXV_SPIN_REF_REG:
truth=(data->size==RXV_SPIN_ROWS && data->cols) || data->data;
break;
case RXV_SPIN_REF_SIZ:
truth=(rxv_spin_size(data)>0); break;
case RXV_SPIN_REF_IND:
truth=(ref->ref!=NULL); break;
default: /* don't know this, false */
truth=RXV_SPIN_FALSE; break;
}
break;
case RXV_SPIN_OP_MATCH:
switch(ref->node.rtyp){
case RXV_SPIN_REF_REG:
truth=(data->size!=RXV_SPIN_ROWS && data->data &&
!ap_regexec(cond->reg,data->data,0,NULL,0)); break;
case RXV_SPIN_REF_SIZ:
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,rxv_spin_size(data));
truth=!ap_regexec(cond->reg,buf,0,NULL,0);
break;
case RXV_SPIN_REF_IND:
if(ref->ip)
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,*ref->ip+1);
else
buf="0";
truth=!ap_regexec(cond->reg,buf,0,NULL,0);
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
case RXV_SPIN_OP_EQ_LIT:
switch(ref->node.rtyp){
case RXV_SPIN_REF_REG:
truth=(data->size!=RXV_SPIN_ROWS && data->data &&
!strcmp(data->data,cond->lit));
break;
case RXV_SPIN_REF_SIZ:
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,rxv_spin_size(data));
truth=!strcmp(buf,cond->lit);
break;
case RXV_SPIN_REF_IND:
if(ref->ip)
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,*ref->ip+1);
else
buf="0";
truth=!strcmp(buf,cond->lit);
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
case RXV_SPIN_OP_EQ_NUM:
switch(ref->node.rtyp){
case RXV_SPIN_REF_REG:
if(data->size!=RXV_SPIN_ROWS && data->data){
num=0; sscanf(data->data,"%lu",&num);
truth=(num==cond->num);
} else{
truth=RXV_SPIN_FALSE;
}
break;
case RXV_SPIN_REF_SIZ:
truth=(rxv_spin_size(data)==cond->num); break;
case RXV_SPIN_REF_IND:
if(ref->ip)
truth=(*ref->ip+1==cond->num);
else
truth=(0==cond->num);
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
case RXV_SPIN_OP_EQ_REF:
if(!rfr){ /* impossible reference, always false */
if(cond->neg &&
(rv=rxv_spin_render(cond->neg,extra))!=APR_SUCCESS)
return rv;
continue;
}
rdta=ref_resolve(rfr,extra->ctx);
if(!rdta){ /* no data, always false */
if(cond->neg &&
(rv=rxv_spin_render(cond->neg,extra))!=APR_SUCCESS)
return rv;
continue;
}
/* we are wasting CPU cycles and memory here as a tradeoff
for slightly simpler code - life isn't perfect ;-) */
switch(rfr->node.rtyp){
case RXV_SPIN_REF_REG:
if(rdta->size==RXV_SPIN_ROWS){
buf=NULL; val=0;
} else{
buf=rdta->data;
if(buf){
val=0; sscanf(buf,"%" APR_SIZE_T_FMT,&val);
} else{
val=0;
}
}
break;
case RXV_SPIN_REF_SIZ:
val=rxv_spin_size(rdta);
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,val);
break;
case RXV_SPIN_REF_IND:
if(rfr->ip){
val=*rfr->ip+1;
buf=apr_psprintf(p,"%" APR_SIZE_T_FMT,val);
} else{
val=0; buf="0";
}
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
switch(ref->node.rtyp){
case RXV_SPIN_REF_REG:
truth=(data->size!=RXV_SPIN_ROWS && data->data && buf &&
!strcmp(data->data,buf));
break;
case RXV_SPIN_REF_SIZ:
truth=(rxv_spin_size(data)==val); break;
case RXV_SPIN_REF_IND:
if(ref->ip)
truth=(*ref->ip+1==val);
else
truth=(0==val);
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
case RXV_SPIN_OP_MODULO:
switch(ref->node.rtyp){
case RXV_SPIN_REF_REG:
if(data->size!=RXV_SPIN_ROWS && data->data){
num=0; sscanf(data->data,"%lu",&num);
truth=num%cond->num==cond->res;
} else{
truth=RXV_SPIN_FALSE;
}
break;
case RXV_SPIN_REF_SIZ:
truth=(rxv_spin_size(data)%cond->num==cond->res); break;
case RXV_SPIN_REF_IND:
if(ref->ip)
truth=(*ref->ip+1%cond->num==cond->res);
else
truth=(0%cond->num==cond->res);
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
if(truth){
if(cond->pos && (rv=rxv_spin_render(cond->pos,extra))!=APR_SUCCESS)
return rv;
} else if(cond->neg){
if((rv=rxv_spin_render(cond->neg,extra))!=APR_SUCCESS)
return rv;
}
} else if(cond->neg){
if((rv=rxv_spin_render(cond->neg,extra))!=APR_SUCCESS)
return rv;
}
break;
}
if(node->next==first) break;
}
return APR_SUCCESS;
}