diff options
Diffstat (limited to 'contrib/libxo/libxo/libxo.c')
-rw-r--r-- | contrib/libxo/libxo/libxo.c | 581 |
1 files changed, 497 insertions, 84 deletions
diff --git a/contrib/libxo/libxo/libxo.c b/contrib/libxo/libxo/libxo.c index cceebfa..194a096 100644 --- a/contrib/libxo/libxo/libxo.c +++ b/contrib/libxo/libxo/libxo.c @@ -19,7 +19,8 @@ * http://juniper.github.io/libxo/libxo-manual.html * * For first time readers, the core bits of code to start looking at are: - * - xo_do_emit() -- the central function of the library + * - xo_do_emit() -- parse and emit a set of fields + * - xo_do_emit_fields -- the central function of the library * - xo_do_format_field() -- handles formatting a single field * - xo_transiton() -- the state machine that keeps things sane * and of course the "xo_handle_t" data structure, which carries all @@ -120,6 +121,7 @@ const char xo_version[] = LIBXO_VERSION; const char xo_version_extra[] = LIBXO_VERSION_EXTRA; +static const char xo_default_format[] = "%s"; #ifndef UNUSED #define UNUSED __attribute__ ((__unused__)) @@ -338,6 +340,7 @@ typedef unsigned long xo_xff_flags_t; #define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ #define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ +#define XFF_ARGUMENT (1<<21) /* Content provided via argument */ /* Flags to turn off when we don't want i18n processing */ #define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) @@ -1046,7 +1049,7 @@ xo_is_utf8 (char ch) return (ch & 0x80); } -static int +static inline int xo_utf8_to_wc_len (const char *buf) { unsigned b = (unsigned char) *buf; @@ -1105,9 +1108,13 @@ xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz) * bits we pull off the first character is dependent on the length, * but we put 6 bits off all other bytes. */ -static wchar_t +static inline wchar_t xo_utf8_char (const char *buf, int len) { + /* Most common case: singleton byte */ + if (len == 1) + return (unsigned char) buf[0]; + int i; wchar_t wc; const unsigned char *cp = (const unsigned char *) buf; @@ -1281,6 +1288,195 @@ xo_data_escape (xo_handle_t *xop, const char *str, int len) xo_buf_escape(xop, &xop->xo_data, str, len, 0); } +#ifdef LIBXO_NO_RETAIN +/* + * Empty implementations of the retain logic + */ + +void +xo_retain_clear_all (void) +{ + return; +} + +void +xo_retain_clear (const char *fmt UNUSED) +{ + return; +} +static void +xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED, + unsigned num_fields UNUSED) +{ + return; +} + +static int +xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED, + unsigned *nump UNUSED) +{ + return -1; +} + +#else /* !LIBXO_NO_RETAIN */ +/* + * Retain: We retain parsed field definitions to enhance performance, + * especially inside loops. We depend on the caller treating the format + * strings as immutable, so that we can retain pointers into them. We + * hold the pointers in a hash table, so allow quick access. Retained + * information is retained until xo_retain_clear is called. + */ + +/* + * xo_retain_entry_t holds information about one retained set of + * parsed fields. + */ +typedef struct xo_retain_entry_s { + struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */ + unsigned long xre_hits; /* Number of times we've hit */ + const char *xre_format; /* Pointer to format string */ + unsigned xre_num_fields; /* Number of fields saved */ + xo_field_info_t *xre_fields; /* Pointer to fields */ +} xo_retain_entry_t; + +/* + * xo_retain_t holds a complete set of parsed fields as a hash table. + */ +#ifndef XO_RETAIN_SIZE +#define XO_RETAIN_SIZE 6 +#endif /* XO_RETAIN_SIZE */ +#define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE) + +typedef struct xo_retain_s { + xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE]; +} xo_retain_t; + +static THREAD_LOCAL(xo_retain_t) xo_retain; +static THREAD_LOCAL(unsigned) xo_retain_count; + +/* + * Simple hash function based on Thomas Wang's paper. The original is + * gone, but an archive is available on the Way Back Machine: + * + * http://web.archive.org/web/20071223173210/\ + * http://www.concentric.net/~Ttwang/tech/inthash.htm + * + * For our purposes, we can assume the low four bits are uninteresting + * since any string less that 16 bytes wouldn't be worthy of + * retaining. We toss the high bits also, since these bits are likely + * to be common among constant format strings. We then run Wang's + * algorithm, and cap the result at RETAIN_HASH_SIZE. + */ +static unsigned +xo_retain_hash (const char *fmt) +{ + volatile uintptr_t iptr = (uintptr_t) (const void *) fmt; + + /* Discard low four bits and high bits; they aren't interesting */ + uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1))); + + val = (val ^ 61) ^ (val >> 16); + val = val + (val << 3); + val = val ^ (val >> 4); + val = val * 0x3a8f05c5; /* My large prime number */ + val = val ^ (val >> 15); + val &= RETAIN_HASH_SIZE - 1; + + return val; +} + +/* + * Walk all buckets, clearing all retained entries + */ +void +xo_retain_clear_all (void) +{ + int i; + xo_retain_entry_t *xrep, *next; + + for (i = 0; i < RETAIN_HASH_SIZE; i++) { + for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) { + next = xrep->xre_next; + xo_free(xrep); + } + xo_retain.xr_bucket[i] = NULL; + } + xo_retain_count = 0; +} + +/* + * Walk all buckets, clearing all retained entries + */ +void +xo_retain_clear (const char *fmt) +{ + xo_retain_entry_t **xrepp; + unsigned hash = xo_retain_hash(fmt); + + for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp; + xrepp = &(*xrepp)->xre_next) { + if ((*xrepp)->xre_format == fmt) { + *xrepp = (*xrepp)->xre_next; + xo_retain_count -= 1; + return; + } + } +} + +/* + * Search the hash for an entry matching 'fmt'; return it's fields. + */ +static int +xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump) +{ + if (xo_retain_count == 0) + return -1; + + unsigned hash = xo_retain_hash(fmt); + xo_retain_entry_t *xrep; + + for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL; + xrep = xrep->xre_next) { + if (xrep->xre_format == fmt) { + *valp = xrep->xre_fields; + *nump = xrep->xre_num_fields; + xrep->xre_hits += 1; + return 0; + } + } + + return -1; +} + +static void +xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields) +{ + unsigned hash = xo_retain_hash(fmt); + xo_retain_entry_t *xrep; + unsigned sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields); + xo_field_info_t *xfip; + + xrep = xo_realloc(NULL, sz); + if (xrep == NULL) + return; + + xfip = (xo_field_info_t *) &xrep[1]; + memcpy(xfip, fields, num_fields * sizeof(*fields)); + + bzero(xrep, sizeof(*xrep)); + + xrep->xre_format = fmt; + xrep->xre_fields = xfip; + xrep->xre_num_fields = num_fields; + + /* Record the field info in the retain bucket */ + xrep->xre_next = xo_retain.xr_bucket[hash]; + xo_retain.xr_bucket[hash] = xrep; + xo_retain_count += 1; +} + +#endif /* !LIBXO_NO_RETAIN */ + /* * Generate a warning. Normally, this is a text message written to * standard error. If the XOF_WARN_XML flag is set, then we generate @@ -1574,6 +1770,19 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) break; } + switch (xo_style(xop)) { + case XO_STYLE_HTML: + if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { + static char div_close[] = "</div>"; + XOIF_CLEAR(xop, XOIF_DIV_OPEN); + xo_data_append(xop, div_close, sizeof(div_close) - 1); + + if (XOF_ISSET(xop, XOF_PRETTY)) + xo_data_append(xop, "\n", 1); + } + break; + } + (void) xo_flush_h(xop); } @@ -1680,6 +1889,39 @@ xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) } /** + * Set the default handler to output to a file. + * @xop libxo handle + * @fp FILE pointer to use + */ +int +xo_set_file_h (xo_handle_t *xop, FILE *fp) +{ + xop = xo_default(xop); + + if (fp == NULL) { + xo_failure(xop, "xo_set_file: NULL fp"); + return -1; + } + + xop->xo_opaque = fp; + xop->xo_write = xo_write_to_file; + xop->xo_close = xo_close_file; + xop->xo_flush = xo_flush_file; + + return 0; +} + +/** + * Set the default handler to output to a file. + * @fp FILE pointer to use + */ +int +xo_set_file (FILE *fp) +{ + return xo_set_file_h(NULL, fp); +} + +/** * Release any resources held by the handle. * @xop XO handle to alter (or NULL for default handle) */ @@ -1824,9 +2066,11 @@ static xo_mapping_t xo_xof_names[] = { { XOF_LOG_SYSLOG, "log-syslog" }, { XOF_NO_HUMANIZE, "no-humanize" }, { XOF_NO_LOCALE, "no-locale" }, + { XOF_RETAIN_NONE, "no-retain" }, { XOF_NO_TOP, "no-top" }, { XOF_NOT_FIRST, "not-first" }, { XOF_PRETTY, "pretty" }, + { XOF_RETAIN_ALL, "retain" }, { XOF_UNDERSCORES, "underscores" }, { XOF_UNITS, "units" }, { XOF_WARN, "warn" }, @@ -3374,6 +3618,15 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, static char div_end[] = "\">"; static char div_close[] = "</div>"; + /* The encoding format defaults to the normal format */ + if (encoding == NULL) { + char *enc = alloca(vlen + 1); + memcpy(enc, value, vlen); + enc[vlen] = '\0'; + encoding = xo_fix_encoding(xop, enc); + elen = strlen(encoding); + } + /* * To build our XPath predicate, we need to save the va_list before * we format our data, and then restore it before we format the @@ -3406,15 +3659,6 @@ xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, else xo_buf_append(pbp, "='", 2); - /* The encoding format defaults to the normal format */ - if (encoding == NULL) { - char *enc = alloca(vlen + 1); - memcpy(enc, value, vlen); - enc[vlen] = '\0'; - encoding = xo_fix_encoding(xop, enc); - elen = strlen(encoding); - } - xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); xo_do_format_field(xop, pbp, encoding, elen, pflags); @@ -3613,10 +3857,9 @@ xo_format_text (xo_handle_t *xop, const char *str, int len) } static void -xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip) +xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { - const char *str = xfip->xfi_content; - unsigned len = xfip->xfi_clen; const char *fmt = xfip->xfi_format; unsigned flen = xfip->xfi_flen; xo_xff_flags_t flags = xfip->xfi_flags; @@ -4083,10 +4326,9 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen, } static void -xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip) +xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { - const char *str = xfip->xfi_content; - unsigned len = xfip->xfi_clen; const char *fmt = xfip->xfi_format; unsigned flen = xfip->xfi_flen; @@ -4335,13 +4577,13 @@ xo_colors_enabled (xo_handle_t *xop UNUSED) } static void -xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp) +xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp) { char buf[BUFSIZ]; char *cp = buf, *ep = buf + sizeof(buf); unsigned i, bit; xo_colors_t *oldp = &xop->xo_colors; - const char *code; + const char *code = NULL; /* * Start the buffer with an escape. We don't want to add the '[' @@ -4460,10 +4702,9 @@ xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) } static void -xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip) +xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { - const char *str = xfip->xfi_content; - unsigned len = xfip->xfi_clen; const char *fmt = xfip->xfi_format; unsigned flen = xfip->xfi_flen; @@ -4534,10 +4775,9 @@ xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip) } static void -xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip) +xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { - const char *str = xfip->xfi_content; - unsigned len = xfip->xfi_clen; const char *fmt = xfip->xfi_format; unsigned flen = xfip->xfi_flen; xo_xff_flags_t flags = xfip->xfi_flags; @@ -4589,10 +4829,9 @@ xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip) } static int -xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip) +xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { - const char *str = xfip->xfi_content; - unsigned len = xfip->xfi_clen; const char *fmt = xfip->xfi_format; unsigned flen = xfip->xfi_flen; @@ -4639,7 +4878,8 @@ xo_anchor_clear (xo_handle_t *xop) * format it when the end anchor tag is seen. */ static void -xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip) +xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) return; @@ -4656,11 +4896,12 @@ xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip) * Now we find the width, if possible. If it's not there, * we'll get it on the end anchor. */ - xop->xo_anchor_min_width = xo_find_width(xop, xfip); + xop->xo_anchor_min_width = xo_find_width(xop, xfip, str, len); } static void -xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip) +xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip, + const char *str, unsigned len) { if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) return; @@ -4672,7 +4913,7 @@ xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip) XOIF_CLEAR(xop, XOIF_UNITS_PENDING); - int width = xo_find_width(xop, xfip); + int width = xo_find_width(xop, xfip, str, len); if (width == 0) width = xop->xo_anchor_min_width; @@ -4787,6 +5028,7 @@ static xo_mapping_t xo_role_names[] = { #define XO_ROLE_NEWLINE '\n' static xo_mapping_t xo_modifier_names[] = { + { XFF_ARGUMENT, "argument" }, { XFF_COLON, "colon" }, { XFF_COMMA, "comma" }, { XFF_DISPLAY_ONLY, "display" }, @@ -4858,6 +5100,7 @@ xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) * '[': start a section of anchored text * ']': end a section of anchored text * The following modifiers are also supported: + * 'a': content is provided via argument (const char *), not descriptor * 'c': flag: emit a colon after the label * 'd': field is only emitted for display styles (text and html) * 'e': field is only emitted for encoding styles (xml and json) @@ -4884,7 +5127,7 @@ xo_parse_roles (xo_handle_t *xop, const char *fmt, xo_xff_flags_t flags = 0; uint8_t fnum = 0; - for (sp = basep; sp; sp++) { + for (sp = basep; sp && *sp; sp++) { if (*sp == ':' || *sp == '/' || *sp == '}') break; @@ -4961,6 +5204,10 @@ xo_parse_roles (xo_handle_t *xop, const char *fmt, fnum = (fnum * 10) + (*sp - '0'); break; + case 'a': + flags |= XFF_ARGUMENT; + break; + case 'c': flags |= XFF_COLON; break; @@ -5133,7 +5380,6 @@ static int xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, unsigned num_fields, const char *fmt) { - static const char default_format[] = "%s"; const char *cp, *sp, *ep, *basep; unsigned field = 0; xo_field_info_t *xfip = fields; @@ -5267,12 +5513,12 @@ xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, xfip->xfi_next = ++sp; /* If we have content, then we have a default format */ - if (xfip->xfi_clen || format) { + if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) { if (format) { xfip->xfi_format = format; xfip->xfi_flen = flen; } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { - xfip->xfi_format = default_format; + xfip->xfi_format = xo_default_format; xfip->xfi_flen = 2; } } @@ -5568,9 +5814,8 @@ xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, * Summary: i18n aighn't cheap. */ static const char * -xo_gettext_build_format (xo_handle_t *xop UNUSED, - xo_field_info_t *fields UNUSED, - int this_field UNUSED, +xo_gettext_build_format (xo_handle_t *xop, + xo_field_info_t *fields, int this_field, const char *fmt, char **new_fmtp) { if (xo_style_is_encoding(xop)) @@ -5686,17 +5931,22 @@ xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, #endif /* HAVE_GETTEXT */ /* - * The central function for emitting libxo output. + * Emit a set of fields. This is really the core of libxo. */ static int -xo_do_emit (xo_handle_t *xop, const char *fmt) +xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields, + unsigned max_fields, const char *fmt) { int gettext_inuse = 0; int gettext_changed = 0; int gettext_reordered = 0; + unsigned ftype; + xo_xff_flags_t flags; xo_field_info_t *new_fields = NULL; - + xo_field_info_t *xfip; + unsigned field; int rc = 0; + int flush = XOF_ISSET(xop, XOF_FLUSH); int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); char *new_fmt = NULL; @@ -5704,20 +5954,6 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) flush_line = 0; - xop->xo_columns = 0; /* Always reset it */ - xop->xo_errno = errno; /* Save for "%m" */ - - unsigned max_fields = xo_count_fields(xop, fmt), field; - xo_field_info_t fields[max_fields], *xfip; - - bzero(fields, max_fields * sizeof(fields[0])); - - if (xo_parse_fields(xop, fields, max_fields, fmt)) - return -1; /* Warning already displayed */ - - unsigned ftype; - xo_xff_flags_t flags; - /* * Some overhead for gettext; if the fields in the msgstr returned * by gettext are reordered, then we need to record start and end @@ -5745,6 +5981,18 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) min_fstart = field; } + const char *content = xfip->xfi_content; + int clen = xfip->xfi_clen; + + if (flags & XFF_ARGUMENT) { + /* + * Argument flag means the content isn't given in the descriptor, + * but as a UTF-8 string ('const char *') argument in xo_vap. + */ + content = va_arg(xop->xo_vap, char *); + clen = content ? strlen(content) : 0; + } + if (ftype == XO_ROLE_NEWLINE) { xo_line_close(xop); if (flush_line && xo_flush_h(xop) < 0) @@ -5773,15 +6021,15 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) } if (ftype == 'V') - xo_format_value(xop, xfip->xfi_content, xfip->xfi_clen, + xo_format_value(xop, content, clen, xfip->xfi_format, xfip->xfi_flen, xfip->xfi_encoding, xfip->xfi_elen, flags); else if (ftype == '[') - xo_anchor_start(xop, xfip); + xo_anchor_start(xop, xfip, content, clen); else if (ftype == ']') - xo_anchor_stop(xop, xfip); + xo_anchor_stop(xop, xfip, content, clen); else if (ftype == 'C') - xo_format_colors(xop, xfip); + xo_format_colors(xop, xfip, content, clen); else if (ftype == 'G') { /* @@ -5792,7 +6040,7 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) * Since gettext returns strings in a static buffer, we make * a copy in new_fmt. */ - xo_set_gettext_domain(xop, xfip); + xo_set_gettext_domain(xop, xfip, content, clen); if (!gettext_inuse) { /* Only translate once */ gettext_inuse = 1; @@ -5843,17 +6091,17 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) } continue; - } else if (xfip->xfi_clen || xfip->xfi_format) { + } else if (clen || xfip->xfi_format) { const char *class_name = xo_class_name(ftype); if (class_name) xo_format_content(xop, class_name, xo_tag_name(ftype), - xfip->xfi_content, xfip->xfi_clen, + content, clen, xfip->xfi_format, xfip->xfi_flen, flags); else if (ftype == 'T') - xo_format_title(xop, xfip); + xo_format_title(xop, xfip, content, clen); else if (ftype == 'U') - xo_format_units(xop, xfip); + xo_format_units(xop, xfip, content, clen); else xo_failure(xop, "unknown field type: '%c'", ftype); } @@ -5884,7 +6132,7 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { if (xo_write(xop) < 0) rc = -1; /* Report failure */ - else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0) + else if (xo_flush_h(xop) < 0) rc = -1; } @@ -5905,6 +6153,53 @@ xo_do_emit (xo_handle_t *xop, const char *fmt) } /* + * Parse and emit a set of fields + */ +static int +xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt) +{ + xop->xo_columns = 0; /* Always reset it */ + xop->xo_errno = errno; /* Save for "%m" */ + + if (fmt == NULL) + return 0; + + unsigned max_fields; + xo_field_info_t *fields = NULL; + + /* Adjust XOEF_RETAIN based on global flags */ + if (XOF_ISSET(xop, XOF_RETAIN_ALL)) + flags |= XOEF_RETAIN; + if (XOF_ISSET(xop, XOF_RETAIN_NONE)) + flags &= ~XOEF_RETAIN; + + /* + * Check for 'retain' flag, telling us to retain the field + * information. If we've already saved it, then we can avoid + * re-parsing the format string. + */ + if (!(flags & XOEF_RETAIN) + || xo_retain_find(fmt, &fields, &max_fields) != 0 + || fields == NULL) { + + /* Nothing retained; parse the format string */ + max_fields = xo_count_fields(xop, fmt); + fields = alloca(max_fields * sizeof(fields[0])); + bzero(fields, max_fields * sizeof(fields[0])); + + if (xo_parse_fields(xop, fields, max_fields, fmt)) + return -1; /* Warning already displayed */ + + if (flags & XOEF_RETAIN) { + /* Retain the info */ + xo_retain_add(fmt, fields, max_fields); + } + } + + return xo_do_emit_fields(xop, fields, max_fields, fmt); +} + +/* * Rebuild a format string in a gettext-friendly format. This function * is exposed to tools can perform this function. See xo(1). */ @@ -5944,7 +6239,7 @@ xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) xop = xo_default(xop); va_copy(xop->xo_vap, vap); - rc = xo_do_emit(xop, fmt); + rc = xo_do_emit(xop, 0, fmt); va_end(xop->xo_vap); bzero(&xop->xo_vap, sizeof(xop->xo_vap)); @@ -5958,7 +6253,7 @@ xo_emit_h (xo_handle_t *xop, const char *fmt, ...) xop = xo_default(xop); va_start(xop->xo_vap, fmt); - rc = xo_do_emit(xop, fmt); + rc = xo_do_emit(xop, 0, fmt); va_end(xop->xo_vap); bzero(&xop->xo_vap, sizeof(xop->xo_vap)); @@ -5972,13 +6267,137 @@ xo_emit (const char *fmt, ...) int rc; va_start(xop->xo_vap, fmt); - rc = xo_do_emit(xop, fmt); + rc = xo_do_emit(xop, 0, fmt); + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + + return rc; +} + +int +xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags, + const char *fmt, va_list vap) +{ + int rc; + + xop = xo_default(xop); + va_copy(xop->xo_vap, vap); + rc = xo_do_emit(xop, flags, fmt); + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + + return rc; +} + +int +xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...) +{ + int rc; + + xop = xo_default(xop); + va_start(xop->xo_vap, fmt); + rc = xo_do_emit(xop, flags, fmt); + va_end(xop->xo_vap); + bzero(&xop->xo_vap, sizeof(xop->xo_vap)); + + return rc; +} + +int +xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...) +{ + xo_handle_t *xop = xo_default(NULL); + int rc; + + va_start(xop->xo_vap, fmt); + rc = xo_do_emit(xop, flags, fmt); va_end(xop->xo_vap); bzero(&xop->xo_vap, sizeof(xop->xo_vap)); return rc; } +/* + * Emit a single field by providing the info information typically provided + * inside the field description (role, modifiers, and formats). This is + * a convenience function to avoid callers using snprintf to build field + * descriptions. + */ +int +xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents, + const char *fmt, const char *efmt, + va_list vap) +{ + int rc; + + xop = xo_default(xop); + + if (rolmod == NULL) + rolmod = "V"; + + xo_field_info_t xfi; + + bzero(&xfi, sizeof(xfi)); + + const char *cp; + cp = xo_parse_roles(xop, rolmod, rolmod, &xfi); + if (cp == NULL) + return -1; + + xfi.xfi_start = fmt; + xfi.xfi_content = contents; + xfi.xfi_format = fmt; + xfi.xfi_encoding = efmt; + xfi.xfi_clen = contents ? strlen(contents) : 0; + xfi.xfi_flen = fmt ? strlen(fmt) : 0; + xfi.xfi_elen = efmt ? strlen(efmt) : 0; + + /* If we have content, then we have a default format */ + if (contents && fmt == NULL + && xo_role_wants_default_format(xfi.xfi_ftype)) { + xfi.xfi_format = xo_default_format; + xfi.xfi_flen = 2; + } + + + + va_copy(xop->xo_vap, vap); + + rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field"); + + va_end(xop->xo_vap); + + return rc; +} + +int +xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents, + const char *fmt, const char *efmt, ...) +{ + int rc; + va_list vap; + + va_start(vap, efmt); + rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap); + va_end(vap); + + return rc; +} + +int +xo_emit_field (const char *rolmod, const char *contents, + const char *fmt, const char *efmt, ...) +{ + int rc; + va_list vap; + + va_start(vap, efmt); + rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap); + va_end(vap); + + return rc; +} + int xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) { @@ -6392,7 +6811,7 @@ xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) } int -xo_open_list_h (xo_handle_t *xop, const char *name UNUSED) +xo_open_list_h (xo_handle_t *xop, const char *name) { return xo_open_list_hf(xop, 0, name); } @@ -6404,7 +6823,7 @@ xo_open_list (const char *name) } int -xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED) +xo_open_list_hd (xo_handle_t *xop, const char *name) { return xo_open_list_hf(xop, XOF_DTRT, name); } @@ -7113,6 +7532,11 @@ xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, xsp->xs_state, new_state); } + /* Handle the flush flag */ + if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH)) + if (xo_flush_h(xop)) + rc = -1; + return rc; marker_prevents_close: @@ -7179,22 +7603,11 @@ xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) int xo_flush_h (xo_handle_t *xop) { - static char div_close[] = "</div>"; int rc; xop = xo_default(xop); switch (xo_style(xop)) { - case XO_STYLE_HTML: - if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { - XOIF_CLEAR(xop, XOIF_DIV_OPEN); - xo_data_append(xop, div_close, sizeof(div_close) - 1); - - if (XOF_ISSET(xop, XOF_PRETTY)) - xo_data_append(xop, "\n", 1); - } - break; - case XO_STYLE_ENCODER: xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL); } @@ -7435,7 +7848,7 @@ xo_set_program (const char *name) } void -xo_set_version_h (xo_handle_t *xop, const char *version UNUSED) +xo_set_version_h (xo_handle_t *xop, const char *version) { xop = xo_default(xop); |