/* * This file is part of libdom. * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2007 John-Mark Bell * Copyright 2009 Bo Yang */ #include #include #include #include #include #include #include "core/string.h" #include "core/document.h" #include "utils/utils.h" /** * Type of a DOM string */ enum dom_string_type { DOM_STRING_CDATA = 0, DOM_STRING_INTERNED = 1 }; /** * A DOM string * * Strings are reference counted so destruction is performed correctly. */ typedef struct dom_string_internal { dom_string base; union { struct { uint8_t *ptr; /**< Pointer to string data */ size_t len; /**< Byte length of string */ } cdata; lwc_string *intern; /**< Interned string */ } data; enum dom_string_type type; /**< String type */ } dom_string_internal; /** * Empty string, for comparisons against NULL */ static const dom_string_internal empty_string = { { 0 }, { { (uint8_t *) "", 0 } }, DOM_STRING_CDATA }; void dom_string_destroy(dom_string *str) { dom_string_internal *istr = (void *) str; if (str != NULL) { assert(istr->base.refcnt == 0); switch (istr->type) { case DOM_STRING_INTERNED: if (istr->data.intern != NULL) { lwc_string_unref(istr->data.intern); } break; case DOM_STRING_CDATA: free(istr->data.cdata.ptr); break; } free(str); } } /** * Create a DOM string from a string of characters * * \param ptr Pointer to string of characters * \param len Length, in bytes, of string of characters * \param str Pointer to location to receive result * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * The returned string will already be referenced, so there is no need * to explicitly reference it. * * The string of characters passed in will be copied for use by the * returned DOM string. */ dom_exception dom_string_create(const uint8_t *ptr, size_t len, dom_string **str) { dom_string_internal *ret; if (ptr == NULL || len == 0) { ptr = (const uint8_t *) ""; len = 0; } ret = malloc(sizeof(*ret)); if (ret == NULL) return DOM_NO_MEM_ERR; ret->data.cdata.ptr = malloc(len + 1); if (ret->data.cdata.ptr == NULL) { free(ret); return DOM_NO_MEM_ERR; } memcpy(ret->data.cdata.ptr, ptr, len); ret->data.cdata.ptr[len] = '\0'; ret->data.cdata.len = len; ret->base.refcnt = 1; ret->type = DOM_STRING_CDATA; *str = (dom_string *)ret; return DOM_NO_ERR; } /** * Create an interned DOM string from a string of characters * * \param ptr Pointer to string of characters * \param len Length, in bytes, of string of characters * \param str Pointer to location to receive result * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * The returned string will already be referenced, so there is no need * to explicitly reference it. * * The string of characters passed in will be copied for use by the * returned DOM string. */ dom_exception dom_string_create_interned(const uint8_t *ptr, size_t len, dom_string **str) { dom_string_internal *ret; if (ptr == NULL || len == 0) { ptr = (const uint8_t *) ""; len = 0; } ret = malloc(sizeof(*ret)); if (ret == NULL) return DOM_NO_MEM_ERR; if (lwc_intern_string((const char *) ptr, len, &ret->data.intern) != lwc_error_ok) { free(ret); return DOM_NO_MEM_ERR; } ret->base.refcnt = 1; ret->type = DOM_STRING_INTERNED; *str = (dom_string *)ret; return DOM_NO_ERR; } /** * Make the dom_string be interned * * \param str The dom_string to be interned * \param lwcstr The result lwc_string * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ dom_exception dom_string_intern(dom_string *str, struct lwc_string_s **lwcstr) { dom_string_internal *istr = (void *) str; /* If this string is already interned, do nothing */ if (istr->type != DOM_STRING_INTERNED) { lwc_string *ret; lwc_error lerr; lerr = lwc_intern_string((const char *) istr->data.cdata.ptr, istr->data.cdata.len, &ret); if (lerr != lwc_error_ok) { return _dom_exception_from_lwc_error(lerr); } free(istr->data.cdata.ptr); istr->data.intern = ret; istr->type = DOM_STRING_INTERNED; } *lwcstr = lwc_string_ref(istr->data.intern); return DOM_NO_ERR; } /** * Case sensitively compare two DOM strings * * \param s1 The first string to compare * \param s2 The second string to compare * \return true if strings match, false otherwise */ bool dom_string_isequal(const dom_string *s1, const dom_string *s2) { size_t len; const dom_string_internal *is1 = (void *) s1; const dom_string_internal *is2 = (void *) s2; if (s1 == NULL) is1 = &empty_string; if (s2 == NULL) is2 = &empty_string; if (is1->type == DOM_STRING_INTERNED && is2->type == DOM_STRING_INTERNED) { bool match; (void) lwc_string_isequal(is1->data.intern, is2->data.intern, &match); return match; } len = dom_string_byte_length((dom_string *) is1); if (len != dom_string_byte_length((dom_string *)is2)) return false; return 0 == memcmp(dom_string_data((dom_string *) is1), dom_string_data((dom_string *)is2), len); } /** * Trivial locale-agnostic lower case convertor */ static inline uint8_t dolower(const uint8_t c) { if ('A' <= c && c <= 'Z') return c + 'a' - 'A'; return c; } /** * Case insensitively compare two DOM strings * * \param s1 The first string to compare * \param s2 The second string to compare * \return true if strings match, false otherwise */ bool dom_string_caseless_isequal(const dom_string *s1, const dom_string *s2) { const uint8_t *d1 = NULL; const uint8_t *d2 = NULL; size_t len; const dom_string_internal *is1 = (void *) s1; const dom_string_internal *is2 = (void *) s2; if (s1 == NULL) is1 = &empty_string; if (s2 == NULL) is2 = &empty_string; if (is1->type == DOM_STRING_INTERNED && is2->type == DOM_STRING_INTERNED) { bool match; if (lwc_string_caseless_isequal(is1->data.intern, is2->data.intern, &match) != lwc_error_ok) return false; return match; } len = dom_string_byte_length((dom_string *) is1); if (len != dom_string_byte_length((dom_string *)is2)) return false; d1 = (const uint8_t *) dom_string_data((dom_string *) is1); d2 = (const uint8_t *) dom_string_data((dom_string *)is2); while (len > 0) { if (dolower(*d1) != dolower(*d2)) return false; d1++; d2++; len--; } return true; } /** * Case sensitively compare DOM string with lwc_string * * \param s1 The first string to compare * \param s2 The second string to compare * \return true if strings match, false otherwise * * Returns false if either are NULL. */ bool dom_string_lwc_isequal(const dom_string *s1, lwc_string *s2) { size_t len; dom_string_internal *is1 = (void *) s1; if (s1 == NULL || s2 == NULL) return false; if (is1->type == DOM_STRING_INTERNED) { bool match; (void) lwc_string_isequal(is1->data.intern, s2, &match); return match; } /* Handle non-interned case */ len = dom_string_byte_length(s1); if (len != lwc_string_length(s2)) return false; return 0 == memcmp(dom_string_data(s1), lwc_string_data(s2), len); } /** * Case insensitively compare DOM string with lwc_string * * \param s1 The first string to compare * \param s2 The second string to compare * \return true if strings match, false otherwise * * Returns false if either are NULL. */ bool dom_string_caseless_lwc_isequal(const dom_string *s1, lwc_string *s2) { size_t len; const uint8_t *d1 = NULL; const uint8_t *d2 = NULL; dom_string_internal *is1 = (void *) s1; if (s1 == NULL || s2 == NULL) return false; if (is1->type == DOM_STRING_INTERNED) { bool match; if (lwc_string_caseless_isequal(is1->data.intern, s2, &match) != lwc_error_ok) return false; return match; } len = dom_string_byte_length(s1); if (len != lwc_string_length(s2)) return false; d1 = (const uint8_t *) dom_string_data(s1); d2 = (const uint8_t *) lwc_string_data(s2); while (len > 0) { if (dolower(*d1) != dolower(*d2)) return false; d1++; d2++; len--; } return true; } /** * Get the index of the first occurrence of a character in a dom string * * \param str The string to search in * \param chr UCS4 value to look for * \return Character index of found character, or -1 if none found */ uint32_t dom_string_index(dom_string *str, uint32_t chr) { const uint8_t *s; size_t clen, slen; uint32_t c, index; parserutils_error err; s = (const uint8_t *) dom_string_data(str); slen = dom_string_byte_length(str); index = 0; while (slen > 0) { err = parserutils_charset_utf8_to_ucs4(s, slen, &c, &clen); if (err != PARSERUTILS_OK) { return (uint32_t) -1; } if (c == chr) { return index; } s += clen; slen -= clen; index++; } return (uint32_t) -1; } /** * Get the index of the last occurrence of a character in a dom string * * \param str The string to search in * \param chr UCS4 value to look for * \return Character index of found character, or -1 if none found */ uint32_t dom_string_rindex(dom_string *str, uint32_t chr) { const uint8_t *s; size_t clen = 0, slen; uint32_t c, coff, index; parserutils_error err; s = (const uint8_t *) dom_string_data(str); slen = dom_string_byte_length(str); index = dom_string_length(str); while (slen > 0) { err = parserutils_charset_utf8_prev(s, slen, (uint32_t *) &coff); if (err == PARSERUTILS_OK) { err = parserutils_charset_utf8_to_ucs4(s + coff, slen - clen, &c, &clen); } if (err != PARSERUTILS_OK) { return (uint32_t) -1; } if (c == chr) { return index; } slen -= clen; index--; } return (uint32_t) -1; } /** * Get the length, in characters, of a dom string * * \param str The string to measure the length of * \return The length of the string, in characters */ uint32_t dom_string_length(dom_string *str) { const uint8_t *s; size_t slen, clen; parserutils_error err; s = (const uint8_t *) dom_string_data(str); slen = dom_string_byte_length(str); err = parserutils_charset_utf8_length(s, slen, &clen); if (err != PARSERUTILS_OK) { return 0; } return clen; } /** * Get the UCS4 character at position index * * \param index The position of the charater * \param ch The UCS4 character * \return DOM_NO_ERR on success, appropriate dom_exception on failure. */ dom_exception dom_string_at(dom_string *str, uint32_t index, uint32_t *ch) { const uint8_t *s; size_t clen, slen; uint32_t c, i; parserutils_error err; s = (const uint8_t *) dom_string_data(str); slen = dom_string_byte_length(str); i = 0; while (slen > 0) { err = parserutils_charset_utf8_char_byte_length(s, &clen); if (err != PARSERUTILS_OK) { return (uint32_t) -1; } i++; if (i == index + 1) break; s += clen; slen -= clen; } if (i == index + 1) { err = parserutils_charset_utf8_to_ucs4(s, slen, &c, &clen); if (err != PARSERUTILS_OK) { return (uint32_t) -1; } *ch = c; return DOM_NO_ERR; } else { return DOM_DOMSTRING_SIZE_ERR; } } /** * Concatenate two dom strings * * \param s1 The first string * \param s2 The second string * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * The returned string will be referenced. The client * should dereference it once it has finished with it. */ dom_exception dom_string_concat(dom_string *s1, dom_string *s2, dom_string **result) { dom_string_internal *concat; const uint8_t *s1ptr, *s2ptr; size_t s1len, s2len; assert(s1 != NULL); assert(s2 != NULL); s1ptr = (const uint8_t *) dom_string_data(s1); s2ptr = (const uint8_t *) dom_string_data(s2); s1len = dom_string_byte_length(s1); s2len = dom_string_byte_length(s2); concat = malloc(sizeof(*concat)); if (concat == NULL) { return DOM_NO_MEM_ERR; } concat->data.cdata.ptr = malloc(s1len + s2len + 1); if (concat->data.cdata.ptr == NULL) { free(concat); return DOM_NO_MEM_ERR; } memcpy(concat->data.cdata.ptr, s1ptr, s1len); memcpy(concat->data.cdata.ptr + s1len, s2ptr, s2len); concat->data.cdata.ptr[s1len + s2len] = '\0'; concat->data.cdata.len = s1len + s2len; concat->base.refcnt = 1; concat->type = DOM_STRING_CDATA; *result = (dom_string *)concat; return DOM_NO_ERR; } /** * Extract a substring from a dom string * * \param str The string to extract from * \param i1 The character index of the start of the substring * \param i2 The character index of the end of the substring * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion * * The returned string will have its reference count increased. The client * should dereference it once it has finished with it. */ dom_exception dom_string_substr(dom_string *str, uint32_t i1, uint32_t i2, dom_string **result) { const uint8_t *s; size_t slen; uint32_t b1, b2; parserutils_error err; /* target string is NULL equivalent to empty. */ if (str == NULL) str = (dom_string *)&empty_string; s = (const uint8_t *) dom_string_data(str); slen = dom_string_byte_length(str); /* Initialise the byte index of the start to 0 */ b1 = 0; /* Make the end a character offset from the start */ i2 -= i1; /* Calculate the byte index of the start */ while (i1 > 0) { err = parserutils_charset_utf8_next(s, slen, b1, &b1); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } i1--; } /* Initialise the byte index of the end to that of the start */ b2 = b1; /* Calculate the byte index of the end */ while (i2 > 0) { err = parserutils_charset_utf8_next(s, slen, b2, &b2); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } i2--; } /* Create a string from the specified byte range */ return dom_string_create(s + b1, b2 - b1, result); } /** * Insert data into a dom string at the given location * * \param target Pointer to string to insert into * \param source Pointer to string to insert * \param offset Character offset of location to insert at * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, * DOM_NO_MEM_ERR on memory exhaustion, * DOM_INDEX_SIZE_ERR if ::offset > len(::target). * * The returned string will have its reference count increased. The client * should dereference it once it has finished with it. */ dom_exception dom_string_insert(dom_string *target, dom_string *source, uint32_t offset, dom_string **result) { dom_string_internal *res; const uint8_t *t, *s; uint32_t tlen, slen, clen; uint32_t ins = 0; parserutils_error err; /* target string is NULL equivalent to empty. */ if (target == NULL) target = (dom_string *)&empty_string; t = (const uint8_t *) dom_string_data(target); tlen = dom_string_byte_length(target); s = (const uint8_t *) dom_string_data(source); slen = dom_string_byte_length(source); clen = dom_string_length(target); if (offset > clen) return DOM_INDEX_SIZE_ERR; /* Calculate the byte index of the insertion point */ if (offset == clen) { /* Optimisation for append */ ins = tlen; } else { while (offset > 0) { err = parserutils_charset_utf8_next(t, tlen, ins, &ins); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } offset--; } } /* Allocate result string */ res = malloc(sizeof(*res)); if (res == NULL) { return DOM_NO_MEM_ERR; } /* Allocate data buffer for result contents */ res->data.cdata.ptr = malloc(tlen + slen + 1); if (res->data.cdata.ptr == NULL) { free(res); return DOM_NO_MEM_ERR; } /* Copy initial portion of target, if any, into result */ if (ins > 0) { memcpy(res->data.cdata.ptr, t, ins); } /* Copy inserted data into result */ memcpy(res->data.cdata.ptr + ins, s, slen); /* Copy remainder of target, if any, into result */ if (tlen - ins > 0) { memcpy(res->data.cdata.ptr + ins + slen, t + ins, tlen - ins); } res->data.cdata.ptr[tlen + slen] = '\0'; res->data.cdata.len = tlen + slen; res->base.refcnt = 1; res->type = DOM_STRING_CDATA; *result = (dom_string *)res; return DOM_NO_ERR; } /** * Replace a section of a dom string * * \param target Pointer to string of which to replace a section * \param source Pointer to replacement string * \param i1 Character index of start of region to replace * \param i2 Character index of end of region to replace * \param result Pointer to location to receive result * \return DOM_NO_ERR on success, DOM_NO_MEM_ERR on memory exhaustion. * * The returned string will have its reference count increased. The client * should dereference it once it has finished with it. */ dom_exception dom_string_replace(dom_string *target, dom_string *source, uint32_t i1, uint32_t i2, dom_string **result) { dom_string_internal *res; const uint8_t *t, *s; uint32_t tlen, slen; uint32_t b1, b2; parserutils_error err; /* target string is NULL equivalent to empty. */ if (target == NULL) target = (dom_string *)&empty_string; t = (const uint8_t *) dom_string_data(target); tlen = dom_string_byte_length(target); s = (const uint8_t *) dom_string_data(source); slen = dom_string_byte_length(source); /* Initialise the byte index of the start to 0 */ b1 = 0; /* Make the end a character offset from the start */ i2 -= i1; /* Calculate the byte index of the start */ while (i1 > 0) { err = parserutils_charset_utf8_next(t, tlen, b1, &b1); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } i1--; } /* Initialise the byte index of the end to that of the start */ b2 = b1; /* Calculate the byte index of the end */ while (i2 > 0) { err = parserutils_charset_utf8_next(t, tlen, b2, &b2); if (err != PARSERUTILS_OK) { return DOM_NO_MEM_ERR; } i2--; } /* Allocate result string */ res = malloc(sizeof(*res)); if (res == NULL) { return DOM_NO_MEM_ERR; } /* Allocate data buffer for result contents */ res->data.cdata.ptr = malloc(tlen + slen - (b2 - b1) + 1); if (res->data.cdata.ptr == NULL) { free(res); return DOM_NO_MEM_ERR; } /* Copy initial portion of target, if any, into result */ if (b1 > 0) { memcpy(res->data.cdata.ptr, t, b1); } /* Copy replacement data into result */ if (slen > 0) { memcpy(res->data.cdata.ptr + b1, s, slen); } /* Copy remainder of target, if any, into result */ if (tlen - b2 > 0) { memcpy(res->data.cdata.ptr + b1 + slen, t + b2, tlen - b2); } res->data.cdata.ptr[tlen + slen - (b2 - b1)] = '\0'; res->data.cdata.len = tlen + slen - (b2 - b1); res->base.refcnt = 1; res->type = DOM_STRING_CDATA; *result = (dom_string *)res; return DOM_NO_ERR; } /** * Calculate a hash value from a dom string * * \param str The string to calculate a hash of * \return The hash value associated with the string */ uint32_t dom_string_hash(dom_string *str) { const uint8_t *s = (const uint8_t *) dom_string_data(str); size_t slen = dom_string_byte_length(str); uint32_t hash = 0x811c9dc5; while (slen > 0) { hash *= 0x01000193; hash ^= *s; s++; slen--; } return hash; } /** * Convert a lwc_error to a dom_exception * * \param err The input lwc_error * \return the dom_exception */ dom_exception _dom_exception_from_lwc_error(lwc_error err) { switch (err) { case lwc_error_ok: return DOM_NO_ERR; case lwc_error_oom: return DOM_NO_MEM_ERR; case lwc_error_range: return DOM_INDEX_SIZE_ERR; } return DOM_NO_ERR; } /** * Get the raw character data of the dom_string. * * \param str The dom_string object * \return The C string pointer * * @note: This function is just provided for the convenience of accessing the * raw C string character, no change on the result string is allowed. */ const char *dom_string_data(const dom_string *str) { dom_string_internal *istr = (void *) str; if (istr->type == DOM_STRING_CDATA) { return (const char *) istr->data.cdata.ptr; } else { return lwc_string_data(istr->data.intern); } } /** Get the byte length of this dom_string * * \param str The dom_string object */ size_t dom_string_byte_length(const dom_string *str) { dom_string_internal *istr = (void *) str; if (istr->type == DOM_STRING_CDATA) { return istr->data.cdata.len; } else { return lwc_string_length(istr->data.intern); } } /** Convert the given string to uppercase * * \param source * \param ascii_only Whether to only convert [a-z] to [A-Z] * \param upper Result pointer for uppercase string. Caller owns ref * * \return DOM_NO_ERR on success. * * \note Right now, will return DOM_NOT_SUPPORTED_ERR if ascii_only is false. */ dom_exception dom_string_toupper(dom_string *source, bool ascii_only, dom_string **upper) { const uint8_t *orig_s = (const uint8_t *) dom_string_data(source); const size_t nbytes = dom_string_byte_length(source); uint8_t *copy_s; size_t index = 0; dom_exception exc; if (ascii_only == false) return DOM_NOT_SUPPORTED_ERR; copy_s = malloc(nbytes); if (copy_s == NULL) return DOM_NO_MEM_ERR; memcpy(copy_s, orig_s, nbytes); while (index < nbytes) { if (orig_s[index] >= 'a' && orig_s[index] <= 'z') { copy_s[index] -= 'a' - 'A'; } index++; } if (((dom_string_internal *) ((void *) source))->type == DOM_STRING_CDATA) { exc = dom_string_create(copy_s, nbytes, upper); } else { exc = dom_string_create_interned(copy_s, nbytes, upper); } free(copy_s); return exc; } /** Convert the given string to lowercase * * \param source * \param ascii_only Whether to only convert [a-z] to [A-Z] * \param lower Result pointer for lowercase string. Caller owns ref * * \return DOM_NO_ERR on success. * * \note Right now, will return DOM_NOT_SUPPORTED_ERR if ascii_only is false. */ dom_exception dom_string_tolower(dom_string *source, bool ascii_only, dom_string **lower) { dom_string_internal *isource = (void *) source; dom_exception exc = DOM_NO_ERR; if (ascii_only == false) return DOM_NOT_SUPPORTED_ERR; if (isource->type == DOM_STRING_CDATA) { const uint8_t *orig_s = (const uint8_t *) dom_string_data(source); const size_t nbytes = dom_string_byte_length(source); size_t index = 0; uint8_t *copy_s; copy_s = malloc(nbytes); if (copy_s == NULL) return DOM_NO_MEM_ERR; memcpy(copy_s, orig_s, nbytes); while (index < nbytes) { if (orig_s[index] >= 'A' && orig_s[index] <= 'Z') { copy_s[index] += 'a' - 'A'; } index++; } exc = dom_string_create(copy_s, nbytes, lower); free(copy_s); } else { bool equal; lwc_error err; lwc_string *l; err = lwc_string_tolower(isource->data.intern, &l); if (err != lwc_error_ok) { return DOM_NO_MEM_ERR; } if (lwc_string_isequal(isource->data.intern, l, &equal) == lwc_error_ok && equal == true) { /* String is already lower case. */ *lower = dom_string_ref(source); } else { /* TODO: create dom_string wrapper around existing * lwc string. */ exc = dom_string_create_interned( (const uint8_t *)lwc_string_data(l), lwc_string_length(l), lower); } lwc_string_unref(l); } return exc; } /* exported function documented in string.h */ dom_exception dom_string_whitespace_op(dom_string *s, enum dom_whitespace_op op, dom_string **ret) { const uint8_t *src_text = (const uint8_t *) dom_string_data(s); size_t len = dom_string_byte_length(s); const uint8_t *src_pos; const uint8_t *src_end; dom_exception exc; uint8_t *temp_pos; uint8_t *temp; if (len == 0) { *ret = dom_string_ref(s); } temp = malloc(len); if (temp == NULL) { return DOM_NO_MEM_ERR; } src_pos = src_text; src_end = src_text + len; temp_pos = temp; if (op & DOM_WHITESPACE_STRIP_LEADING) { while (src_pos < src_end) { if (*src_pos == ' ' || *src_pos == '\t' || *src_pos == '\n' || *src_pos == '\r' || *src_pos == '\f') src_pos++; else break; } } while (src_pos < src_end) { if ((op & DOM_WHITESPACE_COLLAPSE) && (*src_pos == ' ' || *src_pos == '\t' || *src_pos == '\n' || *src_pos == '\r' || *src_pos == '\f')) { /* Got a whitespace character */ do { /* Skip all adjacent whitespace */ src_pos++; } while (src_pos < src_end && (*src_pos == ' ' || *src_pos == '\t' || *src_pos == '\n' || *src_pos == '\r' || *src_pos == '\f')); /* Gets replaced with single space in output */ *temp_pos++ = ' '; } else { /* Otherwise, copy to output */ *temp_pos++ = *src_pos++; } } if (op & DOM_WHITESPACE_STRIP_TRAILING) { while (temp_pos > temp) { temp_pos--; if (*temp_pos != ' ' && *temp_pos != '\t' && *temp_pos != '\n' && *temp_pos != '\r' && *temp_pos != '\f') { temp_pos++; break; } } } /* New length */ len = temp_pos - temp; /* Make new string */ if (((dom_string_internal *) ((void *) s))->type == DOM_STRING_CDATA) { exc = dom_string_create(temp, len, ret); } else { exc = dom_string_create_interned(temp, len, ret); } free(temp); return exc; }