Coverage Report

Created: 2026-11-12 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libxml2/xmlstring.c
Line
Count
Source
1
/*
2
 * string.c : an XML string utilities module
3
 *
4
 * This module provides various utility functions for manipulating
5
 * the xmlChar* type. All functions named xmlStr* have been moved here
6
 * from the parser.c file (their original home).
7
 *
8
 * See Copyright for the status of this software.
9
 *
10
 * UTF8 string routines from: William Brack
11
 *
12
 * Author: Daniel Veillard
13
 */
14
15
#define IN_LIBXML
16
#include "libxml.h"
17
18
#include <stdlib.h>
19
#include <string.h>
20
#include <limits.h>
21
#include <libxml/xmlmemory.h>
22
#include <libxml/parserInternals.h>
23
#include <libxml/xmlstring.h>
24
25
#include "private/parser.h"
26
#include "private/string.h"
27
28
#ifndef va_copy
29
  #ifdef __va_copy
30
    #define va_copy(dest, src) __va_copy(dest, src)
31
  #else
32
    #define va_copy(dest, src) memcpy(&(dest), &(src), sizeof(va_list))
33
  #endif
34
#endif
35
36
/************************************************************************
37
 *                                                                      *
38
 *                Commodity functions to handle xmlChars                *
39
 *                                                                      *
40
 ************************************************************************/
41
42
/**
43
 * a strndup for array of xmlChar's
44
 *
45
 * @param cur  the input xmlChar *
46
 * @param len  the len of `cur`
47
 * @returns a new xmlChar * or NULL
48
 */
49
xmlChar *
50
170M
xmlStrndup(const xmlChar *cur, int len) {
51
170M
    xmlChar *ret;
52
53
170M
    if ((cur == NULL) || (len < 0)) return(NULL);
54
170M
    ret = xmlMalloc((size_t) len + 1);
55
170M
    if (ret == NULL) {
56
0
        return(NULL);
57
0
    }
58
170M
    memcpy(ret, cur, len);
59
170M
    ret[len] = 0;
60
170M
    return(ret);
61
170M
}
62
63
/**
64
 * a strdup for array of xmlChar's. Since they are supposed to be
65
 * encoded in UTF-8 or an encoding with 8bit based chars, we assume
66
 * a termination mark of '0'.
67
 *
68
 * @param cur  the input xmlChar *
69
 * @returns a new xmlChar * or NULL
70
 */
71
xmlChar *
72
110M
xmlStrdup(const xmlChar *cur) {
73
110M
    const xmlChar *p = cur;
74
75
110M
    if (cur == NULL) return(NULL);
76
5.58G
    while (*p != 0) p++; /* non input consuming */
77
110M
    return(xmlStrndup(cur, p - cur));
78
110M
}
79
80
/**
81
 * a strndup for char's to xmlChar's
82
 *
83
 * @param cur  the input char *
84
 * @param len  the len of `cur`
85
 * @returns a new xmlChar * or NULL
86
 */
87
88
xmlChar *
89
434k
xmlCharStrndup(const char *cur, int len) {
90
434k
    int i;
91
434k
    xmlChar *ret;
92
93
434k
    if ((cur == NULL) || (len < 0)) return(NULL);
94
434k
    ret = xmlMalloc((size_t) len + 1);
95
434k
    if (ret == NULL) {
96
0
        return(NULL);
97
0
    }
98
2.08M
    for (i = 0;i < len;i++) {
99
        /* Explicit sign change */
100
1.64M
        ret[i] = (xmlChar) cur[i];
101
1.64M
        if (ret[i] == 0) return(ret);
102
1.64M
    }
103
434k
    ret[len] = 0;
104
434k
    return(ret);
105
434k
}
106
107
/**
108
 * a strdup for char's to xmlChar's
109
 *
110
 * @param cur  the input char *
111
 * @returns a new xmlChar * or NULL
112
 */
113
114
xmlChar *
115
434k
xmlCharStrdup(const char *cur) {
116
434k
    const char *p = cur;
117
118
434k
    if (cur == NULL) return(NULL);
119
2.08M
    while (*p != '\0') p++; /* non input consuming */
120
434k
    return(xmlCharStrndup(cur, p - cur));
121
434k
}
122
123
/**
124
 * a strcmp for xmlChar's
125
 *
126
 * @param str1  the first xmlChar *
127
 * @param str2  the second xmlChar *
128
 * @returns the integer result of the comparison
129
 */
130
131
int
132
1.45M
xmlStrcmp(const xmlChar *str1, const xmlChar *str2) {
133
1.45M
    if (str1 == str2) return(0);
134
1.45M
    if (str1 == NULL) return(-1);
135
1.43M
    if (str2 == NULL) return(1);
136
1.42M
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
137
1.42M
    return(strcmp((const char *)str1, (const char *)str2));
138
#else
139
    do {
140
        int tmp = *str1++ - *str2;
141
        if (tmp != 0) return(tmp);
142
    } while (*str2++ != 0);
143
    return 0;
144
#endif
145
1.43M
}
146
147
/**
148
 * Check if both strings are equal of have same content.
149
 * Should be a bit more readable and faster than #xmlStrcmp
150
 *
151
 * @param str1  the first xmlChar *
152
 * @param str2  the second xmlChar *
153
 * @returns 1 if they are equal, 0 if they are different
154
 */
155
156
int
157
504M
xmlStrEqual(const xmlChar *str1, const xmlChar *str2) {
158
504M
    if (str1 == str2) return(1);
159
438M
    if (str1 == NULL) return(0);
160
410M
    if (str2 == NULL) return(0);
161
404M
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
162
404M
    return(strcmp((const char *)str1, (const char *)str2) == 0);
163
#else
164
    do {
165
        if (*str1++ != *str2) return(0);
166
    } while (*str2++);
167
    return(1);
168
#endif
169
410M
}
170
171
/**
172
 * Check if a QName is Equal to a given string
173
 *
174
 * @param pref  the prefix of the QName
175
 * @param name  the localname of the QName
176
 * @param str  the second xmlChar *
177
 * @returns 1 if they are equal, 0 if they are different
178
 */
179
180
int
181
65.5k
xmlStrQEqual(const xmlChar *pref, const xmlChar *name, const xmlChar *str) {
182
65.5k
    if (pref == NULL) return(xmlStrEqual(name, str));
183
2.86k
    if (name == NULL) return(0);
184
2.86k
    if (str == NULL) return(0);
185
186
10.1k
    do {
187
10.1k
        if (*pref++ != *str) return(0);
188
10.1k
    } while ((*str++) && (*pref));
189
2.86k
    if (*str++ != ':') return(0);
190
8.28k
    do {
191
8.28k
        if (*name++ != *str) return(0);
192
8.28k
    } while (*str++);
193
2.86k
    return(1);
194
2.86k
}
195
196
/**
197
 * a strncmp for xmlChar's
198
 *
199
 * @param str1  the first xmlChar *
200
 * @param str2  the second xmlChar *
201
 * @param len  the max comparison length
202
 * @returns the integer result of the comparison
203
 */
204
205
int
206
559M
xmlStrncmp(const xmlChar *str1, const xmlChar *str2, int len) {
207
559M
    if (len <= 0) return(0);
208
559M
    if (str1 == str2) return(0);
209
559M
    if (str1 == NULL) return(-1);
210
559M
    if (str2 == NULL) return(1);
211
559M
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
212
559M
    return(strncmp((const char *)str1, (const char *)str2, len));
213
#else
214
    do {
215
        int tmp = *str1++ - *str2;
216
        if (tmp != 0 || --len == 0) return(tmp);
217
    } while (*str2++ != 0);
218
    return 0;
219
#endif
220
559M
}
221
222
static const xmlChar casemap[256] = {
223
    0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
224
    0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
225
    0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
226
    0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
227
    0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
228
    0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
229
    0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
230
    0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
231
    0x40,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
232
    0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
233
    0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
234
    0x78,0x79,0x7A,0x7B,0x5C,0x5D,0x5E,0x5F,
235
    0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
236
    0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
237
    0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
238
    0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
239
    0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,
240
    0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,
241
    0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,
242
    0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,
243
    0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,
244
    0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,
245
    0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,
246
    0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF,
247
    0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,
248
    0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,
249
    0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,
250
    0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF,
251
    0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,
252
    0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,
253
    0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,
254
    0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF
255
};
256
257
/**
258
 * a strcasecmp for xmlChar's
259
 *
260
 * @param str1  the first xmlChar *
261
 * @param str2  the second xmlChar *
262
 * @returns the integer result of the comparison
263
 */
264
265
int
266
621k
xmlStrcasecmp(const xmlChar *str1, const xmlChar *str2) {
267
621k
    register int tmp;
268
269
621k
    if (str1 == str2) return(0);
270
621k
    if (str1 == NULL) return(-1);
271
621k
    if (str2 == NULL) return(1);
272
1.11M
    do {
273
1.11M
        tmp = casemap[*str1++] - casemap[*str2];
274
1.11M
        if (tmp != 0) return(tmp);
275
1.11M
    } while (*str2++ != 0);
276
59.1k
    return 0;
277
621k
}
278
279
/**
280
 * a strncasecmp for xmlChar's
281
 *
282
 * @param str1  the first xmlChar *
283
 * @param str2  the second xmlChar *
284
 * @param len  the max comparison length
285
 * @returns the integer result of the comparison
286
 */
287
288
int
289
0
xmlStrncasecmp(const xmlChar *str1, const xmlChar *str2, int len) {
290
0
    register int tmp;
291
292
0
    if (len <= 0) return(0);
293
0
    if (str1 == str2) return(0);
294
0
    if (str1 == NULL) return(-1);
295
0
    if (str2 == NULL) return(1);
296
0
    do {
297
0
        tmp = casemap[*str1++] - casemap[*str2];
298
0
        if (tmp != 0 || --len == 0) return(tmp);
299
0
    } while (*str2++ != 0);
300
0
    return 0;
301
0
}
302
303
/**
304
 * a strchr for xmlChar's
305
 *
306
 * @param str  the xmlChar * array
307
 * @param val  the xmlChar to search
308
 * @returns the xmlChar * for the first occurrence or NULL.
309
 */
310
311
const xmlChar *
312
3.67M
xmlStrchr(const xmlChar *str, xmlChar val) {
313
3.67M
    if (str == NULL) return(NULL);
314
104M
    while (*str != 0) { /* non input consuming */
315
101M
        if (*str == val) return((xmlChar *) str);
316
101M
        str++;
317
101M
    }
318
3.51M
    return(NULL);
319
3.67M
}
320
321
/**
322
 * a strstr for xmlChar's
323
 *
324
 * @param str  the xmlChar * array (haystack)
325
 * @param val  the xmlChar to search (needle)
326
 * @returns the xmlChar * for the first occurrence or NULL.
327
 */
328
329
const xmlChar *
330
565k
xmlStrstr(const xmlChar *str, const xmlChar *val) {
331
565k
    int n;
332
333
565k
    if (str == NULL) return(NULL);
334
565k
    if (val == NULL) return(NULL);
335
565k
    n = xmlStrlen(val);
336
337
565k
    if (n == 0) return(str);
338
11.1M
    while (*str != 0) { /* non input consuming */
339
10.5M
        if (*str == *val) {
340
42.4k
            if (!xmlStrncmp(str, val, n)) return((const xmlChar *) str);
341
42.4k
        }
342
10.5M
        str++;
343
10.5M
    }
344
552k
    return(NULL);
345
563k
}
346
347
/**
348
 * a case-ignoring strstr for xmlChar's
349
 *
350
 * @param str  the xmlChar * array (haystack)
351
 * @param val  the xmlChar to search (needle)
352
 * @returns the xmlChar * for the first occurrence or NULL.
353
 */
354
355
const xmlChar *
356
0
xmlStrcasestr(const xmlChar *str, const xmlChar *val) {
357
0
    int n;
358
359
0
    if (str == NULL) return(NULL);
360
0
    if (val == NULL) return(NULL);
361
0
    n = xmlStrlen(val);
362
363
0
    if (n == 0) return(str);
364
0
    while (*str != 0) { /* non input consuming */
365
0
        if (casemap[*str] == casemap[*val])
366
0
            if (!xmlStrncasecmp(str, val, n)) return(str);
367
0
        str++;
368
0
    }
369
0
    return(NULL);
370
0
}
371
372
/**
373
 * Extract a substring of a given string
374
 *
375
 * @param str  the xmlChar * array (haystack)
376
 * @param start  the index of the first char (zero based)
377
 * @param len  the length of the substring
378
 * @returns the xmlChar * for the first occurrence or NULL.
379
 */
380
381
xmlChar *
382
0
xmlStrsub(const xmlChar *str, int start, int len) {
383
0
    int i;
384
385
0
    if (str == NULL) return(NULL);
386
0
    if (start < 0) return(NULL);
387
0
    if (len < 0) return(NULL);
388
389
0
    for (i = 0;i < start;i++) {
390
0
        if (*str == 0) return(NULL);
391
0
        str++;
392
0
    }
393
0
    if (*str == 0) return(NULL);
394
0
    return(xmlStrndup(str, len));
395
0
}
396
397
/**
398
 * length of a xmlChar's string
399
 *
400
 * @param str  the xmlChar * array
401
 * @returns the number of xmlChar contained in the ARRAY.
402
 */
403
404
int
405
264M
xmlStrlen(const xmlChar *str) {
406
264M
    size_t len = str ? strlen((const char *)str) : 0;
407
264M
    return(len > INT_MAX ? 0 : len);
408
264M
}
409
410
/**
411
 * a strncat for array of xmlChar's, it will extend `cur` with the len
412
 * first bytes of `add`. Note that if `len` < 0 then this is an API error
413
 * and NULL will be returned.
414
 *
415
 * @param cur  the original xmlChar * array
416
 * @param add  the xmlChar * array added
417
 * @param len  the length of `add`
418
 * @returns a new xmlChar *, the original `cur` is reallocated and should
419
 * not be freed.
420
 */
421
422
xmlChar *
423
1.20M
xmlStrncat(xmlChar *cur, const xmlChar *add, int len) {
424
1.20M
    int size;
425
1.20M
    xmlChar *ret;
426
427
1.20M
    if ((add == NULL) || (len == 0))
428
11.7k
        return(cur);
429
430
1.18M
    if (len < 0) {
431
0
        if (cur != NULL)
432
0
            xmlFree(cur);
433
0
        return(NULL);
434
0
    }
435
436
1.18M
    if (cur == NULL)
437
15.4k
        return(xmlStrndup(add, len));
438
439
1.17M
    size = xmlStrlen(cur);
440
1.17M
    if ((size < 0) || (size > INT_MAX - len)) {
441
0
        xmlFree(cur);
442
0
        return(NULL);
443
0
    }
444
1.17M
    ret = (xmlChar *) xmlRealloc(cur, (size_t) size + len + 1);
445
1.17M
    if (ret == NULL) {
446
0
        xmlFree(cur);
447
0
        return(NULL);
448
0
    }
449
1.17M
    memcpy(&ret[size], add, len);
450
1.17M
    ret[size + len] = 0;
451
1.17M
    return(ret);
452
1.17M
}
453
454
/**
455
 * same as #xmlStrncat, but creates a new string.  The original
456
 * two strings are not freed. If `len` is < 0 then the length
457
 * will be calculated automatically.
458
 *
459
 * @param str1  first xmlChar string
460
 * @param str2  second xmlChar string
461
 * @param len  the len of `str2` or < 0
462
 * @returns a new xmlChar * or NULL
463
 */
464
xmlChar *
465
18.8k
xmlStrncatNew(const xmlChar *str1, const xmlChar *str2, int len) {
466
18.8k
    int size;
467
18.8k
    xmlChar *ret;
468
469
18.8k
    if (len < 0) {
470
9.45k
        len = xmlStrlen(str2);
471
9.45k
        if (len < 0)
472
0
            return(NULL);
473
9.45k
    }
474
18.8k
    if (str1 == NULL)
475
0
        return(xmlStrndup(str2, len));
476
18.8k
    if ((str2 == NULL) || (len == 0))
477
721
        return(xmlStrdup(str1));
478
479
18.1k
    size = xmlStrlen(str1);
480
18.1k
    if ((size < 0) || (size > INT_MAX - len))
481
0
        return(NULL);
482
18.1k
    ret = (xmlChar *) xmlMalloc((size_t) size + len + 1);
483
18.1k
    if (ret == NULL)
484
0
        return(NULL);
485
18.1k
    memcpy(ret, str1, size);
486
18.1k
    memcpy(&ret[size], str2, len);
487
18.1k
    ret[size + len] = 0;
488
18.1k
    return(ret);
489
18.1k
}
490
491
/**
492
 * a strcat for array of xmlChar's. Since they are supposed to be
493
 * encoded in UTF-8 or an encoding with 8bit based chars, we assume
494
 * a termination mark of '0'.
495
 *
496
 * @param cur  the original xmlChar * array
497
 * @param add  the xmlChar * array added
498
 * @returns a new xmlChar * containing the concatenated string. The original
499
 * `cur` is reallocated and should not be freed.
500
 */
501
xmlChar *
502
2.68k
xmlStrcat(xmlChar *cur, const xmlChar *add) {
503
2.68k
    const xmlChar *p = add;
504
505
2.68k
    if (add == NULL) return(cur);
506
2.68k
    if (cur == NULL)
507
1.55k
        return(xmlStrdup(add));
508
509
287k
    while (*p != 0) p++; /* non input consuming */
510
1.12k
    return(xmlStrncat(cur, add, p - add));
511
2.68k
}
512
513
/**
514
 * Formats `msg` and places result into `buf`.
515
 *
516
 * @param buf  the result buffer.
517
 * @param len  the result buffer length.
518
 * @param msg  the message with printf formatting.
519
 * @param ...   extra parameters for the message.
520
 * @returns the number of characters written to `buf` or -1 if an error occurs.
521
 */
522
int
523
40.8k
xmlStrPrintf(xmlChar *buf, int len, const char *msg, ...) {
524
40.8k
    va_list args;
525
40.8k
    int ret;
526
527
40.8k
    if((buf == NULL) || (msg == NULL) || (len <= 0)) {
528
0
        return(-1);
529
0
    }
530
531
40.8k
    va_start(args, msg);
532
40.8k
    ret = vsnprintf((char *) buf, len, (const char *) msg, args);
533
40.8k
    va_end(args);
534
40.8k
    buf[len - 1] = 0; /* be safe ! */
535
536
40.8k
    return(ret);
537
40.8k
}
538
539
/**
540
 * Formats `msg` and places result into `buf`.
541
 *
542
 * @param buf  the result buffer.
543
 * @param len  the result buffer length.
544
 * @param msg  the message with printf formatting.
545
 * @param ap  extra parameters for the message.
546
 * @returns the number of characters written to `buf` or -1 if an error occurs.
547
 */
548
int
549
875k
xmlStrVPrintf(xmlChar *buf, int len, const char *msg, va_list ap) {
550
875k
    int ret;
551
552
875k
    if((buf == NULL) || (msg == NULL) || (len <= 0)) {
553
0
        return(-1);
554
0
    }
555
556
875k
    ret = vsnprintf((char *) buf, len, (const char *) msg, ap);
557
875k
    buf[len - 1] = 0; /* be safe ! */
558
559
875k
    return(ret);
560
875k
}
561
562
/**
563
 * Creates a newly allocated string according to format.
564
 *
565
 * @param out  pointer to the resulting string
566
 * @param maxSize  maximum size of the output buffer
567
 * @param msg  printf format string
568
 * @param ap  arguments for format string
569
 * @returns 0 on success, 1 if the result was truncated or on other
570
 * errors, -1 if a memory allocation failed.
571
 */
572
int
573
4.80M
xmlStrVASPrintf(xmlChar **out, int maxSize, const char *msg, va_list ap) {
574
4.80M
    char empty[1];
575
4.80M
    va_list copy;
576
4.80M
    xmlChar *buf;
577
4.80M
    int res, size;
578
4.80M
    int truncated = 0;
579
580
4.80M
    if (out == NULL)
581
0
        return(1);
582
4.80M
    *out = NULL;
583
4.80M
    if (msg == NULL)
584
0
        return(1);
585
4.80M
    if (maxSize < 32)
586
0
        maxSize = 32;
587
588
4.80M
    va_copy(copy, ap);
589
4.80M
    res = vsnprintf(empty, 1, msg, copy);
590
4.80M
    va_end(copy);
591
592
4.80M
    if (res > 0) {
593
        /* snprintf seems to work according to C99. */
594
595
4.80M
        if (res < maxSize) {
596
4.79M
            size = res + 1;
597
4.79M
        } else {
598
2.48k
            size = maxSize;
599
2.48k
            truncated = 1;
600
2.48k
        }
601
4.80M
        buf = xmlMalloc(size);
602
4.80M
        if (buf == NULL)
603
0
            return(-1);
604
4.80M
        if (vsnprintf((char *) buf, size, msg, ap) < 0) {
605
0
            xmlFree(buf);
606
0
            return(1);
607
0
        }
608
4.80M
    } else {
609
        /*
610
         * Unfortunately, older snprintf implementations don't follow the
611
         * C99 spec. If the output exceeds the size of the buffer, they can
612
         * return -1, 0 or the number of characters written instead of the
613
         * needed size. Older MSCVRT also won't write a terminating null
614
         * byte if the buffer is too small.
615
         *
616
         * If the value returned is non-negative and strictly less than
617
         * the buffer size (without terminating null), the result should
618
         * have been written completely, so we double the buffer size
619
         * until this condition is true. This assumes that snprintf will
620
         * eventually return a non-negative value. Otherwise, we will
621
         * allocate more and more memory until we run out.
622
         *
623
         * Note that this code path is also executed on conforming
624
         * platforms if the output is the empty string.
625
         */
626
627
0
        buf = NULL;
628
0
        size = 32;
629
0
        while (1) {
630
0
            buf = xmlMalloc(size);
631
0
            if (buf == NULL)
632
0
                return(-1);
633
634
0
            va_copy(copy, ap);
635
0
            res = vsnprintf((char *) buf, size, msg, copy);
636
0
            va_end(copy);
637
0
            if ((res >= 0) && (res < size - 1))
638
0
                break;
639
640
0
            if (size >= maxSize) {
641
0
                truncated = 1;
642
0
                break;
643
0
            }
644
645
0
            xmlFree(buf);
646
647
0
            if (size > maxSize / 2)
648
0
                size = maxSize;
649
0
            else
650
0
                size *= 2;
651
0
        }
652
0
    }
653
654
    /*
655
     * If the output was truncated, make sure that the buffer doesn't
656
     * end with a truncated UTF-8 sequence.
657
     */
658
4.80M
    if (truncated != 0) {
659
2.48k
        int i = size - 1;
660
661
4.82k
        while (i > 0) {
662
            /* Break after ASCII */
663
4.82k
            if (buf[i-1] < 0x80)
664
904
                break;
665
3.91k
            i -= 1;
666
            /* Break before non-ASCII */
667
3.91k
            if (buf[i] >= 0xc0)
668
1.57k
                break;
669
3.91k
        }
670
671
2.48k
        buf[i] = 0;
672
2.48k
    }
673
674
4.80M
    *out = (xmlChar *) buf;
675
4.80M
    return(truncated);
676
4.80M
}
677
678
/**
679
 * See xmlStrVASPrintf.
680
 *
681
 * @param out  pointer to the resulting string
682
 * @param maxSize  maximum size of the output buffer
683
 * @param msg  printf format string
684
 * @param ...  arguments for format string
685
 * @returns 0 on success, 1 if the result was truncated or on other
686
 * errors, -1 if a memory allocation failed.
687
 */
688
int
689
0
xmlStrASPrintf(xmlChar **out, int maxSize, const char *msg, ...) {
690
0
    va_list ap;
691
0
    int ret;
692
693
0
    va_start(ap, msg);
694
0
    ret = xmlStrVASPrintf(out, maxSize, msg, ap);
695
0
    va_end(ap);
696
697
0
    return(ret);
698
0
}
699
700
/************************************************************************
701
 *                                                                      *
702
 *              Generic UTF8 handling routines                          *
703
 *                                                                      *
704
 * From rfc2044: encoding of the Unicode values on UTF-8:               *
705
 *                                                                      *
706
 * UCS-4 range (hex.)           UTF-8 octet sequence (binary)           *
707
 * 0000 0000-0000 007F   0xxxxxxx                                       *
708
 * 0000 0080-0000 07FF   110xxxxx 10xxxxxx                              *
709
 * 0000 0800-0000 FFFF   1110xxxx 10xxxxxx 10xxxxxx                     *
710
 *                                                                      *
711
 * I hope we won't use values > 0xFFFF anytime soon !                   *
712
 *                                                                      *
713
 ************************************************************************/
714
715
716
/**
717
 * calculates the internal size of a UTF8 character
718
 *
719
 * @param utf  pointer to the UTF8 character
720
 * @returns the numbers of bytes in the character, -1 on format error
721
 */
722
int
723
0
xmlUTF8Size(const xmlChar *utf) {
724
0
    xmlChar mask;
725
0
    int len;
726
727
0
    if (utf == NULL)
728
0
        return -1;
729
0
    if (*utf < 0x80)
730
0
        return 1;
731
    /* check valid UTF8 character */
732
0
    if (!(*utf & 0x40))
733
0
        return -1;
734
    /* determine number of bytes in char */
735
0
    len = 2;
736
0
    for (mask=0x20; mask != 0; mask>>=1) {
737
0
        if (!(*utf & mask))
738
0
            return len;
739
0
        len++;
740
0
    }
741
0
    return -1;
742
0
}
743
744
/**
745
 * compares the two UCS4 values
746
 *
747
 * @param utf1  pointer to first UTF8 char
748
 * @param utf2  pointer to second UTF8 char
749
 * @returns result of the compare as with #xmlStrncmp
750
 */
751
int
752
0
xmlUTF8Charcmp(const xmlChar *utf1, const xmlChar *utf2) {
753
754
0
    if (utf1 == NULL ) {
755
0
        if (utf2 == NULL)
756
0
            return 0;
757
0
        return -1;
758
0
    }
759
0
    return xmlStrncmp(utf1, utf2, xmlUTF8Size(utf1));
760
0
}
761
762
/**
763
 * compute the length of an UTF8 string, it doesn't do a full UTF8
764
 * checking of the content of the string.
765
 *
766
 * @param utf  a sequence of UTF-8 encoded bytes
767
 * @returns the number of characters in the string or -1 in case of error
768
 */
769
int
770
10.8k
xmlUTF8Strlen(const xmlChar *utf) {
771
10.8k
    size_t ret = 0;
772
773
10.8k
    if (utf == NULL)
774
0
        return(-1);
775
776
33.5M
    while (*utf != 0) {
777
33.5M
        if (utf[0] & 0x80) {
778
67.2k
            if ((utf[1] & 0xc0) != 0x80)
779
0
                return(-1);
780
67.2k
            if ((utf[0] & 0xe0) == 0xe0) {
781
44.6k
                if ((utf[2] & 0xc0) != 0x80)
782
0
                    return(-1);
783
44.6k
                if ((utf[0] & 0xf0) == 0xf0) {
784
43.8k
                    if ((utf[0] & 0xf8) != 0xf0 || (utf[3] & 0xc0) != 0x80)
785
0
                        return(-1);
786
43.8k
                    utf += 4;
787
43.8k
                } else {
788
802
                    utf += 3;
789
802
                }
790
44.6k
            } else {
791
22.5k
                utf += 2;
792
22.5k
            }
793
33.4M
        } else {
794
33.4M
            utf++;
795
33.4M
        }
796
33.5M
        ret++;
797
33.5M
    }
798
10.8k
    return(ret > INT_MAX ? 0 : ret);
799
10.8k
}
800
801
/**
802
 * Read the first UTF8 character from `utf`
803
 *
804
 * @param utf  a sequence of UTF-8 encoded bytes
805
 * @param len  a pointer to the minimum number of bytes present in
806
 *        the sequence.  This is used to assure the next character
807
 *        is completely contained within the sequence.
808
 * @returns the char value or -1 in case of error, and sets *len to
809
 *        the actual number of bytes consumed (0 in case of error)
810
 */
811
int
812
140M
xmlGetUTF8Char(const unsigned char *utf, int *len) {
813
140M
    unsigned int c;
814
815
140M
    if (utf == NULL)
816
0
        goto error;
817
140M
    if (len == NULL)
818
0
        goto error;
819
820
140M
    c = utf[0];
821
140M
    if (c < 0x80) {
822
129M
        if (*len < 1)
823
0
            goto error;
824
        /* 1-byte code */
825
129M
        *len = 1;
826
129M
    } else {
827
10.6M
        if ((*len < 2) || ((utf[1] & 0xc0) != 0x80))
828
396k
            goto error;
829
10.2M
        if (c < 0xe0) {
830
5.83M
            if (c < 0xc2)
831
51.7k
                goto error;
832
            /* 2-byte code */
833
5.78M
            *len = 2;
834
5.78M
            c = (c & 0x1f) << 6;
835
5.78M
            c |= utf[1] & 0x3f;
836
5.78M
        } else {
837
4.37M
            if ((*len < 3) || ((utf[2] & 0xc0) != 0x80))
838
9.94k
                goto error;
839
4.36M
            if (c < 0xf0) {
840
                /* 3-byte code */
841
4.28M
                *len = 3;
842
4.28M
                c = (c & 0xf) << 12;
843
4.28M
                c |= (utf[1] & 0x3f) << 6;
844
4.28M
                c |= utf[2] & 0x3f;
845
4.28M
                if ((c < 0x800) || ((c >= 0xd800) && (c < 0xe000)))
846
3.98k
                    goto error;
847
4.28M
            } else {
848
79.6k
                if ((*len < 4) || ((utf[3] & 0xc0) != 0x80))
849
3.70k
                    goto error;
850
                /*
851
                 * The lead byte of a 4-byte sequence is 11110xxx and its
852
                 * payload is the last 3 bits. Lead bytes from 0xF8 aren't
853
                 * valid in UTF-8 (RFC 3629) because they would start a
854
                 * sequence of five or more bytes, but masking with 0x7
855
                 * reads them as 0xF0 to 0xF7, so F8 90 80 80 would decode
856
                 * as U+10000. Reject them before decoding. Lead bytes 0xF5
857
                 * to 0xF7 give values from 0x140000, which the range check
858
                 * below rejects.
859
                 */
860
75.9k
                if (c >= 0xf8)
861
3.62k
                    goto error;
862
72.3k
                *len = 4;
863
                /* 4-byte code */
864
72.3k
                c = (c & 0x7) << 18;
865
72.3k
                c |= (utf[1] & 0x3f) << 12;
866
72.3k
                c |= (utf[2] & 0x3f) << 6;
867
72.3k
                c |= utf[3] & 0x3f;
868
72.3k
                if ((c < 0x10000) || (c >= 0x110000))
869
2.91k
                    goto error;
870
72.3k
            }
871
4.36M
        }
872
10.2M
    }
873
140M
    return(c);
874
875
472k
error:
876
472k
    if (len != NULL)
877
472k
  *len = 0;
878
472k
    return(-1);
879
140M
}
880
881
/**
882
 * Checks `utf` for being valid UTF-8. `utf` is assumed to be
883
 * null-terminated. This function is not super-strict, as it will
884
 * allow longer UTF-8 sequences than necessary. Note that Java is
885
 * capable of producing these sequences if provoked. Also note, this
886
 * routine checks for the 4-byte maximum size, but does not check for
887
 * 0x10ffff maximum value.
888
 *
889
 * @param utf  Pointer to putative UTF-8 encoded string.
890
 * @returns value: true if `utf` is valid.
891
 **/
892
int
893
xmlCheckUTF8(const unsigned char *utf)
894
0
{
895
0
    int ix;
896
0
    unsigned char c;
897
898
0
    if (utf == NULL)
899
0
        return(0);
900
    /*
901
     * utf is a string of 1, 2, 3 or 4 bytes.  The valid strings
902
     * are as follows (in "bit format"):
903
     *    0xxxxxxx                                      valid 1-byte
904
     *    110xxxxx 10xxxxxx                             valid 2-byte
905
     *    1110xxxx 10xxxxxx 10xxxxxx                    valid 3-byte
906
     *    11110xxx 10xxxxxx 10xxxxxx 10xxxxxx           valid 4-byte
907
     */
908
0
    while ((c = utf[0])) {      /* string is 0-terminated */
909
0
        ix = 0;
910
0
        if ((c & 0x80) == 0x00) { /* 1-byte code, starts with 10 */
911
0
            ix = 1;
912
0
  } else if ((c & 0xe0) == 0xc0) {/* 2-byte code, starts with 110 */
913
0
      if ((utf[1] & 0xc0 ) != 0x80)
914
0
          return 0;
915
0
      ix = 2;
916
0
  } else if ((c & 0xf0) == 0xe0) {/* 3-byte code, starts with 1110 */
917
0
      if (((utf[1] & 0xc0) != 0x80) ||
918
0
          ((utf[2] & 0xc0) != 0x80))
919
0
        return 0;
920
0
      ix = 3;
921
0
  } else if ((c & 0xf8) == 0xf0) {/* 4-byte code, starts with 11110 */
922
0
      if (((utf[1] & 0xc0) != 0x80) ||
923
0
          ((utf[2] & 0xc0) != 0x80) ||
924
0
    ((utf[3] & 0xc0) != 0x80))
925
0
        return 0;
926
0
      ix = 4;
927
0
  } else       /* unknown encoding */
928
0
      return 0;
929
0
        utf += ix;
930
0
      }
931
0
      return(1);
932
0
}
933
934
/**
935
 * storage size of an UTF8 string
936
 * the behaviour is not guaranteed if the input string is not UTF-8
937
 *
938
 * @param utf  a sequence of UTF-8 encoded bytes
939
 * @param len  the number of characters in the array
940
 * @returns the storage size of
941
 * the first 'len' characters of ARRAY
942
 */
943
944
int
945
6.94M
xmlUTF8Strsize(const xmlChar *utf, int len) {
946
6.94M
    const xmlChar *ptr=utf;
947
6.94M
    int ch;
948
6.94M
    size_t ret;
949
950
6.94M
    if (utf == NULL)
951
0
        return(0);
952
953
6.94M
    if (len <= 0)
954
0
        return(0);
955
956
13.9M
    while ( len-- > 0) {
957
7.05M
        if ( !*ptr )
958
5.97k
            break;
959
7.05M
        ch = *ptr++;
960
7.05M
        if ((ch & 0x80))
961
11.7k
            while ((ch<<=1) & 0x80 ) {
962
7.63k
    if (*ptr == 0) break;
963
7.63k
                ptr++;
964
7.63k
      }
965
7.05M
    }
966
6.94M
    ret = ptr - utf;
967
6.94M
    return (ret > INT_MAX ? 0 : ret);
968
6.94M
}
969
970
971
/**
972
 * a strndup for array of UTF8's
973
 *
974
 * @param utf  the input UTF8 *
975
 * @param len  the len of `utf` (in chars)
976
 * @returns a new UTF8 * or NULL
977
 */
978
xmlChar *
979
7.32k
xmlUTF8Strndup(const xmlChar *utf, int len) {
980
7.32k
    xmlChar *ret;
981
7.32k
    int i;
982
983
7.32k
    if ((utf == NULL) || (len < 0)) return(NULL);
984
7.32k
    i = xmlUTF8Strsize(utf, len);
985
7.32k
    ret = xmlMalloc((size_t) i + 1);
986
7.32k
    if (ret == NULL) {
987
0
        return(NULL);
988
0
    }
989
7.32k
    memcpy(ret, utf, i);
990
7.32k
    ret[i] = 0;
991
7.32k
    return(ret);
992
7.32k
}
993
994
/**
995
 * a function to provide the equivalent of fetching a
996
 * character from a string array
997
 *
998
 * @param utf  the input UTF8 *
999
 * @param pos  the position of the desired UTF8 char (in chars)
1000
 * @returns a pointer to the UTF8 character or NULL
1001
 */
1002
const xmlChar *
1003
2.75M
xmlUTF8Strpos(const xmlChar *utf, int pos) {
1004
2.75M
    int ch;
1005
1006
2.75M
    if (utf == NULL) return(NULL);
1007
2.75M
    if (pos < 0)
1008
0
        return(NULL);
1009
355M
    while (pos--) {
1010
352M
        ch = *utf++;
1011
352M
        if (ch == 0)
1012
0
            return(NULL);
1013
352M
        if ( ch & 0x80 ) {
1014
            /* if not simple ascii, verify proper format */
1015
104k
            if ( (ch & 0xc0) != 0xc0 )
1016
0
                return(NULL);
1017
            /* then skip over remaining bytes for this char */
1018
225k
            while ( (ch <<= 1) & 0x80 )
1019
121k
                if ( (*utf++ & 0xc0) != 0x80 )
1020
0
                    return(NULL);
1021
104k
        }
1022
352M
    }
1023
2.75M
    return((xmlChar *)utf);
1024
2.75M
}
1025
1026
/**
1027
 * a function to provide the relative location of a UTF8 char
1028
 *
1029
 * @param utf  the input UTF8 *
1030
 * @param utfchar  the UTF8 character to be found
1031
 * @returns the relative character position of the desired char
1032
 * or -1 if not found
1033
 */
1034
int
1035
4.15M
xmlUTF8Strloc(const xmlChar *utf, const xmlChar *utfchar) {
1036
4.15M
    size_t i;
1037
4.15M
    int size;
1038
4.15M
    int ch;
1039
1040
4.15M
    if (utf==NULL || utfchar==NULL) return -1;
1041
4.15M
    size = xmlUTF8Strsize(utfchar, 1);
1042
559M
        for(i=0; (ch=*utf) != 0; i++) {
1043
559M
            if (xmlStrncmp(utf, utfchar, size)==0)
1044
4.13M
                return(i > INT_MAX ? 0 : i);
1045
555M
            utf++;
1046
555M
            if ( ch & 0x80 ) {
1047
                /* if not simple ascii, verify proper format */
1048
111k
                if ( (ch & 0xc0) != 0xc0 )
1049
0
                    return(-1);
1050
                /* then skip over remaining bytes for this char */
1051
249k
                while ( (ch <<= 1) & 0x80 )
1052
137k
                    if ( (*utf++ & 0xc0) != 0x80 )
1053
0
                        return(-1);
1054
111k
            }
1055
555M
        }
1056
1057
24.2k
    return(-1);
1058
4.15M
}
1059
/**
1060
 * Create a substring from a given UTF-8 string
1061
 * Note:  positions are given in units of UTF-8 chars
1062
 *
1063
 * @param utf  a sequence of UTF-8 encoded bytes
1064
 * @param start  relative pos of first char
1065
 * @param len  total number to copy
1066
 * @returns a pointer to a newly created string or NULL if the
1067
 * start index is out of bounds or a memory allocation failed.
1068
 * If len is too large, the result is truncated.
1069
 */
1070
1071
xmlChar *
1072
7.32k
xmlUTF8Strsub(const xmlChar *utf, int start, int len) {
1073
7.32k
    int i;
1074
7.32k
    int ch;
1075
1076
7.32k
    if (utf == NULL) return(NULL);
1077
7.32k
    if (start < 0) return(NULL);
1078
7.32k
    if (len < 0) return(NULL);
1079
1080
    /*
1081
     * Skip over any leading chars
1082
     */
1083
30.8M
    for (i = 0; i < start; i++) {
1084
30.8M
        ch = *utf++;
1085
30.8M
        if (ch == 0)
1086
0
            return(NULL);
1087
        /* skip over remaining bytes for this char */
1088
30.8M
        if (ch & 0x80) {
1089
64.9k
            ch <<= 1;
1090
216k
            while (ch & 0x80) {
1091
151k
                if (*utf++ == 0)
1092
0
                    return(NULL);
1093
151k
                ch <<= 1;
1094
151k
            }
1095
64.9k
        }
1096
30.8M
    }
1097
1098
7.32k
    return(xmlUTF8Strndup(utf, len));
1099
7.32k
}
1100
1101
/**
1102
 * Replaces a string with an escaped string.
1103
 *
1104
 * `msg` must be a heap-allocated buffer created by libxml2 that may be
1105
 * returned, or that may be freed and replaced.
1106
 *
1107
 * @param msg  a pointer to the string in which to escape '%' characters.
1108
 * @returns the same string with all '%' characters escaped.
1109
 */
1110
xmlChar *
1111
xmlEscapeFormatString(xmlChar **msg)
1112
0
{
1113
0
    xmlChar *msgPtr = NULL;
1114
0
    xmlChar *result = NULL;
1115
0
    xmlChar *resultPtr = NULL;
1116
0
    size_t count = 0;
1117
0
    size_t msgLen = 0;
1118
0
    size_t resultLen = 0;
1119
1120
0
    if (!msg || !*msg)
1121
0
        return(NULL);
1122
1123
0
    for (msgPtr = *msg; *msgPtr != '\0'; ++msgPtr) {
1124
0
        ++msgLen;
1125
0
        if (*msgPtr == '%')
1126
0
            ++count;
1127
0
    }
1128
1129
0
    if (count == 0)
1130
0
        return(*msg);
1131
1132
0
    if ((count > INT_MAX) || (msgLen > INT_MAX - count))
1133
0
        return(NULL);
1134
0
    resultLen = msgLen + count + 1;
1135
0
    result = xmlMalloc(resultLen);
1136
0
    if (result == NULL) {
1137
        /* Clear *msg to prevent format string vulnerabilities in
1138
           out-of-memory situations. */
1139
0
        xmlFree(*msg);
1140
0
        *msg = NULL;
1141
0
        return(NULL);
1142
0
    }
1143
1144
0
    for (msgPtr = *msg, resultPtr = result; *msgPtr != '\0'; ++msgPtr, ++resultPtr) {
1145
0
        *resultPtr = *msgPtr;
1146
0
        if (*msgPtr == '%')
1147
0
            *(++resultPtr) = '%';
1148
0
    }
1149
0
    result[resultLen - 1] = '\0';
1150
1151
0
    xmlFree(*msg);
1152
0
    *msg = result;
1153
1154
0
    return *msg;
1155
0
}
1156