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