/**
* processor.c
*
* Copyright (C) 2003 - 2007 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"
apr_status_t rxv_spin_parse(rxv_spin_extra_t *extra){
/* 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;
}
apr_status_t rxv_spin_render(rxv_spin_node_t *node,rxv_spin_extra_t *extra){
char *buf;
unsigned long val;
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;
for(;node;node=node->next){
switch(node->type){
case RXV_SPIN_TXT:
txt=(rxv_spin_txt_t *)node;
/* we already have this in a bucket, copy */
if(txt->used){
apr_bucket_copy(txt->bucket,&b);
} else{
APR_BUCKET_REMOVE(txt->bucket);
b=txt->bucket;
txt->used=RXV_SPIN_TRUE;
}
APR_BRIGADE_INSERT_TAIL(bb,b);
break;
case RXV_SPIN_REF:
ref=(rxv_spin_ref_t *)node;
if(!ref->looked){ /* only look if we didn't already */
if(ref->loop){
/* if reference is enclosed in a loop, we have to look in the data
that belongs to that loop; only if loop data is rows we do the
search, as singles cannot have columns */
if(ref->loop->ref->data->type==RXV_SPIN_DATA_RWS){
ref->data=apr_hash_get(ref->loop->ref->data->cols,ref->chunk,
APR_HASH_KEY_STRING);
}
} else{ /* not loop */
ref->data=apr_hash_get(extra->ctx->data->cols,ref->chunk,
APR_HASH_KEY_STRING);
}
ref->looked=RXV_SPIN_TRUE;
}
if(ref->data){
char *buf=NULL;
apr_size_t len=0,index;
rxv_spin_data_t *data; /* fully resolved data element */
if(ref->loop){
data=ref->data+ref->loop->index;
index=ref->loop->index+1;
} else{
data=ref->data;
index=0;
}
/* skip metadata */
if(data->type>RXV_SPIN_DATA_RWS)
continue;
/* prepare data and size for bucket */
switch(ref->type){
case RXV_SPIN_REF_REG:
if(data->type!=RXV_SPIN_DATA_SGL)
continue;
buf=data->data;
len=data->size;
break;
case RXV_SPIN_REF_SIZ:
buf=apr_ltoa(extra->pool,data->size);
len=strlen(buf);
break;
case RXV_SPIN_REF_IND:
buf=apr_ltoa(extra->pool,index);
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;
if(!ref->looked){ /* only look if we didn't already */
if(ref->loop){
/* if reference is enclosed in a loop, we have to look in the data
that belongs to that loop; only if loop data is rows we do the
search, as singles cannot have columns */
if(ref->loop->ref->data->type==RXV_SPIN_DATA_RWS){
ref->data=apr_hash_get(ref->loop->ref->data->cols,ref->chunk,
APR_HASH_KEY_STRING);
}
} else{ /* not loop */
ref->data=apr_hash_get(extra->ctx->data->cols,ref->chunk,
APR_HASH_KEY_STRING);
}
ref->looked=RXV_SPIN_TRUE;
}
if(ref->data){
rxv_spin_data_t *data; /* fully resolved data element */
if(ref->loop)
data=ref->data+ref->loop->index;
else
data=ref->data;
switch(data->type){
case RXV_SPIN_DATA_SGL:
if(data->data){
loop->index=0;
if((rv=rxv_spin_render(loop->stmt,extra))!=APR_SUCCESS)
return rv;
}
break;
case RXV_SPIN_DATA_RWS:
if(data->cols)
for(loop->index=0;loop->indexsize;loop->index++)
if((rv=rxv_spin_render(loop->stmt,extra))!=APR_SUCCESS)
return rv;
break;
default: /* can't loop around this, skip */
continue;
}
}
break;
case RXV_SPIN_IF:
cond=(rxv_spin_if_t *)node;
ref=cond->ref;
rfr=cond->rfr;
if(!ref->looked){ /* only look if we didn't already */
if(ref->loop){
/* if reference is enclosed in a loop, we have to look in the data
that belongs to that loop; only if loop data is rows we do the
search, as singles cannot have columns */
if(ref->loop->ref->data->type==RXV_SPIN_DATA_RWS){
ref->data=apr_hash_get(ref->loop->ref->data->cols,ref->chunk,
APR_HASH_KEY_STRING);
}
} else{ /* not loop */
ref->data=apr_hash_get(extra->ctx->data->cols,ref->chunk,
APR_HASH_KEY_STRING);
}
ref->looked=RXV_SPIN_TRUE;
}
if(ref->data){
unsigned int truth;
rxv_spin_data_t *data,*rdta; /* fully resolved data elements */
if(ref->loop)
data=ref->data+ref->loop->index;
else
data=ref->data;
/* metadata always yields false */
if(data->type>RXV_SPIN_DATA_RWS){
if(cond->neg && (rv=rxv_spin_render(cond->neg,extra))!=APR_SUCCESS)
return rv;
continue;
}
switch(cond->oper){
case RXV_SPIN_OP_NONE:
switch(ref->type){
case RXV_SPIN_REF_REG:
truth=((data->type==RXV_SPIN_DATA_SGL && data->data) ||
(data->type==RXV_SPIN_DATA_RWS && data->cols) ||
RXV_SPIN_FALSE);
break;
case RXV_SPIN_REF_SIZ:
truth=(data->size>0); break;
case RXV_SPIN_REF_IND:
truth=(ref->loop!=NULL || RXV_SPIN_FALSE); break;
default: /* don't know this, false */
truth=RXV_SPIN_FALSE; break;
}
break;
case RXV_SPIN_OP_MATCH:
switch(ref->type){
case RXV_SPIN_REF_REG:
truth=(data->type==RXV_SPIN_DATA_SGL && data->data &&
!ap_regexec(cond->reg,data->data,0,NULL,0)); break;
case RXV_SPIN_REF_SIZ:
if((buf=apr_ltoa(extra->pool,data->size))){
truth=!ap_regexec(cond->reg,buf,0,NULL,0); break;
}
return APR_ENOMEM;
case RXV_SPIN_REF_IND:
if(ref->loop){
if((buf=apr_ltoa(extra->pool,ref->loop->index+1))){
truth=!ap_regexec(cond->reg,buf,0,NULL,0); break;
}
} else{
truth=!ap_regexec(cond->reg,"0",0,NULL,0); break;
}
return APR_ENOMEM;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
case RXV_SPIN_OP_EQ_LIT:
switch(ref->type){
case RXV_SPIN_REF_REG:
truth=(data->type==RXV_SPIN_DATA_SGL && data->data &&
!strcmp(data->data,cond->lit));
break;
case RXV_SPIN_REF_SIZ:
if((buf=apr_ltoa(extra->pool,data->size))){
truth=!strcmp(buf,cond->lit); break;
}
return APR_ENOMEM;
case RXV_SPIN_REF_IND:
if(ref->loop){
if((buf=apr_ltoa(extra->pool,ref->loop->index+1))){
truth=!strcmp(buf,cond->lit); break;
}
} else{
truth=!strcmp("0",cond->lit); break;
}
return APR_ENOMEM;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
break;
case RXV_SPIN_OP_EQ_NUM:
switch(ref->type){
case RXV_SPIN_REF_REG:
truth=(data->type==RXV_SPIN_DATA_SGL && data->data &&
atol(data->data)==cond->num); break;
case RXV_SPIN_REF_SIZ:
truth=(data->size==cond->num); break;
case RXV_SPIN_REF_IND:
if(ref->loop)
truth=(ref->loop->index+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;
}
if(!rfr->looked){ /* only look if we didn't already */
if(rfr->loop){
/* if reference is enclosed in a loop, we have to look in the data
that belongs to that loop; only if loop data is rows we do the
search, as singles cannot have columns */
if(rfr->loop->ref->data->type==RXV_SPIN_DATA_RWS){
rfr->data=apr_hash_get(rfr->loop->ref->data->cols,
rfr->chunk,APR_HASH_KEY_STRING);
}
} else{ /* not loop */
rfr->data=apr_hash_get(extra->ctx->data->cols,
rfr->chunk,APR_HASH_KEY_STRING);
}
rfr->looked=RXV_SPIN_TRUE;
}
if(rfr->data){
if(rfr->loop)
rdta=rfr->data+rfr->loop->index;
else
rdta=rfr->data;
/* metadata always yields false */
if(rdta->type>RXV_SPIN_DATA_RWS){
if(cond->neg &&
(rv=rxv_spin_render(cond->neg,extra))!=APR_SUCCESS)
return rv;
continue;
}
} else{ /* 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->type){
case RXV_SPIN_REF_REG:
if(rdta->type==RXV_SPIN_DATA_SGL){
buf=rdta->data; val=(buf?atol(buf):0);
} else{
buf=NULL; val=0;
}
break;
case RXV_SPIN_REF_SIZ:
if(!(buf=apr_ltoa(extra->pool,rdta->size)))
return APR_ENOMEM;
val=rdta->size;
break;
case RXV_SPIN_REF_IND:
if(rfr->loop){
val=rfr->loop->index+1;
if(!(buf=apr_ltoa(extra->pool,val)))
return APR_ENOMEM;
} else{
buf="0"; val=0;
}
break;
default: /* must be an error, we don't know this */
return APR_EGENERAL;
}
switch(ref->type){
case RXV_SPIN_REF_REG:
truth=(data->type==RXV_SPIN_DATA_SGL && data->data && buf &&
!strcmp(data->data,buf));
break;
case RXV_SPIN_REF_SIZ:
truth=(data->size==val); break;
case RXV_SPIN_REF_IND:
if(ref->loop)
truth=(ref->loop->index+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->type){
case RXV_SPIN_REF_REG:
truth=(data->type==RXV_SPIN_DATA_SGL && data->data &&
atol(data->data)%cond->num==cond->res); break;
case RXV_SPIN_REF_SIZ:
truth=(data->size%cond->num==cond->res); break;
case RXV_SPIN_REF_IND:
if(ref->loop)
truth=(ref->loop->index+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;
}
}
return APR_SUCCESS;
}