/* Copyright 2006 Joachim Zobel . * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include APU_DECLARE_DATA const apr_bucket_type_t bucket_type_sax; #include "frag_buffer.h" #include "buckets_sax.h" #include "sax_util.h" /***************************************************************************** * Helpers for sax event handling *****************************************************************************/ // Aligned sizes #define SAX_BUCKET_SIZE APR_ALIGN_DEFAULT(sizeof(bucket_sax)) #define SAX_EVENT_SIZE(x) APR_ALIGN_DEFAULT(sizeof(x)) #define SAX_BASE_SIZE(x) (SAX_BUCKET_SIZE + SAX_EVENT_SIZE(x)) /* * sax_event_which_to_str */ const char *sax_event_which_to_str(sax_event_e which) { #define TO_STR(x) case x: return #x; switch (which) { TO_STR(START_ELT) TO_STR(END_ELT) TO_STR(START_NS) TO_STR(END_NS) TO_STR(PROC_INSTR) TO_STR(CHARACTER) TO_STR(COMMENT) TO_STR(XML_DECL) TO_STR(WHITE) TO_STR(DEFAULT) TO_STR(START_CD) TO_STR(END_CD) default: return "UNKNOWN"; } } /** * Write a debug message to the error log * @param bs - The sax bucket (whose request will be used) * @param msg - will be logged (usual printf format) */ static void sax_event_debug_log(const char *file, int line, bucket_sax * bs, const char *msg, ...) { va_list args; va_start(args, msg); request_rec *r = bs->bctx->r_log; if (r) ap_log_rerror(file, line, APLOG_DEBUG, 0, r, apr_pvsprintf(bs->bctx->p_tmp, msg, args)); va_end(args); } /* * sax_hr_size */ double sax_hr_size(apr_size_t sz, const char **punit) { const double lsz = log2(sz); if (lsz < 10) { *punit = "B"; return sz; } else if (lsz < 20) { *punit = "k"; return (double) sz / 1024; } else if (lsz < 30) { *punit = "M"; return (double) sz / (1024 * 1024); } else { *punit = "G"; return (double) sz / (1024 * 1024 * 1024); } } /* * Memory debugging */ /* * From apr_bucket_alloc.c */ typedef struct node_header_t { apr_size_t size; apr_bucket_alloc_t *alloc; apr_memnode_t *memnode; struct node_header_t *next; } node_header_t; #define SIZEOF_NODE_HEADER_T APR_ALIGN_DEFAULT(sizeof(node_header_t)) /* * sax_get_bucket_mem_size */ apr_size_t sax_get_bucket_mem_size(bucket_sax * bs) { node_header_t *node = (node_header_t *) ((char *) bs - SIZEOF_NODE_HEADER_T); return node->size; } /** * Create a unique id for start/end tags * @return The unique id */ static se_id_t sax_event_get_unique_id(void) { static volatile se_id_t se_id; if (++se_id <= 0) { se_id = 1; } return se_id; } typedef struct { se_id_t se_id; } se_tag_t; /* * sax_event_set_start_id */ void sax_event_set_start_id(sax_ctx * ctx, void *event) { se_tag_t *tag = event; se_id_t *se_id = apr_array_push(ctx->starts); tag->se_id = *se_id = sax_event_get_unique_id(); } /* * sax_event_set_end_id */ void sax_event_set_end_id(sax_ctx * ctx, void *event) { se_tag_t *tag = event; se_id_t *se_id = apr_array_pop(ctx->starts); tag->se_id = -*se_id; } /* * sax_unify_name */ const xml_char_t *sax_unify_name(apr_pool_t * p, unq_set_t * set, const xml_char_t * name) { const xml_char_t *rv = NULL; if (!name) { /* NULL is already unified */ return rv; } rv = apr_table_get(set, name); if (!rv) { /* Not found, we need to create a new entry */ rv = apr_pstrdup(p, name); /* We add the new entry to the set */ apr_table_setn(set, rv, rv); } return rv; } /** * Lookup uri for a given prefix * @param c - The parsing context. This is needed for name unification. * @param prefix - The prefix (unified) * @return - The uri for the prefix, NULL if noe was found */ const xml_char_t *sax_lookup_uri(bucket_ctx * bc, const xml_char_t * prefix) { const xml_char_t *rv = NULL; apr_array_header_t *namespaces = bc->namespaces; int i; for (i = 0; i < namespaces->nelts; i++) { start_ns_t *sn = (start_ns_t *) (namespaces->elts + i * SAX_EVENT_SIZE(start_ns_t)); #define NVL(x, y) (x?x:y) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, bc->r_log, "sax_lookup_uri compares %#x:%s|%#x:%s.", sn->prefix, NVL((const char *) sn->prefix, "-"), prefix, NVL((const char *) prefix, "-")); if (sn->prefix == prefix) { return sn->uri; } } return NULL; } /** * Parse full names to extract the namespace * @param c - The parsing context. This is needed for name unification. * @param name - The full name (e. g. i18n:translate) * @param pname - The struct to hold the parsed name. */ static void sax_parse_name(sax_ctx * sc, const xml_char_t * name, ns_name_t * pname) { bucket_ctx *c = &sc->bctx; apr_pool_t *pool = sc->pool; char *p_sep = strchr(name, ':'); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, c->r_log, "sax_parse_name will parse %s.", name); if (p_sep) { *p_sep = '\0'; pname->name = sax_unify_name(pool, c->unq.name, p_sep + 1); pname->prefix = sax_unify_name(pool, c->unq.prefix, name); /* restore seperator */ *p_sep = ':'; } else { pname->name = sax_unify_name(pool, c->unq.name, name); /* Not found */ pname->prefix = NULL; } pname->uri = sax_lookup_uri(c, pname->prefix); #define NVL(x, y) (x?x:y) ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, c->r_log, "sax_parse_name parse %#x:%s|%#x:%s|%#x:%s.", pname->uri, NVL((const char *) pname->uri, "-"), pname->name, NVL((const char *) pname->name, "-"), pname->prefix, NVL((const char *) pname->prefix, "-") ); } /* * sax_ctx_init */ void sax_ctx_init_pool(sax_ctx * ctx, apr_pool_t * rp, apr_bucket_brigade * bb, ap_filter_t * f, abort_fn abort) { request_rec *r = f->r; ctx->bctx.unq.uri = apr_table_make(rp, 5); ctx->bctx.unq.name = apr_table_make(rp, 40); ctx->bctx.unq.prefix = apr_table_make(rp, 5); ctx->bctx.namespaces = apr_array_make(rp, 5, SAX_EVENT_SIZE(start_ns_t)); ctx->bctx.r_log = r; apr_pool_create(&ctx->bctx.p_tmp, rp); apr_pool_tag(ctx->bctx.p_tmp, "sax-brigade-temporary"); ctx->bctx.sum_mem = 0; ctx->mctx = apr_palloc(rp, sizeof(morph_ctx)); ctx->mctx->frag_buf = frag_create(rp); ctx->mctx->newns = apr_array_make(rp, 5, SAX_EVENT_SIZE(start_ns_t)); // ctx->mctx->r = r ; ctx->is_cdata = 0; ctx->starts = apr_array_make(rp, 20, sizeof(se_id_t)); ctx->pool = r->pool; ctx->bb = bb; ctx->list = bb->bucket_alloc; ctx->f = f; ctx->r = r; ctx->abort = abort; ctx->rv = APR_SUCCESS; } /* * sax_ctx_init_again */ void sax_ctx_init_again(sax_ctx * ctx, bucket_ctx * bctx, morph_ctx * mctx, apr_bucket_brigade * bb, ap_filter_t * f, abort_fn abort) { request_rec *r = f->r; // Not shure if this is a good idea. // apr_pool_t *rp = apr_pool_parent_get(bctx->p_tmp); apr_pool_t *rp = r->pool; ctx->bctx = *bctx; ctx->mctx = mctx; ctx->is_cdata = 0; ctx->starts = apr_array_make(rp, 20, sizeof(se_id_t)); ctx->pool = r->pool; ctx->bb = bb; ctx->list = bb->bucket_alloc; ctx->f = f; ctx->r = r; ctx->abort = abort; ctx->rv = APR_SUCCESS; } /* forward decl. */ static void sax_event_frag_write_NOT_IMPL(void *ignored, morph_ctx * mctx); /** * Initializes a given sax bucket. * @param bs - The bucket. * @param which - The sax event type of the bucket. * @param event - The event data. * @param bctx - The bucket context. * @param mctx - The morph context. */ static void sax_bucket_init(bucket_sax * bs, sax_event_e which, void *event, bucket_ctx * bctx, morph_ctx * mctx) { sax_bucket_set_which(bs, which); bs->event = event; bs->bctx = bctx; bs->mctx = mctx; } /***************************************************************************** * Create sax events *****************************************************************************/ /* * sax_bucket_create_ns */ bucket_sax *sax_bucket_create_ns(sax_ctx * c, const xml_char_t * prefix, const xml_char_t * uri) { /* buf is needed for byte computations */ char *buf = NULL; bucket_sax *bs = NULL; start_ns_t *sn = NULL; /* We allocate it all at once */ buf = apr_bucket_alloc(SAX_BASE_SIZE(start_ns_t), c->list); bs = (void *) buf; buf += SAX_BUCKET_SIZE; sn = (void *) buf; sn->prefix = sax_unify_name(c->pool, c->bctx.unq.prefix, prefix); sn->uri = sax_unify_name(c->pool, c->bctx.unq.uri, uri); sax_bucket_init(bs, START_NS, sn, &c->bctx, c->mctx); return bs; } const char XMLNS[] = "xmlns"; const char XMLNS_URI[] = "http://www.w3.org/2000/xmlns/"; /* * sax_bucket_recreate_elt */ bucket_sax *sax_bucket_recreate_elt(bucket_sax * bso, apr_bucket_alloc_t * list) { start_elt_t *old = bso->event; // char * is needed for byte computations char *buf = NULL; char *val = NULL; apr_size_t len_val = 0; apr_size_t len_att = 0; bucket_sax *bsn; start_elt_t *new; // We count the attributes while (old->atts[len_att].name.name) { len_val += strlen(old->atts[len_att].value) + 1; len_att++; } // We allocate it all at once buf = apr_bucket_alloc(SAX_BASE_SIZE(start_elt_t) + (len_att + 1) * sizeof(attr_t) + len_val, list); // We copy the "static" part bsn = (void *) buf; *bsn = *bso; buf += SAX_BUCKET_SIZE; bsn->event = new = (void *) buf; *new = *old; buf += SAX_EVENT_SIZE(start_elt_t); new->atts = (void *) buf; memcpy(new->atts, old->atts, (len_att + 1) * sizeof(attr_t)); val = buf + (len_att + 1) * sizeof(attr_t); int i = 0; while (old->atts[i].name.name) { strcpy(val, old->atts[i].value); new->atts[i].value = val; val += strlen(new->atts[i].value) + 1; i++; } return bsn; } /* * sax_bucket_create_elt */ bucket_sax *sax_bucket_create_elt(sax_ctx * c, const xml_char_t * name, const xml_char_t ** atts) { // The bucket is created on the stack bucket_sax bs; start_elt_t se; apr_size_t len_att = 0; se.empty = 0; while (atts[len_att]) { len_att++; } len_att = len_att / 2; sax_bucket_init(&bs, START_ELT, &se, &c->bctx, c->mctx); sax_parse_name(c, name, &se.name); se.atts = apr_bucket_alloc(sizeof(attr_t) * (len_att + 1), c->list); int i = 0; int j = 0; while (atts[i]) { sax_parse_name(c, atts[i], &se.atts[j].name); i++; // We assign the attribute value se.atts[j].value = atts[i]; i++; j++; } // terminatig attribute se.atts[j].name.uri = NULL; se.atts[j].name.name = NULL; se.atts[j].name.prefix = NULL; se.atts[j].value = NULL; // and then recreated in bucket memory. bucket_sax *rv = sax_bucket_recreate_elt(&bs, c->list); // This avoids duplication of the code that knows about // the buckets memory layout. apr_bucket_free(se.atts); return rv; } /* * sax_bucket_create_elt_2 */ bucket_sax *sax_bucket_create_elt_2(sax_ctx * c, const xml_char_t * localname, const xml_char_t * prefix, const xml_char_t * URI, int nb_attributes, const xml_char_t ** attributes) { // Create the bucket on the stack bucket_sax bs; start_elt_t se; apr_pool_t *pool = c->pool; all_unq_t unq = c->bctx.unq; se.name.name = sax_unify_name(pool, unq.name, localname); se.name.prefix = sax_unify_name(pool, unq.prefix, prefix); se.name.uri = sax_unify_name(pool, unq.uri, URI); se.empty = 0; // leave a trace message ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, c->bctx.r_log, "sax_bucket_create_elt_2 called for \"%s:%s\".", prefix, localname); // and initialize the bucket sax_bucket_init(&bs, START_ELT, &se, &c->bctx, c->mctx); // including its attributes. // First allocate temporary memory for the attributes. se.atts = apr_bucket_alloc(sizeof(attr_t) * (nb_attributes + 1), c->list); int i = 0; int j = 0; void *mem_val = NULL; char *ptr_val = NULL; apr_off_t len_val = 0; // check if and how much bucket memory we need // for copying attribute values for (; i < nb_attributes; i++, j += 5) { if (*(attributes[j + 4])) { len_val += attributes[j + 4] - attributes[j + 3] + 1; } } if (len_val > 0) { // allocate memory for attribute values ptr_val = mem_val = apr_bucket_alloc(len_val, c->list); } // Walk the attributes, for (i = 0, j = 0; i < nb_attributes; i++, j += 5) { // fill the current attribute se.atts[i].name.name = sax_unify_name(pool, unq.name, attributes[j]); se.atts[i].name.prefix = sax_unify_name(pool, unq.prefix, attributes[j + 1]); se.atts[i].name.uri = sax_unify_name(pool, unq.uri, attributes[j + 2]); if (*(attributes[j + 4])) { se.atts[i].value = strncpy(ptr_val, attributes[j + 3], attributes[j + 4] - attributes[j + 3]); ptr_val += attributes[j + 4] - attributes[j + 3]; *ptr_val = '\0'; ptr_val++; } else { se.atts[i].value = attributes[j + 3]; } } // and the terminatig attribute. se.atts[i].name.uri = NULL; se.atts[i].name.name = NULL; se.atts[i].name.prefix = NULL; se.atts[i].value = NULL; // Recreate the bucket in bucket memory bucket_sax *b = sax_bucket_recreate_elt(&bs, c->list); // and free the temporary memory. if (mem_val) { apr_bucket_free(mem_val); } if (se.atts) { apr_bucket_free(se.atts); } return b; } /* * sax_bucket_create_empty */ bucket_sax *sax_bucket_create_empty(sax_ctx * c, sax_event_e which) { bucket_sax *bs = apr_bucket_alloc(SAX_BUCKET_SIZE, c->list); sax_bucket_init(bs, which, NULL, &c->bctx, c->mctx); return bs; } /* * sax_bucket_create_char */ bucket_sax *sax_bucket_create_char(sax_ctx * c, const xml_char_t * ebuf, int len, int encode) { char *buf = apr_bucket_alloc(SAX_BASE_SIZE(character_t) + len + 1, c->list); character_t *chr = NULL; bucket_sax *bs = (void *) buf; request_rec *r_log = c->bctx.r_log; buf += SAX_BUCKET_SIZE; chr = (void *) buf; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r_log, "sax_bucket_create_char called with \"%s\".", apr_pstrmemdup(c->bctx.p_tmp, ebuf, len)); sax_bucket_init(bs, CHARACTER, chr, &c->bctx, c->mctx); chr->len = len; chr->encode = encode; memcpy(chr->text, ebuf, len); /* text is \0 terminated in addition to the length */ chr->text[len] = '\0'; ap_assert(bs->which == CHARACTER); return bs; } /* * sax_bucket_create_xml_decl */ bucket_sax *sax_bucket_create_xml_decl(sax_ctx * c, const xml_char_t * version, const xml_char_t * encoding, int standalone) { char *buf = apr_bucket_alloc(SAX_BASE_SIZE(xml_decl_t), c->list); xml_decl_t *xd = NULL; bucket_sax *bs = (void *) buf; buf += SAX_BUCKET_SIZE; xd = (void *) buf; xd->version = apr_pstrdup(c->pool, version); xd->encoding = apr_pstrdup(c->pool, encoding); xd->standalone = standalone; sax_bucket_init(bs, XML_DECL, xd, &c->bctx, c->mctx); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, c->bctx.r_log, "xml_decl sax bucket with \"%s\", \"%s\", %d created", xd->version, xd->encoding, xd->standalone); return bs; } /* * sax_bucket_create_proc_instr */ bucket_sax *sax_bucket_create_proc_instr(sax_ctx * c, const xml_char_t * target, const xml_char_t * data) { const int len_tg = strlen(target); const int len_d = strlen(data); char *buf = apr_bucket_alloc(SAX_BASE_SIZE(proc_instr_t) + len_tg + 1 + len_d + 1, c->list); proc_instr_t *pi = NULL; bucket_sax *bs = (void *) buf; buf += SAX_BUCKET_SIZE; pi = (void *) buf; buf += SAX_EVENT_SIZE(proc_instr_t); strcpy(buf, target); pi->target = buf; buf += len_tg + 1; strcpy(buf, data); pi->data = buf; sax_bucket_init(bs, PROC_INSTR, pi, &c->bctx, c->mctx); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, c->bctx.r_log, "proc_instr sax bucket with \"%s\", \"%s\" created", pi->target, pi->data); return bs; } /***************************************************************************** * SAX event helpers *****************************************************************************/ /** * Writes a ns name to a frag buffer. * @param ns - The ns name. * @param frag_buf - The buffer. */ static void sax_event_frag_write_ns_name(const ns_name_t * ns, frag_buffer_t * frag_buf) { const int len_pfx = ns->prefix ? strlen(ns->prefix) : 0; // An xmlns default namespaces yields an attribute that has // a prefix and an uri, but no name. const int len_nm = ns->name ? strlen(ns->name) : 0; if (len_pfx > 0) { frag_write(frag_buf, ns->prefix, len_pfx); } if (len_pfx > 0 && len_nm > 0) { frag_write(frag_buf, ":", 1); } if (len_nm > 0) { frag_write(frag_buf, ns->name, len_nm); } } /** * Writes an attribute to a frag buffer. * @param attr - The attribute. * @param frag_buf - The buffer. */ static void sax_event_frag_write_attr(const attr_t * attr, frag_buffer_t * frag_buf) { sax_event_frag_write_ns_name(&attr->name, frag_buf); frag_write(frag_buf, "=\"", 2); frag_write_enc(frag_buf, attr->value); frag_write(frag_buf, "\"", 1); } /***************************************************************************** * Parser Helpers *****************************************************************************/ /* * sax_pass_buckets */ apr_status_t sax_pass_buckets(sax_ctx * sctx, int do_abort) { // Test the request pool sax_check_pool(sctx->r->pool); // we pass the current brigade ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, sctx->r, "Passing brigade to %s.", sctx->f->next->frec->name); sctx->rv = ap_pass_brigade(sctx->f->next, sctx->bb); // clean up the tmp pool apr_pool_clear(sctx->bctx.p_tmp); // and clear the brigade for reuse apr_brigade_cleanup(sctx->bb); if (do_abort && (sctx->rv != APR_SUCCESS || sctx->f->c->aborted)) { sctx->abort(sctx->f); } // Test the request pool sax_check_pool(sctx->r->pool); return sctx->rv; } /* * sax_bucket_wrap */ apr_bucket *sax_bucket_wrap(sax_ctx * c, bucket_sax * e) { apr_bucket_brigade *bb = c->bb; apr_bucket *b = apr_bucket_alloc(sizeof(*b), bb->bucket_alloc); APR_BUCKET_INIT(b); /* make does not assign this */ b->list = bb->bucket_alloc; b->free = apr_bucket_free; // start and length are meaningless for SAX buckets b = apr_bucket_shared_make(b, e, -1, -1); b->type = &bucket_type_sax; return b; } static const apr_size_t SZ_PASS_LIMIT = 4 * 1024 * 1024; /* * sax_bucket_append_to */ apr_bucket *sax_bucket_append_to(sax_ctx * c, bucket_sax * e, apr_bucket_brigade * bb) { apr_bucket *b = sax_bucket_wrap(c, e); APR_BRIGADE_INSERT_TAIL(bb, b); bucket_ctx *bctx = e->bctx; bctx->sum_mem += sax_get_bucket_mem_size(e); const char *unit; double sz = sax_hr_size(sax_get_bucket_mem_size(e) - SIZEOF_NODE_HEADER_T, &unit); ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, bctx->r_log, "SAX %s bucket with %.4g %s bytes usable mem. appended.", sax_event_which_to_str(e->which), sz, unit); /* if (bctx->sum_mem > SZ_PASS_LIMIT) { sz = sax_hr_size(bctx->sum_mem, &unit); ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, bctx->r_log, "%.4g %s bytes in use. Passing buckets.", sz, unit); sax_pass_buckets(c, 1); } */ return b; } /* * sax_log_req_create */ /* request_rec *sax_log_req_create(request_rec * r) { apr_pool_t *rp = r->pool; // We copy the request rec request_rec *rv = apr_pmemdup(rp, rp, sizeof(request_rec)); // and change the copies pool apr_pool_create(&rv->pool, rp); return rv; } */ /* * sax_log_req_clear */ /* void sax_log_req_clear(request_rec * r) { apr_pool_clear(r->pool); } */ /***************************************************************************** * SAX event methods *****************************************************************************/ /** * Writes a start element to a frag buffer. * @param se - The start element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_START_ELT(void *event, morph_ctx * mctx) { start_elt_t *se = event; const attr_t *attr = se->atts; frag_write(mctx->frag_buf, "<", 1); /* tag */ sax_event_frag_write_ns_name(&se->name, mctx->frag_buf); /* add namespace attributes */ start_ns_t *pns = NULL; while (pns = apr_array_pop(mctx->newns)) { attr_t ns_attr; ns_attr.name.uri = XMLNS_URI; /* note that this name is a prefix */ ns_attr.name.name = pns->prefix; ns_attr.name.prefix = XMLNS; ns_attr.value = pns->uri; frag_write(mctx->frag_buf, " ", 1); sax_event_frag_write_attr(&ns_attr, mctx->frag_buf); } /* add other attributes */ for (; attr->value; attr++) { frag_write(mctx->frag_buf, " ", 1); sax_event_frag_write_attr(attr, mctx->frag_buf); } if (se->empty) { frag_write(mctx->frag_buf, " />", 3); } else { frag_write(mctx->frag_buf, ">", 1); } } /** * Writes an end element to a frag buffer. * @param ee - The end element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_END_ELT(void *event, morph_ctx * mctx) { end_elt_t *ee = event; if (!ee->empty) { frag_write(mctx->frag_buf, "name, mctx->frag_buf); frag_write(mctx->frag_buf, ">", 1); } } /** * Writes a character event to a frag buffer. * @param c - The characterelement. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_CHARACTER(void *event, morph_ctx * mctx) { character_t *c = event; if (c->encode) { frag_write_enc(mctx->frag_buf, c->text); } else { frag_write(mctx->frag_buf, c->text, c->len); } } /** * Writes a start cd event to a frag buffer. * @param ignored - ignored. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_START_CD(void *ignored, morph_ctx * mctx) { frag_write(mctx->frag_buf, "frag_buf, "]]>", 3); } /** * Writes a comment event to a frag buffer. * @param c - The character element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_COMMENT(void *event, morph_ctx * mctx) { character_t *c = event; frag_write(mctx->frag_buf, "", 3); } /** * Writes an XML declaration to a frag buffer. * @param se - The start element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_XML_DECL(void *event, morph_ctx * mctx) { xml_decl_t *xd = event; if (!xd->version) { // No version, no xml decl. return; } frag_write(mctx->frag_buf, "frag_buf, xd->version, strlen(xd->version)); frag_write(mctx->frag_buf, "\"", 1); if (xd->encoding) { frag_write(mctx->frag_buf, " encoding=\"", 11); frag_write(mctx->frag_buf, xd->encoding, strlen(xd->encoding)); frag_write(mctx->frag_buf, "\"", 1); } if (xd->standalone == 1) { frag_write(mctx->frag_buf, " standalone=\"yes\"", 17); } else if (xd->standalone == 0) { frag_write(mctx->frag_buf, " standalone=\"no\"", 16); } frag_write(mctx->frag_buf, "?>", 2); } /** * Writes a processing instruction to a frag buffer. * @param event - The proc. instr. event. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_PROC_INSTR(void *event, morph_ctx * mctx) { proc_instr_t *pi = event; frag_write(mctx->frag_buf, "frag_buf, pi->target, strlen(pi->target)); frag_write(mctx->frag_buf, " ", 1); frag_write(mctx->frag_buf, pi->data, strlen(pi->data)); frag_write(mctx->frag_buf, "?>", 2); } /** * Writes a "not implemented" frag buffer. * @param ignored - The character element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_NOT_IMPL(void *ignored, morph_ctx * mctx) { frag_write(mctx->frag_buf, "[frag_write NOT IMPLEMENTED]", 28); } /** * Writes nothing. * @param ignored - The character element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_START_NS(void *event, morph_ctx * mctx) { start_ns_t *sn = event; /* Push the ns event on the newns stack */ start_ns_t *psn = apr_array_push(mctx->newns); /* We copy the event. This way we do not have * to care about the bucket beeing destroyed. */ *psn = *sn; } /** * Writes nothing. * @param ignored - The character element. * @param mctx - The context holding the frag_buffer. */ static void sax_event_frag_write_NO_OP(void *ignored, morph_ctx * mctx) { } /** * Writes a sax bucket to a frag buffer. * @param sb - The sax bucket. * @param mctx - The context holding the buffer. */ static void sax_event_frag_write(bucket_sax * sb, morph_ctx * mctx) { frag_write_f frag_write = sb->frag_write; frag_write(sb->event, mctx); } /* * sax_bucket_set_which */ void sax_bucket_set_which(bucket_sax * bs, sax_event_e which) { #define CASE_WHICH(x) case x: bs->frag_write= sax_event_frag_write_##x ; break ; bs->which = which; switch (which) { CASE_WHICH(START_ELT) CASE_WHICH(END_ELT) CASE_WHICH(PROC_INSTR) CASE_WHICH(START_CD) CASE_WHICH(END_CD) CASE_WHICH(CHARACTER) // CASE_WHICH(WHITE) CASE_WHICH(COMMENT) CASE_WHICH(XML_DECL) CASE_WHICH(START_NS) case END_NS: bs->frag_write = sax_event_frag_write_NO_OP; break; case DEFAULT: bs->frag_write = sax_event_frag_write_CHARACTER; break; default: bs->frag_write = sax_event_frag_write_NOT_IMPL; break; } } /***************************************************************************** * SAX bucket methods *****************************************************************************/ static void sax_bucket_destroy(void *data) { bucket_sax *bs = data; if (apr_bucket_shared_destroy(data)) { bs->bctx->sum_mem -= sax_get_bucket_mem_size(bs); /* All create functions only allocate one block */ apr_bucket_free(data); } else { ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, bs->bctx->r_log, "SAX bucket with %d bytes not destroyed.", sax_get_bucket_mem_size(bs)); } } /* * sax_bucket_read */ static apr_status_t sax_bucket_read(apr_bucket * e, const char **str, apr_size_t * len, apr_read_type_e block) { bucket_sax *bs = e->data; frag_buffer_t *frag_buf = bs->mctx->frag_buf; sax_event_debug_log(APLOG_MARK, bs, "sax_bucket_read called for type %s.", sax_event_which_to_str(bs->which)); if (bs->which == START_ELT) { start_elt_t *se = bs->event; sax_event_debug_log(APLOG_MARK, bs, "start_elt has name %s.", se->name.name); } if (bs->which == END_ELT) { end_elt_t *ee = bs->event; sax_event_debug_log(APLOG_MARK, bs, "end_elt has name %s.", ee->name.name); } if (bs->which == XML_DECL) { xml_decl_t *xd = bs->event; sax_event_debug_log(APLOG_MARK, bs, "About to write xml_decl sax bucket with \"%s\", \"%s\", %d.", xd->version, xd->encoding, xd->standalone); } if (bs->which == START_ELT) { // Check if this is an empty bucket. In case of an empty // bucket both start and end bucket should be in the same brigade. apr_bucket *nx = APR_BUCKET_NEXT(e); if (BUCKET_IS_SAX(nx) && (sax_inspect_se_id(e) == -sax_inspect_se_id(nx))) { // This is a pair bucket_sax *bs_nx = nx->data; start_elt_t *se = bs->event; end_elt_t *ee = bs_nx->event; se->empty = ee->empty = 1; } } /* write the sax event to the frag buffer */ frag_clean(frag_buf); sax_event_frag_write(bs, bs->mctx); /* write the frag buffer to a linear buffer */ const apr_size_t len_loc = frag_length(frag_buf); char *buf_loc = apr_bucket_alloc(len_loc, e->list); frag_to_buffer(frag_buf, 0, buf_loc, len_loc); frag_clean(frag_buf); /* Put the linear buffer in the e bucket morphed to a heap bucket */ apr_bucket_heap_make(e, buf_loc, len_loc, apr_bucket_free); /* return the heap bucket buffer */ apr_bucket_heap *h = e->data; *str = h->base + e->start; *len = e->length; //apr_pool_clear(bs->bctx->p_tmp); /* clean up the sax bucket */ sax_bucket_destroy(bs); return APR_SUCCESS; } APU_DECLARE_DATA const apr_bucket_type_t bucket_type_sax = { "SAX", 5, APR_BUCKET_DATA, sax_bucket_destroy, sax_bucket_read, apr_bucket_setaside_noop, apr_bucket_split_notimpl, apr_bucket_shared_copy };