Fix an allocation error when copying entities

xmlInitParser(void) {
    if (xmlParserInitialized != 0)
	return;

#ifdef LIBXML_THREAD_ENABLED
    __xmlGlobalInitMutexLock();
    if (xmlParserInitialized == 0) {
#endif
	xmlInitThreads();
	xmlInitGlobals();
	if ((xmlGenericError == xmlGenericErrorDefaultFunc) ||
	    (xmlGenericError == NULL))
	    initGenericErrorDefaultFunc(NULL);
	xmlInitMemory();
	xmlInitCharEncodingHandlers();
	xmlDefaultSAXHandlerInit();
	xmlRegisterDefaultInputCallbacks();
#ifdef LIBXML_OUTPUT_ENABLED
	xmlRegisterDefaultOutputCallbacks();
#endif /* LIBXML_OUTPUT_ENABLED */
#ifdef LIBXML_HTML_ENABLED
	htmlInitAutoClose();
	htmlDefaultSAXHandlerInit();
#endif
#ifdef LIBXML_XPATH_ENABLED
	xmlXPathInit();
#endif
	xmlParserInitialized = 1;
#ifdef LIBXML_THREAD_ENABLED
    }
    __xmlGlobalInitMutexUnlock();
#endif
}

Heap-based buffer overflow in libxml2, as used in Google Chrome before 16.0.912.75, allows remote attackers to cause a denial of service or possibly have unspecified other impact via unknown vectors.


xmlHasFeature(xmlFeature feature)
{
    switch (feature) {
	case XML_WITH_THREAD:
#ifdef LIBXML_THREAD_ENABLED
	    return(1);
#else
	    return(0);
#endif
        case XML_WITH_TREE:
#ifdef LIBXML_TREE_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_OUTPUT:
#ifdef LIBXML_OUTPUT_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_PUSH:
#ifdef LIBXML_PUSH_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_READER:
#ifdef LIBXML_READER_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_PATTERN:
#ifdef LIBXML_PATTERN_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_WRITER:
#ifdef LIBXML_WRITER_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_SAX1:
#ifdef LIBXML_SAX1_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_FTP:
#ifdef LIBXML_FTP_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_HTTP:
#ifdef LIBXML_HTTP_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_VALID:
#ifdef LIBXML_VALID_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_HTML:
#ifdef LIBXML_HTML_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_LEGACY:
#ifdef LIBXML_LEGACY_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_C14N:
#ifdef LIBXML_C14N_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_CATALOG:
#ifdef LIBXML_CATALOG_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_XPATH:
#ifdef LIBXML_XPATH_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_XPTR:
#ifdef LIBXML_XPTR_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_XINCLUDE:
#ifdef LIBXML_XINCLUDE_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_ICONV:
#ifdef LIBXML_ICONV_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_ISO8859X:
#ifdef LIBXML_ISO8859X_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_UNICODE:
#ifdef LIBXML_UNICODE_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_REGEXP:
#ifdef LIBXML_REGEXP_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_AUTOMATA:
#ifdef LIBXML_AUTOMATA_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_EXPR:
#ifdef LIBXML_EXPR_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_SCHEMAS:
#ifdef LIBXML_SCHEMAS_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_SCHEMATRON:
#ifdef LIBXML_SCHEMATRON_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_MODULES:
#ifdef LIBXML_MODULES_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_DEBUG:
#ifdef LIBXML_DEBUG_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_DEBUG_MEM:
#ifdef DEBUG_MEMORY_LOCATION
            return(1);
#else
            return(0);
#endif
        case XML_WITH_DEBUG_RUN:
#ifdef LIBXML_DEBUG_RUNTIME
            return(1);
#else
            return(0);
#endif
        case XML_WITH_ZLIB:
#ifdef LIBXML_ZLIB_ENABLED
            return(1);
#else
            return(0);
#endif
        case XML_WITH_ICU:
#ifdef LIBXML_ICU_ENABLED
            return(1);
#else
            return(0);
#endif
        default:
	    break;
     }
     return(0);
}


xmlParseComment(xmlParserCtxtPtr ctxt) {
    xmlChar *buf = NULL;
    int size = XML_PARSER_BUFFER_SIZE;
    int len = 0;
    xmlParserInputState state;
    const xmlChar *in;
    int nbchar = 0, ccol;
    int inputid;

    /*
     * Check that there is a comment right here.
     */
    if ((RAW != '<') || (NXT(1) != '!') ||
        (NXT(2) != '-') || (NXT(3) != '-')) return;
    state = ctxt->instate;
    ctxt->instate = XML_PARSER_COMMENT;
    inputid = ctxt->input->id;
    SKIP(4);
    SHRINK;
    GROW;

    /*
     * Accelerated common case where input don't need to be
     * modified before passing it to the handler.
     */
    in = ctxt->input->cur;
    do {
	if (*in == 0xA) {
	    do {
		ctxt->input->line++; ctxt->input->col = 1;
		in++;
	    } while (*in == 0xA);
	}
get_more:
        ccol = ctxt->input->col;
	while (((*in > '-') && (*in <= 0x7F)) ||
	       ((*in >= 0x20) && (*in < '-')) ||
	       (*in == 0x09)) {
		    in++;
		    ccol++;
	}
	ctxt->input->col = ccol;
	if (*in == 0xA) {
	    do {
		ctxt->input->line++; ctxt->input->col = 1;
		in++;
	    } while (*in == 0xA);
	    goto get_more;
	}
	nbchar = in - ctxt->input->cur;
	/*
	 * save current set of data
	 */
	if (nbchar > 0) {
	    if ((ctxt->sax != NULL) &&
		(ctxt->sax->comment != NULL)) {
		if (buf == NULL) {
		    if ((*in == '-') && (in[1] == '-'))
		        size = nbchar + 1;
		    else
		        size = XML_PARSER_BUFFER_SIZE + nbchar;
		    buf = (xmlChar *) xmlMallocAtomic(size * sizeof(xmlChar));
		    if (buf == NULL) {
		        xmlErrMemory(ctxt, NULL);
			ctxt->instate = state;
			return;
		    }
		    len = 0;
		} else if (len + nbchar + 1 >= size) {
		    xmlChar *new_buf;
		    size  += len + nbchar + XML_PARSER_BUFFER_SIZE;
		    new_buf = (xmlChar *) xmlRealloc(buf,
		                                     size * sizeof(xmlChar));
		    if (new_buf == NULL) {
		        xmlFree (buf);
			xmlErrMemory(ctxt, NULL);
			ctxt->instate = state;
			return;
		    }
		    buf = new_buf;
		}
		memcpy(&buf[len], ctxt->input->cur, nbchar);
		len += nbchar;
		buf[len] = 0;
	    }
	}
	ctxt->input->cur = in;
	if (*in == 0xA) {
	    in++;
	    ctxt->input->line++; ctxt->input->col = 1;
	}
	if (*in == 0xD) {
	    in++;
	    if (*in == 0xA) {
		ctxt->input->cur = in;
		in++;
		ctxt->input->line++; ctxt->input->col = 1;
		continue; /* while */
	    }
	    in--;
	}
	SHRINK;
	GROW;
	in = ctxt->input->cur;
	if (*in == '-') {
	    if (in[1] == '-') {
	        if (in[2] == '>') {
		    if (ctxt->input->id != inputid) {
			xmlFatalErrMsg(ctxt, XML_ERR_ENTITY_BOUNDARY,
			"comment doesn't start and stop in the same entity\n");
		    }
		    SKIP(3);
		    if ((ctxt->sax != NULL) && (ctxt->sax->comment != NULL) &&
		        (!ctxt->disableSAX)) {
			if (buf != NULL)
			    ctxt->sax->comment(ctxt->userData, buf);
			else
			    ctxt->sax->comment(ctxt->userData, BAD_CAST "");
		    }
		    if (buf != NULL)
		        xmlFree(buf);
		    ctxt->instate = state;
		    return;
		}
		if (buf != NULL)
		    xmlFatalErrMsgStr(ctxt, XML_ERR_COMMENT_NOT_FINISHED,
		                      "Comment not terminated \n<!--%.50s\n",
				      buf);
		else
		    xmlFatalErrMsgStr(ctxt, XML_ERR_COMMENT_NOT_FINISHED,
		                      "Comment not terminated \n", NULL);
		in++;
		ctxt->input->col++;
	    }
	    in++;
	    ctxt->input->col++;
	    goto get_more;
	}
    } while (((*in >= 0x20) && (*in <= 0x7F)) || (*in == 0x09));
    xmlParseCommentComplex(ctxt, buf, len, size);
    ctxt->instate = state;
    return;
}


xmlParseElement(xmlParserCtxtPtr ctxt) {
    const xmlChar *name;
    const xmlChar *prefix = NULL;
    const xmlChar *URI = NULL;
    xmlParserNodeInfo node_info;
    int line, tlen;
    xmlNodePtr ret;
    int nsNr = ctxt->nsNr;

    if (((unsigned int) ctxt->nameNr > xmlParserMaxDepth) &&
        ((ctxt->options & XML_PARSE_HUGE) == 0)) {
	xmlFatalErrMsgInt(ctxt, XML_ERR_INTERNAL_ERROR,
		 "Excessive depth in document: %d use XML_PARSE_HUGE option\n",
			  xmlParserMaxDepth);
	ctxt->instate = XML_PARSER_EOF;
	return;
    }

    /* Capture start position */
    if (ctxt->record_info) {
        node_info.begin_pos = ctxt->input->consumed +
                          (CUR_PTR - ctxt->input->base);
	node_info.begin_line = ctxt->input->line;
    }

    if (ctxt->spaceNr == 0)
	spacePush(ctxt, -1);
    else if (*ctxt->space == -2)
	spacePush(ctxt, -1);
    else
	spacePush(ctxt, *ctxt->space);

    line = ctxt->input->line;
#ifdef LIBXML_SAX1_ENABLED
    if (ctxt->sax2)
#endif /* LIBXML_SAX1_ENABLED */
        name = xmlParseStartTag2(ctxt, &prefix, &URI, &tlen);
#ifdef LIBXML_SAX1_ENABLED
    else
	name = xmlParseStartTag(ctxt);
#endif /* LIBXML_SAX1_ENABLED */
    if (ctxt->instate == XML_PARSER_EOF)
	return;
    if (name == NULL) {
	spacePop(ctxt);
        return;
    }
    namePush(ctxt, name);
    ret = ctxt->node;

#ifdef LIBXML_VALID_ENABLED
    /*
     * [ VC: Root Element Type ]
     * The Name in the document type declaration must match the element
     * type of the root element.
     */
    if (ctxt->validate && ctxt->wellFormed && ctxt->myDoc &&
        ctxt->node && (ctxt->node == ctxt->myDoc->children))
        ctxt->valid &= xmlValidateRoot(&ctxt->vctxt, ctxt->myDoc);
#endif /* LIBXML_VALID_ENABLED */

    /*
     * Check for an Empty Element.
     */
    if ((RAW == '/') && (NXT(1) == '>')) {
        SKIP(2);
	if (ctxt->sax2) {
	    if ((ctxt->sax != NULL) && (ctxt->sax->endElementNs != NULL) &&
		(!ctxt->disableSAX))
		ctxt->sax->endElementNs(ctxt->userData, name, prefix, URI);
#ifdef LIBXML_SAX1_ENABLED
	} else {
	    if ((ctxt->sax != NULL) && (ctxt->sax->endElement != NULL) &&
		(!ctxt->disableSAX))
		ctxt->sax->endElement(ctxt->userData, name);
#endif /* LIBXML_SAX1_ENABLED */
	}
	namePop(ctxt);
	spacePop(ctxt);
	if (nsNr != ctxt->nsNr)
	    nsPop(ctxt, ctxt->nsNr - nsNr);
	if ( ret != NULL && ctxt->record_info ) {
	   node_info.end_pos = ctxt->input->consumed +
			      (CUR_PTR - ctxt->input->base);
	   node_info.end_line = ctxt->input->line;
	   node_info.node = ret;
	   xmlParserAddNodeInfo(ctxt, &node_info);
	}
	return;
    }
    if (RAW == '>') {
        NEXT1;
    } else {
        xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_GT_REQUIRED,
		     "Couldn't find end of Start Tag %s line %d\n",
		                name, line, NULL);

	/*
	 * end of parsing of this node.
	 */
	nodePop(ctxt);
	namePop(ctxt);
	spacePop(ctxt);
	if (nsNr != ctxt->nsNr)
	    nsPop(ctxt, ctxt->nsNr - nsNr);

	/*
	 * Capture end position and add node
	 */
	if ( ret != NULL && ctxt->record_info ) {
	   node_info.end_pos = ctxt->input->consumed +
			      (CUR_PTR - ctxt->input->base);
	   node_info.end_line = ctxt->input->line;
	   node_info.node = ret;
	   xmlParserAddNodeInfo(ctxt, &node_info);
	}
	return;
    }

    /*
     * Parse the content of the element:
     */
    xmlParseContent(ctxt);
    if (!IS_BYTE_CHAR(RAW)) {
        xmlFatalErrMsgStrIntStr(ctxt, XML_ERR_TAG_NOT_FINISHED,
	 "Premature end of data in tag %s line %d\n",
		                name, line, NULL);

	/*
	 * end of parsing of this node.
	 */
	nodePop(ctxt);
	namePop(ctxt);
	spacePop(ctxt);
	if (nsNr != ctxt->nsNr)
	    nsPop(ctxt, ctxt->nsNr - nsNr);
	return;
    }

    /*
     * parse the end of tag: '</' should be here.
     */
    if (ctxt->sax2) {
	xmlParseEndTag2(ctxt, prefix, URI, line, ctxt->nsNr - nsNr, tlen);
	namePop(ctxt);
    }
#ifdef LIBXML_SAX1_ENABLED
      else
	xmlParseEndTag1(ctxt, line);
#endif /* LIBXML_SAX1_ENABLED */

    /*
     * Capture end position and add node
     */
    if ( ret != NULL && ctxt->record_info ) {
       node_info.end_pos = ctxt->input->consumed +
                          (CUR_PTR - ctxt->input->base);
       node_info.end_line = ctxt->input->line;
       node_info.node = ret;
       xmlParserAddNodeInfo(ctxt, &node_info);
    }
}


xmlReadIO(xmlInputReadCallback ioread, xmlInputCloseCallback ioclose,
          void *ioctx, const char *URL, const char *encoding, int options)
{
    xmlParserCtxtPtr ctxt;
    xmlParserInputBufferPtr input;
    xmlParserInputPtr stream;

    if (ioread == NULL)
        return (NULL);

    input = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx,
                                         XML_CHAR_ENCODING_NONE);
    if (input == NULL)
        return (NULL);
    ctxt = xmlNewParserCtxt();
    if (ctxt == NULL) {
        xmlFreeParserInputBuffer(input);
        return (NULL);
    }
    stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE);
    if (stream == NULL) {
        xmlFreeParserInputBuffer(input);
	xmlFreeParserCtxt(ctxt);
        return (NULL);
    }
    inputPush(ctxt, stream);
    return (xmlDoRead(ctxt, URL, encoding, options, 0));
}


xmlCtxtReadIO(xmlParserCtxtPtr ctxt, xmlInputReadCallback ioread,
              xmlInputCloseCallback ioclose, void *ioctx,
	      const char *URL,
              const char *encoding, int options)
{
    xmlParserInputBufferPtr input;
    xmlParserInputPtr stream;

    if (ioread == NULL)
        return (NULL);
    if (ctxt == NULL)
        return (NULL);

    xmlCtxtReset(ctxt);

    input = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx,
                                         XML_CHAR_ENCODING_NONE);
    if (input == NULL)
        return (NULL);
    stream = xmlNewIOInputStream(ctxt, input, XML_CHAR_ENCODING_NONE);
    if (stream == NULL) {
        xmlFreeParserInputBuffer(input);
        return (NULL);
    }
    inputPush(ctxt, stream);
    return (xmlDoRead(ctxt, URL, encoding, options, 1));
}


xmlParseCtxtExternalEntity(xmlParserCtxtPtr ctx, const xmlChar *URL,
	               const xmlChar *ID, xmlNodePtr *lst) {
    xmlParserCtxtPtr ctxt;
    xmlDocPtr newDoc;
    xmlNodePtr newRoot;
    xmlSAXHandlerPtr oldsax = NULL;
    int ret = 0;
    xmlChar start[4];
    xmlCharEncoding enc;

    if (ctx == NULL) return(-1);

    if (((ctx->depth > 40) && ((ctx->options & XML_PARSE_HUGE) == 0)) ||
        (ctx->depth > 1024)) {
	return(XML_ERR_ENTITY_LOOP);
    }

    if (lst != NULL)
        *lst = NULL;
    if ((URL == NULL) && (ID == NULL))
	return(-1);
    if (ctx->myDoc == NULL) /* @@ relax but check for dereferences */
	return(-1);

    ctxt = xmlCreateEntityParserCtxtInternal(URL, ID, NULL, ctx);
    if (ctxt == NULL) {
	return(-1);
    }

    oldsax = ctxt->sax;
    ctxt->sax = ctx->sax;
    xmlDetectSAX2(ctxt);
    newDoc = xmlNewDoc(BAD_CAST "1.0");
    if (newDoc == NULL) {
	xmlFreeParserCtxt(ctxt);
	return(-1);
    }
    newDoc->properties = XML_DOC_INTERNAL;
    if (ctx->myDoc->dict) {
	newDoc->dict = ctx->myDoc->dict;
	xmlDictReference(newDoc->dict);
    }
    if (ctx->myDoc != NULL) {
	newDoc->intSubset = ctx->myDoc->intSubset;
	newDoc->extSubset = ctx->myDoc->extSubset;
    }
    if (ctx->myDoc->URL != NULL) {
	newDoc->URL = xmlStrdup(ctx->myDoc->URL);
    }
    newRoot = xmlNewDocNode(newDoc, NULL, BAD_CAST "pseudoroot", NULL);
    if (newRoot == NULL) {
	ctxt->sax = oldsax;
	xmlFreeParserCtxt(ctxt);
	newDoc->intSubset = NULL;
	newDoc->extSubset = NULL;
        xmlFreeDoc(newDoc);
	return(-1);
    }
    xmlAddChild((xmlNodePtr) newDoc, newRoot);
    nodePush(ctxt, newDoc->children);
    if (ctx->myDoc == NULL) {
	ctxt->myDoc = newDoc;
    } else {
	ctxt->myDoc = ctx->myDoc;
	newDoc->children->doc = ctx->myDoc;
    }

    /*
     * Get the 4 first bytes and decode the charset
     * if enc != XML_CHAR_ENCODING_NONE
     * plug some encoding conversion routines.
     */
    GROW
    if ((ctxt->input->end - ctxt->input->cur) >= 4) {
	start[0] = RAW;
	start[1] = NXT(1);
	start[2] = NXT(2);
	start[3] = NXT(3);
	enc = xmlDetectCharEncoding(start, 4);
	if (enc != XML_CHAR_ENCODING_NONE) {
	    xmlSwitchEncoding(ctxt, enc);
	}
    }

    /*
     * Parse a possible text declaration first
     */
    if ((CMP5(CUR_PTR, '<', '?', 'x', 'm', 'l')) && (IS_BLANK_CH(NXT(5)))) {
	xmlParseTextDecl(ctxt);
	/*
	 * An XML-1.0 document can't reference an entity not XML-1.0
	 */
	if ((xmlStrEqual(ctx->version, BAD_CAST "1.0")) &&
	    (!xmlStrEqual(ctxt->input->version, BAD_CAST "1.0"))) {
	    xmlFatalErrMsg(ctxt, XML_ERR_VERSION_MISMATCH,
	                   "Version mismatch between document and entity\n");
	}
    }

    /*
     * Doing validity checking on chunk doesn't make sense
     */
    ctxt->instate = XML_PARSER_CONTENT;
    ctxt->validate = ctx->validate;
    ctxt->valid = ctx->valid;
    ctxt->loadsubset = ctx->loadsubset;
    ctxt->depth = ctx->depth + 1;
    ctxt->replaceEntities = ctx->replaceEntities;
    if (ctxt->validate) {
	ctxt->vctxt.error = ctx->vctxt.error;
	ctxt->vctxt.warning = ctx->vctxt.warning;
    } else {
	ctxt->vctxt.error = NULL;
	ctxt->vctxt.warning = NULL;
    }
    ctxt->vctxt.nodeTab = NULL;
    ctxt->vctxt.nodeNr = 0;
    ctxt->vctxt.nodeMax = 0;
    ctxt->vctxt.node = NULL;
    if (ctxt->dict != NULL) xmlDictFree(ctxt->dict);
    ctxt->dict = ctx->dict;
    ctxt->str_xml = xmlDictLookup(ctxt->dict, BAD_CAST "xml", 3);
    ctxt->str_xmlns = xmlDictLookup(ctxt->dict, BAD_CAST "xmlns", 5);
    ctxt->str_xml_ns = xmlDictLookup(ctxt->dict, XML_XML_NAMESPACE, 36);
    ctxt->dictNames = ctx->dictNames;
    ctxt->attsDefault = ctx->attsDefault;
    ctxt->attsSpecial = ctx->attsSpecial;
    ctxt->linenumbers = ctx->linenumbers;

    xmlParseContent(ctxt);

    ctx->validate = ctxt->validate;
    ctx->valid = ctxt->valid;
    if ((RAW == '<') && (NXT(1) == '/')) {
	xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL);
    } else if (RAW != 0) {
	xmlFatalErr(ctxt, XML_ERR_EXTRA_CONTENT, NULL);
    }
    if (ctxt->node != newDoc->children) {
	xmlFatalErr(ctxt, XML_ERR_NOT_WELL_BALANCED, NULL);
    }

    if (!ctxt->wellFormed) {
        if (ctxt->errNo == 0)
	    ret = 1;
	else
	    ret = ctxt->errNo;
    } else {
	if (lst != NULL) {
	    xmlNodePtr cur;

	    /*
	     * Return the newly created nodeset after unlinking it from
	     * they pseudo parent.
	     */
	    cur = newDoc->children->children;
	    *lst = cur;
	    while (cur != NULL) {
		cur->parent = NULL;
		cur = cur->next;
	    }
            newDoc->children->children = NULL;
	}
	ret = 0;
    }
    ctxt->sax = oldsax;
    ctxt->dict = NULL;
    ctxt->attsDefault = NULL;
    ctxt->attsSpecial = NULL;
    xmlFreeParserCtxt(ctxt);
    newDoc->intSubset = NULL;
    newDoc->extSubset = NULL;
    xmlFreeDoc(newDoc);

    return(ret);
}


xmlCreateIOParserCtxt(xmlSAXHandlerPtr sax, void *user_data,
	xmlInputReadCallback   ioread, xmlInputCloseCallback  ioclose,
	void *ioctx, xmlCharEncoding enc) {
    xmlParserCtxtPtr ctxt;
    xmlParserInputPtr inputStream;
    xmlParserInputBufferPtr buf;

    if (ioread == NULL) return(NULL);

    buf = xmlParserInputBufferCreateIO(ioread, ioclose, ioctx, enc);
    if (buf == NULL) return(NULL);

    ctxt = xmlNewParserCtxt();
    if (ctxt == NULL) {
	xmlFreeParserInputBuffer(buf);
	return(NULL);
    }
    if (sax != NULL) {
#ifdef LIBXML_SAX1_ENABLED
	if (ctxt->sax != (xmlSAXHandlerPtr) &xmlDefaultSAXHandler)
#endif /* LIBXML_SAX1_ENABLED */
	    xmlFree(ctxt->sax);
	ctxt->sax = (xmlSAXHandlerPtr) xmlMalloc(sizeof(xmlSAXHandler));
	if (ctxt->sax == NULL) {
	    xmlErrMemory(ctxt, NULL);
	    xmlFreeParserCtxt(ctxt);
	    return(NULL);
	}
	memset(ctxt->sax, 0, sizeof(xmlSAXHandler));
	if (sax->initialized == XML_SAX2_MAGIC)
	    memcpy(ctxt->sax, sax, sizeof(xmlSAXHandler));
	else
	    memcpy(ctxt->sax, sax, sizeof(xmlSAXHandlerV1));
	if (user_data != NULL)
	    ctxt->userData = user_data;
    }

    inputStream = xmlNewIOInputStream(ctxt, buf, enc);
    if (inputStream == NULL) {
	xmlFreeParserCtxt(ctxt);
	return(NULL);
    }
    inputPush(ctxt, inputStream);

    return(ctxt);
}


patch 9.0.0104: going beyond allocated memory when evaluating string constant

Problem:    Going beyond allocated memory when evaluating string constant.
Solution:   Properly skip over <Key> form.

check_for_opt_number_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_number_arg(args, idx) != FAIL);
}

Heap-based Buffer Overflow in GitHub repository vim/vim prior to 9.0.0102.


check_for_string_or_list_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_LIST)
    {
	semsg(_(e_string_or_list_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_opt_string_or_list_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_string_or_list_arg(args, idx));
}


check_for_list_or_dict_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_LIST
	    && args[idx].v_type != VAR_DICT)
    {
	semsg(_(e_list_or_dict_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_list_or_blob_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_LIST && args[idx].v_type != VAR_BLOB)
    {
	semsg(_(e_list_or_blob_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


typval_compare_list(
	typval_T    *tv1,
	typval_T    *tv2,
	exprtype_T  type,
	int	    ic,
	int	    *res)
{
    int	    val = 0;

    if (type == EXPR_IS || type == EXPR_ISNOT)
    {
	val = (tv1->v_type == tv2->v_type
				      && tv1->vval.v_list == tv2->vval.v_list);
	if (type == EXPR_ISNOT)
	    val = !val;
    }
    else if (tv1->v_type != tv2->v_type
	    || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
    {
	if (tv1->v_type != tv2->v_type)
	    emsg(_(e_can_only_compare_list_with_list));
	else
	    emsg(_(e_invalid_operation_for_list));
	return FAIL;
    }
    else
    {
	val = list_equal(tv1->vval.v_list, tv2->vval.v_list,
							ic, FALSE);
	if (type == EXPR_NEQUAL)
	    val = !val;
    }
    *res = val;
    return OK;
}


tv_get_bool(typval_T *varp)
{
    return tv_get_bool_or_number_chk(varp, NULL, TRUE);
}


check_for_buffer_arg(typval_T *args, int idx)
{
    return check_for_string_or_number_arg(args, idx);
}


eval_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate)
{
    char_u	*p;
    char_u	*end;
    int		extra = interpolate ? 1 : 0;
    int		off = interpolate ? 0 : 1;
    int		len;

    // Find the end of the string, skipping backslashed characters.
    for (p = *arg + off; *p != NUL && *p != '"'; MB_PTR_ADV(p))
    {
	if (*p == '\\' && p[1] != NUL)
	{
	    ++p;
	    // A "\<x>" form occupies at least 4 characters, and produces up
	    // to 9 characters (6 for the char and 3 for a modifier):
	    // reserve space for 5 extra.
	    if (*p == '<')
	    {
		int		modifiers = 0;
		int		flags = FSK_KEYCODE | FSK_IN_STRING;

		extra += 5;

		// Skip to the '>' to avoid using '{' inside for string
		// interpolation.
		if (p[1] != '*')
		    flags |= FSK_SIMPLIFY;
		if (find_special_key(&p, &modifiers, flags, NULL) != 0)
		    --p;  // leave "p" on the ">"
	    }
	}
	else if (interpolate && (*p == '{' || *p == '}'))
	{
	    if (*p == '{' && p[1] != '{') // start of expression
		break;
	    ++p;
	    if (p[-1] == '}' && *p != '}') // single '}' is an error
	    {
		semsg(_(e_stray_closing_curly_str), *arg);
		return FAIL;
	    }
	    --extra;  // "{{" becomes "{", "}}" becomes "}"
	}
    }

    if (*p != '"' && !(interpolate && *p == '{'))
    {
	semsg(_(e_missing_double_quote_str), *arg);
	return FAIL;
    }

    // If only parsing, set *arg and return here
    if (!evaluate)
    {
	*arg = p + off;
	return OK;
    }

    // Copy the string into allocated memory, handling backslashed
    // characters.
    rettv->v_type = VAR_STRING;
    len = (int)(p - *arg + extra);
    rettv->vval.v_string = alloc(len);
    if (rettv->vval.v_string == NULL)
	return FAIL;
    end = rettv->vval.v_string;

    for (p = *arg + off; *p != NUL && *p != '"'; )
    {
	if (*p == '\\')
	{
	    switch (*++p)
	    {
		case 'b': *end++ = BS; ++p; break;
		case 'e': *end++ = ESC; ++p; break;
		case 'f': *end++ = FF; ++p; break;
		case 'n': *end++ = NL; ++p; break;
		case 'r': *end++ = CAR; ++p; break;
		case 't': *end++ = TAB; ++p; break;

		case 'X': // hex: "\x1", "\x12"
		case 'x':
		case 'u': // Unicode: "\u0023"
		case 'U':
			  if (vim_isxdigit(p[1]))
			  {
			      int	n, nr;
			      int	c = toupper(*p);

			      if (c == 'X')
				  n = 2;
			      else if (*p == 'u')
				  n = 4;
			      else
				  n = 8;
			      nr = 0;
			      while (--n >= 0 && vim_isxdigit(p[1]))
			      {
				  ++p;
				  nr = (nr << 4) + hex2nr(*p);
			      }
			      ++p;
			      // For "\u" store the number according to
			      // 'encoding'.
			      if (c != 'X')
				  end += (*mb_char2bytes)(nr, end);
			      else
				  *end++ = nr;
			  }
			  break;

			  // octal: "\1", "\12", "\123"
		case '0':
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7': *end = *p++ - '0';
			  if (*p >= '0' && *p <= '7')
			  {
			      *end = (*end << 3) + *p++ - '0';
			      if (*p >= '0' && *p <= '7')
				  *end = (*end << 3) + *p++ - '0';
			  }
			  ++end;
			  break;

			  // Special key, e.g.: "\<C-W>"
		case '<':
			  {
			      int flags = FSK_KEYCODE | FSK_IN_STRING;

			      if (p[1] != '*')
				  flags |= FSK_SIMPLIFY;
			      extra = trans_special(&p, end, flags, FALSE, NULL);
			      if (extra != 0)
			      {
				  end += extra;
				  if (end >= rettv->vval.v_string + len)
				      iemsg("eval_string() used more space than allocated");
				  break;
			      }
			  }
			  // FALLTHROUGH

		default: MB_COPY_CHAR(p, end);
			  break;
	    }
	}
	else
	{
	    if (interpolate && (*p == '{' || *p == '}'))
	    {
		if (*p == '{' && p[1] != '{') // start of expression
		    break;
		++p;  // reduce "{{" to "{" and "}}" to "}"
	    }
	    MB_COPY_CHAR(p, end);
	}
    }
    *end = NUL;
    if (*p == '"' && !interpolate)
	++p;
    *arg = p;

    return OK;
}


tv_stringify(typval_T *varp, char_u *buf)
{
    if (varp->v_type == VAR_LIST
	    || varp->v_type == VAR_DICT
	    || varp->v_type == VAR_BLOB
	    || varp->v_type == VAR_FUNC
	    || varp->v_type == VAR_PARTIAL
	    || varp->v_type == VAR_FLOAT)
    {
	typval_T tmp;

	f_string(varp, &tmp);
	tv_get_string_buf(&tmp, buf);
	clear_tv(varp);
	*varp = tmp;
	return tmp.vval.v_string;
    }
    return tv_get_string_buf(varp, buf);
}


tv_get_float_chk(typval_T *varp, int *error)
{
    switch (varp->v_type)
    {
	case VAR_NUMBER:
	    return (float_T)(varp->vval.v_number);
	case VAR_FLOAT:
	    return varp->vval.v_float;
	case VAR_FUNC:
	case VAR_PARTIAL:
	    emsg(_(e_using_funcref_as_float));
	    break;
	case VAR_STRING:
	    emsg(_(e_using_string_as_float));
	    break;
	case VAR_LIST:
	    emsg(_(e_using_list_as_float));
	    break;
	case VAR_DICT:
	    emsg(_(e_using_dictionary_as_float));
	    break;
	case VAR_BOOL:
	    emsg(_(e_using_boolean_value_as_float));
	    break;
	case VAR_SPECIAL:
	    emsg(_(e_using_special_value_as_float));
	    break;
	case VAR_JOB:
# ifdef FEAT_JOB_CHANNEL
	    emsg(_(e_using_job_as_float));
	    break;
# endif
	case VAR_CHANNEL:
# ifdef FEAT_JOB_CHANNEL
	    emsg(_(e_using_channel_as_float));
	    break;
# endif
	case VAR_BLOB:
	    emsg(_(e_using_blob_as_float));
	    break;
	case VAR_VOID:
	    emsg(_(e_cannot_use_void_value));
	    break;
	case VAR_UNKNOWN:
	case VAR_ANY:
	case VAR_INSTR:
	    internal_error_no_abort("tv_get_float(UNKNOWN)");
	    break;
    }
    if (error != NULL)
	*error = TRUE;
    return 0;
}


check_for_opt_string_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_string_arg(args, idx) != FAIL);
}


check_for_opt_lnum_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_lnum_arg(args, idx));
}


tv_get_string_buf_chk_strict(typval_T *varp, char_u *buf, int strict)
{
    switch (varp->v_type)
    {
	case VAR_NUMBER:
	    if (strict)
	    {
		emsg(_(e_using_number_as_string));
		break;
	    }
	    vim_snprintf((char *)buf, NUMBUFLEN, "%lld",
					    (varnumber_T)varp->vval.v_number);
	    return buf;
	case VAR_FUNC:
	case VAR_PARTIAL:
	    emsg(_(e_using_funcref_as_string));
	    break;
	case VAR_LIST:
	    emsg(_(e_using_list_as_string));
	    break;
	case VAR_DICT:
	    emsg(_(e_using_dictionary_as_string));
	    break;
	case VAR_FLOAT:
#ifdef FEAT_FLOAT
	    if (strict)
	    {
		emsg(_(e_using_float_as_string));
		break;
	    }
	    vim_snprintf((char *)buf, NUMBUFLEN, "%g", varp->vval.v_float);
	    return buf;
#endif
	case VAR_STRING:
	    if (varp->vval.v_string != NULL)
		return varp->vval.v_string;
	    return (char_u *)"";
	case VAR_BOOL:
	case VAR_SPECIAL:
	    STRCPY(buf, get_var_special_name(varp->vval.v_number));
	    return buf;
	case VAR_BLOB:
	    emsg(_(e_using_blob_as_string));
	    break;
	case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
	    if (in_vim9script())
	    {
		semsg(_(e_using_invalid_value_as_string_str), "job");
		break;
	    }
	    return job_to_string_buf(varp, buf);
#endif
	    break;
	case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
	    if (in_vim9script())
	    {
		semsg(_(e_using_invalid_value_as_string_str), "channel");
		break;
	    }
	    return channel_to_string_buf(varp, buf);
#endif
	    break;
	case VAR_VOID:
	    emsg(_(e_cannot_use_void_value));
	    break;
	case VAR_UNKNOWN:
	case VAR_ANY:
	case VAR_INSTR:
	    semsg(_(e_using_invalid_value_as_string_str),
						  vartype_name(varp->v_type));
	    break;
    }
    return NULL;
}


tv_get_string_strict(typval_T *varp)
{
    static char_u   mybuf[NUMBUFLEN];
    char_u	    *res =  tv_get_string_buf_chk_strict(
						 varp, mybuf, in_vim9script());

    return res != NULL ? res : (char_u *)"";
}


copy_tv(typval_T *from, typval_T *to)
{
    to->v_type = from->v_type;
    to->v_lock = 0;
    switch (from->v_type)
    {
	case VAR_NUMBER:
	case VAR_BOOL:
	case VAR_SPECIAL:
	    to->vval.v_number = from->vval.v_number;
	    break;
	case VAR_FLOAT:
#ifdef FEAT_FLOAT
	    to->vval.v_float = from->vval.v_float;
	    break;
#endif
	case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
	    to->vval.v_job = from->vval.v_job;
	    if (to->vval.v_job != NULL)
		++to->vval.v_job->jv_refcount;
	    break;
#endif
	case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
	    to->vval.v_channel = from->vval.v_channel;
	    if (to->vval.v_channel != NULL)
		++to->vval.v_channel->ch_refcount;
	    break;
#endif
	case VAR_INSTR:
	    to->vval.v_instr = from->vval.v_instr;
	    break;

	case VAR_STRING:
	case VAR_FUNC:
	    if (from->vval.v_string == NULL)
		to->vval.v_string = NULL;
	    else
	    {
		to->vval.v_string = vim_strsave(from->vval.v_string);
		if (from->v_type == VAR_FUNC)
		    func_ref(to->vval.v_string);
	    }
	    break;
	case VAR_PARTIAL:
	    if (from->vval.v_partial == NULL)
		to->vval.v_partial = NULL;
	    else
	    {
		to->vval.v_partial = from->vval.v_partial;
		++to->vval.v_partial->pt_refcount;
	    }
	    break;
	case VAR_BLOB:
	    if (from->vval.v_blob == NULL)
		to->vval.v_blob = NULL;
	    else
	    {
		to->vval.v_blob = from->vval.v_blob;
		++to->vval.v_blob->bv_refcount;
	    }
	    break;
	case VAR_LIST:
	    if (from->vval.v_list == NULL)
		to->vval.v_list = NULL;
	    else
	    {
		to->vval.v_list = from->vval.v_list;
		++to->vval.v_list->lv_refcount;
	    }
	    break;
	case VAR_DICT:
	    if (from->vval.v_dict == NULL)
		to->vval.v_dict = NULL;
	    else
	    {
		to->vval.v_dict = from->vval.v_dict;
		++to->vval.v_dict->dv_refcount;
	    }
	    break;
	case VAR_VOID:
	    emsg(_(e_cannot_use_void_value));
	    break;
	case VAR_UNKNOWN:
	case VAR_ANY:
	    internal_error_no_abort("copy_tv(UNKNOWN)");
	    break;
    }
}


tv_get_bool_chk(typval_T *varp, int *denote)
{
    return tv_get_bool_or_number_chk(varp, denote, TRUE);
}


check_for_string_or_number_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_NUMBER)
    {
	semsg(_(e_string_or_number_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


typval_compare_string(
	typval_T    *tv1,
	typval_T    *tv2,
	exprtype_T  type,
	int	    ic,
	int	    *res)
{
    int		i = 0;
    int		val = FALSE;
    char_u	*s1, *s2;
    char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];

    if (in_vim9script()
	  && ((tv1->v_type != VAR_STRING && tv1->v_type != VAR_SPECIAL)
	   || (tv2->v_type != VAR_STRING && tv2->v_type != VAR_SPECIAL)))
    {
	semsg(_(e_cannot_compare_str_with_str),
		   vartype_name(tv1->v_type), vartype_name(tv2->v_type));
	return FAIL;
    }
    s1 = tv_get_string_buf(tv1, buf1);
    s2 = tv_get_string_buf(tv2, buf2);
    if (type != EXPR_MATCH && type != EXPR_NOMATCH)
	i = ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2);
    switch (type)
    {
	case EXPR_IS:	    if (in_vim9script())
			    {
				// Really check it is the same string, not just
				// the same value.
				val = tv1->vval.v_string == tv2->vval.v_string;
				break;
			    }
			    // FALLTHROUGH
	case EXPR_EQUAL:    val = (i == 0); break;
	case EXPR_ISNOT:    if (in_vim9script())
			    {
				// Really check it is not the same string, not
				// just a different value.
				val = tv1->vval.v_string != tv2->vval.v_string;
				break;
			    }
			    // FALLTHROUGH
	case EXPR_NEQUAL:   val = (i != 0); break;
	case EXPR_GREATER:  val = (i > 0); break;
	case EXPR_GEQUAL:   val = (i >= 0); break;
	case EXPR_SMALLER:  val = (i < 0); break;
	case EXPR_SEQUAL:   val = (i <= 0); break;

	case EXPR_MATCH:
	case EXPR_NOMATCH:
		val = pattern_match(s2, s1, ic);
		if (type == EXPR_NOMATCH)
		    val = !val;
		break;

	default:  break;  // avoid gcc warning
    }
    *res = val;
    return OK;
}


eval_option(
    char_u	**arg,
    typval_T	*rettv,	// when NULL, only check if option exists
    int		evaluate)
{
    char_u	*option_end;
    long	numval;
    char_u	*stringval;
    getoption_T	opt_type;
    int		c;
    int		working = (**arg == '+');    // has("+option")
    int		ret = OK;
    int		scope;

    // Isolate the option name and find its value.
    option_end = find_option_end(arg, &scope);
    if (option_end == NULL)
    {
	if (rettv != NULL)
	    semsg(_(e_option_name_missing_str), *arg);
	return FAIL;
    }

    if (!evaluate)
    {
	*arg = option_end;
	return OK;
    }

    c = *option_end;
    *option_end = NUL;
    opt_type = get_option_value(*arg, &numval,
			       rettv == NULL ? NULL : &stringval, NULL, scope);

    if (opt_type == gov_unknown)
    {
	if (rettv != NULL)
	    semsg(_(e_unknown_option_str), *arg);
	ret = FAIL;
    }
    else if (rettv != NULL)
    {
	rettv->v_lock = 0;
	if (opt_type == gov_hidden_string)
	{
	    rettv->v_type = VAR_STRING;
	    rettv->vval.v_string = NULL;
	}
	else if (opt_type == gov_hidden_bool || opt_type == gov_hidden_number)
	{
	    rettv->v_type = in_vim9script() && opt_type == gov_hidden_bool
						       ? VAR_BOOL : VAR_NUMBER;
	    rettv->vval.v_number = 0;
	}
	else if (opt_type == gov_bool || opt_type == gov_number)
	{
	    if (in_vim9script() && opt_type == gov_bool)
	    {
		rettv->v_type = VAR_BOOL;
		rettv->vval.v_number = numval ? VVAL_TRUE : VVAL_FALSE;
	    }
	    else
	    {
		rettv->v_type = VAR_NUMBER;
		rettv->vval.v_number = numval;
	    }
	}
	else				// string option
	{
	    rettv->v_type = VAR_STRING;
	    rettv->vval.v_string = stringval;
	}
    }
    else if (working && (opt_type == gov_hidden_bool
			|| opt_type == gov_hidden_number
			|| opt_type == gov_hidden_string))
	ret = FAIL;

    *option_end = c;		    // put back for error messages
    *arg = option_end;

    return ret;
}


tv_get_number(typval_T *varp)
{
    int		error = FALSE;

    return tv_get_number_chk(varp, &error);	// return 0L on error
}


check_for_opt_buffer_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_buffer_arg(args, idx));
}


tv_get_number_chk(typval_T *varp, int *denote)
{
    return tv_get_bool_or_number_chk(varp, denote, FALSE);
}


check_for_string_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING)
    {
	semsg(_(e_string_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


tv_check_lock(typval_T *tv, char_u *name, int use_gettext)
{
    int	lock = 0;

    switch (tv->v_type)
    {
	case VAR_BLOB:
	    if (tv->vval.v_blob != NULL)
		lock = tv->vval.v_blob->bv_lock;
	    break;
	case VAR_LIST:
	    if (tv->vval.v_list != NULL)
		lock = tv->vval.v_list->lv_lock;
	    break;
	case VAR_DICT:
	    if (tv->vval.v_dict != NULL)
		lock = tv->vval.v_dict->dv_lock;
	    break;
	default:
	    break;
    }
    return value_check_lock(tv->v_lock, name, use_gettext)
		   || (lock != 0 && value_check_lock(lock, name, use_gettext));
}


typval_tostring(typval_T *arg, int quotes)
{
    char_u	*tofree;
    char_u	numbuf[NUMBUFLEN];
    char_u	*ret = NULL;

    if (arg == NULL)
	return vim_strsave((char_u *)"(does not exist)");
    if (!quotes && arg->v_type == VAR_STRING)
    {
	ret = vim_strsave(arg->vval.v_string == NULL ? (char_u *)""
							 : arg->vval.v_string);
    }
    else
    {
	ret = tv2string(arg, &tofree, numbuf, 0);
	// Make a copy if we have a value but it's not in allocated memory.
	if (ret != NULL && tofree == NULL)
	    ret = vim_strsave(ret);
    }
    return ret;
}


tv_get_string_buf(typval_T *varp, char_u *buf)
{
    char_u	*res = tv_get_string_buf_chk(varp, buf);

    return res != NULL ? res : (char_u *)"";
}


tv_get_lnum_buf(typval_T *argvars, buf_T *buf)
{
    if (argvars[0].v_type == VAR_STRING
	    && argvars[0].vval.v_string != NULL
	    && argvars[0].vval.v_string[0] == '$'
	    && argvars[0].vval.v_string[1] == NUL
	    && buf != NULL)
	return buf->b_ml.ml_line_count;
    return (linenr_T)tv_get_number_chk(&argvars[0], NULL);
}


eval_interp_string(char_u **arg, typval_T *rettv, int evaluate)
{
    typval_T	tv;
    int		ret = OK;
    int		quote;
    garray_T	ga;
    char_u	*p;

    ga_init2(&ga, 1, 80);

    // *arg is on the '$' character, move it to the first string character.
    ++*arg;
    quote = **arg;
    ++*arg;

    for (;;)
    {
	// Get the string up to the matching quote or to a single '{'.
	// "arg" is advanced to either the quote or the '{'.
	if (quote == '"')
	    ret = eval_string(arg, &tv, evaluate, TRUE);
	else
	    ret = eval_lit_string(arg, &tv, evaluate, TRUE);
	if (ret == FAIL)
	    break;
	if (evaluate)
	{
	    ga_concat(&ga, tv.vval.v_string);
	    clear_tv(&tv);
	}

	if (**arg != '{')
	{
	    // found terminating quote
	    ++*arg;
	    break;
	}
	p = eval_one_expr_in_str(*arg, &ga, evaluate);
	if (p == NULL)
	{
	    ret = FAIL;
	    break;
	}
	*arg = p;
    }

    rettv->v_type = VAR_STRING;
    if (ret == FAIL || !evaluate || ga_append(&ga, NUL) == FAIL)
    {
	ga_clear(&ga);
	rettv->vval.v_string = NULL;
	return ret;
    }

    rettv->vval.v_string = ga.ga_data;
    return OK;
}


tv_get_string(typval_T *varp)
{
    static char_u   mybuf[NUMBUFLEN];

    return tv_get_string_buf(varp, mybuf);
}


free_tv(typval_T *varp)
{
    if (varp != NULL)
    {
	switch (varp->v_type)
	{
	    case VAR_FUNC:
		func_unref(varp->vval.v_string);
		// FALLTHROUGH
	    case VAR_STRING:
		vim_free(varp->vval.v_string);
		break;
	    case VAR_PARTIAL:
		partial_unref(varp->vval.v_partial);
		break;
	    case VAR_BLOB:
		blob_unref(varp->vval.v_blob);
		break;
	    case VAR_LIST:
		list_unref(varp->vval.v_list);
		break;
	    case VAR_DICT:
		dict_unref(varp->vval.v_dict);
		break;
	    case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
		job_unref(varp->vval.v_job);
		break;
#endif
	    case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
		channel_unref(varp->vval.v_channel);
		break;
#endif
	    case VAR_NUMBER:
	    case VAR_FLOAT:
	    case VAR_ANY:
	    case VAR_UNKNOWN:
	    case VAR_VOID:
	    case VAR_BOOL:
	    case VAR_SPECIAL:
	    case VAR_INSTR:
		break;
	}
	vim_free(varp);
    }
}


check_for_opt_job_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_job_arg(args, idx) != FAIL);
}


typval_compare_blob(
	typval_T    *tv1,
	typval_T    *tv2,
	exprtype_T  type,
	int	    *res)
{
    int	    val = 0;

    if (type == EXPR_IS || type == EXPR_ISNOT)
    {
	val = (tv1->v_type == tv2->v_type
			&& tv1->vval.v_blob == tv2->vval.v_blob);
	if (type == EXPR_ISNOT)
	    val = !val;
    }
    else if (tv1->v_type != tv2->v_type
	    || (type != EXPR_EQUAL && type != EXPR_NEQUAL))
    {
	if (tv1->v_type != tv2->v_type)
	    emsg(_(e_can_only_compare_blob_with_blob));
	else
	    emsg(_(e_invalid_operation_for_blob));
	return FAIL;
    }
    else
    {
	val = blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);
	if (type == EXPR_NEQUAL)
	    val = !val;
    }
    *res = val;
    return OK;
}


tv2string(
    typval_T	*tv,
    char_u	**tofree,
    char_u	*numbuf,
    int		copyID)
{
    return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE);
}


alloc_tv(void)
{
    return ALLOC_CLEAR_ONE(typval_T);
}


check_for_string_or_list_or_dict_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING
	    && args[idx].v_type != VAR_LIST
	    && args[idx].v_type != VAR_DICT)
    {
	semsg(_(e_string_list_or_dict_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


eval_env_var(char_u **arg, typval_T *rettv, int evaluate)
{
    char_u	*string = NULL;
    int		len;
    int		cc;
    char_u	*name;
    int		mustfree = FALSE;

    ++*arg;
    name = *arg;
    len = get_env_len(arg);
    if (evaluate)
    {
	if (len == 0)
	    return FAIL; // invalid empty name

	cc = name[len];
	name[len] = NUL;
	// first try vim_getenv(), fast for normal environment vars
	string = vim_getenv(name, &mustfree);
	if (string != NULL && *string != NUL)
	{
	    if (!mustfree)
		string = vim_strsave(string);
	}
	else
	{
	    if (mustfree)
		vim_free(string);

	    // next try expanding things like $VIM and ${HOME}
	    string = expand_env_save(name - 1);
	    if (string != NULL && *string == '$')
		VIM_CLEAR(string);
	}
	name[len] = cc;

	rettv->v_type = VAR_STRING;
	rettv->vval.v_string = string;
	rettv->v_lock = 0;
    }

    return OK;
}


clear_tv(typval_T *varp)
{
    if (varp != NULL)
    {
	switch (varp->v_type)
	{
	    case VAR_FUNC:
		func_unref(varp->vval.v_string);
		// FALLTHROUGH
	    case VAR_STRING:
		VIM_CLEAR(varp->vval.v_string);
		break;
	    case VAR_PARTIAL:
		partial_unref(varp->vval.v_partial);
		varp->vval.v_partial = NULL;
		break;
	    case VAR_BLOB:
		blob_unref(varp->vval.v_blob);
		varp->vval.v_blob = NULL;
		break;
	    case VAR_LIST:
		list_unref(varp->vval.v_list);
		varp->vval.v_list = NULL;
		break;
	    case VAR_DICT:
		dict_unref(varp->vval.v_dict);
		varp->vval.v_dict = NULL;
		break;
	    case VAR_NUMBER:
	    case VAR_BOOL:
	    case VAR_SPECIAL:
		varp->vval.v_number = 0;
		break;
	    case VAR_FLOAT:
#ifdef FEAT_FLOAT
		varp->vval.v_float = 0.0;
		break;
#endif
	    case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
		job_unref(varp->vval.v_job);
		varp->vval.v_job = NULL;
#endif
		break;
	    case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
		channel_unref(varp->vval.v_channel);
		varp->vval.v_channel = NULL;
#endif
		break;
	    case VAR_INSTR:
		VIM_CLEAR(varp->vval.v_instr);
		break;
	    case VAR_UNKNOWN:
	    case VAR_ANY:
	    case VAR_VOID:
		break;
	}
	varp->v_lock = 0;
    }
}


check_for_lnum_arg(typval_T *args, int idx)
{
    return check_for_string_or_number_arg(args, idx);
}


check_for_string_or_number_or_list_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING
	    && args[idx].v_type != VAR_NUMBER
	    && args[idx].v_type != VAR_LIST)
    {
	semsg(_(e_string_number_or_list_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_number_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_NUMBER)
    {
	semsg(_(e_number_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_float_or_nr_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_FLOAT && args[idx].v_type != VAR_NUMBER)
    {
	semsg(_(e_float_or_number_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


typval_compare(
    typval_T	*tv1,	// first operand
    typval_T	*tv2,	// second operand
    exprtype_T	type,   // operator
    int		ic)     // ignore case
{
    varnumber_T	n1, n2;
    int		res = 0;
    int		type_is = type == EXPR_IS || type == EXPR_ISNOT;

    if (type_is && tv1->v_type != tv2->v_type)
    {
	// For "is" a different type always means FALSE, for "notis"
	// it means TRUE.
	n1 = (type == EXPR_ISNOT);
    }
    else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
		|| (tv2->v_type == VAR_SPECIAL
					   && tv2->vval.v_number == VVAL_NULL))
	    && tv1->v_type != tv2->v_type
	    && (type == EXPR_EQUAL || type == EXPR_NEQUAL))
    {
	n1 = typval_compare_null(tv1, tv2);
	if (n1 == MAYBE)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	if (type == EXPR_NEQUAL)
	    n1 = !n1;
    }
    else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
    {
	if (typval_compare_blob(tv1, tv2, type, &res) == FAIL)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = res;
    }
    else if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
    {
	if (typval_compare_list(tv1, tv2, type, ic, &res) == FAIL)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = res;
    }
    else if (tv1->v_type == VAR_DICT || tv2->v_type == VAR_DICT)
    {
	if (typval_compare_dict(tv1, tv2, type, ic, &res) == FAIL)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = res;
    }
    else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC
	|| tv1->v_type == VAR_PARTIAL || tv2->v_type == VAR_PARTIAL)
    {
	if (typval_compare_func(tv1, tv2, type, ic, &res) == FAIL)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = res;
    }

#ifdef FEAT_FLOAT
    // If one of the two variables is a float, compare as a float.
    // When using "=~" or "!~", always compare as string.
    else if ((tv1->v_type == VAR_FLOAT || tv2->v_type == VAR_FLOAT)
	    && type != EXPR_MATCH && type != EXPR_NOMATCH)
    {
	float_T f1, f2;
	int	error = FALSE;

	f1 = tv_get_float_chk(tv1, &error);
	if (!error)
	    f2 = tv_get_float_chk(tv2, &error);
	if (error)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = FALSE;
	switch (type)
	{
	    case EXPR_IS:
	    case EXPR_EQUAL:    n1 = (f1 == f2); break;
	    case EXPR_ISNOT:
	    case EXPR_NEQUAL:   n1 = (f1 != f2); break;
	    case EXPR_GREATER:  n1 = (f1 > f2); break;
	    case EXPR_GEQUAL:   n1 = (f1 >= f2); break;
	    case EXPR_SMALLER:  n1 = (f1 < f2); break;
	    case EXPR_SEQUAL:   n1 = (f1 <= f2); break;
	    case EXPR_UNKNOWN:
	    case EXPR_MATCH:
	    default:  break;  // avoid gcc warning
	}
    }
#endif

    // If one of the two variables is a number, compare as a number.
    // When using "=~" or "!~", always compare as string.
    else if ((tv1->v_type == VAR_NUMBER || tv2->v_type == VAR_NUMBER)
	    && type != EXPR_MATCH && type != EXPR_NOMATCH)
    {
	int error = FALSE;

	n1 = tv_get_number_chk(tv1, &error);
	if (!error)
	    n2 = tv_get_number_chk(tv2, &error);
	if (error)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	switch (type)
	{
	    case EXPR_IS:
	    case EXPR_EQUAL:    n1 = (n1 == n2); break;
	    case EXPR_ISNOT:
	    case EXPR_NEQUAL:   n1 = (n1 != n2); break;
	    case EXPR_GREATER:  n1 = (n1 > n2); break;
	    case EXPR_GEQUAL:   n1 = (n1 >= n2); break;
	    case EXPR_SMALLER:  n1 = (n1 < n2); break;
	    case EXPR_SEQUAL:   n1 = (n1 <= n2); break;
	    case EXPR_UNKNOWN:
	    case EXPR_MATCH:
	    default:  break;  // avoid gcc warning
	}
    }
    else if (in_vim9script() && (tv1->v_type == VAR_BOOL
				    || tv2->v_type == VAR_BOOL
				    || (tv1->v_type == VAR_SPECIAL
					      && tv2->v_type == VAR_SPECIAL)))
    {
	if (tv1->v_type != tv2->v_type)
	{
	    semsg(_(e_cannot_compare_str_with_str),
		       vartype_name(tv1->v_type), vartype_name(tv2->v_type));
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = tv1->vval.v_number;
	n2 = tv2->vval.v_number;
	switch (type)
	{
	    case EXPR_IS:
	    case EXPR_EQUAL:    n1 = (n1 == n2); break;
	    case EXPR_ISNOT:
	    case EXPR_NEQUAL:   n1 = (n1 != n2); break;
	    default:
		semsg(_(e_invalid_operation_for_str),
						   vartype_name(tv1->v_type));
		clear_tv(tv1);
		return FAIL;
	}
    }
#ifdef FEAT_JOB_CHANNEL
    else if (tv1->v_type == tv2->v_type
	    && (tv1->v_type == VAR_CHANNEL || tv1->v_type == VAR_JOB)
	    && (type == EXPR_NEQUAL || type == EXPR_EQUAL))
    {
	if (tv1->v_type == VAR_CHANNEL)
	    n1 = tv1->vval.v_channel == tv2->vval.v_channel;
	else
	    n1 = tv1->vval.v_job == tv2->vval.v_job;
	if (type == EXPR_NEQUAL)
	    n1 = !n1;
    }
#endif
    else
    {
	if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL)
	{
	    clear_tv(tv1);
	    return FAIL;
	}
	n1 = res;
    }
    clear_tv(tv1);
    if (in_vim9script())
    {
	tv1->v_type = VAR_BOOL;
	tv1->vval.v_number = n1 ? VVAL_TRUE : VVAL_FALSE;
    }
    else
    {
	tv1->v_type = VAR_NUMBER;
	tv1->vval.v_number = n1;
    }

    return OK;
}


tv_islocked(typval_T *tv)
{
    return (tv->v_lock & VAR_LOCKED)
	|| (tv->v_type == VAR_LIST
		&& tv->vval.v_list != NULL
		&& (tv->vval.v_list->lv_lock & VAR_LOCKED))
	|| (tv->v_type == VAR_DICT
		&& tv->vval.v_dict != NULL
		&& (tv->vval.v_dict->dv_lock & VAR_LOCKED));
}


check_for_string_or_func_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_PARTIAL
	    && args[idx].v_type != VAR_FUNC
	    && args[idx].v_type != VAR_STRING)
    {
	semsg(_(e_string_or_function_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_dict_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_DICT)
    {
	semsg(_(e_dict_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_opt_buffer_or_dict_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_UNKNOWN
	    && args[idx].v_type != VAR_STRING
	    && args[idx].v_type != VAR_NUMBER
	    && args[idx].v_type != VAR_DICT)
    {
	semsg(_(e_string_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


func_equal(
    typval_T *tv1,
    typval_T *tv2,
    int	     ic)	    // ignore case
{
    char_u	*s1, *s2;
    dict_T	*d1, *d2;
    int		a1, a2;
    int		i;

    // empty and NULL function name considered the same
    s1 = tv1->v_type == VAR_FUNC ? tv1->vval.v_string
					   : partial_name(tv1->vval.v_partial);
    if (s1 != NULL && *s1 == NUL)
	s1 = NULL;
    s2 = tv2->v_type == VAR_FUNC ? tv2->vval.v_string
					   : partial_name(tv2->vval.v_partial);
    if (s2 != NULL && *s2 == NUL)
	s2 = NULL;
    if (s1 == NULL || s2 == NULL)
    {
	if (s1 != s2)
	    return FALSE;
    }
    else if (STRCMP(s1, s2) != 0)
	return FALSE;

    // empty dict and NULL dict is different
    d1 = tv1->v_type == VAR_FUNC ? NULL : tv1->vval.v_partial->pt_dict;
    d2 = tv2->v_type == VAR_FUNC ? NULL : tv2->vval.v_partial->pt_dict;
    if (d1 == NULL || d2 == NULL)
    {
	if (d1 != d2)
	    return FALSE;
    }
    else if (!dict_equal(d1, d2, ic, TRUE))
	return FALSE;

    // empty list and no list considered the same
    a1 = tv1->v_type == VAR_FUNC ? 0 : tv1->vval.v_partial->pt_argc;
    a2 = tv2->v_type == VAR_FUNC ? 0 : tv2->vval.v_partial->pt_argc;
    if (a1 != a2)
	return FALSE;
    for (i = 0; i < a1; ++i)
	if (!tv_equal(tv1->vval.v_partial->pt_argv + i,
		      tv2->vval.v_partial->pt_argv + i, ic, TRUE))
	    return FALSE;

    return TRUE;
}


tv_get_buf_from_arg(typval_T *tv)
{
    buf_T *buf;

    ++emsg_off;
    buf = tv_get_buf(tv, FALSE);
    --emsg_off;
    if (buf == NULL
	    && tv->v_type != VAR_NUMBER
	    && tv->v_type != VAR_STRING)
	// issue errmsg for type error
	(void)tv_get_number(tv);
    return buf;
}


alloc_string_tv(char_u *s)
{
    typval_T	*rettv;

    rettv = alloc_tv();
    if (rettv != NULL)
    {
	rettv->v_type = VAR_STRING;
	rettv->vval.v_string = s;
    }
    else
	vim_free(s);
    return rettv;
}


tv_get_float(typval_T *varp)
{
    return tv_get_float_chk(varp, NULL);
}


typval_compare_dict(
	typval_T    *tv1,
	typval_T    *tv2,
	exprtype_T  type,
	int	    ic,
	int	    *res)
{
    int	    val;

    if (type == EXPR_IS || type == EXPR_ISNOT)
    {
	val = (tv1->v_type == tv2->v_type
			&& tv1->vval.v_dict == tv2->vval.v_dict);
	if (type == EXPR_ISNOT)
	    val = !val;
    }
    else if (tv1->v_type != tv2->v_type
		|| (type != EXPR_EQUAL && type != EXPR_NEQUAL))
    {
	if (tv1->v_type != tv2->v_type)
	    emsg(_(e_can_only_compare_dictionary_with_dictionary));
	else
	    emsg(_(e_invalid_operation_for_dictionary));
	return FAIL;
    }
    else
    {
	val = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, FALSE);
	if (type == EXPR_NEQUAL)
	    val = !val;
    }
    *res = val;
    return OK;
}


check_for_job_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_JOB)
    {
	semsg(_(e_job_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_string_or_blob_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_BLOB)
    {
	semsg(_(e_string_or_blob_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_opt_dict_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_dict_arg(args, idx) != FAIL);
}


check_for_string_or_list_or_blob_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING
	    && args[idx].v_type != VAR_LIST
	    && args[idx].v_type != VAR_BLOB)
    {
	semsg(_(e_string_list_or_blob_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_opt_string_or_number_or_list_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_string_or_number_or_list_arg(args, idx) != FAIL);
}


tv_get_bool_or_number_chk(typval_T *varp, int *denote, int want_bool)
{
    varnumber_T	n = 0L;

    switch (varp->v_type)
    {
	case VAR_NUMBER:
	    if (in_vim9script() && want_bool && varp->vval.v_number != 0
						   && varp->vval.v_number != 1)
	    {
		semsg(_(e_using_number_as_bool_nr), varp->vval.v_number);
		break;
	    }
	    return varp->vval.v_number;
	case VAR_FLOAT:
#ifdef FEAT_FLOAT
	    emsg(_(e_using_float_as_number));
	    break;
#endif
	case VAR_FUNC:
	case VAR_PARTIAL:
	    emsg(_(e_using_funcref_as_number));
	    break;
	case VAR_STRING:
	    if (in_vim9script())
	    {
		emsg_using_string_as(varp, !want_bool);
		break;
	    }
	    if (varp->vval.v_string != NULL)
		vim_str2nr(varp->vval.v_string, NULL, NULL,
					    STR2NR_ALL, &n, NULL, 0, FALSE);
	    return n;
	case VAR_LIST:
	    emsg(_(e_using_list_as_number));
	    break;
	case VAR_DICT:
	    emsg(_(e_using_dictionary_as_number));
	    break;
	case VAR_BOOL:
	case VAR_SPECIAL:
	    if (!want_bool && in_vim9script())
	    {
		if (varp->v_type == VAR_BOOL)
		    emsg(_(e_using_bool_as_number));
		else
		    emsg(_(e_using_special_as_number));
		break;
	    }
	    return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
	case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
	    emsg(_(e_using_job_as_number));
	    break;
#endif
	case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
	    emsg(_(e_using_channel_as_number));
	    break;
#endif
	case VAR_BLOB:
	    emsg(_(e_using_blob_as_number));
	    break;
	case VAR_VOID:
	    emsg(_(e_cannot_use_void_value));
	    break;
	case VAR_UNKNOWN:
	case VAR_ANY:
	case VAR_INSTR:
	    internal_error_no_abort("tv_get_number(UNKNOWN)");
	    break;
    }
    if (denote == NULL)		// useful for values that must be unsigned
	n = -1;
    else
	*denote = TRUE;
    return n;
}


tv_equal(
    typval_T *tv1,
    typval_T *tv2,
    int	     ic,	    // ignore case
    int	     recursive)	    // TRUE when used recursively
{
    char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
    char_u	*s1, *s2;
    static int  recursive_cnt = 0;	    // catch recursive loops
    int		r;
    static int	tv_equal_recurse_limit;

    // Catch lists and dicts that have an endless loop by limiting
    // recursiveness to a limit.  We guess they are equal then.
    // A fixed limit has the problem of still taking an awful long time.
    // Reduce the limit every time running into it. That should work fine for
    // deeply linked structures that are not recursively linked and catch
    // recursiveness quickly.
    if (!recursive)
	tv_equal_recurse_limit = 1000;
    if (recursive_cnt >= tv_equal_recurse_limit)
    {
	--tv_equal_recurse_limit;
	return TRUE;
    }

    // For VAR_FUNC and VAR_PARTIAL compare the function name, bound dict and
    // arguments.
    if ((tv1->v_type == VAR_FUNC
		|| (tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial != NULL))
	    && (tv2->v_type == VAR_FUNC
		|| (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial != NULL)))
    {
	++recursive_cnt;
	r = func_equal(tv1, tv2, ic);
	--recursive_cnt;
	return r;
    }

    if (tv1->v_type != tv2->v_type
	    && ((tv1->v_type != VAR_BOOL && tv1->v_type != VAR_SPECIAL)
		|| (tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL)))
	return FALSE;

    switch (tv1->v_type)
    {
	case VAR_LIST:
	    ++recursive_cnt;
	    r = list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, TRUE);
	    --recursive_cnt;
	    return r;

	case VAR_DICT:
	    ++recursive_cnt;
	    r = dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, TRUE);
	    --recursive_cnt;
	    return r;

	case VAR_BLOB:
	    return blob_equal(tv1->vval.v_blob, tv2->vval.v_blob);

	case VAR_NUMBER:
	case VAR_BOOL:
	case VAR_SPECIAL:
	    return tv1->vval.v_number == tv2->vval.v_number;

	case VAR_STRING:
	    s1 = tv_get_string_buf(tv1, buf1);
	    s2 = tv_get_string_buf(tv2, buf2);
	    return ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0);

	case VAR_FLOAT:
#ifdef FEAT_FLOAT
	    return tv1->vval.v_float == tv2->vval.v_float;
#endif
	case VAR_JOB:
#ifdef FEAT_JOB_CHANNEL
	    return tv1->vval.v_job == tv2->vval.v_job;
#endif
	case VAR_CHANNEL:
#ifdef FEAT_JOB_CHANNEL
	    return tv1->vval.v_channel == tv2->vval.v_channel;
#endif
	case VAR_INSTR:
	    return tv1->vval.v_instr == tv2->vval.v_instr;

	case VAR_PARTIAL:
	    return tv1->vval.v_partial == tv2->vval.v_partial;

	case VAR_FUNC:
	    return tv1->vval.v_string == tv2->vval.v_string;

	case VAR_UNKNOWN:
	case VAR_ANY:
	case VAR_VOID:
	    break;
    }

    // VAR_UNKNOWN can be the result of a invalid expression, let's say it
    // does not equal anything, not even itself.
    return FALSE;
}


check_for_bool_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_BOOL
	    && !(args[idx].v_type == VAR_NUMBER
		&& (args[idx].vval.v_number == 0
		    || args[idx].vval.v_number == 1)))
    {
	semsg(_(e_bool_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_nonempty_string_arg(typval_T *args, int idx)
{
    if (check_for_string_arg(args, idx) == FAIL)
	return FAIL;
    if (args[idx].vval.v_string == NULL || *args[idx].vval.v_string == NUL)
    {
	semsg(_(e_non_empty_string_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_list_or_dict_or_blob_or_string_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_LIST
	    && args[idx].v_type != VAR_DICT
	    && args[idx].v_type != VAR_BLOB
	    && args[idx].v_type != VAR_STRING)
    {
	semsg(_(e_list_dict_blob_or_string_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_unknown_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_UNKNOWN)
    {
	semsg(_(e_too_many_arguments), idx + 1);
	return FAIL;
    }
    return OK;
}


tv_get_string_buf_chk(typval_T *varp, char_u *buf)
{
    return tv_get_string_buf_chk_strict(varp, buf, FALSE);
}


typval_compare_null(typval_T *tv1, typval_T *tv2)
{
    if ((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
	    || (tv2->v_type == VAR_SPECIAL && tv2->vval.v_number == VVAL_NULL))
    {
	typval_T	*tv = tv1->v_type == VAR_SPECIAL ? tv2 : tv1;

	switch (tv->v_type)
	{
	    case VAR_BLOB: return tv->vval.v_blob == NULL;
#ifdef FEAT_JOB_CHANNEL
	    case VAR_CHANNEL: return tv->vval.v_channel == NULL;
#endif
	    case VAR_DICT: return tv->vval.v_dict == NULL;
	    case VAR_FUNC: return tv->vval.v_string == NULL;
#ifdef FEAT_JOB_CHANNEL
	    case VAR_JOB: return tv->vval.v_job == NULL;
#endif
	    case VAR_LIST: return tv->vval.v_list == NULL;
	    case VAR_PARTIAL: return tv->vval.v_partial == NULL;
	    case VAR_STRING: return tv->vval.v_string == NULL;

	    case VAR_NUMBER: if (!in_vim9script())
				 return tv->vval.v_number == 0;
			     break;
#ifdef FEAT_FLOAT
	    case VAR_FLOAT: if (!in_vim9script())
				 return tv->vval.v_float == 0.0;
			     break;
#endif
	    default: break;
	}
    }
    // although comparing null with number, float or bool is not very useful
    // we won't give an error
    return FALSE;
}


check_for_string_or_dict_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_STRING && args[idx].v_type != VAR_DICT)
    {
	semsg(_(e_string_or_dict_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


tv_get_buf(typval_T *tv, int curtab_only)
{
    char_u	*name = tv->vval.v_string;
    buf_T	*buf;

    if (tv->v_type == VAR_NUMBER)
	return buflist_findnr((int)tv->vval.v_number);
    if (tv->v_type != VAR_STRING)
	return NULL;
    if (name == NULL || *name == NUL)
	return curbuf;
    if (name[0] == '$' && name[1] == NUL)
	return lastbuf;

    buf = buflist_find_by_name(name, curtab_only);

    // If not found, try expanding the name, like done for bufexists().
    if (buf == NULL)
	buf = find_buffer(tv);

    return buf;
}


check_for_opt_bool_arg(typval_T *args, int idx)
{
    if (args[idx].v_type == VAR_UNKNOWN)
	return OK;
    return check_for_bool_arg(args, idx);
}


tv_get_lnum(typval_T *argvars)
{
    linenr_T	lnum = -1;

    if (argvars[0].v_type != VAR_STRING || !in_vim9script())
	lnum = (linenr_T)tv_get_number_chk(&argvars[0], NULL);
    if (lnum <= 0 && argvars[0].v_type != VAR_NUMBER)
    {
	int	fnum;
	pos_T	*fp;

	// no valid number, try using arg like line()
	fp = var2fpos(&argvars[0], TRUE, &fnum, FALSE);
	if (fp != NULL)
	    lnum = fp->lnum;
    }
    return lnum;
}


check_for_list_or_dict_or_blob_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_LIST
	    && args[idx].v_type != VAR_DICT
	    && args[idx].v_type != VAR_BLOB)
    {
	semsg(_(e_list_dict_or_blob_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


typval_compare_func(
	typval_T    *tv1,
	typval_T    *tv2,
	exprtype_T  type,
	int	    ic,
	int	    *res)
{
    int	    val = 0;

    if (type != EXPR_EQUAL && type != EXPR_NEQUAL
	    && type != EXPR_IS && type != EXPR_ISNOT)
    {
	emsg(_(e_invalid_operation_for_funcrefs));
	return FAIL;
    }
    if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL)
	    || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL))
	// When both partials are NULL, then they are equal.
	// Otherwise they are not equal.
	val = (tv1->vval.v_partial == tv2->vval.v_partial);
    else if (type == EXPR_IS || type == EXPR_ISNOT)
    {
	if (tv1->v_type == VAR_FUNC && tv2->v_type == VAR_FUNC)
	    // strings are considered the same if their value is
	    // the same
	    val = tv_equal(tv1, tv2, ic, FALSE);
	else if (tv1->v_type == VAR_PARTIAL && tv2->v_type == VAR_PARTIAL)
	    val = (tv1->vval.v_partial == tv2->vval.v_partial);
	else
	    val = FALSE;
    }
    else
	val = tv_equal(tv1, tv2, ic, FALSE);
    if (type == EXPR_NEQUAL || type == EXPR_ISNOT)
	val = !val;
    *res = val;
    return OK;
}


check_for_chan_or_job_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_CHANNEL && args[idx].v_type != VAR_JOB)
    {
	semsg(_(e_chan_or_job_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


tv_get_string_chk(typval_T *varp)
{
    static char_u   mybuf[NUMBUFLEN];

    return tv_get_string_buf_chk(varp, mybuf);
}


init_tv(typval_T *varp)
{
    if (varp != NULL)
	CLEAR_POINTER(varp);
}


check_for_opt_list_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_list_arg(args, idx) != FAIL);
}


check_for_blob_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_BLOB)
    {
	semsg(_(e_blob_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_list_arg(typval_T *args, int idx)
{
    if (args[idx].v_type != VAR_LIST)
    {
	    semsg(_(e_list_required_for_argument_nr), idx + 1);
	return FAIL;
    }
    return OK;
}


check_for_opt_string_or_number_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_string_or_number_arg(args, idx) != FAIL);
}


eval_lit_string(char_u **arg, typval_T *rettv, int evaluate, int interpolate)
{
    char_u	*p;
    char_u	*str;
    int		reduce = interpolate ? -1 : 0;
    int		off = interpolate ? 0 : 1;

    // Find the end of the string, skipping ''.
    for (p = *arg + off; *p != NUL; MB_PTR_ADV(p))
    {
	if (*p == '\'')
	{
	    if (p[1] != '\'')
		break;
	    ++reduce;
	    ++p;
	}
	else if (interpolate)
	{
	    if (*p == '{')
	    {
		if (p[1] != '{')
		    break;
		++p;
		++reduce;
	    }
	    else if (*p == '}')
	    {
		++p;
		if (*p != '}')
		{
		    semsg(_(e_stray_closing_curly_str), *arg);
		    return FAIL;
		}
		++reduce;
	    }
	}
    }

    if (*p != '\'' && !(interpolate && *p == '{'))
    {
	semsg(_(e_missing_single_quote_str), *arg);
	return FAIL;
    }

    // If only parsing return after setting "*arg"
    if (!evaluate)
    {
	*arg = p + off;
	return OK;
    }

    // Copy the string into allocated memory, handling '' to ' reduction and
    // any expressions.
    str = alloc((p - *arg) - reduce);
    if (str == NULL)
	return FAIL;
    rettv->v_type = VAR_STRING;
    rettv->vval.v_string = str;

    for (p = *arg + off; *p != NUL; )
    {
	if (*p == '\'')
	{
	    if (p[1] != '\'')
		break;
	    ++p;
	}
	else if (interpolate && (*p == '{' || *p == '}'))
	{
	    if (*p == '{' && p[1] != '{')
		break;
	    ++p;
	}
	MB_COPY_CHAR(p, str);
    }
    *str = NUL;
    *arg = p + off;

    return OK;
}


eval_number(
	char_u	    **arg,
	typval_T    *rettv,
	int	    evaluate,
	int	    want_string UNUSED)
{
    int		len;
    int		skip_quotes = !in_old_script(4);
#ifdef FEAT_FLOAT
    char_u	*p;
    int		get_float = FALSE;

    // We accept a float when the format matches
    // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?".  This is very
    // strict to avoid backwards compatibility problems.
    // With script version 2 and later the leading digit can be
    // omitted.
    // Don't look for a float after the "." operator, so that
    // ":let vers = 1.2.3" doesn't fail.
    if (**arg == '.')
	p = *arg;
    else
    {
	p = *arg + 1;
	if (skip_quotes)
	    for (;;)
	    {
		if (*p == '\'')
		    ++p;
		if (!vim_isdigit(*p))
		    break;
		p = skipdigits(p);
	    }
	else
	    p = skipdigits(p);
    }
    if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
    {
	get_float = TRUE;
	p = skipdigits(p + 2);
	if (*p == 'e' || *p == 'E')
	{
	    ++p;
	    if (*p == '-' || *p == '+')
		++p;
	    if (!vim_isdigit(*p))
		get_float = FALSE;
	    else
		p = skipdigits(p + 1);
	}
	if (ASCII_ISALPHA(*p) || *p == '.')
	    get_float = FALSE;
    }
    if (get_float)
    {
	float_T	f;

	*arg += string2float(*arg, &f, skip_quotes);
	if (evaluate)
	{
	    rettv->v_type = VAR_FLOAT;
	    rettv->vval.v_float = f;
	}
    }
    else
#endif
    if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z'))
    {
	char_u  *bp;
	blob_T  *blob = NULL;  // init for gcc

	// Blob constant: 0z0123456789abcdef
	if (evaluate)
	    blob = blob_alloc();
	for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2)
	{
	    if (!vim_isxdigit(bp[1]))
	    {
		if (blob != NULL)
		{
		    emsg(_(e_blob_literal_should_have_an_even_number_of_hex_characters));
		    ga_clear(&blob->bv_ga);
		    VIM_CLEAR(blob);
		}
		return FAIL;
	    }
	    if (blob != NULL)
		ga_append(&blob->bv_ga,
			     (hex2nr(*bp) << 4) + hex2nr(*(bp+1)));
	    if (bp[2] == '.' && vim_isxdigit(bp[3]))
		++bp;
	}
	if (blob != NULL)
	    rettv_blob_set(rettv, blob);
	*arg = bp;
    }
    else
    {
	varnumber_T	n;

	// decimal, hex or octal number
	vim_str2nr(*arg, NULL, &len, skip_quotes
		      ? STR2NR_NO_OCT + STR2NR_QUOTE
		      : STR2NR_ALL, &n, NULL, 0, TRUE);
	if (len == 0)
	{
	    if (evaluate)
		semsg(_(e_invalid_expression_str), *arg);
	    return FAIL;
	}
	*arg += len;
	if (evaluate)
	{
	    rettv->v_type = VAR_NUMBER;
	    rettv->vval.v_number = n;
	}
    }
    return OK;
}


check_for_opt_chan_or_job_arg(typval_T *args, int idx)
{
    return (args[idx].v_type == VAR_UNKNOWN
	    || check_for_chan_or_job_arg(args, idx) != FAIL);
}


fix memory leaks in function cmdopts_parse

size_t get_default_max_mem_usage(void)
{
	size_t total_mem_size = jas_get_total_mem_size();
	size_t max_mem;
	if (total_mem_size) {
		max_mem = 0.90 * total_mem_size;
	} else {
		max_mem = JAS_DEFAULT_MAX_MEM_USAGE;
	}
	return max_mem;
}

A vulnerability found in jasper. This security vulnerability happens because of a memory leak bug in function cmdopts_parse that can cause a crash or segmentation fault.


int addopt(char *optstr, int maxlen, const char *s)
{
	size_t n;
	size_t m;

	n = strlen(optstr);
	m = n + strlen(s) + 1;
	if (m > JAS_CAST(size_t, maxlen)) {
		return 1;
	}
	if (n > 0) {
		strcat(optstr, "\n");
	}
	strcat(optstr, s);
	return 0;
}


void cmdusage()
{
	const char *s;
	int i;
	cmdinfo();
	fprintf(stderr, "usage: %s [options]\n", cmdname);
	for (i = 0, s = helpinfo[i]; s; ++i, s = helpinfo[i]) {
		fprintf(stderr, "%s", s);
	}

	fprintf(stderr, "The following formats are supported:\n");
	int fmtind;
	const jas_image_fmtinfo_t *fmtinfo;
	int numfmts = jas_image_getnumfmts();
	for (fmtind = 0; fmtind < numfmts; ++fmtind) {
		if (!(fmtinfo = jas_image_getfmtbyind(fmtind))) {
			break;
		}
		fprintf(stderr, "    %s %-5s    %s\n",
		  fmtinfo->enabled ? "[enabled] " : "[disabled]",
		  fmtinfo->name,
		  fmtinfo->desc);
	}
	exit(EXIT_FAILURE);
}


jas_image_t *converttosrgb(jas_image_t *inimage)
{
	jas_image_t *outimage;
	jas_cmpixmap_t inpixmap;
	jas_cmpixmap_t outpixmap;
	jas_cmcmptfmt_t incmptfmts[16];
	jas_cmcmptfmt_t outcmptfmts[16];

	outprof = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB);
	assert(outprof);
	xform = jas_cmxform_create(jas_image_cmprof(inimage), outprof, 0, JAS_CMXFORM_FWD, JAS_CMXFORM_INTENT_PER, JAS_CMXFORM_OPTM_SPEED);
	assert(xform);

	inpixmap.numcmpts = jas_image_numcmpts(oldimage);
	outpixmap.numcmpts = 3;
	for (i = 0; i < inpixmap.numcmpts; ++i) {
		inpixmap.cmptfmts[i] = &incmptfmts[i];
	}
	for (i = 0; i < outpixmap.numcmpts; ++i)
		outpixmap.cmptfmts[i] = &outcmptfmts[i];
	if (jas_cmxform_apply(xform, &inpixmap, &outpixmap))
		abort();

	jas_xform_destroy(xform);
	jas_cmprof_destroy(outprof);
	return 0;
}


void cleanup(void)
{
#if defined(JAS_USE_JAS_INIT)
	jas_cleanup();
#else
	jas_cleanup_thread();
	jas_cleanup_library();
#endif
}


cmdopts_t *cmdopts_parse(int argc, char **argv)
{
	enum {
		CMDOPT_HELP = 0,
		CMDOPT_VERBOSE,
		CMDOPT_QUIET,
		CMDOPT_INFILE,
		CMDOPT_INFMT,
		CMDOPT_INOPT,
		CMDOPT_OUTFILE,
		CMDOPT_OUTFMT,
		CMDOPT_OUTOPT,
		CMDOPT_VERSION,
		CMDOPT_DEBUG,
		CMDOPT_CMPTNO,
		CMDOPT_SRGB,
		CMDOPT_MAXMEM,
		CMDOPT_LIST_ENABLED_CODECS,
		CMDOPT_LIST_ALL_CODECS,
		CMDOPT_ENABLE_FORMAT,
		CMDOPT_ENABLE_ALL_FORMATS,
	};

	static const jas_opt_t cmdoptions[] = {
		{CMDOPT_HELP, "help", 0},
		{CMDOPT_VERBOSE, "verbose", 0},
		{CMDOPT_QUIET, "quiet", 0},
		{CMDOPT_QUIET, "q", 0},
		{CMDOPT_INFILE, "input", JAS_OPT_HASARG},
		{CMDOPT_INFILE, "f", JAS_OPT_HASARG},
		{CMDOPT_INFMT, "input-format", JAS_OPT_HASARG},
		{CMDOPT_INFMT, "t", JAS_OPT_HASARG},
		{CMDOPT_INOPT, "input-option", JAS_OPT_HASARG},
		{CMDOPT_INOPT, "o", JAS_OPT_HASARG},
		{CMDOPT_OUTFILE, "output", JAS_OPT_HASARG},
		{CMDOPT_OUTFILE, "F", JAS_OPT_HASARG},
		{CMDOPT_OUTFMT, "output-format", JAS_OPT_HASARG},
		{CMDOPT_OUTFMT, "T", JAS_OPT_HASARG},
		{CMDOPT_OUTOPT, "output-option", JAS_OPT_HASARG},
		{CMDOPT_OUTOPT, "O", JAS_OPT_HASARG},
		{CMDOPT_VERSION, "version", 0},
		{CMDOPT_DEBUG, "debug-level", JAS_OPT_HASARG},
		{CMDOPT_CMPTNO, "cmptno", JAS_OPT_HASARG},
		{CMDOPT_SRGB, "force-srgb", 0},
		{CMDOPT_SRGB, "S", 0},
		{CMDOPT_MAXMEM, "memory-limit", JAS_OPT_HASARG},
		{CMDOPT_LIST_ENABLED_CODECS, "list-enabled-formats", 0},
		{CMDOPT_LIST_ALL_CODECS, "list-all-formats", 0},
		{CMDOPT_ENABLE_FORMAT, "enable-format", JAS_OPT_HASARG},
		{CMDOPT_ENABLE_ALL_FORMATS, "enable-all-formats", 0},
		{-1, 0, 0}
	};

	cmdopts_t *cmdopts;
	int c;

	if (!(cmdopts = malloc(sizeof(cmdopts_t)))) {
		fprintf(stderr, "error: insufficient memory\n");
		exit(EXIT_FAILURE);
	}

	cmdopts->infile = 0;
	cmdopts->infmt = -1;
	cmdopts->infmt_str = 0;
	cmdopts->inopts = 0;
	cmdopts->inoptsbuf[0] = '\0';
	cmdopts->outfile = 0;
	cmdopts->outfmt = -1;
	cmdopts->outfmt_str = 0;
	cmdopts->outopts = 0;
	cmdopts->outoptsbuf[0] = '\0';
	cmdopts->verbose = 0;
	cmdopts->version = 0;
	cmdopts->cmptno = -1;
	cmdopts->debug = 0;
	cmdopts->srgb = 0;
	cmdopts->list_codecs = 0;
	cmdopts->list_codecs_all = 0;
	cmdopts->help = 0;
	cmdopts->max_mem = get_default_max_mem_usage();
	cmdopts->enable_format = 0;
	cmdopts->enable_all_formats = 0;

	while ((c = jas_getopt(argc, argv, cmdoptions)) != EOF) {
		switch (c) {
		case CMDOPT_HELP:
			cmdopts->help = 1;
			break;
		case CMDOPT_VERBOSE:
			cmdopts->verbose = 1;
			break;
		case CMDOPT_QUIET:
			cmdopts->verbose = -1;
			break;
		case CMDOPT_VERSION:
			cmdopts->version = 1;
			break;
		case CMDOPT_LIST_ENABLED_CODECS:
			cmdopts->list_codecs = 1;
			cmdopts->list_codecs_all = 0;
			break;
		case CMDOPT_LIST_ALL_CODECS:
			cmdopts->list_codecs = 1;
			cmdopts->list_codecs_all = 1;
			break;
		case CMDOPT_DEBUG:
			cmdopts->debug = atoi(jas_optarg);
			break;
		case CMDOPT_INFILE:
			cmdopts->infile = jas_optarg;
			break;
		case CMDOPT_INFMT:
			cmdopts->infmt_str= jas_optarg;
			break;
		case CMDOPT_INOPT:
			addopt(cmdopts->inoptsbuf, OPTSMAX, jas_optarg);
			cmdopts->inopts = cmdopts->inoptsbuf;
			break;
		case CMDOPT_OUTFILE:
			cmdopts->outfile = jas_optarg;
			break;
		case CMDOPT_OUTFMT:
			cmdopts->outfmt_str = jas_optarg;
			break;
		case CMDOPT_OUTOPT:
			addopt(cmdopts->outoptsbuf, OPTSMAX, jas_optarg);
			cmdopts->outopts = cmdopts->outoptsbuf;
			break;
		case CMDOPT_CMPTNO:
			cmdopts->cmptno = atoi(jas_optarg);
			break;
		case CMDOPT_SRGB:
			cmdopts->srgb = 1;
			break;
		case CMDOPT_MAXMEM:
			cmdopts->max_mem = strtoull(jas_optarg, 0, 10);
			break;
		case CMDOPT_ENABLE_FORMAT:
			cmdopts->enable_format = jas_optarg;
			break;
		case CMDOPT_ENABLE_ALL_FORMATS:
			cmdopts->enable_all_formats = 1;
			break;
		default:
			cmdopts_destroy(cmdopts);
			badusage();
			break;
		}
	}

	while (jas_optind < argc) {
		fprintf(stderr,
		  "warning: ignoring bogus command line argument %s\n",
		  argv[jas_optind]);
		++jas_optind;
	}

	if (cmdopts->version || cmdopts->list_codecs || cmdopts->help) {
		goto done;
	}

	if (!cmdopts->outfmt_str && !cmdopts->outfile) {
		fprintf(stderr, "error: cannot determine output format\n");
		cmdopts_destroy(cmdopts);
		badusage();
	}

done:
	return cmdopts;
}


int main(int argc, char **argv)
{
	jas_image_t *image;
	cmdopts_t *cmdopts;
	jas_stream_t *in;
	jas_stream_t *out;
	jas_tmr_t dectmr;
	jas_tmr_t enctmr;
	double dectime;
	double enctime;
	int_fast16_t numcmpts;
	int i;

	/* Determine the base name of this command. */
	if ((cmdname = strrchr(argv[0], '/'))) {
		++cmdname;
	} else {
		cmdname = argv[0];
	}

	/* Parse the command line options. */
	if (!(cmdopts = cmdopts_parse(argc, argv))) {
		fprintf(stderr, "error: cannot parse command line\n");
		exit(EXIT_FAILURE);
	}

	if (cmdopts->version) {
		printf("%s\n", JAS_VERSION);
		fprintf(stderr, "libjasper %s\n", jas_getversion());
		exit(EXIT_SUCCESS);
	}

#if defined(JAS_USE_JAS_INIT)
	if (jas_init()) {
		fprintf(stderr, "cannot initialize JasPer library\n");
		exit(EXIT_FAILURE);
	}
	jas_set_max_mem_usage(cmdopts->max_mem);
	jas_setdbglevel(cmdopts->debug);
	atexit(cleanup);
#else
	jas_conf_clear();
	static jas_std_allocator_t allocator;
	jas_std_allocator_init(&allocator);
	jas_conf_set_allocator(&allocator.base);
	jas_conf_set_debug_level(cmdopts->debug);
	jas_conf_set_max_mem_usage(cmdopts->max_mem);
	if (cmdopts->verbose < 0) {
		jas_conf_set_vlogmsgf(jas_vlogmsgf_discard);
	}
	if (jas_init_library()) {
		fprintf(stderr, "cannot initialize JasPer library\n");
		exit(EXIT_FAILURE);
	}
	if (jas_init_thread()) {
		fprintf(stderr, "cannot initialize thread\n");
		exit(EXIT_FAILURE);
	}
	atexit(cleanup);
#endif

	if (cmdopts->enable_all_formats || cmdopts->enable_format) {
		for (i = 0; i < jas_image_getnumfmts(); ++i) {
			const jas_image_fmtinfo_t *fmtinfo = jas_image_getfmtbyind(i);
			if (cmdopts->enable_all_formats || !strcmp(fmtinfo->name,
			  cmdopts->enable_format)) {
				jas_image_setfmtenable(i, 1);
			}
		}
	}

	if (cmdopts->help) {
		cmdusage();
	}

	if (cmdopts->list_codecs) {
		size_t num_formats = jas_image_getnumfmts();
		for (i = 0; i < num_formats; ++i) {
			const jas_image_fmtinfo_t *fmt;
			fmt = jas_image_getfmtbyind(i);
			if (cmdopts->list_codecs_all || fmt->enabled) {
				printf("%s\n", fmt->name);
			}
		}
		exit(EXIT_SUCCESS);
	}

	if (cmdopts->infmt_str) {
		if ((cmdopts->infmt = jas_image_strtofmt(cmdopts->infmt_str)) < 0) {
			fprintf(stderr, "warning: ignoring invalid input format %s\n",
			  cmdopts->infmt_str);
			cmdopts->infmt = -1;
		}
	} else {
		cmdopts->infmt = -1;
	}
	if (cmdopts->outfmt_str) {
		if ((cmdopts->outfmt = jas_image_strtofmt(cmdopts->outfmt_str)) < 0) {
			fprintf(stderr, "error: invalid output format %s\n",
			cmdopts->outfmt_str);
			exit(EXIT_FAILURE);
		}
	} else {
		if (cmdopts->outfmt < 0 && cmdopts->outfile) {
			if ((cmdopts->outfmt = jas_image_fmtfromname(cmdopts->outfile)) <
			  0) {
				cmdopts->outfmt = -1;
			}
		}
	}
	if (cmdopts->outfmt < 0) {
		fprintf(stderr, "error: cannot determine output image format\n");
		exit(EXIT_FAILURE);
	}

	if (cmdopts->verbose > 0) {
		cmdinfo();
	}

	/* Open the input image file. */
	if (cmdopts->infile) {
		/* The input image is to be read from a file. */
		if (!(in = jas_stream_fopen(cmdopts->infile, "rb"))) {
			fprintf(stderr, "error: cannot open input image file %s\n",
			  cmdopts->infile);
			exit(EXIT_FAILURE);
		}
	} else {
		/* The input image is to be read from standard input. */
		if (!(in = jas_stream_fdopen(0, "rb"))) {
			fprintf(stderr, "error: cannot open standard input\n");
			exit(EXIT_FAILURE);
		}
	}

	/* Open the output image file. */
	if (cmdopts->outfile) {
		/* The output image is to be written to a file. */
		if (!(out = jas_stream_fopen(cmdopts->outfile, "w+b"))) {
			fprintf(stderr, "error: cannot open output image file %s\n",
			  cmdopts->outfile);
			exit(EXIT_FAILURE);
		}
	} else {
		/* The output image is to be written to standard output. */
		if (!(out = jas_stream_fdopen(1, "w+b"))) {
			fprintf(stderr, "error: cannot open standard output\n");
			exit(EXIT_FAILURE);
		}
	}

	if (cmdopts->infmt < 0) {
		if ((cmdopts->infmt = jas_image_getfmt(in)) < 0) {
			fprintf(stderr, "error: input image has unknown format\n");
			exit(EXIT_FAILURE);
		}
	}

	/* Get the input image data. */
	jas_tmr_start(&dectmr);
	if (!(image = jas_image_decode(in, cmdopts->infmt, cmdopts->inopts))) {
		fprintf(stderr, "error: cannot load image data\n");
		exit(EXIT_FAILURE);
	}
	jas_tmr_stop(&dectmr);
	dectime = jas_tmr_get(&dectmr);

	/* If requested, throw away all of the components except one.
	  Why might this be desirable?  It is a hack, really.
	  None of the image formats other than the JPEG-2000 ones support
	  images with two, four, five, or more components.  This hack
	  allows such images to be decoded with the non-JPEG-2000 decoders,
	  one component at a time. */
	numcmpts = jas_image_numcmpts(image);
	if (cmdopts->cmptno >= 0 && cmdopts->cmptno < numcmpts) {
		for (i = numcmpts - 1; i >= 0; --i) {
			if (i != cmdopts->cmptno) {
				jas_image_delcmpt(image, i);
			}
		}
	}

	if (cmdopts->srgb) {
		jas_image_t *newimage;
		jas_cmprof_t *outprof;
		if (cmdopts->verbose >= 1) {
			fprintf(stderr, "forcing conversion to sRGB\n");
		}
		if (!(outprof = jas_cmprof_createfromclrspc(JAS_CLRSPC_SRGB))) {
			fprintf(stderr, "cannot create sRGB profile\n");
			exit(EXIT_FAILURE);
		}
		if (!(newimage = jas_image_chclrspc(image, outprof,
		  JAS_CMXFORM_INTENT_PER))) {
			fprintf(stderr, "cannot convert to sRGB\n");
			exit(EXIT_FAILURE);
		}
		jas_image_destroy(image);
		jas_cmprof_destroy(outprof);
		image = newimage;
	}

	/* Generate the output image data. */
	jas_tmr_start(&enctmr);
	if (jas_image_encode(image, out, cmdopts->outfmt, cmdopts->outopts)) {
		fprintf(stderr, "error: cannot encode image\n");
		exit(EXIT_FAILURE);
	}
	jas_stream_flush(out);
	jas_tmr_stop(&enctmr);
	enctime = jas_tmr_get(&enctmr);

	if (cmdopts->verbose > 0) {
		fprintf(stderr, "decoding time = %f\n", dectime);
		fprintf(stderr, "encoding time = %f\n", enctime);
	}

	/* If this fails, we don't care. */
	(void) jas_stream_close(in);

	/* Close the output image stream. */
	if (jas_stream_close(out)) {
		fprintf(stderr, "error: cannot close output image file\n");
		exit(EXIT_FAILURE);
	}

	cmdopts_destroy(cmdopts);
	jas_image_destroy(image);

	/* Success at last! :-) */
	return EXIT_SUCCESS;
}


xkb: ProcXkbSetDeviceInfo should work on all attached SDs.

If called with XkbUseCoreKbd, run through all attached SDs and replicate the
call. This way, we keep the SDs in sync with the MD as long as core clients
control the MDs.

SetKeyExplicit(XkbSrvInfoPtr xkbi,xkbSetMapReq *req,CARD8 *wire,
							XkbChangesPtr changes)
{
register unsigned	i,first,last;
XkbServerMapPtr		xkb = xkbi->desc->server;
CARD8 *			start;

    start= wire;
    first= req->firstKeyExplicit;
    last=  req->firstKeyExplicit+req->nKeyExplicit-1;
    bzero(&xkb->explicit[first],req->nKeyExplicit);
    for (i=0;i<req->totalKeyExplicit;i++,wire+= 2) {
	xkb->explicit[wire[0]]= wire[1];
    }
    if (first>0) {
	if (changes->map.changed&XkbExplicitComponentsMask) {
	    int oldLast;
	    oldLast= changes->map.first_key_explicit+
					changes->map.num_key_explicit-1;
	    if (changes->map.first_key_explicit<first)
		first= changes->map.first_key_explicit;
	    if (oldLast>last)
		last= oldLast;
	}
	changes->map.first_key_explicit= first;
	changes->map.num_key_explicit= (last-first)+1;
    }
    wire+= XkbPaddedSize(wire-start)-(wire-start);
    return (char *)wire;
}

A flaw was found in the Xorg-x11-server. The specific flaw exists within the handling of ProcXkbSetDeviceInfo requests. The issue results from the lack of proper validation of user-supplied data, which can result in a memory access past the end of an allocated buffer. This flaw allows an attacker to escalate privileges and execute arbitrary code in the context of root.


_CheckSetDoodad(	char **		wire_inout,
			XkbGeometryPtr	geom,
			XkbSectionPtr	section,
			ClientPtr	client)
{
char *			wire;
xkbDoodadWireDesc *	dWire;
XkbDoodadPtr		doodad;

    dWire= (xkbDoodadWireDesc *)(*wire_inout);
    wire= (char *)&dWire[1];
    if (client->swapped) {
	register int n;
	swapl(&dWire->any.name,n);
	swaps(&dWire->any.top,n);
	swaps(&dWire->any.left,n);
	swaps(&dWire->any.angle,n);
    }
    CHK_ATOM_ONLY(dWire->any.name);
    doodad= XkbAddGeomDoodad(geom,section,dWire->any.name);
    if (!doodad)
	return BadAlloc;
    doodad->any.type= dWire->any.type;
    doodad->any.priority= dWire->any.priority;
    doodad->any.top= dWire->any.top;
    doodad->any.left= dWire->any.left;
    doodad->any.angle= dWire->any.angle;
    switch (doodad->any.type) {
	case XkbOutlineDoodad:
	case XkbSolidDoodad:
	    if (dWire->shape.colorNdx>=geom->num_colors) {
		client->errorValue= _XkbErrCode3(0x40,geom->num_colors,
							dWire->shape.colorNdx);
		return BadMatch;
	    }
	    if (dWire->shape.shapeNdx>=geom->num_shapes) {
		client->errorValue= _XkbErrCode3(0x41,geom->num_shapes,
							dWire->shape.shapeNdx);
		return BadMatch;
	    }
	    doodad->shape.color_ndx= dWire->shape.colorNdx;
	    doodad->shape.shape_ndx= dWire->shape.shapeNdx;
	    break;
	case XkbTextDoodad:
	    if (dWire->text.colorNdx>=geom->num_colors) {
		client->errorValue= _XkbErrCode3(0x42,geom->num_colors,
							dWire->text.colorNdx);
		return BadMatch;
	    }
	    if (client->swapped) {
		register int n;
		swaps(&dWire->text.width,n);
		swaps(&dWire->text.height,n);
	    }
	    doodad->text.width= dWire->text.width;
	    doodad->text.height= dWire->text.height;
	    doodad->text.color_ndx= dWire->text.colorNdx;
	    doodad->text.text= _GetCountedString(&wire,client->swapped);
	    doodad->text.font= _GetCountedString(&wire,client->swapped);
	    break;
	case XkbIndicatorDoodad:
	    if (dWire->indicator.onColorNdx>=geom->num_colors) {
		client->errorValue= _XkbErrCode3(0x43,geom->num_colors,
						dWire->indicator.onColorNdx);
		return BadMatch;
	    }
	    if (dWire->indicator.offColorNdx>=geom->num_colors) {
		client->errorValue= _XkbErrCode3(0x44,geom->num_colors,
						dWire->indicator.offColorNdx);
		return BadMatch;
	    }
	    if (dWire->indicator.shapeNdx>=geom->num_shapes) {
		client->errorValue= _XkbErrCode3(0x45,geom->num_shapes,
						dWire->indicator.shapeNdx);
		return BadMatch;
	    }
	    doodad->indicator.shape_ndx= dWire->indicator.shapeNdx;
	    doodad->indicator.on_color_ndx= dWire->indicator.onColorNdx;
	    doodad->indicator.off_color_ndx= dWire->indicator.offColorNdx;
	    break;
	case XkbLogoDoodad:
	    if (dWire->logo.colorNdx>=geom->num_colors) {
		client->errorValue= _XkbErrCode3(0x46,geom->num_colors,
							dWire->logo.colorNdx);
		return BadMatch;
	    }
	    if (dWire->logo.shapeNdx>=geom->num_shapes) {
		client->errorValue= _XkbErrCode3(0x47,geom->num_shapes,
							dWire->logo.shapeNdx);
		return BadMatch;
	    }
	    doodad->logo.color_ndx= dWire->logo.colorNdx;
	    doodad->logo.shape_ndx= dWire->logo.shapeNdx;
	    doodad->logo.logo_name= _GetCountedString(&wire,client->swapped);
	    break;
	default:
	    client->errorValue= _XkbErrCode2(0x4F,dWire->any.type);
	    return BadValue;
    }
    *wire_inout= wire;
    return Success;
}


GetComponentSpec(unsigned char **pWire,Bool allowExpr,int *errRtrn)
{
int		len;
register int	i;
unsigned char	*wire,*str,*tmp,*legal;

    if (allowExpr)	legal= &componentExprLegal[0];
    else		legal= &componentSpecLegal[0];

    wire= *pWire;
    len= (*(unsigned char *)wire++);
    if (len>0) {
	str= (unsigned char *)_XkbCalloc(1, len+1);
	if (str) {
	    tmp= str;
	    for (i=0;i<len;i++) {
		if (legal[(*wire)/8]&(1<<((*wire)%8)))
		    *tmp++= *wire++;
		else wire++;
	    }
	    if (tmp!=str)
		*tmp++= '\0';
	    else {
		_XkbFree(str);
		str= NULL;
	    }
	}
	else {
	    *errRtrn= BadAlloc;
	}
    }
    else {
	str= NULL;
    }
    *pWire= wire;
    return (char *)str;
}


ProcXkbPerClientFlags(ClientPtr client)
{
    DeviceIntPtr 		dev;
    xkbPerClientFlagsReply 	rep;
    XkbInterestPtr		interest;
    Mask access_mode = DixGetAttrAccess | DixSetAttrAccess;

    REQUEST(xkbPerClientFlagsReq);
    REQUEST_SIZE_MATCH(xkbPerClientFlagsReq);

    if (!(client->xkbClientFlags&_XkbClientInitialized))
	return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, access_mode);
    CHK_MASK_LEGAL(0x01,stuff->change,XkbPCF_AllFlagsMask);
    CHK_MASK_MATCH(0x02,stuff->change,stuff->value);

    interest = XkbFindClientResource((DevicePtr)dev,client);
    rep.type= X_Reply;
    rep.length = 0;
    rep.sequenceNumber = client->sequence;
    if (stuff->change) {
	client->xkbClientFlags&= ~stuff->change;
	client->xkbClientFlags|= stuff->value;
    }
    if (stuff->change&XkbPCF_AutoResetControlsMask) {
	Bool	want;
	want= stuff->value&XkbPCF_AutoResetControlsMask;
	if (interest && !want) {
	    interest->autoCtrls= interest->autoCtrlValues= 0;
	}
	else if (want && (!interest)) {
	    XID id = FakeClientID(client->index);
	    AddResource(id,RT_XKBCLIENT,dev);
	    interest= XkbAddClientResource((DevicePtr)dev,client,id);
	    if (!interest)
		return BadAlloc;
	}
	if (interest && want ) {
	    register unsigned affect;
	    affect= stuff->ctrlsToChange;

	    CHK_MASK_LEGAL(0x03,affect,XkbAllBooleanCtrlsMask);
	    CHK_MASK_MATCH(0x04,affect,stuff->autoCtrls);
	    CHK_MASK_MATCH(0x05,stuff->autoCtrls,stuff->autoCtrlValues);

	    interest->autoCtrls&= ~affect;
	    interest->autoCtrlValues&= ~affect;
	    interest->autoCtrls|= stuff->autoCtrls&affect;
	    interest->autoCtrlValues|= stuff->autoCtrlValues&affect;
	}
    }
    rep.supported = XkbPCF_AllFlagsMask;
    rep.value= client->xkbClientFlags&XkbPCF_AllFlagsMask;
    if (interest) {
	rep.autoCtrls= interest->autoCtrls;
	rep.autoCtrlValues= interest->autoCtrlValues;
    }
    else {
	rep.autoCtrls= rep.autoCtrlValues= 0;
    }
    if ( client->swapped ) {
	register int n;
	swaps(&rep.sequenceNumber, n);
	swapl(&rep.supported,n);
	swapl(&rep.value,n);
	swapl(&rep.autoCtrls,n);
	swapl(&rep.autoCtrlValues,n);
    }
    WriteToClient(client,SIZEOF(xkbPerClientFlagsReply), (char *)&rep);
    return client->noClientException;
}


ProcXkbGetGeometry(ClientPtr client)
{
    DeviceIntPtr 	dev;
    xkbGetGeometryReply rep;
    XkbGeometryPtr	geom;
    Bool		shouldFree;
    Status		status;

    REQUEST(xkbGetGeometryReq);
    REQUEST_SIZE_MATCH(xkbGetGeometryReq);

    if (!(client->xkbClientFlags&_XkbClientInitialized))
	return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixGetAttrAccess);
    CHK_ATOM_OR_NONE(stuff->name);

    geom= XkbLookupNamedGeometry(dev,stuff->name,&shouldFree);
    rep.type= X_Reply;
    rep.deviceID= dev->id;
    rep.sequenceNumber= client->sequence;
    rep.length= 0;
    status= XkbComputeGetGeometryReplySize(geom,&rep,stuff->name);
    if (status!=Success)
	 return status;
    else return XkbSendGeometry(client,geom,&rep,shouldFree);
}


ProcXkbGetControls(ClientPtr client)
{
    xkbGetControlsReply rep;
    XkbControlsPtr	xkb;
    DeviceIntPtr 	dev;
    register int 	n;

    REQUEST(xkbGetControlsReq);
    REQUEST_SIZE_MATCH(xkbGetControlsReq);

    if (!(client->xkbClientFlags&_XkbClientInitialized))
	return BadAccess;

    CHK_KBD_DEVICE(dev, stuff->deviceSpec, client, DixGetAttrAccess);

    xkb = dev->key->xkbInfo->desc->ctrls;
    rep.type = X_Reply;
    rep.length = (SIZEOF(xkbGetControlsReply)-
		  SIZEOF(xGenericReply)) >> 2;
    rep.sequenceNumber = client->sequence;
    rep.deviceID = ((DeviceIntPtr)dev)->id;
    rep.numGroups = xkb->num_groups;
    rep.groupsWrap = xkb->groups_wrap;
    rep.internalMods = xkb->internal.mask;
    rep.ignoreLockMods = xkb->ignore_lock.mask;
    rep.internalRealMods = xkb->internal.real_mods;
    rep.ignoreLockRealMods = xkb->ignore_lock.real_mods;
    rep.internalVMods = xkb->internal.vmods;
    rep.ignoreLockVMods = xkb->ignore_lock.vmods;
    rep.enabledCtrls = xkb->enabled_ctrls;
    rep.repeatDelay = xkb->repeat_delay;
    rep.repeatInterval = xkb->repeat_interval;
    rep.slowKeysDelay = xkb->slow_keys_delay;
    rep.debounceDelay = xkb->debounce_delay;
    rep.mkDelay = xkb->mk_delay;
    rep.mkInterval = xkb->mk_interval;
    rep.mkTimeToMax = xkb->mk_time_to_max;
    rep.mkMaxSpeed = xkb->mk_max_speed;
    rep.mkCurve = xkb->mk_curve;
    rep.mkDfltBtn = xkb->mk_dflt_btn;
    rep.axTimeout = xkb->ax_timeout;
    rep.axtCtrlsMask = xkb->axt_ctrls_mask;
    rep.axtCtrlsValues = xkb->axt_ctrls_values;
    rep.axtOptsMask = xkb->axt_opts_mask;
    rep.axtOptsValues = xkb->axt_opts_values;
    rep.axOptions = xkb->ax_options;
    memcpy(rep.perKeyRepeat,xkb->per_key_repeat,XkbPerKeyBitArraySize);
    if (client->swapped) {
    	swaps(&rep.sequenceNumber, n);
	swapl(&rep.length,n);
	swaps(&rep.internalVMods, n);
	swaps(&rep.ignoreLockVMods, n);
	swapl(&rep.enabledCtrls, n);
	swaps(&rep.repeatDelay, n);
	swaps(&rep.repeatInterval, n);
	swaps(&rep.slowKeysDelay, n);
	swaps(&rep.debounceDelay, n);
	swaps(&rep.mkDelay, n);
	swaps(&rep.mkInterval, n);
	swaps(&rep.mkTimeToMax, n);
	swaps(&rep.mkMaxSpeed, n);
	swaps(&rep.mkCurve, n);
	swaps(&rep.axTimeout, n);
	swapl(&rep.axtCtrlsMask, n);
	swapl(&rep.axtCtrlsValues, n);
	swaps(&rep.axtOptsMask, n);
	swaps(&rep.axtOptsValues, n);
	swaps(&rep.axOptions, n);
    }
    WriteToClient(client, SIZEOF(xkbGetControlsReply), (char *)&rep);
    return(client->noClientException);
}


SetDeviceIndicators(	char *			wire,
			DeviceIntPtr		dev,
			unsigned		changed,
			int			num,
			int *			status_rtrn,
			ClientPtr		client,
			xkbExtensionDeviceNotify *ev)
{
xkbDeviceLedsWireDesc *		ledWire;
int				i;
XkbEventCauseRec		cause;
unsigned			namec,mapc,statec;
xkbExtensionDeviceNotify	ed;
XkbChangesRec			changes;
DeviceIntPtr			kbd;

    bzero((char *)&ed,sizeof(xkbExtensionDeviceNotify));
    bzero((char *)&changes,sizeof(XkbChangesRec));
    XkbSetCauseXkbReq(&cause,X_kbSetDeviceInfo,client);
    ledWire= (xkbDeviceLedsWireDesc *)wire;
    for (i=0;i<num;i++) {
	register int			n;
	register unsigned 		bit;
	CARD32 *			atomWire;
	xkbIndicatorMapWireDesc *	mapWire;
	XkbSrvLedInfoPtr		sli;

	namec= mapc= statec= 0;
    	sli= XkbFindSrvLedInfo(dev,ledWire->ledClass,ledWire->ledID,
						XkbXI_IndicatorMapsMask);
	if (!sli) {
	    /* SHOULD NEVER HAPPEN!! */
	    return (char *)ledWire;
	}

	atomWire= (CARD32 *)&ledWire[1];
	if (changed&XkbXI_IndicatorNamesMask) {
	    namec= sli->namesPresent|ledWire->namesPresent;
	    bzero((char *)sli->names,XkbNumIndicators*sizeof(Atom));
	}
	if (ledWire->namesPresent) {
	    sli->namesPresent= ledWire->namesPresent;
	    bzero((char *)sli->names,XkbNumIndicators*sizeof(Atom));
	    for (n=0,bit=1;n<XkbNumIndicators;n++,bit<<=1) {
		if (ledWire->namesPresent&bit) {
		     sli->names[n]= (Atom)*atomWire;
		     if (sli->names[n]==None)
			ledWire->namesPresent&= ~bit;
		     atomWire++;
		}
	    }
	}
	mapWire= (xkbIndicatorMapWireDesc *)atomWire;
	if (changed&XkbXI_IndicatorMapsMask) {
	    mapc= sli->mapsPresent|ledWire->mapsPresent;
	    sli->mapsPresent= ledWire->mapsPresent;
	    bzero((char*)sli->maps,XkbNumIndicators*sizeof(XkbIndicatorMapRec));
	}
	if (ledWire->mapsPresent) {
	    for (n=0,bit=1;n<XkbNumIndicators;n++,bit<<=1) {
		if (ledWire->mapsPresent&bit) {
		    sli->maps[n].flags=		mapWire->flags;
		    sli->maps[n].which_groups=	mapWire->whichGroups;
		    sli->maps[n].groups=	mapWire->groups;
		    sli->maps[n].which_mods=	mapWire->whichMods;
		    sli->maps[n].mods.mask=	mapWire->mods;
		    sli->maps[n].mods.real_mods=mapWire->realMods;
		    sli->maps[n].mods.vmods=	mapWire->virtualMods;
		    sli->maps[n].ctrls=		mapWire->ctrls;
		    mapWire++;
		}
	    }
	}
	if (changed&XkbXI_IndicatorStateMask) {
	    statec= sli->effectiveState^ledWire->state;
	    sli->explicitState&= ~statec;
	    sli->explicitState|= (ledWire->state&statec);
	}
	if (namec)
	    XkbApplyLedNameChanges(dev,sli,namec,&ed,&changes,&cause);
	if (mapc)
	    XkbApplyLedMapChanges(dev,sli,mapc,&ed,&changes,&cause);
	if (statec)
	    XkbApplyLedStateChanges(dev,sli,statec,&ed,&changes,&cause);

	kbd= dev;
	if ((sli->flags&XkbSLI_HasOwnState)==0)
	    kbd = inputInfo.keyboard;

	XkbFlushLedEvents(dev,kbd,sli,&ed,&changes,&cause);
	ledWire= (xkbDeviceLedsWireDesc *)mapWire;
    }
    return (char *)ledWire;
}


XkbWriteGeomOverlay(char *wire,XkbOverlayPtr ol,Bool swap)
{
register int		r;
XkbOverlayRowPtr	row;
xkbOverlayWireDesc *	olWire;

   olWire= (xkbOverlayWireDesc *)wire;
   olWire->name= ol->name;
   olWire->nRows= ol->num_rows;
   if (swap) {
	register int n;
	swapl(&olWire->name,n);
   }
   wire= (char *)&olWire[1];
   for (r=0,row=ol->rows;r<ol->num_rows;r++,row++) {
   	unsigned int		k;
	XkbOverlayKeyPtr	key;
	xkbOverlayRowWireDesc *	rowWire;
	rowWire= (xkbOverlayRowWireDesc *)wire;
	rowWire->rowUnder= row->row_under;
	rowWire->nKeys= row->num_keys;
	wire= (char *)&rowWire[1];
	for (k=0,key=row->keys;k<row->num_keys;k++,key++) {
	    xkbOverlayKeyWireDesc *	keyWire;
	    keyWire= (xkbOverlayKeyWireDesc *)wire;
	    memcpy(keyWire->over,key->over.name,XkbKeyNameLength);
	    memcpy(keyWire->under,key->under.name,XkbKeyNameLength);
	    wire= (char *)&keyWire[1];
	}
   }
   return wire;
}