Bucket functions
Firstly we introduce two functions to deal with the data insertions:
one for the files, one for the simple entity replacements:
Creating a File bucket requires an open filehandle and a byte range within
the file. Since we're transmitting the entire file, we just stat its
size to set the byte range. We open it with a shared lock and with
sendfile enabled for maximum performance.
static apr_bucket* txt_file_bucket(request_rec* r, const char* fname) {
apr_file_t* file = NULL ;
apr_finfo_t finfo ;
if ( apr_stat(&finfo, fname, APR_FINFO_SIZE, r->pool) != APR_SUCCESS ) {
return NULL ;
}
if ( apr_file_open(&file, fname, APR_READ|APR_SHARELOCK|APR_SENDFILE_ENABLED,
APR_OS_DEFAULT, r->pool ) != APR_SUCCESS ) {
return NULL ;
}
if ( ! file ) {
return NULL ;
}
return apr_bucket_file_create(file, 0, finfo.size, r->pool,
r->connection->bucket_alloc) ;
}
Creating the simple text replacements, we can just make a bucket of an
inline string. The appropriate bucket type for such data is transient:
static apr_bucket* txt_esc(char c, apr_bucket_alloc_t* alloc ) {
switch (c) {
case '<': return apr_bucket_transient_create("<", 4, alloc) ;
case '>': return apr_bucket_transient_create(">", 4, alloc) ;
case '&': return apr_bucket_transient_create("&", 5, alloc) ;
case '"': return apr_bucket_transient_create(""", 6, alloc) ;
default: return NULL ; /* shut compilers up */
}
}
Actually this is not the most efficient way to do this. We will
discuss alternative formulations of the above below.