/** * 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; }