/* $Id$ */ /* * Copyright (c) 2012--2021 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include "kcgi.h" #include "kcgihtml.h" #include "extern.h" /* * Maximum size of printing a signed 64-bit integer. */ #define INT_MAXSZ 22 /* * A type of HTML5 element. * Note: we don't list transitional elements, though I do note them in * the tag array. */ enum htype { TAG_FLOW, /* flow (block) element */ TAG_PHRASE,/* phrasing (inline) element */ TAG_VOID, /* auto-closing (e.g., INPUT) */ TAG_INSTRUCTION /* instruction */ }; /* * A tag describes an HTML element and its properties. */ struct tag { enum htype flags; const char *name; }; static const uint16_t entities[KENTITY__MAX] = { 198, /* KENTITY_AElig */ 193, /* KENTITY_Aacute */ 194, /* KENTITY_Acirc */ 192, /* KENTITY_Agrave */ 197, /* KENTITY_Aring */ 195, /* KENTITY_Atilde */ 196, /* KENTITY_Auml */ 199, /* KENTITY_Ccedil */ 8225, /* KENTITY_Dagger */ 208, /* KENTITY_ETH */ 201, /* KENTITY_Eacute */ 202, /* KENTITY_Ecirc */ 200, /* KENTITY_Egrave */ 203, /* KENTITY_Euml */ 205, /* KENTITY_Iacute */ 206, /* KENTITY_Icirc */ 204, /* KENTITY_Igrave */ 207, /* KENTITY_Iuml */ 209, /* KENTITY_Ntilde */ 338, /* KENTITY_OElig */ 211, /* KENTITY_Oacute */ 212, /* KENTITY_Ocirc */ 210, /* KENTITY_Ograve */ 216, /* KENTITY_Oslash */ 213, /* KENTITY_Otilde */ 214, /* KENTITY_Ouml */ 352, /* KENTITY_Scaron */ 222, /* KENTITY_THORN */ 218, /* KENTITY_Uacute */ 219, /* KENTITY_Ucirc */ 217, /* KENTITY_Ugrave */ 220, /* KENTITY_Uuml */ 221, /* KENTITY_Yacute */ 376, /* KENTITY_Yuml */ 225, /* KENTITY_aacute */ 226, /* KENTITY_acirc */ 180, /* KENTITY_acute */ 230, /* KENTITY_aelig */ 224, /* KENTITY_agrave */ 38, /* KENTITY_amp */ 39, /* KENTITY_apos */ 229, /* KENTITY_aring */ 227, /* KENTITY_atilde */ 228, /* KENTITY_auml */ 8222, /* KENTITY_bdquo */ 166, /* KENTITY_brvbar */ 231, /* KENTITY_ccedil */ 184, /* KENTITY_cedil */ 162, /* KENTITY_cent */ 710, /* KENTITY_circ */ 169, /* KENTITY_copy */ 164, /* KENTITY_curren */ 8224, /* KENTITY_dagger */ 176, /* KENTITY_deg */ 247, /* KENTITY_divide */ 233, /* KENTITY_eacute */ 234, /* KENTITY_ecirc */ 232, /* KENTITY_egrave */ 8195, /* KENTITY_emsp */ 8194, /* KENTITY_ensp */ 240, /* KENTITY_eth */ 235, /* KENTITY_euml */ 8364, /* KENTITY_euro */ 189, /* KENTITY_frac12 */ 188, /* KENTITY_frac14 */ 190, /* KENTITY_frac34 */ 62, /* KENTITY_gt */ 8230, /* KENTITY_hellip */ 237, /* KENTITY_iacute */ 238, /* KENTITY_icirc */ 161, /* KENTITY_iexcl */ 236, /* KENTITY_igrave */ 191, /* KENTITY_iquest */ 239, /* KENTITY_iuml */ 171, /* KENTITY_laquo */ 8220, /* KENTITY_ldquo */ 8206, /* KENTITY_lrm */ 8249, /* KENTITY_lsaquo */ 8216, /* KENTITY_lsquo */ 60, /* KENTITY_lt */ 175, /* KENTITY_macr */ 8212, /* KENTITY_mdash */ 181, /* KENTITY_micro */ 183, /* KENTITY_middot */ 160, /* KENTITY_nbsp */ 8211, /* KENTITY_ndash */ 172, /* KENTITY_not */ 241, /* KENTITY_ntilde */ 243, /* KENTITY_oacute */ 244, /* KENTITY_ocirc */ 339, /* KENTITY_oelig */ 242, /* KENTITY_ograve */ 170, /* KENTITY_ordf */ 186, /* KENTITY_ordm */ 248, /* KENTITY_oslash */ 245, /* KENTITY_otilde */ 246, /* KENTITY_ouml */ 182, /* KENTITY_para */ 8240, /* KENTITY_permil */ 177, /* KENTITY_plusmn */ 163, /* KENTITY_pound */ 34, /* KENTITY_quot */ 187, /* KENTITY_raquo */ 8221, /* KENTITY_rdquo */ 174, /* KENTITY_reg */ 8207, /* KENTITY_rlm */ 8250, /* KENTITY_rsaquo */ 8217, /* KENTITY_rsquo */ 8218, /* KENTITY_sbquo */ 353, /* KENTITY_scaron */ 167, /* KENTITY_sect */ 173, /* KENTITY_shy */ 185, /* KENTITY_sup1 */ 178, /* KENTITY_sup2 */ 179, /* KENTITY_sup3 */ 223, /* KENTITY_szlig */ 8201, /* KENTITY_thinsp */ 254, /* KENTITY_thorn */ 732, /* KENTITY_tilde */ 215, /* KENTITY_times */ 8482, /* KENTITY_trade */ 250, /* KENTITY_uacute */ 251, /* KENTITY_ucirc */ 249, /* KENTITY_ugrave */ 168, /* KENTITY_uml */ 252, /* KENTITY_uuml */ 253, /* KENTITY_yacute */ 165, /* KENTITY_yen */ 255, /* KENTITY_yuml */ 8205, /* KENTITY_zwj */ 8204, /* KENTITY_zwnj */ }; static const struct tag tags[KELEM__MAX] = { { TAG_PHRASE, "a" }, /* KELEM_A */ /* XXX: TRANS */ { TAG_PHRASE, "abbr" }, /* KELEM_ABBR */ { TAG_PHRASE, "address" }, /* KELEM_ADDRESS */ { TAG_VOID, "area" }, /* KELEM_AREA */ { TAG_FLOW, "article" }, /* KELEM_ARTICLE */ { TAG_FLOW, "aside" }, /* KELEM_ASIDE */ { TAG_FLOW, "audio" }, /* KELEM_AUDIO */ /* XXX: TRANS */ { TAG_PHRASE, "b" }, /* KELEM_B */ { TAG_VOID, "base" }, /* KELEM_BASE */ { TAG_PHRASE, "bdi" }, /* KELEM_BDI */ { TAG_PHRASE, "bdo" }, /* KELEM_BDO */ { TAG_FLOW, "blockquote" }, /* KELEM_BLOCKQUOTE */ { TAG_FLOW, "body" }, /* KELEM_BODY */ { TAG_VOID, "br" }, /* KELEM_BR */ { TAG_PHRASE, "button" }, /* KELEM_BUTTON */ { TAG_FLOW, "canvas" }, /* KELEM_CANVAS */ /* XXX: TRANS */ { TAG_FLOW, "caption" }, /* KELEM_CAPTION */ { TAG_PHRASE, "cite" }, /* KELEM_CITE */ { TAG_PHRASE, "code" }, /* KELEM_CODE */ { TAG_VOID, "col" }, /* KELEM_COL */ { TAG_PHRASE, "colgroup" }, /* KELEM_COLGROUP */ { TAG_PHRASE, "data" }, /* KELEM_DATA*/ { TAG_PHRASE, "datalist" }, /* KELEM_DATALIST */ { TAG_FLOW, "dd" }, /* KELEM_DD */ { TAG_PHRASE, "del" }, /* KELEM_DEL */ /* XXX: TRANS */ { TAG_FLOW, "details" }, /* KELEM_DETAILS */ { TAG_PHRASE, "dfn" }, /* KELEM_DFN */ { TAG_FLOW, "dialog" }, /* KELEM_DIALOG */ { TAG_FLOW, "div" }, /* KELEM_DIV */ { TAG_FLOW, "dl" }, /* KELEM_DL */ { TAG_INSTRUCTION, "!DOCTYPE html" }, /* KELEM_DOCTYPE */ { TAG_FLOW, "dt" }, /* KELEM_DT */ { TAG_PHRASE, "em" }, /* KELEM_EM */ { TAG_VOID, "embed" }, /* KELEM_EMBED */ { TAG_FLOW, "fieldset" }, /* KELEM_FIELDSET */ { TAG_FLOW, "figcaption" }, /* KELEM_FIGCAPTION */ { TAG_FLOW, "figure" }, /* KELEM_FIGURE */ { TAG_FLOW, "footer" }, /* KELEM_FOOTER */ { TAG_FLOW, "form" }, /* KELEM_FORM */ { TAG_PHRASE, "h1" }, /* KELEM_H1 */ { TAG_PHRASE, "h2" }, /* KELEM_H2 */ { TAG_PHRASE, "h3" }, /* KELEM_H3 */ { TAG_PHRASE, "h4" }, /* KELEM_H4 */ { TAG_PHRASE, "h5" }, /* KELEM_H5 */ { TAG_PHRASE, "h6" }, /* KELEM_H6 */ { TAG_FLOW, "head" }, /* KELEM_HEAD */ { TAG_FLOW, "header" }, /* KELEM_HEADER */ { TAG_FLOW, "hgroup" }, /* KELEM_HGROUP */ { TAG_VOID, "hr" }, /* KELEM_HR */ { TAG_FLOW, "html" }, /* KELEM_HTML */ { TAG_PHRASE, "i" }, /* KELEM_I */ { TAG_PHRASE, "iframe" }, /* KELEM_IFRAME */ { TAG_VOID, "img" }, /* KELEM_IMG */ { TAG_VOID, "input" }, /* KELEM_INPUT */ { TAG_PHRASE, "ins" }, /* KELEM_INS */ /* XXX: TRANS */ { TAG_PHRASE, "kbd" }, /* KELEM_KBD */ { TAG_VOID, "keygen" }, /* KELEM_KEYGEN */ { TAG_PHRASE, "label" }, /* KELEM_LABEL */ { TAG_PHRASE, "legend" }, /* KELEM_LEGEND */ { TAG_FLOW, "li" }, /* KELEM_LI */ { TAG_VOID, "link" }, /* KELEM_LINK */ { TAG_FLOW, "main" }, /* KELEM_MAIN */ { TAG_FLOW, "map" }, /* KELEM_MAP */ /* XXX: TRANS */ { TAG_PHRASE, "mark" }, /* KELEM_MARK */ { TAG_FLOW, "menu" }, /* KELEM_MENU */ { TAG_VOID, "meta" }, /* KELEM_META */ { TAG_PHRASE, "meter" }, /* KELEM_METER */ { TAG_FLOW, "nav" }, /* KELEM_NAV */ { TAG_FLOW, "noscript" }, /* KELEM_NOSCRIPT */ /* XXX: TRANS */ { TAG_FLOW, "object" }, /* KELEM_OBJECT */ /* XXX: TRANS */ { TAG_FLOW, "ol" }, /* KELEM_OL */ { TAG_FLOW, "optgroup" }, /* KELEM_OPTGROUP */ { TAG_PHRASE, "option" }, /* KELEM_OPTION */ { TAG_PHRASE, "output" }, /* KELEM_OUTPUT */ { TAG_PHRASE, "p" }, /* KELEM_P */ { TAG_VOID, "param" }, /* KELEM_PARAM */ { TAG_PHRASE, "picture" }, /* KELEM_PICTURE */ /* XXX: TRANS */ { TAG_PHRASE, "pre" }, /* KELEM_PRE */ { TAG_PHRASE, "progress" }, /* KELEM_PROGRESS */ { TAG_PHRASE, "q" }, /* KELEM_Q */ { TAG_PHRASE, "rb" }, /* KELEM_RB */ { TAG_PHRASE, "rp" }, /* KELEM_RP */ { TAG_PHRASE, "rt" }, /* KELEM_RT */ { TAG_PHRASE, "rtc" }, /* KELEM_RTC */ { TAG_PHRASE, "ruby" }, /* KELEM_RUBY */ { TAG_PHRASE, "s" }, /* KELEM_S */ { TAG_PHRASE, "samp" }, /* KELEM_SAMP */ { TAG_FLOW, "script" }, /* KELEM_SCRIPT */ { TAG_FLOW, "section" }, /* KELEM_SECTION */ { TAG_FLOW, "select" }, /* KELEM_SELECT */ { TAG_PHRASE, "small" }, /* KELEM_SMALL */ { TAG_VOID, "source" }, /* KELEM_SOURCE */ { TAG_PHRASE, "span" }, /* KELEM_SPAN */ { TAG_PHRASE, "strong" }, /* KELEM_STRONG */ { TAG_FLOW, "style" }, /* KELEM_STYLE */ { TAG_PHRASE, "sub" }, /* KELEM_SUB */ { TAG_PHRASE, "summary" }, /* KELEM_SUMMARY */ { TAG_PHRASE, "sup" }, /* KELEM_SUP */ { TAG_FLOW, "table" }, /* KELEM_TABLE */ { TAG_FLOW, "tbody" }, /* KELEM_TBODY */ { TAG_FLOW, "td" }, /* KELEM_TD */ { TAG_PHRASE, "textarea" }, /* KELEM_TEXTAREA */ { TAG_FLOW, "tfoot" }, /* KELEM_TFOOT */ { TAG_FLOW, "th" }, /* KELEM_TH */ { TAG_FLOW, "thead" }, /* KELEM_THEAD */ { TAG_PHRASE, "time" }, /* KELEM_TIME */ { TAG_PHRASE, "title" }, /* KELEM_TITLE */ { TAG_FLOW, "tr" }, /* KELEM_TR */ { TAG_VOID, "track" }, /* KELEM_TRACK */ { TAG_PHRASE, "u" }, /* KELEM_U */ { TAG_FLOW, "ul" }, /* KELEM_UL */ { TAG_PHRASE, "var" }, /* KELEM_VAR */ { TAG_FLOW, "video" }, /* KELEM_VIDEO */ /* XXX: TRANS */ { TAG_VOID, "wbr" }, /* KELEM_WBR */ }; static const char *const attrs[KATTR__MAX] = { "accept-charset", /* KATTR_ACCEPT_CHARSET */ "accesskey", /* KATTR_ACCESSKEY */ "action", /* KATTR_ACTION */ "allowfullscreen", /* KATTR_ALLOWFULLSCREEN */ "allowpaymentrequest", /* KATTR_ALLOWPAYMENTREQUEST */ "alt", /* KATTR_ALT */ "async", /* KATTR_ASYNC */ "autocomplete", /* KATTR_AUTOCOMPLETE */ "autofocus", /* KATTR_AUTOFOCUS */ "autoplay", /* KATTR_AUTOPLAY */ "border", /* KATTR_BORDER */ "challenge", /* KATTR_CHALLENGE */ "charset", /* KATTR_CHARSET */ "checked", /* KATTR_CHECKED */ "cite", /* KATTR_CITE */ "class", /* KATTR_CLASS */ "cols", /* KATTR_COLS */ "colspan", /* KATTR_COLSPAN */ "content", /* KATTR_CONTENT */ "contenteditable", /* KATTR_CONTENTEDITABLE */ "contextmenu", /* KATTR_CONTEXTMENU */ "controls", /* KATTR_CONTROLS */ "coords", /* KATTR_COORDS */ "crossorigin", /* KATTR_CROSSORIGIN */ "data", /* KATTR_DATA */ "datetime", /* KATTR_DATETIME */ "default", /* KATTR_DEFAULT */ "defer", /* KATTR_DEFER */ "dir", /* KATTR_DIR */ "dirname", /* KATTR_DIRNAME */ "disabled", /* KATTR_DISABLED */ "download", /* KATTR_DOWNLOAD */ "draggable", /* KATTR_DRAGGABLE */ "dropzone", /* KATTR_DROPZONE */ "enctype", /* KATTR_ENCTYPE */ "for", /* KATTR_FOR */ "form", /* KATTR_FORM */ "formaction", /* KATTR_FORMACTION */ "formenctype", /* KATTR_FORMENCTYPE */ "formmethod", /* KATTR_FORMMETHOD */ "formnovalidate", /* KATTR_FORMNOVALIDATE */ "formtarget", /* KATTR_FORMTARGET */ "header", /* KATTR_HEADER */ "headers", /* KATTR_HEADERS */ "height", /* KATTR_HEIGHT */ "hidden", /* KATTR_HIDDEN */ "high", /* KATTR_HIGH */ "href", /* KATTR_HREF */ "hreflang", /* KATTR_HREFLANG */ "http-equiv", /* KATTR_HTTP_EQUIV */ "icon", /* KATTR_ICON */ "id", /* KATTR_ID */ "ismap", /* KATTR_ISMAP */ "keytype", /* KATTR_KEYTYPE */ "kind", /* KATTR_KIND */ "label", /* KATTR_LABEL */ "lang", /* KATTR_LANG */ "language", /* KATTR_LANGUAGE */ "list", /* KATTR_LIST */ "longdesc", /* KATTR_LONGDESC */ "loop", /* KATTR_LOOP */ "low", /* KATTR_LOW */ "manifest", /* KATTR_MANIFEST */ "max", /* KATTR_MAX */ "maxlength", /* KATTR_MAXLENGTH */ "media", /* KATTR_MEDIA */ "mediagroup", /* KATTR_MEDIAGROUP */ "method", /* KATTR_METHOD */ "min", /* KATTR_MIN */ "minlength", /* KATTR_MINLENGTH */ "multiple", /* KATTR_MULTIPLE */ "muted", /* KATTR_MUTED */ "name", /* KATTR_NAME */ "nonce", /* KATTR_NONCE */ "novalidate", /* KATTR_NOVALIDATE */ "onabort", /* KATTR_ONABORT */ "onafterprint", /* KATTR_ONAFTERPRINT */ "onauxclick", /* KATTR_ONAUXCLICK */ "onbeforeprint", /* KATTR_ONBEFOREPRINT */ "onbeforeunload", /* KATTR_ONBEFOREUNLOAD */ "onblur", /* KATTR_ONBLUR */ "oncancel", /* KATTR_ONCANCEL */ "oncanplay", /* KATTR_ONCANPLAY */ "oncanplaythrough", /* KATTR_ONCANPLAYTHROUGH */ "onchange", /* KATTR_ONCHANGE */ "onclick", /* KATTR_ONCLICK */ "onclose", /* KATTR_ONCLOSE */ "oncontextmenu", /* KATTR_ONCONTEXTMENU */ "oncuechange", /* KATTR_ONCUECHANGE */ "oncut", /* KATTR_ONCUT */ "ondblclick", /* KATTR_ONDBLCLICK */ "ondrag", /* KATTR_ONDRAG */ "ondragend", /* KATTR_ONDRAGEND */ "ondragenter", /* KATTR_ONDRAGENTER */ "ondragexit", /* KATTR_ONDRAGEXIT */ "ondragleave", /* KATTR_ONDRAGLEAVE */ "ondragover", /* KATTR_ONDRAGOVER */ "ondragstart", /* KATTR_ONDRAGSTART */ "ondrop", /* KATTR_ONDROP */ "ondurationchange", /* KATTR_ONDURATIONCHANGE */ "onemptied", /* KATTR_ONEMPTIED */ "onended", /* KATTR_ONENDED */ "onerror", /* KATTR_ONERROR */ "onfocus", /* KATTR_ONFOCUS */ "onhashchange", /* KATTR_ONHASHCHANGE */ "oninput", /* KATTR_ONINPUT */ "oninvalid", /* KATTR_ONINVALID */ "onkeydown", /* KATTR_ONKEYDOWN */ "onkeypress", /* KATTR_ONKEYPRESS */ "onkeyup", /* KATTR_ONKEYUP */ "onload", /* KATTR_ONLOAD */ "onloadeddata", /* KATTR_ONLOADEDDATA */ "onloadedmetadata", /* KATTR_ONLOADEDMETADATA */ "onloadstart", /* KATTR_ONLOADSTART */ "onmessage", /* KATTR_ONMESSAGE */ "onmousedown", /* KATTR_ONMOUSEDOWN */ "onmouseenter", /* KATTR_ONMOUSEENTER */ "onmouseleave", /* KATTR_ONMOUSELEAVE */ "onmousemove", /* KATTR_ONMOUSEMOVE */ "onmouseout", /* KATTR_ONMOUSEOUT */ "onmouseover", /* KATTR_ONMOUSEOVER */ "onmouseup", /* KATTR_ONMOUSEUP */ "onmousewheel", /* KATTR_ONMOUSEWHEEL */ "onoffline", /* KATTR_ONOFFLINE */ "ononline", /* KATTR_ONONLINE */ "onpagehide", /* KATTR_ONPAGEHIDE */ "onpageshow", /* KATTR_ONPAGESHOW */ "onpaste", /* KATTR_ONPASTE */ "onpause", /* KATTR_ONPAUSE */ "onplay", /* KATTR_ONPLAY */ "onplaying", /* KATTR_ONPLAYING */ "onpopstate", /* KATTR_ONPOPSTATE */ "onprogress", /* KATTR_ONPROGRESS */ "onratechange", /* KATTR_ONRATECHANGE */ "onreadystatechange", /* KATTR_ONREADYSTATECHANGE */ "onreset", /* KATTR_ONRESET */ "onresize", /* KATTR_ONRESIZE */ "onscroll", /* KATTR_ONSCROLL */ "onseeked", /* KATTR_ONSEEKED */ "onseeking", /* KATTR_ONSEEKING */ "onselect", /* KATTR_ONSELECT */ "onshow", /* KATTR_ONSHOW */ "onstalled", /* KATTR_ONSTALLED */ "onstorage", /* KATTR_ONSTORAGE */ "onsubmit", /* KATTR_ONSUBMIT */ "onsuspend", /* KATTR_ONSUSPEND */ "ontimeupdate", /* KATTR_ONTIMEUPDATE */ "ontoggle", /* KATTR_ONTOGGLE */ "onunload", /* KATTR_ONUNLOAD */ "onvolumechange", /* KATTR_ONVOLUMECHANGE */ "onwaiting", /* KATTR_ONWAITING */ "onwheel", /* KATTR_ONWHEEL */ "open", /* KATTR_OPEN */ "optimum", /* KATTR_OPTIMUM */ "pattern", /* KATTR_PATTERN */ "placeholder", /* KATTR_PLACEHOLDER */ "poster", /* KATTR_POSTER */ "preload", /* KATTR_PRELOAD */ "radiogroup", /* KATTR_RADIOGROUP */ "readonly", /* KATTR_READONLY */ "referrerpolicy", /* KATTR_REFERRERPOLICY */ "rel", /* KATTR_REL */ "required", /* KATTR_REQUIRED */ "rev", /* KATTR_REV */ "reversed", /* KATTR_REVERSED */ "role", /* KATTR_ROLE */ "rows", /* KATTR_ROWS */ "rowspan", /* KATTR_ROWSPAN */ "sandbox", /* KATTR_SANDBOX */ "scope", /* KATTR_SCOPE */ "seamless", /* KATTR_SEAMLESS */ "selected", /* KATTR_SELECTED */ "shape", /* KATTR_SHAPE */ "size", /* KATTR_SIZE */ "sizes", /* KATTR_SIZES */ "span", /* KATTR_SPAN */ "spellcheck", /* KATTR_SPELLCHECK */ "src", /* KATTR_SRC */ "srcdoc", /* KATTR_SRCDOC */ "srclang", /* KATTR_SRCLANG */ "srcset", /* KATTR_SRCSET */ "start", /* KATTR_START */ "step", /* KATTR_STEP */ "style", /* KATTR_STYLE */ "tabindex", /* KATTR_TABINDEX */ "target", /* KATTR_TARGET */ "title", /* KATTR_TITLE */ "translate", /* KATTR_TRANSLATE */ "type", /* KATTR_TYPE */ "typemustmatch", /* KATTR_TYPEMUSTMATCH */ "usemap", /* KATTR_USEMAP */ "value", /* KATTR_VALUE */ "width", /* KATTR_WIDTH */ "wrap", /* KATTR_WRAP */ }; size_t khtml_elemat(const struct khtmlreq *req) { return req->elemsz; } enum kcgi_err khtml_elem(struct khtmlreq *req, enum kelem elem) { return khtml_attr(req, elem, KATTR__MAX); } /* * Open a tag---only used when pretty-printing and a noop otherwise. * If we're a flow tag, emit a newline (unless already omitted). * Then if we're at a newline regardless of tag type, indent properly to * the point where we'll omit the tag name. */ static enum kcgi_err khtml_flow_open(struct khtmlreq *req, enum kelem elem) { size_t i; enum kcgi_err er; if ( ! (KHTML_PRETTY & req->opts)) return(KCGI_OK); if (TAG_FLOW == tags[elem].flags && ! req->newln) { if (KCGI_OK != (er = kcgi_writer_putc(req->arg, '\n'))) return(er); req->newln = 1; } if (req->newln) for (i = 0; i < req->elemsz; i++) if (KCGI_OK != (er = kcgi_writer_puts(req->arg, " "))) return(er); req->newln = 0; return(KCGI_OK); } /* * If we're pretty-printing and closing a flow or instruction tag, emit * a newline. * Otherwise do nothing. * Returns the usual write error code. */ static enum kcgi_err khtml_flow_close(struct khtmlreq *req, enum kelem elem) { enum kcgi_err er; if ( ! (KHTML_PRETTY & req->opts)) return(KCGI_OK); if (TAG_FLOW == tags[elem].flags || TAG_INSTRUCTION == tags[elem].flags) { if (KCGI_OK != (er = kcgi_writer_putc(req->arg, '\n'))) return(er); req->newln = 1; } else req->newln = 0; return(KCGI_OK); } enum kcgi_err khtml_attrx(struct khtmlreq *req, enum kelem elem, ...) { va_list ap; enum kattr at; enum kcgi_err er = KCGI_OK; if (tags[elem].flags != TAG_VOID && tags[elem].flags != TAG_INSTRUCTION && req->elemsz >= KHTML_STACK_MAX) { kutil_warnx(NULL, NULL, "maximum html stack size exceeded"); return KCGI_ENOMEM; } if ((er = khtml_flow_open(req, elem)) != KCGI_OK) return er; if ((er = kcgi_writer_putc(req->arg, '<')) != KCGI_OK) return er; if ((er = kcgi_writer_puts(req->arg, tags[elem].name)) != KCGI_OK) return er; va_start(ap, elem); while ((at = va_arg(ap, enum kattr)) != KATTR__MAX) { if ((er = kcgi_writer_putc(req->arg, ' ')) != KCGI_OK) goto out; if ((er = kcgi_writer_puts(req->arg, attrs[at])) != KCGI_OK) goto out; if ((er = kcgi_writer_puts(req->arg, "=\"")) != KCGI_OK) goto out; switch (va_arg(ap, enum kattrx)) { case KATTRX_STRING: er = khtml_puts(req, va_arg(ap, char *)); break; case KATTRX_INT: er = khtml_int(req, va_arg(ap, int64_t)); break; case KATTRX_DOUBLE: er = khtml_double(req, va_arg(ap, double)); break; } if (er != KCGI_OK) goto out; if ((er = kcgi_writer_putc(req->arg, '"')) != KCGI_OK) goto out; } va_end(ap); if (tags[elem].flags == TAG_VOID && (er = kcgi_writer_putc(req->arg, '/')) != KCGI_OK) return er; if ((er = kcgi_writer_putc(req->arg, '>')) != KCGI_OK) return er; if ((er = khtml_flow_close(req, elem)) != KCGI_OK) return er; if (tags[elem].flags != TAG_VOID && tags[elem].flags != TAG_INSTRUCTION) req->elems[req->elemsz++] = elem; return KCGI_OK; out: va_end(ap); return er; } enum kcgi_err khtml_attr(struct khtmlreq *req, enum kelem elem, ...) { va_list ap; enum kattr at; const char *cp; enum kcgi_err er; if (tags[elem].flags != TAG_VOID && tags[elem].flags != TAG_INSTRUCTION && req->elemsz >= KHTML_STACK_MAX) { kutil_warnx(NULL, NULL, "maximum html stack size exceeded"); return KCGI_ENOMEM; } if ((er = khtml_flow_open(req, elem)) != KCGI_OK) return er; if ((er = kcgi_writer_putc(req->arg, '<')) != KCGI_OK) return er; if ((er = kcgi_writer_puts(req->arg, tags[elem].name)) != KCGI_OK) return er; va_start(ap, elem); while ((at = va_arg(ap, enum kattr)) != KATTR__MAX) { cp = va_arg(ap, char *); /* * FIXME: shouldn't we allow situations where there's no * value, like