mirror of
https://gitlab.gnome.org/GNOME/libxml2.git
synced 2026-10-24 01:09:01 +03:00
This change makes the behavior of xmlStrncat coherent, if it returns NULL, the *cur pointer should be freed as the doc said that it shouldn't be freed by the caller. If this function takes ownership of the pointer, we should make sure to free the memory everytime we return NULL or we can have memory leaks. See https://gitlab.gnome.org/GNOME/libxml2/-/issues/1073
1145 lines
30 KiB
C
1145 lines
30 KiB
C
/*
|
|
* string.c : an XML string utilities module
|
|
*
|
|
* This module provides various utility functions for manipulating
|
|
* the xmlChar* type. All functions named xmlStr* have been moved here
|
|
* from the parser.c file (their original home).
|
|
*
|
|
* See Copyright for the status of this software.
|
|
*
|
|
* UTF8 string routines from: William Brack
|
|
*
|
|
* Author: Daniel Veillard
|
|
*/
|
|
|
|
#define IN_LIBXML
|
|
#include "libxml.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#include <libxml/xmlmemory.h>
|
|
#include <libxml/parserInternals.h>
|
|
#include <libxml/xmlstring.h>
|
|
|
|
#include "private/parser.h"
|
|
#include "private/string.h"
|
|
|
|
#ifndef va_copy
|
|
#ifdef __va_copy
|
|
#define va_copy(dest, src) __va_copy(dest, src)
|
|
#else
|
|
#define va_copy(dest, src) memcpy(&(dest), &(src), sizeof(va_list))
|
|
#endif
|
|
#endif
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Commodity functions to handle xmlChars *
|
|
* *
|
|
************************************************************************/
|
|
|
|
/**
|
|
* a strndup for array of xmlChar's
|
|
*
|
|
* @param cur the input xmlChar *
|
|
* @param len the len of `cur`
|
|
* @returns a new xmlChar * or NULL
|
|
*/
|
|
xmlChar *
|
|
xmlStrndup(const xmlChar *cur, int len) {
|
|
xmlChar *ret;
|
|
|
|
if ((cur == NULL) || (len < 0)) return(NULL);
|
|
ret = xmlMalloc((size_t) len + 1);
|
|
if (ret == NULL) {
|
|
return(NULL);
|
|
}
|
|
memcpy(ret, cur, len);
|
|
ret[len] = 0;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* a strdup for array of xmlChar's. Since they are supposed to be
|
|
* encoded in UTF-8 or an encoding with 8bit based chars, we assume
|
|
* a termination mark of '0'.
|
|
*
|
|
* @param cur the input xmlChar *
|
|
* @returns a new xmlChar * or NULL
|
|
*/
|
|
xmlChar *
|
|
xmlStrdup(const xmlChar *cur) {
|
|
const xmlChar *p = cur;
|
|
|
|
if (cur == NULL) return(NULL);
|
|
while (*p != 0) p++; /* non input consuming */
|
|
return(xmlStrndup(cur, p - cur));
|
|
}
|
|
|
|
/**
|
|
* a strndup for char's to xmlChar's
|
|
*
|
|
* @param cur the input char *
|
|
* @param len the len of `cur`
|
|
* @returns a new xmlChar * or NULL
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlCharStrndup(const char *cur, int len) {
|
|
int i;
|
|
xmlChar *ret;
|
|
|
|
if ((cur == NULL) || (len < 0)) return(NULL);
|
|
ret = xmlMalloc((size_t) len + 1);
|
|
if (ret == NULL) {
|
|
return(NULL);
|
|
}
|
|
for (i = 0;i < len;i++) {
|
|
/* Explicit sign change */
|
|
ret[i] = (xmlChar) cur[i];
|
|
if (ret[i] == 0) return(ret);
|
|
}
|
|
ret[len] = 0;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* a strdup for char's to xmlChar's
|
|
*
|
|
* @param cur the input char *
|
|
* @returns a new xmlChar * or NULL
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlCharStrdup(const char *cur) {
|
|
const char *p = cur;
|
|
|
|
if (cur == NULL) return(NULL);
|
|
while (*p != '\0') p++; /* non input consuming */
|
|
return(xmlCharStrndup(cur, p - cur));
|
|
}
|
|
|
|
/**
|
|
* a strcmp for xmlChar's
|
|
*
|
|
* @param str1 the first xmlChar *
|
|
* @param str2 the second xmlChar *
|
|
* @returns the integer result of the comparison
|
|
*/
|
|
|
|
int
|
|
xmlStrcmp(const xmlChar *str1, const xmlChar *str2) {
|
|
if (str1 == str2) return(0);
|
|
if (str1 == NULL) return(-1);
|
|
if (str2 == NULL) return(1);
|
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
return(strcmp((const char *)str1, (const char *)str2));
|
|
#else
|
|
do {
|
|
int tmp = *str1++ - *str2;
|
|
if (tmp != 0) return(tmp);
|
|
} while (*str2++ != 0);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Check if both strings are equal of have same content.
|
|
* Should be a bit more readable and faster than #xmlStrcmp
|
|
*
|
|
* @param str1 the first xmlChar *
|
|
* @param str2 the second xmlChar *
|
|
* @returns 1 if they are equal, 0 if they are different
|
|
*/
|
|
|
|
int
|
|
xmlStrEqual(const xmlChar *str1, const xmlChar *str2) {
|
|
if (str1 == str2) return(1);
|
|
if (str1 == NULL) return(0);
|
|
if (str2 == NULL) return(0);
|
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
return(strcmp((const char *)str1, (const char *)str2) == 0);
|
|
#else
|
|
do {
|
|
if (*str1++ != *str2) return(0);
|
|
} while (*str2++);
|
|
return(1);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Check if a QName is Equal to a given string
|
|
*
|
|
* @param pref the prefix of the QName
|
|
* @param name the localname of the QName
|
|
* @param str the second xmlChar *
|
|
* @returns 1 if they are equal, 0 if they are different
|
|
*/
|
|
|
|
int
|
|
xmlStrQEqual(const xmlChar *pref, const xmlChar *name, const xmlChar *str) {
|
|
if (pref == NULL) return(xmlStrEqual(name, str));
|
|
if (name == NULL) return(0);
|
|
if (str == NULL) return(0);
|
|
|
|
do {
|
|
if (*pref++ != *str) return(0);
|
|
} while ((*str++) && (*pref));
|
|
if (*str++ != ':') return(0);
|
|
do {
|
|
if (*name++ != *str) return(0);
|
|
} while (*str++);
|
|
return(1);
|
|
}
|
|
|
|
/**
|
|
* a strncmp for xmlChar's
|
|
*
|
|
* @param str1 the first xmlChar *
|
|
* @param str2 the second xmlChar *
|
|
* @param len the max comparison length
|
|
* @returns the integer result of the comparison
|
|
*/
|
|
|
|
int
|
|
xmlStrncmp(const xmlChar *str1, const xmlChar *str2, int len) {
|
|
if (len <= 0) return(0);
|
|
if (str1 == str2) return(0);
|
|
if (str1 == NULL) return(-1);
|
|
if (str2 == NULL) return(1);
|
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
|
return(strncmp((const char *)str1, (const char *)str2, len));
|
|
#else
|
|
do {
|
|
int tmp = *str1++ - *str2;
|
|
if (tmp != 0 || --len == 0) return(tmp);
|
|
} while (*str2++ != 0);
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
static const xmlChar casemap[256] = {
|
|
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
|
|
0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
|
|
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
|
|
0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
|
|
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
|
|
0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
|
|
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
|
|
0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
|
|
0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
|
|
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
|
|
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
|
|
0x78,0x79,0x7A,0x7B,0x5C,0x5D,0x5E,0x5F,
|
|
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
|
|
0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
|
|
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
|
|
0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
|
|
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
|
|
0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
|
|
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
|
|
0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
|
|
0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
|
|
0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
|
|
0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,
|
|
0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
|
|
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
|
|
0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,
|
|
0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,
|
|
0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
|
|
0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,
|
|
0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
|
|
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,
|
|
0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
|
|
};
|
|
|
|
/**
|
|
* a strcasecmp for xmlChar's
|
|
*
|
|
* @param str1 the first xmlChar *
|
|
* @param str2 the second xmlChar *
|
|
* @returns the integer result of the comparison
|
|
*/
|
|
|
|
int
|
|
xmlStrcasecmp(const xmlChar *str1, const xmlChar *str2) {
|
|
register int tmp;
|
|
|
|
if (str1 == str2) return(0);
|
|
if (str1 == NULL) return(-1);
|
|
if (str2 == NULL) return(1);
|
|
do {
|
|
tmp = casemap[*str1++] - casemap[*str2];
|
|
if (tmp != 0) return(tmp);
|
|
} while (*str2++ != 0);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* a strncasecmp for xmlChar's
|
|
*
|
|
* @param str1 the first xmlChar *
|
|
* @param str2 the second xmlChar *
|
|
* @param len the max comparison length
|
|
* @returns the integer result of the comparison
|
|
*/
|
|
|
|
int
|
|
xmlStrncasecmp(const xmlChar *str1, const xmlChar *str2, int len) {
|
|
register int tmp;
|
|
|
|
if (len <= 0) return(0);
|
|
if (str1 == str2) return(0);
|
|
if (str1 == NULL) return(-1);
|
|
if (str2 == NULL) return(1);
|
|
do {
|
|
tmp = casemap[*str1++] - casemap[*str2];
|
|
if (tmp != 0 || --len == 0) return(tmp);
|
|
} while (*str2++ != 0);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* a strchr for xmlChar's
|
|
*
|
|
* @param str the xmlChar * array
|
|
* @param val the xmlChar to search
|
|
* @returns the xmlChar * for the first occurrence or NULL.
|
|
*/
|
|
|
|
const xmlChar *
|
|
xmlStrchr(const xmlChar *str, xmlChar val) {
|
|
if (str == NULL) return(NULL);
|
|
while (*str != 0) { /* non input consuming */
|
|
if (*str == val) return((xmlChar *) str);
|
|
str++;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* a strstr for xmlChar's
|
|
*
|
|
* @param str the xmlChar * array (haystack)
|
|
* @param val the xmlChar to search (needle)
|
|
* @returns the xmlChar * for the first occurrence or NULL.
|
|
*/
|
|
|
|
const xmlChar *
|
|
xmlStrstr(const xmlChar *str, const xmlChar *val) {
|
|
int n;
|
|
|
|
if (str == NULL) return(NULL);
|
|
if (val == NULL) return(NULL);
|
|
n = xmlStrlen(val);
|
|
|
|
if (n == 0) return(str);
|
|
while (*str != 0) { /* non input consuming */
|
|
if (*str == *val) {
|
|
if (!xmlStrncmp(str, val, n)) return((const xmlChar *) str);
|
|
}
|
|
str++;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* a case-ignoring strstr for xmlChar's
|
|
*
|
|
* @param str the xmlChar * array (haystack)
|
|
* @param val the xmlChar to search (needle)
|
|
* @returns the xmlChar * for the first occurrence or NULL.
|
|
*/
|
|
|
|
const xmlChar *
|
|
xmlStrcasestr(const xmlChar *str, const xmlChar *val) {
|
|
int n;
|
|
|
|
if (str == NULL) return(NULL);
|
|
if (val == NULL) return(NULL);
|
|
n = xmlStrlen(val);
|
|
|
|
if (n == 0) return(str);
|
|
while (*str != 0) { /* non input consuming */
|
|
if (casemap[*str] == casemap[*val])
|
|
if (!xmlStrncasecmp(str, val, n)) return(str);
|
|
str++;
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* Extract a substring of a given string
|
|
*
|
|
* @param str the xmlChar * array (haystack)
|
|
* @param start the index of the first char (zero based)
|
|
* @param len the length of the substring
|
|
* @returns the xmlChar * for the first occurrence or NULL.
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlStrsub(const xmlChar *str, int start, int len) {
|
|
int i;
|
|
|
|
if (str == NULL) return(NULL);
|
|
if (start < 0) return(NULL);
|
|
if (len < 0) return(NULL);
|
|
|
|
for (i = 0;i < start;i++) {
|
|
if (*str == 0) return(NULL);
|
|
str++;
|
|
}
|
|
if (*str == 0) return(NULL);
|
|
return(xmlStrndup(str, len));
|
|
}
|
|
|
|
/**
|
|
* length of a xmlChar's string
|
|
*
|
|
* @param str the xmlChar * array
|
|
* @returns the number of xmlChar contained in the ARRAY.
|
|
*/
|
|
|
|
int
|
|
xmlStrlen(const xmlChar *str) {
|
|
size_t len = str ? strlen((const char *)str) : 0;
|
|
return(len > INT_MAX ? 0 : len);
|
|
}
|
|
|
|
/**
|
|
* a strncat for array of xmlChar's, it will extend `cur` with the len
|
|
* first bytes of `add`. Note that if `len` < 0 then this is an API error
|
|
* and NULL will be returned.
|
|
*
|
|
* @param cur the original xmlChar * array
|
|
* @param add the xmlChar * array added
|
|
* @param len the length of `add`
|
|
* @returns a new xmlChar *, the original `cur` is reallocated and should
|
|
* not be freed.
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlStrncat(xmlChar *cur, const xmlChar *add, int len) {
|
|
int size;
|
|
xmlChar *ret;
|
|
|
|
if ((add == NULL) || (len == 0))
|
|
return(cur);
|
|
|
|
if (len < 0) {
|
|
if (cur != NULL)
|
|
xmlFree(cur);
|
|
return(NULL);
|
|
}
|
|
|
|
if (cur == NULL)
|
|
return(xmlStrndup(add, len));
|
|
|
|
size = xmlStrlen(cur);
|
|
if ((size < 0) || (size > INT_MAX - len)) {
|
|
xmlFree(cur);
|
|
return(NULL);
|
|
}
|
|
ret = (xmlChar *) xmlRealloc(cur, (size_t) size + len + 1);
|
|
if (ret == NULL) {
|
|
xmlFree(cur);
|
|
return(NULL);
|
|
}
|
|
memcpy(&ret[size], add, len);
|
|
ret[size + len] = 0;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* same as #xmlStrncat, but creates a new string. The original
|
|
* two strings are not freed. If `len` is < 0 then the length
|
|
* will be calculated automatically.
|
|
*
|
|
* @param str1 first xmlChar string
|
|
* @param str2 second xmlChar string
|
|
* @param len the len of `str2` or < 0
|
|
* @returns a new xmlChar * or NULL
|
|
*/
|
|
xmlChar *
|
|
xmlStrncatNew(const xmlChar *str1, const xmlChar *str2, int len) {
|
|
int size;
|
|
xmlChar *ret;
|
|
|
|
if (len < 0) {
|
|
len = xmlStrlen(str2);
|
|
if (len < 0)
|
|
return(NULL);
|
|
}
|
|
if (str1 == NULL)
|
|
return(xmlStrndup(str2, len));
|
|
if ((str2 == NULL) || (len == 0))
|
|
return(xmlStrdup(str1));
|
|
|
|
size = xmlStrlen(str1);
|
|
if ((size < 0) || (size > INT_MAX - len))
|
|
return(NULL);
|
|
ret = (xmlChar *) xmlMalloc((size_t) size + len + 1);
|
|
if (ret == NULL)
|
|
return(NULL);
|
|
memcpy(ret, str1, size);
|
|
memcpy(&ret[size], str2, len);
|
|
ret[size + len] = 0;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* a strcat for array of xmlChar's. Since they are supposed to be
|
|
* encoded in UTF-8 or an encoding with 8bit based chars, we assume
|
|
* a termination mark of '0'.
|
|
*
|
|
* @param cur the original xmlChar * array
|
|
* @param add the xmlChar * array added
|
|
* @returns a new xmlChar * containing the concatenated string. The original
|
|
* `cur` is reallocated and should not be freed.
|
|
*/
|
|
xmlChar *
|
|
xmlStrcat(xmlChar *cur, const xmlChar *add) {
|
|
const xmlChar *p = add;
|
|
|
|
if (add == NULL) return(cur);
|
|
if (cur == NULL)
|
|
return(xmlStrdup(add));
|
|
|
|
while (*p != 0) p++; /* non input consuming */
|
|
return(xmlStrncat(cur, add, p - add));
|
|
}
|
|
|
|
/**
|
|
* Formats `msg` and places result into `buf`.
|
|
*
|
|
* @param buf the result buffer.
|
|
* @param len the result buffer length.
|
|
* @param msg the message with printf formatting.
|
|
* @param ... extra parameters for the message.
|
|
* @returns the number of characters written to `buf` or -1 if an error occurs.
|
|
*/
|
|
int
|
|
xmlStrPrintf(xmlChar *buf, int len, const char *msg, ...) {
|
|
va_list args;
|
|
int ret;
|
|
|
|
if((buf == NULL) || (msg == NULL) || (len <= 0)) {
|
|
return(-1);
|
|
}
|
|
|
|
va_start(args, msg);
|
|
ret = vsnprintf((char *) buf, len, (const char *) msg, args);
|
|
va_end(args);
|
|
buf[len - 1] = 0; /* be safe ! */
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* Formats `msg` and places result into `buf`.
|
|
*
|
|
* @param buf the result buffer.
|
|
* @param len the result buffer length.
|
|
* @param msg the message with printf formatting.
|
|
* @param ap extra parameters for the message.
|
|
* @returns the number of characters written to `buf` or -1 if an error occurs.
|
|
*/
|
|
int
|
|
xmlStrVPrintf(xmlChar *buf, int len, const char *msg, va_list ap) {
|
|
int ret;
|
|
|
|
if((buf == NULL) || (msg == NULL) || (len <= 0)) {
|
|
return(-1);
|
|
}
|
|
|
|
ret = vsnprintf((char *) buf, len, (const char *) msg, ap);
|
|
buf[len - 1] = 0; /* be safe ! */
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* Creates a newly allocated string according to format.
|
|
*
|
|
* @param out pointer to the resulting string
|
|
* @param maxSize maximum size of the output buffer
|
|
* @param msg printf format string
|
|
* @param ap arguments for format string
|
|
* @returns 0 on success, 1 if the result was truncated or on other
|
|
* errors, -1 if a memory allocation failed.
|
|
*/
|
|
int
|
|
xmlStrVASPrintf(xmlChar **out, int maxSize, const char *msg, va_list ap) {
|
|
char empty[1];
|
|
va_list copy;
|
|
xmlChar *buf;
|
|
int res, size;
|
|
int truncated = 0;
|
|
|
|
if (out == NULL)
|
|
return(1);
|
|
*out = NULL;
|
|
if (msg == NULL)
|
|
return(1);
|
|
if (maxSize < 32)
|
|
maxSize = 32;
|
|
|
|
va_copy(copy, ap);
|
|
res = vsnprintf(empty, 1, msg, copy);
|
|
va_end(copy);
|
|
|
|
if (res > 0) {
|
|
/* snprintf seems to work according to C99. */
|
|
|
|
if (res < maxSize) {
|
|
size = res + 1;
|
|
} else {
|
|
size = maxSize;
|
|
truncated = 1;
|
|
}
|
|
buf = xmlMalloc(size);
|
|
if (buf == NULL)
|
|
return(-1);
|
|
if (vsnprintf((char *) buf, size, msg, ap) < 0) {
|
|
xmlFree(buf);
|
|
return(1);
|
|
}
|
|
} else {
|
|
/*
|
|
* Unfortunately, older snprintf implementations don't follow the
|
|
* C99 spec. If the output exceeds the size of the buffer, they can
|
|
* return -1, 0 or the number of characters written instead of the
|
|
* needed size. Older MSCVRT also won't write a terminating null
|
|
* byte if the buffer is too small.
|
|
*
|
|
* If the value returned is non-negative and strictly less than
|
|
* the buffer size (without terminating null), the result should
|
|
* have been written completely, so we double the buffer size
|
|
* until this condition is true. This assumes that snprintf will
|
|
* eventually return a non-negative value. Otherwise, we will
|
|
* allocate more and more memory until we run out.
|
|
*
|
|
* Note that this code path is also executed on conforming
|
|
* platforms if the output is the empty string.
|
|
*/
|
|
|
|
buf = NULL;
|
|
size = 32;
|
|
while (1) {
|
|
buf = xmlMalloc(size);
|
|
if (buf == NULL)
|
|
return(-1);
|
|
|
|
va_copy(copy, ap);
|
|
res = vsnprintf((char *) buf, size, msg, copy);
|
|
va_end(copy);
|
|
if ((res >= 0) && (res < size - 1))
|
|
break;
|
|
|
|
if (size >= maxSize) {
|
|
truncated = 1;
|
|
break;
|
|
}
|
|
|
|
xmlFree(buf);
|
|
|
|
if (size > maxSize / 2)
|
|
size = maxSize;
|
|
else
|
|
size *= 2;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If the output was truncated, make sure that the buffer doesn't
|
|
* end with a truncated UTF-8 sequence.
|
|
*/
|
|
if (truncated != 0) {
|
|
int i = size - 1;
|
|
|
|
while (i > 0) {
|
|
/* Break after ASCII */
|
|
if (buf[i-1] < 0x80)
|
|
break;
|
|
i -= 1;
|
|
/* Break before non-ASCII */
|
|
if (buf[i] >= 0xc0)
|
|
break;
|
|
}
|
|
|
|
buf[i] = 0;
|
|
}
|
|
|
|
*out = (xmlChar *) buf;
|
|
return(truncated);
|
|
}
|
|
|
|
/**
|
|
* See xmlStrVASPrintf.
|
|
*
|
|
* @param out pointer to the resulting string
|
|
* @param maxSize maximum size of the output buffer
|
|
* @param msg printf format string
|
|
* @param ... arguments for format string
|
|
* @returns 0 on success, 1 if the result was truncated or on other
|
|
* errors, -1 if a memory allocation failed.
|
|
*/
|
|
int
|
|
xmlStrASPrintf(xmlChar **out, int maxSize, const char *msg, ...) {
|
|
va_list ap;
|
|
int ret;
|
|
|
|
va_start(ap, msg);
|
|
ret = xmlStrVASPrintf(out, maxSize, msg, ap);
|
|
va_end(ap);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
/************************************************************************
|
|
* *
|
|
* Generic UTF8 handling routines *
|
|
* *
|
|
* From rfc2044: encoding of the Unicode values on UTF-8: *
|
|
* *
|
|
* UCS-4 range (hex.) UTF-8 octet sequence (binary) *
|
|
* 0000 0000-0000 007F 0xxxxxxx *
|
|
* 0000 0080-0000 07FF 110xxxxx 10xxxxxx *
|
|
* 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx *
|
|
* *
|
|
* I hope we won't use values > 0xFFFF anytime soon ! *
|
|
* *
|
|
************************************************************************/
|
|
|
|
|
|
/**
|
|
* calculates the internal size of a UTF8 character
|
|
*
|
|
* @param utf pointer to the UTF8 character
|
|
* @returns the numbers of bytes in the character, -1 on format error
|
|
*/
|
|
int
|
|
xmlUTF8Size(const xmlChar *utf) {
|
|
xmlChar mask;
|
|
int len;
|
|
|
|
if (utf == NULL)
|
|
return -1;
|
|
if (*utf < 0x80)
|
|
return 1;
|
|
/* check valid UTF8 character */
|
|
if (!(*utf & 0x40))
|
|
return -1;
|
|
/* determine number of bytes in char */
|
|
len = 2;
|
|
for (mask=0x20; mask != 0; mask>>=1) {
|
|
if (!(*utf & mask))
|
|
return len;
|
|
len++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* compares the two UCS4 values
|
|
*
|
|
* @param utf1 pointer to first UTF8 char
|
|
* @param utf2 pointer to second UTF8 char
|
|
* @returns result of the compare as with #xmlStrncmp
|
|
*/
|
|
int
|
|
xmlUTF8Charcmp(const xmlChar *utf1, const xmlChar *utf2) {
|
|
|
|
if (utf1 == NULL ) {
|
|
if (utf2 == NULL)
|
|
return 0;
|
|
return -1;
|
|
}
|
|
return xmlStrncmp(utf1, utf2, xmlUTF8Size(utf1));
|
|
}
|
|
|
|
/**
|
|
* compute the length of an UTF8 string, it doesn't do a full UTF8
|
|
* checking of the content of the string.
|
|
*
|
|
* @param utf a sequence of UTF-8 encoded bytes
|
|
* @returns the number of characters in the string or -1 in case of error
|
|
*/
|
|
int
|
|
xmlUTF8Strlen(const xmlChar *utf) {
|
|
size_t ret = 0;
|
|
|
|
if (utf == NULL)
|
|
return(-1);
|
|
|
|
while (*utf != 0) {
|
|
if (utf[0] & 0x80) {
|
|
if ((utf[1] & 0xc0) != 0x80)
|
|
return(-1);
|
|
if ((utf[0] & 0xe0) == 0xe0) {
|
|
if ((utf[2] & 0xc0) != 0x80)
|
|
return(-1);
|
|
if ((utf[0] & 0xf0) == 0xf0) {
|
|
if ((utf[0] & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
|
|
return(-1);
|
|
utf += 4;
|
|
} else {
|
|
utf += 3;
|
|
}
|
|
} else {
|
|
utf += 2;
|
|
}
|
|
} else {
|
|
utf++;
|
|
}
|
|
ret++;
|
|
}
|
|
return(ret > INT_MAX ? 0 : ret);
|
|
}
|
|
|
|
/**
|
|
* Read the first UTF8 character from `utf`
|
|
*
|
|
* @param utf a sequence of UTF-8 encoded bytes
|
|
* @param len a pointer to the minimum number of bytes present in
|
|
* the sequence. This is used to assure the next character
|
|
* is completely contained within the sequence.
|
|
* @returns the char value or -1 in case of error, and sets *len to
|
|
* the actual number of bytes consumed (0 in case of error)
|
|
*/
|
|
int
|
|
xmlGetUTF8Char(const unsigned char *utf, int *len) {
|
|
unsigned int c;
|
|
|
|
if (utf == NULL)
|
|
goto error;
|
|
if (len == NULL)
|
|
goto error;
|
|
|
|
c = utf[0];
|
|
if (c < 0x80) {
|
|
if (*len < 1)
|
|
goto error;
|
|
/* 1-byte code */
|
|
*len = 1;
|
|
} else {
|
|
if ((*len < 2) || ((utf[1] & 0xc0) != 0x80))
|
|
goto error;
|
|
if (c < 0xe0) {
|
|
if (c < 0xc2)
|
|
goto error;
|
|
/* 2-byte code */
|
|
*len = 2;
|
|
c = (c & 0x1f) << 6;
|
|
c |= utf[1] & 0x3f;
|
|
} else {
|
|
if ((*len < 3) || ((utf[2] & 0xc0) != 0x80))
|
|
goto error;
|
|
if (c < 0xf0) {
|
|
/* 3-byte code */
|
|
*len = 3;
|
|
c = (c & 0xf) << 12;
|
|
c |= (utf[1] & 0x3f) << 6;
|
|
c |= utf[2] & 0x3f;
|
|
if ((c < 0x800) || ((c >= 0xd800) && (c < 0xe000)))
|
|
goto error;
|
|
} else {
|
|
if ((*len < 4) || ((utf[3] & 0xc0) != 0x80))
|
|
goto error;
|
|
*len = 4;
|
|
/* 4-byte code */
|
|
c = (c & 0x7) << 18;
|
|
c |= (utf[1] & 0x3f) << 12;
|
|
c |= (utf[2] & 0x3f) << 6;
|
|
c |= utf[3] & 0x3f;
|
|
if ((c < 0x10000) || (c >= 0x110000))
|
|
goto error;
|
|
}
|
|
}
|
|
}
|
|
return(c);
|
|
|
|
error:
|
|
if (len != NULL)
|
|
*len = 0;
|
|
return(-1);
|
|
}
|
|
|
|
/**
|
|
* Checks `utf` for being valid UTF-8. `utf` is assumed to be
|
|
* null-terminated. This function is not super-strict, as it will
|
|
* allow longer UTF-8 sequences than necessary. Note that Java is
|
|
* capable of producing these sequences if provoked. Also note, this
|
|
* routine checks for the 4-byte maximum size, but does not check for
|
|
* 0x10ffff maximum value.
|
|
*
|
|
* @param utf Pointer to putative UTF-8 encoded string.
|
|
* @returns value: true if `utf` is valid.
|
|
**/
|
|
int
|
|
xmlCheckUTF8(const unsigned char *utf)
|
|
{
|
|
int ix;
|
|
unsigned char c;
|
|
|
|
if (utf == NULL)
|
|
return(0);
|
|
/*
|
|
* utf is a string of 1, 2, 3 or 4 bytes. The valid strings
|
|
* are as follows (in "bit format"):
|
|
* 0xxxxxxx valid 1-byte
|
|
* 110xxxxx 10xxxxxx valid 2-byte
|
|
* 1110xxxx 10xxxxxx 10xxxxxx valid 3-byte
|
|
* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx valid 4-byte
|
|
*/
|
|
while ((c = utf[0])) { /* string is 0-terminated */
|
|
ix = 0;
|
|
if ((c & 0x80) == 0x00) { /* 1-byte code, starts with 10 */
|
|
ix = 1;
|
|
} else if ((c & 0xe0) == 0xc0) {/* 2-byte code, starts with 110 */
|
|
if ((utf[1] & 0xc0 ) != 0x80)
|
|
return 0;
|
|
ix = 2;
|
|
} else if ((c & 0xf0) == 0xe0) {/* 3-byte code, starts with 1110 */
|
|
if (((utf[1] & 0xc0) != 0x80) ||
|
|
((utf[2] & 0xc0) != 0x80))
|
|
return 0;
|
|
ix = 3;
|
|
} else if ((c & 0xf8) == 0xf0) {/* 4-byte code, starts with 11110 */
|
|
if (((utf[1] & 0xc0) != 0x80) ||
|
|
((utf[2] & 0xc0) != 0x80) ||
|
|
((utf[3] & 0xc0) != 0x80))
|
|
return 0;
|
|
ix = 4;
|
|
} else /* unknown encoding */
|
|
return 0;
|
|
utf += ix;
|
|
}
|
|
return(1);
|
|
}
|
|
|
|
/**
|
|
* storage size of an UTF8 string
|
|
* the behaviour is not guaranteed if the input string is not UTF-8
|
|
*
|
|
* @param utf a sequence of UTF-8 encoded bytes
|
|
* @param len the number of characters in the array
|
|
* @returns the storage size of
|
|
* the first 'len' characters of ARRAY
|
|
*/
|
|
|
|
int
|
|
xmlUTF8Strsize(const xmlChar *utf, int len) {
|
|
const xmlChar *ptr=utf;
|
|
int ch;
|
|
size_t ret;
|
|
|
|
if (utf == NULL)
|
|
return(0);
|
|
|
|
if (len <= 0)
|
|
return(0);
|
|
|
|
while ( len-- > 0) {
|
|
if ( !*ptr )
|
|
break;
|
|
ch = *ptr++;
|
|
if ((ch & 0x80))
|
|
while ((ch<<=1) & 0x80 ) {
|
|
if (*ptr == 0) break;
|
|
ptr++;
|
|
}
|
|
}
|
|
ret = ptr - utf;
|
|
return (ret > INT_MAX ? 0 : ret);
|
|
}
|
|
|
|
|
|
/**
|
|
* a strndup for array of UTF8's
|
|
*
|
|
* @param utf the input UTF8 *
|
|
* @param len the len of `utf` (in chars)
|
|
* @returns a new UTF8 * or NULL
|
|
*/
|
|
xmlChar *
|
|
xmlUTF8Strndup(const xmlChar *utf, int len) {
|
|
xmlChar *ret;
|
|
int i;
|
|
|
|
if ((utf == NULL) || (len < 0)) return(NULL);
|
|
i = xmlUTF8Strsize(utf, len);
|
|
ret = xmlMalloc((size_t) i + 1);
|
|
if (ret == NULL) {
|
|
return(NULL);
|
|
}
|
|
memcpy(ret, utf, i);
|
|
ret[i] = 0;
|
|
return(ret);
|
|
}
|
|
|
|
/**
|
|
* a function to provide the equivalent of fetching a
|
|
* character from a string array
|
|
*
|
|
* @param utf the input UTF8 *
|
|
* @param pos the position of the desired UTF8 char (in chars)
|
|
* @returns a pointer to the UTF8 character or NULL
|
|
*/
|
|
const xmlChar *
|
|
xmlUTF8Strpos(const xmlChar *utf, int pos) {
|
|
int ch;
|
|
|
|
if (utf == NULL) return(NULL);
|
|
if (pos < 0)
|
|
return(NULL);
|
|
while (pos--) {
|
|
ch = *utf++;
|
|
if (ch == 0)
|
|
return(NULL);
|
|
if ( ch & 0x80 ) {
|
|
/* if not simple ascii, verify proper format */
|
|
if ( (ch & 0xc0) != 0xc0 )
|
|
return(NULL);
|
|
/* then skip over remaining bytes for this char */
|
|
while ( (ch <<= 1) & 0x80 )
|
|
if ( (*utf++ & 0xc0) != 0x80 )
|
|
return(NULL);
|
|
}
|
|
}
|
|
return((xmlChar *)utf);
|
|
}
|
|
|
|
/**
|
|
* a function to provide the relative location of a UTF8 char
|
|
*
|
|
* @param utf the input UTF8 *
|
|
* @param utfchar the UTF8 character to be found
|
|
* @returns the relative character position of the desired char
|
|
* or -1 if not found
|
|
*/
|
|
int
|
|
xmlUTF8Strloc(const xmlChar *utf, const xmlChar *utfchar) {
|
|
size_t i;
|
|
int size;
|
|
int ch;
|
|
|
|
if (utf==NULL || utfchar==NULL) return -1;
|
|
size = xmlUTF8Strsize(utfchar, 1);
|
|
for(i=0; (ch=*utf) != 0; i++) {
|
|
if (xmlStrncmp(utf, utfchar, size)==0)
|
|
return(i > INT_MAX ? 0 : i);
|
|
utf++;
|
|
if ( ch & 0x80 ) {
|
|
/* if not simple ascii, verify proper format */
|
|
if ( (ch & 0xc0) != 0xc0 )
|
|
return(-1);
|
|
/* then skip over remaining bytes for this char */
|
|
while ( (ch <<= 1) & 0x80 )
|
|
if ( (*utf++ & 0xc0) != 0x80 )
|
|
return(-1);
|
|
}
|
|
}
|
|
|
|
return(-1);
|
|
}
|
|
/**
|
|
* Create a substring from a given UTF-8 string
|
|
* Note: positions are given in units of UTF-8 chars
|
|
*
|
|
* @param utf a sequence of UTF-8 encoded bytes
|
|
* @param start relative pos of first char
|
|
* @param len total number to copy
|
|
* @returns a pointer to a newly created string or NULL if the
|
|
* start index is out of bounds or a memory allocation failed.
|
|
* If len is too large, the result is truncated.
|
|
*/
|
|
|
|
xmlChar *
|
|
xmlUTF8Strsub(const xmlChar *utf, int start, int len) {
|
|
int i;
|
|
int ch;
|
|
|
|
if (utf == NULL) return(NULL);
|
|
if (start < 0) return(NULL);
|
|
if (len < 0) return(NULL);
|
|
|
|
/*
|
|
* Skip over any leading chars
|
|
*/
|
|
for (i = 0; i < start; i++) {
|
|
ch = *utf++;
|
|
if (ch == 0)
|
|
return(NULL);
|
|
/* skip over remaining bytes for this char */
|
|
if (ch & 0x80) {
|
|
ch <<= 1;
|
|
while (ch & 0x80) {
|
|
if (*utf++ == 0)
|
|
return(NULL);
|
|
ch <<= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return(xmlUTF8Strndup(utf, len));
|
|
}
|
|
|
|
/**
|
|
* Replaces a string with an escaped string.
|
|
*
|
|
* `msg` must be a heap-allocated buffer created by libxml2 that may be
|
|
* returned, or that may be freed and replaced.
|
|
*
|
|
* @param msg a pointer to the string in which to escape '%' characters.
|
|
* @returns the same string with all '%' characters escaped.
|
|
*/
|
|
xmlChar *
|
|
xmlEscapeFormatString(xmlChar **msg)
|
|
{
|
|
xmlChar *msgPtr = NULL;
|
|
xmlChar *result = NULL;
|
|
xmlChar *resultPtr = NULL;
|
|
size_t count = 0;
|
|
size_t msgLen = 0;
|
|
size_t resultLen = 0;
|
|
|
|
if (!msg || !*msg)
|
|
return(NULL);
|
|
|
|
for (msgPtr = *msg; *msgPtr != '\0'; ++msgPtr) {
|
|
++msgLen;
|
|
if (*msgPtr == '%')
|
|
++count;
|
|
}
|
|
|
|
if (count == 0)
|
|
return(*msg);
|
|
|
|
if ((count > INT_MAX) || (msgLen > INT_MAX - count))
|
|
return(NULL);
|
|
resultLen = msgLen + count + 1;
|
|
result = xmlMalloc(resultLen);
|
|
if (result == NULL) {
|
|
/* Clear *msg to prevent format string vulnerabilities in
|
|
out-of-memory situations. */
|
|
xmlFree(*msg);
|
|
*msg = NULL;
|
|
return(NULL);
|
|
}
|
|
|
|
for (msgPtr = *msg, resultPtr = result; *msgPtr != '\0'; ++msgPtr, ++resultPtr) {
|
|
*resultPtr = *msgPtr;
|
|
if (*msgPtr == '%')
|
|
*(++resultPtr) = '%';
|
|
}
|
|
result[resultLen - 1] = '\0';
|
|
|
|
xmlFree(*msg);
|
|
*msg = result;
|
|
|
|
return *msg;
|
|
}
|
|
|