示例#1
0
文件: xlio.c 项目: navoj/xlisp
/* xlPutC - put a character to a file or stream */
xlEXPORT void xlPutC(xlValue fptr,int ch)
{
    int flags;
    
    /* count the character */
    ++xlfsize;

    /* check for output to nil */
    if (fptr == xlNil)
        return;

    /* otherwise, check for output to an unnamed stream */
    switch (xlNodeType(fptr)) {
    case xlFSTREAM:
        fstream_putc(fptr,ch);
        break;
    case xlUSTREAM:
        ustream_putc(fptr,ch);
        break;
    case xlOSTREAM:
        ostream_putc(fptr,ch);
        break;
    default:
        xlError("expecting stream",fptr);
        break;
    }

    /* set the beginning of line flag */
    flags = xlGetPFlags(fptr);
    if (ch == '\n')
        flags |= xlpfBOL;
    else
        flags &= ~xlpfBOL;
    xlSetPFlags(fptr,flags);
}
示例#2
0
/**
 * Emit attributes.
 */
static void
xfmt_handle_pass2_attr(const char *uri,
                       const char *local, const char *value, void *data)
{
    struct xfmt_pass2 *xp2 = data;
    int c;
    bool apos_escape = FALSE;
    size_t len;
    size_t overhead;

    if (uri != NULL) {
        ostream_printf(xp2->os, " %s:", xfmt_uri_to_prefix(xp2, uri));
    } else {
        ostream_putc(xp2->os, ' ');
    }

    /*
     * Inspect value to select proper quoting.
     */

    c = xfmt_quoting_char(value);

    if ('\0' == c) {
        apos_escape = TRUE;
        c = '\'';	/* We'll be quoting "'" so it's safe to use */
    }

    /*
     * Now check for escaping of any '&', '<' or '>'.
     */

    overhead = xfmt_text_escape_overhead(value, TRUE, apos_escape, &len);

    ostream_printf(xp2->os, "%s=%c", local, c);

    if (0 == overhead) {
        ostream_write(xp2->os, value, len);
    } else {
        char *escaped = xfmt_text_escape(value, TRUE, apos_escape,
                                         len + overhead);
        ostream_write(xp2->os, escaped, len + overhead);
        hfree(escaped);
    }

    ostream_putc(xp2->os, c);
}
示例#3
0
/**
 * Indent if we just emitted a new-line.
 */
static void
xfmt_indent(const struct xfmt_pass2 *xp2)
{
    if (xp2->options & XFMT_O_NO_INDENT)
        return;

    if (xp2->last_was_nl) {
        unsigned i;

        for (i = 1; i < xp2->depth; i++) {
            ostream_putc(xp2->os, '\t');
        }
    }
}
示例#4
0
/**
 * Pass 2 handler on each tree node leave.
 */
static void
xfmt_handle_pass2_leave(void *node, void *data)
{
    xnode_t *xn = node;
    struct xfmt_pass2 *xp2 = data;

    if (xnode_is_element(xn)) {
        const char *uri = xnode_element_ns(xn);

        xfmt_indent(xp2);

        /*
         * We don't emit the URI if it is that of the default namespace.
         */

        if (
            uri != NULL && xp2->default_ns != NULL &&
            0 == strcmp(uri, xp2->default_ns)
        ) {
            uri = NULL;
        }

        if (uri != NULL) {
            const char *pre = xfmt_uri_to_prefix(xp2, uri);
            ostream_printf(xp2->os, "</%s:%s>", pre, xnode_element_name(xn));
        } else {
            ostream_printf(xp2->os, "</%s>", xnode_element_name(xn));
        }
        if (!(xp2->options & XFMT_O_SINGLE_LINE)) {
            ostream_putc(xp2->os, '\n');
        }
        /* Reset for next element */
        xp2->had_text = FALSE;
        xp2->last_was_nl = TRUE;
    }

    xfmt_pass2_leaving(xp2);
}
示例#5
0
/**
 * Pass 2 handler on each tree node entry.
 */
static bool
xfmt_handle_pass2_enter(const void *node, void *data)
{
    const xnode_t *xn = node;
    struct xfmt_pass2 *xp2 = data;

    xp2->depth++;

    if (xnode_is_element(xn)) {
        GSList *ns = xfmt_ns_declarations(xp2, xn);
        const char *nsuri = xnode_element_ns(xn);

        if (!xp2->had_text && !xp2->last_was_nl) {
            if (!(xp2->options & XFMT_O_SINGLE_LINE))
                ostream_putc(xp2->os, '\n');
            xp2->last_was_nl = TRUE;
        }

        xfmt_indent(xp2);

        /*
         * Look for the namespace matching the default namespace, in which
         * case we don't have to emit it.
         */

        if (
            nsuri != NULL && xp2->default_ns != NULL &&
            0 == strcmp(nsuri, xp2->default_ns)
        ) {
            nsuri = NULL;
        }

        if (nsuri != NULL) {
            const char *prefix = xfmt_uri_to_prefix(xp2, nsuri);
            ostream_printf(xp2->os, "<%s:%s", prefix, xnode_element_name(xn));
        } else {
            ostream_printf(xp2->os, "<%s", xnode_element_name(xn));
        }

        /*
         * Install default namespace on the root element, if any.
         */

        if (1 == xp2->depth && xp2->default_ns != NULL) {
            int c = xfmt_quoting_char(xp2->default_ns);
            g_assert(c != '\0');
            ostream_printf(xp2->os, " xmlns=%c%s%c", c, xp2->default_ns, c);
        }

        /*
         * Declare namespaces for the element's scope.
         */

        xfmt_pass2_declare_ns(xp2, ns);
        g_slist_free(ns);

        /*
         * Emit attributes.
         */

        xnode_prop_foreach(xn, xfmt_handle_pass2_attr, xp2);

        /*
         * Handle content-less elements specially: we don't let the
         * "leave" callback run.
         *
         * We consider an element with a single empty text child as
         * content-less, so we test with xnode_is_empty() instead of
         * !xnode_has_content().
         */

        xp2->had_text = FALSE;

        if (xnode_is_empty(xn)) {
            ostream_write(xp2->os, XFMT_EMPTY, CONST_STRLEN(XFMT_EMPTY));
            if (!(xp2->options & XFMT_O_SINGLE_LINE))
                ostream_putc(xp2->os, '\n');
            xp2->last_was_nl = TRUE;
            xfmt_pass2_leaving(xp2);	/* No children, no "leave" callback */
            return FALSE;
        }

        ostream_write(xp2->os, XFMT_GT, CONST_STRLEN(XFMT_GT));
        xp2->last_was_nl = FALSE;

    } else if (xnode_is_text(xn)) {
        const char *text = xnode_text(xn);
        size_t len;
        size_t overhead;
        bool amp;

        if (xp2->options & XFMT_O_SKIP_BLANKS) {
            const char *start;
            size_t tlen;

            start = xfmt_strip_blanks(text, &tlen);
            if (0 == tlen)
                goto ignore;

            /* FIXME: handle blank collapsing */
            (void) start;
        }

        /*
         * If text is known to have entities, we must not escape the '&'.
         * This means the generated XML must define that entity in the DTD
         * part of the tree.
         *
         * Computes the required overhead to fully escape the text (0 meaning
         * that no escaping is required).  If the overhead is larger than
         * a leading "<![CDATA[" and a closing ""]]>", we can emit a CDATA
         * section instead, provided the text does not contain "]]>".
         */

        amp = !xnode_text_has_entities(xn);
        overhead = xfmt_text_escape_overhead(text, amp, FALSE, &len);

        if (0 == overhead) {
            ostream_write(xp2->os, text, len);
        } else if (
            overhead >= XFMT_CDATA_OVERHEAD &&
            NULL == strstr(text, XFMT_CDATA_END)
        ) {
            ostream_write(xp2->os,
                          XFMT_CDATA_START, CONST_STRLEN(XFMT_CDATA_START));
            ostream_write(xp2->os, text, len);
            ostream_write(xp2->os,
                          XFMT_CDATA_END, CONST_STRLEN(XFMT_CDATA_END));
        } else {
            char *escaped = xfmt_text_escape(text, amp, FALSE, len + overhead);
            ostream_write(xp2->os, escaped, len + overhead);
            hfree(escaped);
        }

        xp2->last_was_nl = FALSE;
        xp2->had_text = TRUE;
    }

ignore:
    return TRUE;
}
示例#6
0
/**
 * Extended XML formatting of a tree.
 *
 * Namespaces, if any, are automatically assigned a prefix, whose format
 * is "ns%u", the counter being incremented from 0.
 *
 * Users can supply a vector mapping namespaces to prefixes, so that they
 * can force specific prefixes for a given well-known namespace.
 *
 * If there is a default namespace, all the tags belonging to that namespace
 * are emitted without any prefix.
 *
 * The output stream must be explicitly closed by the user upon return.
 *
 * Options can be supplied to tune the output:
 *
 * - XFMT_O_SKIP_BLANKS will skip pure white space nodes.
 * - XFMT_O_COLLAPSE_BLANKS will replace consecutive blanks with 1 space
 * - XFMT_O_NO_INDENT requests that no indentation of the tree be made.
 * - XFMT_O_PROLOGUE emits a leading <?xml?> prologue.
 * - XFMT_O_FORCE_10 force generation of XML 1.0
 * - XFMT_O_SINGLE_LINE emits XML as one big line (implies XFMT_O_NO_INDENT).
 *
 * @param root			the root of the tree to dump
 * @param os			the output stream where tree is dumped
 * @param options		formatting options, as documented above
 * @param pvec			a vector of prefixes to be used for namespaces
 * @param pvcnt			amount of entries in vector
 * @param default_ns	default namespace to install at root element
 *
 * @return TRUE on success.
 */
bool
xfmt_tree_extended(const xnode_t *root, ostream_t *os, uint32 options,
                   const struct xfmt_prefix *pvec, size_t pvcnt, const char *default_ns)
{
    struct xfmt_pass1 xp1;
    struct xfmt_pass2 xp2;
    struct xfmt_invert_ctx ictx;
    const char *dflt_ns;

    g_assert(root != NULL);
    g_assert(os != NULL);

    if (options & XFMT_O_COLLAPSE_BLANKS) {
        /* FIXME */
        g_carp("XFMT_O_COLLAPSE_BLANKS not supported yet");
        stacktrace_where_print(stderr);
    }

    if (options & XFMT_O_SINGLE_LINE)
        options |= XFMT_O_NO_INDENT;

    /*
     * First pass: look at namespaces and construct a table recording the
     * earliest tree depth at which a namespace is used.
     */

    ZERO(&xp1);
    xp1.uri2node = htable_create(HASH_KEY_STRING, 0);
    xp1.uri2prefix = nv_table_make(FALSE);

    if (default_ns != NULL)
        xp1.attr_uris = hset_create(HASH_KEY_STRING, 0);

    htable_insert_const(xp1.uri2node, VXS_XML_URI, root);

    xnode_tree_enter_leave(deconstify_pointer(root),
                           xfmt_handle_pass1_enter, xfmt_handle_pass1_leave, &xp1);

    g_assert(0 == xp1.depth);		/* Sound traversal */

    /*
     * If there was a default namespace, make sure it is used in the tree.
     * Otherwise, discard it.
     */

    if (default_ns != NULL) {
        if (NULL == htable_lookup(xp1.uri2node, default_ns)) {
            g_carp("XFMT default namespace '%s' is not needed", default_ns);
            dflt_ns = NULL;
        } else {
            dflt_ns = default_ns;
        }
    } else {
        dflt_ns = NULL;
    }

    /*
     * Prepare context for second pass.
     */

    ZERO(&xp2);
    xp2.node2uri = htable_create(HASH_KEY_SELF, 0);
    xp2.os = os;
    xp2.options = options;
    xp2.default_ns = dflt_ns;
    xp2.attr_uris = xp1.attr_uris;
    xp2.uri2prefix = xp1.uri2prefix;
    xp2.uris = symtab_make();
    xp2.prefixes = symtab_make();
    xp2.depth = 0;
    xp2.pcount = 0;
    xp2.last_was_nl = TRUE;

    /*
     * Iterate over the hash table we've built to create a table indexed
     * by tree node and listing the namespaces to declare for that node.
     */

    ictx.uri2node = xp1.uri2node;
    ictx.node2uri = xp2.node2uri;

    htable_foreach(xp1.uri2node, xfmt_invert_uri_kv, &ictx);
    htable_free_null(&xp1.uri2node);

    /*
     * Emit prologue if requested.
     */

    if (options & XFMT_O_PROLOGUE) {
        if (options & XFMT_O_FORCE_10) {
            ostream_write(os, XFMT_DECL_10, CONST_STRLEN(XFMT_DECL_10));
        } else {
            ostream_write(os, XFMT_DECL, CONST_STRLEN(XFMT_DECL));
        }
        if (!(options & XFMT_O_SINGLE_LINE)) {
            ostream_putc(os, '\n');
        }
    }

    xfmt_prefix_declare(&xp2, VXS_XML_URI, VXS_XML);

    /*
     * Prepare user-defined URI -> prefix mappings.
     */

    if (pvcnt != 0) {
        size_t i;

        for (i = 0; i < pvcnt; i++) {
            const struct xfmt_prefix *p = &pvec[i];

            xfmt_prefix_declare(&xp2, p->uri, p->prefix);
        }
    }

    /*
     * Second pass: generation.
     */

    xnode_tree_enter_leave(deconstify_pointer(root),
                           xfmt_handle_pass2_enter, xfmt_handle_pass2_leave, &xp2);

    g_assert(0 == xp2.depth);		/* Sound traversal */

    /*
     * Done, cleanup.
     */

    nv_table_free_null(&xp2.uri2prefix);
    symtab_free_null(&xp2.prefixes);
    symtab_free_null(&xp2.uris);
    htable_free_null(&xp2.node2uri);
    hset_free_null(&xp2.attr_uris);

    return !ostream_has_ioerr(os);
}