/** * processor.c * * Copyright (C) 2003, 2004 Bojan Smojver, Rexursive * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licence as published by the Free * Software Foundation; either version 2 of the Licence, 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 Licence for * more details. * * You should have received a copy of the GNU General Public Licence along * with this program; if not, write to the Free Software Foundation, Inc., 59 * Temple Place, Suite 330, Boston, MA 02111-1307 USA * * In addition, as two special exceptions, Bojan Smojver, Rexursive, gives * permission to: * * 1. Link, both statically and dynamically, the code of this program with * Apache HTTP Server, Apache Portable Runtime and Apache Portable Runtime * Utility Library from Apache Software Foundation (or with modified versions * of the above, that use the same licence - Apache Software Licence 1.1, 2.0 * or any later version), and distribute linked combinations including the * program and the above software from Apache Software Foundation. You must * obey the GNU General Public Licence in all respects for all of the code * used other than Apache HTTP Server, Apache Portable Runtime and Apache * Portable Runtime Utility Library. * * 2. Dynamically link any shared library with this program at run-time, * through the interface of SpinApplication/SpinAppEntry or LoadModule * run-time configuration directives of Apache HTTP Server, as provided by * this program or Apache HTTP Server itself, regardless of licensing terms of * those shared libraries. You must obey the GNU General Public Licence in all * respects for all of the code used other than that of those shared * libraries. * * If you modify this file, you may extend these exceptions to your version of * the file, but you are not obligated to do so. If you do not wish to do so, * delete one or both of these exception statements from your version. * */ #include "private.h" #include "scanner.h" static apr_status_t process_nodes(rxv_spin_node_t *node, rxv_spin_extra_t *extra){ apr_bucket_brigade *brigade=extra->brigade; apr_bucket *bucket; rxv_spin_txt_t *txt; rxv_spin_ref_t *ref; rxv_spin_for_t *loop; rxv_spin_if_t *cond; for(;node;node=node->next){ switch(node->type){ case RXV_SPIN_TXT: txt=node->txt; #ifdef DEBUG fprintf(stderr,"txt=%s,%s\n", apr_off_t_toa(extra->pool,txt->offset), apr_off_t_toa(extra->pool,txt->length)); fflush(stderr); #endif if(txt->cache) bucket=apr_bucket_immortal_create(txt->cache,txt->length, extra->brigade->bucket_alloc); else bucket=apr_bucket_file_create(extra->fp,txt->offset,txt->length, extra->pool, extra->brigade->bucket_alloc); if(!bucket) return APR_ENOMEM; APR_BRIGADE_INSERT_TAIL(brigade,bucket); break; case RXV_SPIN_REF: ref=node->ref; #ifdef DEBUG fprintf(stderr,"ref=%s\n",ref->name); fflush(stderr); #endif 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=(rxv_spin_data_t *) apr_hash_get(ref->loop->ref->data->cols,ref->chunk, APR_HASH_KEY_STRING); } } else{ /* not loop */ ref->data=(rxv_spin_data_t *) 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; /* only replace valid singles */ if(data->type==RXV_SPIN_DATA_SGL && data->data && data->size>0){ /* We can do this because the bucket cleanup function always runs * before the data cleanup function, making sure that the data is * set aside (i.e. preserved). This, however, only works if the * data has lifetime of at least that of the bucket (i.e. the * lifetime of the request). */ if(!(bucket=apr_bucket_pool_create(data->data,data->size, extra->pool, extra->brigade->bucket_alloc))) return APR_ENOMEM; APR_BRIGADE_INSERT_TAIL(brigade,bucket); } } break; case RXV_SPIN_FOR: loop=node->loop; ref=loop->ref; #ifdef DEBUG fprintf(stderr,"for=%s\n",ref->name); fflush(stderr); #endif 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=(rxv_spin_data_t *) apr_hash_get(ref->loop->ref->data->cols,ref->chunk, APR_HASH_KEY_STRING); } } else{ /* not loop */ ref->data=(rxv_spin_data_t *) apr_hash_get(extra->ctx->data->cols,ref->chunk, APR_HASH_KEY_STRING); } ref->looked=RXV_SPIN_TRUE; } if(ref->data){ size_t loops; 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: loops=1; break; case RXV_SPIN_DATA_RWS: loops=data->size; break; default: /* this should never happen */ loops=0; break; } for(loop->index=0;loop->indexindex++){ if(data->both) process_nodes(loop->stmt,extra); } } break; case RXV_SPIN_IF: cond=node->cond; ref=cond->ref; #ifdef DEBUG fprintf(stderr,"if=%s\n",ref?ref->name:"NULL"); fflush(stderr); #endif 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=(rxv_spin_data_t *) apr_hash_get(ref->loop->ref->data->cols,ref->chunk, APR_HASH_KEY_STRING); } } else{ /* not loop */ ref->data=(rxv_spin_data_t *) 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; if(data->both){ if(cond->pos) process_nodes(cond->pos,extra); } else if(cond->neg){ process_nodes(cond->neg,extra); } } else if(cond->neg){ process_nodes(cond->neg,extra); } break; } } return APR_SUCCESS; } static apr_status_t clear_nodes(rxv_spin_node_t *node){ rxv_spin_ref_t *ref; rxv_spin_for_t *loop; rxv_spin_if_t *cond; for(;node;node=node->next){ switch(node->type){ case RXV_SPIN_REF: ref=node->ref; ref->data=NULL; ref->looked=RXV_SPIN_FALSE; break; case RXV_SPIN_FOR: loop=node->loop; ref=loop->ref; ref->data=NULL; ref->looked=RXV_SPIN_FALSE; clear_nodes(loop->stmt); break; case RXV_SPIN_IF: cond=node->cond; ref=cond->ref; ref->data=NULL; ref->looked=RXV_SPIN_FALSE; clear_nodes(cond->neg); clear_nodes(cond->pos); break; } } return APR_SUCCESS; } static rxv_spin_cache_t *cache_create(apr_pool_t *pool,apr_hash_t *used){ rxv_spin_cache_t *cache; apr_pool_t *cpool; if(apr_pool_create(&cpool,pool)!=APR_SUCCESS) return NULL; if(!(cache=apr_pcalloc(cpool,sizeof(*cache)))){ apr_pool_destroy(cpool); return NULL; } cache->pool=cpool; /* by default, all cache items are garbage until explicitly good */ cache->kill=1; /* register that we used this template */ apr_hash_set(used,cache,sizeof(cache->pool),cache); return cache; } apr_status_t rxv_spin_file(const char *file,rxv_spin_extra_t *extra, apr_pool_t *pool,apr_finfo_t *finfo){ int status; apr_status_t astatus; yyscan_t yyscanner; rxv_spin_cache_t *ready; FILE *fp; /* do we have it in cache */ if((ready=apr_hash_get(extra->cache,file,APR_HASH_KEY_STRING))){ /* register that we used this template */ apr_hash_set(extra->used,ready,sizeof(ready->pool),ready); /* if the template modification time and size is the same, we're OK */ if(finfo->mtime==ready->mtime && finfo->size==ready->size){ extra->root=ready->root; extra->tpool=ready->pool; extra->ready=ready; } else{ /* file modified, schedule for destruction on connection closing */ ready->kill=1; apr_hash_set(extra->cache,file,APR_HASH_KEY_STRING,NULL); } } /* we have to create cache entry from scratch and parse */ if(!extra->ready){ if(!(ready=cache_create(pool,extra->used))) return APR_ENOMEM; ready->mtime=finfo->mtime; ready->size=finfo->size; ready->sendfile=extra->sendfile; ready->cacheall=extra->cacheall; extra->tpool=ready->pool; extra->ready=ready; /* open an ANSI file descriptor for scanning */ if(!(fp=fopen(file,"r"))) return APR_EACCES; /* need a buffer, we'll use realloc to resize this, therefore malloc */ if(!(extra->buf=malloc(RXV_SPIN_MIN_SENDFILE))){ fclose(fp); return APR_ENOMEM; } extra->bsize=RXV_SPIN_MIN_SENDFILE; if(yylex_init(&yyscanner)){ fclose(fp); return apr_get_os_error(); } yyset_in(fp,yyscanner); yyset_extra(extra,yyscanner); status=rxv_spin_parse(yyscanner); yylex_destroy(yyscanner); /* clean up first, this is not pool space */ if(status){ fclose(fp); return APR_EGENERAL; } extra->ready->root=extra->root; fclose(fp); /* we survived up until now, put the newly parsed template into cache */ if(!(ready->file=apr_pstrdup(ready->pool,file))) return APR_ENOMEM; ready->kill=0; apr_hash_set(extra->cache,ready->file,APR_HASH_KEY_STRING,ready); } else{ /* clean up data */ clear_nodes(extra->root); } /* if we're not fully cached, we'll need the file opened */ if(!ready->cacheall && (astatus=apr_file_open(&extra->fp,file, APR_READ|(ready->sendfile?APR_SENDFILE_ENABLED:0), APR_OS_DEFAULT,extra->pool))!=APR_SUCCESS) return astatus; if((astatus=process_nodes(extra->root,extra))==APR_SUCCESS){ apr_bucket *eos=apr_bucket_eos_create(extra->brigade->bucket_alloc); if(eos) APR_BRIGADE_INSERT_TAIL(extra->brigade,eos); else return APR_ENOMEM; } return astatus; }