ox 2.14.25 → 2.14.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -0
- data/ext/ox/base64.c +1 -1
- data/ext/ox/ox.c +3 -1
- data/ext/ox/parse.c +38 -9
- data/ext/ox/sax.c +5 -1
- data/ext/ox/sax_buf.c +2 -2
- data/lib/ox/version.rb +1 -1
- metadata +2 -3
- data/ext/ox/foo.h +0 -204
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b981c5e93cbbe907c8beb655007ebd2d3bfd740da911372ce82ea329e85773f0
|
|
4
|
+
data.tar.gz: 98935b178f268fad229368d02b2efd14529b4144aebae15b557aed80d20a5aac
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aaa1d3f2183519ef26fd82b80f1bae7bef61216e8404ae03fcc0ad7c9b8e450b2acb423cb7f113be08fa3cab8ba448cf25425e7cc12e138a63e04c93642e2523
|
|
7
|
+
data.tar.gz: b0b0e20339a346d17a3a29b57e8c1d722b5999d346d775c6f587400aa8affa30e63186a5a6109dd59091fd13ecc172ef93d69e8168bc65a2aa70bd66e76f58ec
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All changes to the Ox gem are documented here. Releases follow semantic versioning.
|
|
4
4
|
|
|
5
|
+
## [2.14.27] - 2026-06-18
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed stack overflow issue in the parser that occurred when reading
|
|
10
|
+
special character sequences.
|
|
11
|
+
|
|
12
|
+
- Set a limit of 1000 as the maximum nesting depth of elements to
|
|
13
|
+
avoid stack exhaustion.
|
|
14
|
+
|
|
15
|
+
- Added a note on the symbolize_keys option indicating it should not
|
|
16
|
+
be used on unregulated input where the symbol table could grow
|
|
17
|
+
indefinitely.
|
|
18
|
+
|
|
19
|
+
## [2.14.26] - 2026-05-09
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- Several fixes by by @albanpeignier
|
|
24
|
+
|
|
5
25
|
## [2.14.25] - 2026-04-23
|
|
6
26
|
|
|
7
27
|
### Added
|
data/ext/ox/base64.c
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
static char digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
12
12
|
|
|
13
13
|
/* invalid or terminating characters are set to 'X' or \x58 */
|
|
14
|
-
static uchar s_digits[
|
|
14
|
+
static uchar s_digits[257] = "\
|
|
15
15
|
\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\
|
|
16
16
|
\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\
|
|
17
17
|
\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x58\x3E\x58\x58\x58\x3F\
|
data/ext/ox/ox.c
CHANGED
|
@@ -274,7 +274,9 @@ static VALUE hints_to_overlay(Hints hints) {
|
|
|
274
274
|
* - _:xsd_date_ [true|false|nil] use XSD date format instead of decimal format
|
|
275
275
|
* - _:mode_ [:object|:generic|:limited|:hash|:hash_no_attrs|nil] load method to use for XML
|
|
276
276
|
* - _:effort_ [:strict|:tolerant|:auto_define] set the tolerance level for loading
|
|
277
|
-
* - _:symbolize_keys_ [true|false|nil] symbolize element attribute keys or leave as Strings
|
|
277
|
+
* - _:symbolize_keys_ [true|false|nil] symbolize element attribute keys or leave as Strings.
|
|
278
|
+
* Note that symbolized keys are more efficient but for uncontrolled input it can lead to unlimited
|
|
279
|
+
* growth of the symbol (intern) table.
|
|
278
280
|
* - _:element_key_mod_ [Proc|nil] converts element keys on parse if not nil
|
|
279
281
|
* - _:attr_key_mod_ [Proc|nil] converts attribute keys on parse if not nil
|
|
280
282
|
* - _:skip_ [:skip_none|:skip_return|:skip_white|:skip_off] determines how to handle white space in text
|
data/ext/ox/parse.c
CHANGED
|
@@ -18,11 +18,13 @@
|
|
|
18
18
|
#include "ruby.h"
|
|
19
19
|
#include "special.h"
|
|
20
20
|
|
|
21
|
+
#define MAX_ELEMENT_DEPTH 1000
|
|
22
|
+
|
|
21
23
|
static void mark_pi_cb(void *ptr);
|
|
22
24
|
static void read_instruction(PInfo pi);
|
|
23
25
|
static void read_doctype(PInfo pi);
|
|
24
26
|
static void read_comment(PInfo pi);
|
|
25
|
-
static char *read_element(PInfo pi);
|
|
27
|
+
static char *read_element(PInfo pi, int depth);
|
|
26
28
|
static void read_text(PInfo pi);
|
|
27
29
|
/*static void read_reduced_text(PInfo pi); */
|
|
28
30
|
static void read_cdata(PInfo pi);
|
|
@@ -216,7 +218,7 @@ ox_parse(char *xml, size_t len, ParseCallbacks pcb, char **endp, Options options
|
|
|
216
218
|
helper_stack_cleanup(&pi.helpers);
|
|
217
219
|
return Qnil;
|
|
218
220
|
default:
|
|
219
|
-
read_element(&pi);
|
|
221
|
+
read_element(&pi, 0);
|
|
220
222
|
body_read = 1;
|
|
221
223
|
break;
|
|
222
224
|
}
|
|
@@ -302,6 +304,11 @@ DONE:
|
|
|
302
304
|
}
|
|
303
305
|
end = pi->s;
|
|
304
306
|
next_non_white(pi);
|
|
307
|
+
if ('\0' == *pi->s) {
|
|
308
|
+
attr_stack_cleanup(&attrs);
|
|
309
|
+
set_error(&pi->err, "invalid format, processing instruction not terminated", pi->str, pi->s);
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
305
312
|
if ('=' != *pi->s++) {
|
|
306
313
|
attrs_ok = false;
|
|
307
314
|
break;
|
|
@@ -364,7 +371,8 @@ static void read_delimited(PInfo pi, char end) {
|
|
|
364
371
|
if ('"' == end || '\'' == end) {
|
|
365
372
|
for (c = *pi->s++; end != c; c = *pi->s++) {
|
|
366
373
|
if ('\0' == c) {
|
|
367
|
-
|
|
374
|
+
pi->s--;
|
|
375
|
+
set_error(&pi->err, "invalid format, doctype not terminated", pi->str, pi->s);
|
|
368
376
|
return;
|
|
369
377
|
}
|
|
370
378
|
}
|
|
@@ -375,7 +383,10 @@ static void read_delimited(PInfo pi, char end) {
|
|
|
375
383
|
return;
|
|
376
384
|
}
|
|
377
385
|
switch (c) {
|
|
378
|
-
case '\0':
|
|
386
|
+
case '\0':
|
|
387
|
+
pi->s--;
|
|
388
|
+
set_error(&pi->err, "invalid format, doctype not terminated", pi->str, pi->s);
|
|
389
|
+
return;
|
|
379
390
|
case '"': read_delimited(pi, c); break;
|
|
380
391
|
case '\'': read_delimited(pi, c); break;
|
|
381
392
|
case '[': read_delimited(pi, ']'); break;
|
|
@@ -443,7 +454,7 @@ static void read_comment(PInfo pi) {
|
|
|
443
454
|
|
|
444
455
|
// Entered after the '<' and the first character after that. Returns stat
|
|
445
456
|
// code.
|
|
446
|
-
static char *read_element(PInfo pi) {
|
|
457
|
+
static char *read_element(PInfo pi, int depth) {
|
|
447
458
|
struct _attrStack attrs;
|
|
448
459
|
const char *attr_name;
|
|
449
460
|
const char *attr_value;
|
|
@@ -455,6 +466,10 @@ static char *read_element(PInfo pi) {
|
|
|
455
466
|
int hasChildren = 0;
|
|
456
467
|
int done = 0;
|
|
457
468
|
|
|
469
|
+
if (MAX_ELEMENT_DEPTH < depth) {
|
|
470
|
+
set_error(&pi->err, "element nested too deeply, limit is 1000", pi->str, pi->s);
|
|
471
|
+
return 0;
|
|
472
|
+
}
|
|
458
473
|
attr_stack_init(&attrs);
|
|
459
474
|
if (0 == (ename = read_name_token(pi))) {
|
|
460
475
|
return 0;
|
|
@@ -535,6 +550,7 @@ static char *read_element(PInfo pi) {
|
|
|
535
550
|
break;
|
|
536
551
|
} else {
|
|
537
552
|
attr_stack_cleanup(&attrs);
|
|
553
|
+
pi->s--;
|
|
538
554
|
set_error(&pi->err, "invalid format, no attribute value", pi->str, pi->s);
|
|
539
555
|
return 0;
|
|
540
556
|
}
|
|
@@ -579,7 +595,7 @@ static char *read_element(PInfo pi) {
|
|
|
579
595
|
c = *pi->s++;
|
|
580
596
|
if ('\0' == c) {
|
|
581
597
|
attr_stack_cleanup(&attrs);
|
|
582
|
-
set_error(&pi->err, "invalid format, document not terminated", pi->str, pi->s);
|
|
598
|
+
set_error(&pi->err, "invalid format, document not terminated", pi->str, pi->s - 1);
|
|
583
599
|
return 0;
|
|
584
600
|
}
|
|
585
601
|
if ('<' == c) {
|
|
@@ -674,7 +690,7 @@ static char *read_element(PInfo pi) {
|
|
|
674
690
|
first = 0;
|
|
675
691
|
/* a child element */
|
|
676
692
|
// Child closed with mismatched name.
|
|
677
|
-
if (0 != (name = read_element(pi))) {
|
|
693
|
+
if (0 != (name = read_element(pi, depth + 1))) {
|
|
678
694
|
attr_stack_cleanup(&attrs);
|
|
679
695
|
|
|
680
696
|
if (0 ==
|
|
@@ -701,6 +717,11 @@ static char *read_element(PInfo pi) {
|
|
|
701
717
|
read_text(pi);
|
|
702
718
|
/*read_reduced_text(pi); */
|
|
703
719
|
|
|
720
|
+
if (err_has(&pi->err)) {
|
|
721
|
+
attr_stack_cleanup(&attrs);
|
|
722
|
+
return 0;
|
|
723
|
+
}
|
|
724
|
+
|
|
704
725
|
/* to exit read_text with no errors the next character must be < */
|
|
705
726
|
if ('/' == *(pi->s + 1) &&
|
|
706
727
|
0 == ((TolerantEffort == pi->options->effort) ? strncasecmp(ename, pi->s + 2, elen)
|
|
@@ -734,7 +755,10 @@ static void read_text(PInfo pi) {
|
|
|
734
755
|
done = 1;
|
|
735
756
|
pi->s--;
|
|
736
757
|
break;
|
|
737
|
-
case '\0':
|
|
758
|
+
case '\0':
|
|
759
|
+
pi->s--;
|
|
760
|
+
set_error(&pi->err, "invalid format, document not terminated", pi->str, pi->s);
|
|
761
|
+
return;
|
|
738
762
|
default:
|
|
739
763
|
if (end <= (b + (('&' == c) ? 7 : 0))) { /* extra 8 for special just in case it is sequence of bytes */
|
|
740
764
|
unsigned long size;
|
|
@@ -1027,6 +1051,10 @@ static char *read_coded_chars(PInfo pi, char *text) {
|
|
|
1027
1051
|
|
|
1028
1052
|
for (b = buf, s = pi->s; b < end; b++, s++) {
|
|
1029
1053
|
*b = *s;
|
|
1054
|
+
if ('\0' == *s) {
|
|
1055
|
+
set_error(&pi->err, "Not terminated coded char.", pi->str, pi->s);
|
|
1056
|
+
return NULL;
|
|
1057
|
+
}
|
|
1030
1058
|
if (';' == *s) {
|
|
1031
1059
|
*(b + 1) = '\0';
|
|
1032
1060
|
blen = b - buf;
|
|
@@ -1034,7 +1062,8 @@ static char *read_coded_chars(PInfo pi, char *text) {
|
|
|
1034
1062
|
break;
|
|
1035
1063
|
}
|
|
1036
1064
|
}
|
|
1037
|
-
if (b
|
|
1065
|
+
if (b >= end) {
|
|
1066
|
+
// No terminating ; found in the first 31 bytes after an &.
|
|
1038
1067
|
*text++ = '&';
|
|
1039
1068
|
} else if ('#' == *buf) {
|
|
1040
1069
|
uint64_t u = 0;
|
data/ext/ox/sax.c
CHANGED
|
@@ -1496,7 +1496,11 @@ int ox_sax_collapse_special(SaxDrive dr, char *str, long pos, long line, long co
|
|
|
1496
1496
|
c = '&';
|
|
1497
1497
|
} else {
|
|
1498
1498
|
b = bn;
|
|
1499
|
-
|
|
1499
|
+
if ('\0' != *s2) {
|
|
1500
|
+
s = s2 + 1;
|
|
1501
|
+
} else {
|
|
1502
|
+
s = s2;
|
|
1503
|
+
}
|
|
1500
1504
|
continue;
|
|
1501
1505
|
}
|
|
1502
1506
|
}
|
data/ext/ox/sax_buf.c
CHANGED
|
@@ -69,8 +69,8 @@ void ox_sax_buf_init(Buf buf, VALUE io) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
int ox_sax_buf_read(Buf buf) {
|
|
72
|
-
int
|
|
73
|
-
|
|
72
|
+
int err;
|
|
73
|
+
long shift = 0;
|
|
74
74
|
|
|
75
75
|
// if there is not much room to read into, shift or realloc a larger buffer.
|
|
76
76
|
if (buf->head < buf->tail && 4096 > buf->end - buf->tail) {
|
data/lib/ox/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ox
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.14.
|
|
4
|
+
version: 2.14.27
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Peter Ohler
|
|
@@ -51,7 +51,6 @@ files:
|
|
|
51
51
|
- ext/ox/err.c
|
|
52
52
|
- ext/ox/err.h
|
|
53
53
|
- ext/ox/extconf.rb
|
|
54
|
-
- ext/ox/foo.h
|
|
55
54
|
- ext/ox/gen_load.c
|
|
56
55
|
- ext/ox/hash_load.c
|
|
57
56
|
- ext/ox/helper.h
|
|
@@ -123,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
123
122
|
- !ruby/object:Gem::Version
|
|
124
123
|
version: '0'
|
|
125
124
|
requirements: []
|
|
126
|
-
rubygems_version: 4.0.
|
|
125
|
+
rubygems_version: 4.0.6
|
|
127
126
|
specification_version: 4
|
|
128
127
|
summary: A fast XML parser and object serializer.
|
|
129
128
|
test_files: []
|
data/ext/ox/foo.h
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
/* sax_buf.h
|
|
2
|
-
* Copyright (c) 2011, Peter Ohler
|
|
3
|
-
* All rights reserved.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
#ifndef OX_SAX_BUF_H
|
|
7
|
-
#define OX_SAX_BUF_H
|
|
8
|
-
|
|
9
|
-
#include <stdio.h>
|
|
10
|
-
|
|
11
|
-
typedef struct _buf {
|
|
12
|
-
char base[0x00001000];
|
|
13
|
-
char *head;
|
|
14
|
-
char *end;
|
|
15
|
-
char *tail;
|
|
16
|
-
char *read_end; /* one past last character read */
|
|
17
|
-
char *pro; /* protection start, buffer can not slide past this point */
|
|
18
|
-
char *str; /* start of current string being read */
|
|
19
|
-
off_t pos;
|
|
20
|
-
off_t line;
|
|
21
|
-
off_t col;
|
|
22
|
-
off_t pro_pos;
|
|
23
|
-
off_t pro_line;
|
|
24
|
-
off_t pro_col;
|
|
25
|
-
int (*read_func)(struct _buf *buf);
|
|
26
|
-
union {
|
|
27
|
-
int fd;
|
|
28
|
-
VALUE io;
|
|
29
|
-
const char *str;
|
|
30
|
-
} in;
|
|
31
|
-
struct _saxDrive *dr;
|
|
32
|
-
} *Buf;
|
|
33
|
-
|
|
34
|
-
typedef struct _checkPt {
|
|
35
|
-
off_t pro_dif;
|
|
36
|
-
off_t pos;
|
|
37
|
-
off_t line;
|
|
38
|
-
off_t col;
|
|
39
|
-
char c;
|
|
40
|
-
} *CheckPt;
|
|
41
|
-
|
|
42
|
-
#define CHECK_PT_INIT {-1, 0, 0, 0, '\0'}
|
|
43
|
-
|
|
44
|
-
extern void ox_sax_buf_init(Buf buf, VALUE io);
|
|
45
|
-
extern int ox_sax_buf_read(Buf buf);
|
|
46
|
-
|
|
47
|
-
static inline char buf_get(Buf buf) {
|
|
48
|
-
// printf("*** drive get from '%s' from start: %ld buf: %p from read_end: %ld\n", buf->tail, buf->tail -
|
|
49
|
-
// buf->head, buf->head, buf->read_end - buf->tail);
|
|
50
|
-
if (buf->read_end <= buf->tail) {
|
|
51
|
-
if (0 != ox_sax_buf_read(buf)) {
|
|
52
|
-
return '\0';
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if ('\n' == *buf->tail) {
|
|
56
|
-
buf->line++;
|
|
57
|
-
buf->col = 0;
|
|
58
|
-
} else {
|
|
59
|
-
buf->col++;
|
|
60
|
-
}
|
|
61
|
-
buf->pos++;
|
|
62
|
-
|
|
63
|
-
return *buf->tail++;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
static inline void buf_backup(Buf buf) {
|
|
67
|
-
buf->tail--;
|
|
68
|
-
buf->col--;
|
|
69
|
-
buf->pos--;
|
|
70
|
-
if (0 >= buf->col) {
|
|
71
|
-
buf->line--;
|
|
72
|
-
// allow col to be negative since we never backup twice in a row
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
static inline void buf_protect(Buf buf) {
|
|
77
|
-
buf->pro = buf->tail;
|
|
78
|
-
buf->str = buf->tail; // can't have str before pro
|
|
79
|
-
buf->pro_pos = buf->pos;
|
|
80
|
-
buf->pro_line = buf->line;
|
|
81
|
-
buf->pro_col = buf->col;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
static inline void buf_reset(Buf buf) {
|
|
85
|
-
buf->tail = buf->pro;
|
|
86
|
-
buf->pos = buf->pro_pos;
|
|
87
|
-
buf->line = buf->pro_line;
|
|
88
|
-
buf->col = buf->pro_col;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/* Starts by reading a character so it is safe to use with an empty or
|
|
92
|
-
* compacted buffer.
|
|
93
|
-
*/
|
|
94
|
-
static inline char buf_next_non_white(Buf buf) {
|
|
95
|
-
char c;
|
|
96
|
-
|
|
97
|
-
while ('\0' != (c = buf_get(buf))) {
|
|
98
|
-
switch (c) {
|
|
99
|
-
case ' ':
|
|
100
|
-
case '\t':
|
|
101
|
-
case '\f':
|
|
102
|
-
case '\n':
|
|
103
|
-
case '\r': break;
|
|
104
|
-
default: return c;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return '\0';
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/* Starts by reading a character so it is safe to use with an empty or
|
|
111
|
-
* compacted buffer.
|
|
112
|
-
*/
|
|
113
|
-
static inline char buf_next_white(Buf buf) {
|
|
114
|
-
char c;
|
|
115
|
-
|
|
116
|
-
while ('\0' != (c = buf_get(buf))) {
|
|
117
|
-
switch (c) {
|
|
118
|
-
case ' ':
|
|
119
|
-
case '\t':
|
|
120
|
-
case '\f':
|
|
121
|
-
case '\n':
|
|
122
|
-
case '\r':
|
|
123
|
-
case '\0': return c;
|
|
124
|
-
default: break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return '\0';
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
static inline void buf_cleanup(Buf buf) {
|
|
131
|
-
if (buf->base != buf->head && 0 != buf->head) {
|
|
132
|
-
xfree(buf->head);
|
|
133
|
-
buf->head = 0;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
static inline int is_white(char c) {
|
|
138
|
-
switch (c) {
|
|
139
|
-
case ' ':
|
|
140
|
-
case '\t':
|
|
141
|
-
case '\f':
|
|
142
|
-
case '\n':
|
|
143
|
-
case '\r': return 1;
|
|
144
|
-
default: break;
|
|
145
|
-
}
|
|
146
|
-
return 0;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
static inline void buf_checkpoint(Buf buf, CheckPt cp) {
|
|
150
|
-
cp->pro_dif = (int)(buf->tail - buf->pro);
|
|
151
|
-
cp->pos = buf->pos;
|
|
152
|
-
cp->line = buf->line;
|
|
153
|
-
cp->col = buf->col;
|
|
154
|
-
cp->c = *(buf->tail - 1);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
static inline int buf_checkset(CheckPt cp) {
|
|
158
|
-
return (0 <= cp->pro_dif);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
static inline char buf_checkback(Buf buf, CheckPt cp) {
|
|
162
|
-
buf->tail = buf->pro + cp->pro_dif;
|
|
163
|
-
buf->pos = cp->pos;
|
|
164
|
-
buf->line = cp->line;
|
|
165
|
-
buf->col = cp->col;
|
|
166
|
-
return cp->c;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
static inline void buf_collapse_return(char *str) {
|
|
170
|
-
char *s = str;
|
|
171
|
-
char *back = str;
|
|
172
|
-
|
|
173
|
-
for (; '\0' != *s; s++) {
|
|
174
|
-
if (back != str && '\n' == *s && '\r' == *(back - 1)) {
|
|
175
|
-
*(back - 1) = '\n';
|
|
176
|
-
} else {
|
|
177
|
-
*back++ = *s;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
*back = '\0';
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
static inline void buf_collapse_white(char *str) {
|
|
184
|
-
char *s = str;
|
|
185
|
-
char *back = str;
|
|
186
|
-
|
|
187
|
-
for (; '\0' != *s; s++) {
|
|
188
|
-
switch (*s) {
|
|
189
|
-
case ' ':
|
|
190
|
-
case '\t':
|
|
191
|
-
case '\f':
|
|
192
|
-
case '\n':
|
|
193
|
-
case '\r':
|
|
194
|
-
if (back == str || ' ' != *(back - 1)) {
|
|
195
|
-
*back++ = ' ';
|
|
196
|
-
}
|
|
197
|
-
break;
|
|
198
|
-
default: *back++ = *s; break;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
*back = '\0';
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
#endif /* OX_SAX_BUF_H */
|