master
c 146 lines 4.63 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "../libnetdata.h"
4
5 size_t text_sanitize(unsigned char *dst, const unsigned char *src, size_t dst_size, const unsigned char *char_map, bool utf, const char *empty, size_t *multibyte_length) {
6 if(unlikely(!dst || !dst_size)) return 0;
7
8 // skip leading spaces and invalid characters
9 while(src && *src && !IS_UTF8_BYTE(*src) && (isspace(*src) || iscntrl(*src) || !isprint(*src)))
10 src++;
11
12 if(unlikely(!src || !*src)) {
13 strncpyz((char *)dst, empty, dst_size);
14 dst[dst_size - 1] = '\0';
15 size_t len = strlen((char *)dst);
16 if(multibyte_length) *multibyte_length = len;
17 return len;
18 }
19
20 unsigned char *d = dst;
21
22 // make room for the final string termination
23 unsigned char *end = &dst[dst_size - 1];
24
25 // copy while converting, but keep only one space
26 // we start wil last_is_space = 1 to skip leading spaces
27 int last_is_space = 1;
28
29 size_t mblen = 0;
30
31 while(*src && d < end) {
32 unsigned char c = *src;
33
34 if(IS_UTF8_STARTBYTE(c)) {
35 size_t utf8_character_bytes = 1;
36 bool valid_sequence = true;
37
38 // Determine expected sequence length based on start byte
39 if((c & 0xE0) == 0xC0) utf8_character_bytes = 2; // 2-byte sequence
40 else if((c & 0xF0) == 0xE0) utf8_character_bytes = 3; // 3-byte sequence
41 else if((c & 0xF8) == 0xF0) utf8_character_bytes = 4; // 4-byte sequence
42
43 if(utf8_character_bytes == 1)
44 valid_sequence = false;
45 else {
46 // make sure all the continuation bytes are valid
47 // also check for premature string termination (truncated UTF-8)
48 for(size_t i = 1; i < utf8_character_bytes; i++) {
49 if(src[i] == '\0' || !IS_UTF8_BYTE(src[i]) || IS_UTF8_STARTBYTE(src[i])) {
50 valid_sequence = false;
51 break;
52 }
53 }
54 }
55
56 if(utf) {
57 if (valid_sequence && d + utf8_character_bytes <= end) {
58 // it is a valid utf8 character, and we have room at the destination
59 for (size_t i = 0; i < utf8_character_bytes; i++)
60 *d++ = *src++;
61 }
62 else {
63 // invalid or truncated UTF-8 sequence - hex encode it
64 // each byte becomes 2 hex chars, so check we have room for at least 2
65 if(d + 1 < end) {
66 *d++ = hex_digits_lower[(*src & 0xF0) >> 4];
67 *d++ = hex_digits_lower[(*src & 0x0F)];
68 }
69 src++;
70
71 // hex encode any continuation bytes
72 while(IS_UTF8_BYTE(*src) && !IS_UTF8_STARTBYTE(*src)) {
73 if(d + 1 < end) {
74 *d++ = hex_digits_lower[(*src & 0xF0) >> 4];
75 *d++ = hex_digits_lower[(*src & 0x0F)];
76 }
77 src++;
78 }
79 }
80 }
81 else {
82 *d++ = '_'; // this fits, we tested in the while() above
83
84 src++; // skip the utf8 start byte
85 // and skip the rest too
86 while(IS_UTF8_BYTE(*src) && !IS_UTF8_STARTBYTE(*src))
87 src++;
88 }
89 last_is_space = false;
90 mblen++;
91 continue;
92 }
93
94 c = char_map[c];
95 if(c == ' ') {
96 // a space character
97
98 if(!last_is_space) {
99 // add one space
100 *d++ = c;
101 mblen++;
102 }
103
104 last_is_space++;
105 }
106 else {
107 *d++ = c;
108 last_is_space = 0;
109 mblen++;
110 }
111
112 src++;
113 }
114
115 // remove trailing spaces
116 while(d > dst && !IS_UTF8_BYTE(*(d - 1)) && *(d - 1) == ' ') {
117 d--;
118 mblen--;
119 }
120
121 // put a termination at the end of what we copied
122 *d = '\0';
123
124 // check if dst is all underscores and empty it if it is
125 if(*dst == '_') {
126 unsigned char *t = dst;
127 while (*t == '_') t++;
128 if (unlikely(*t == '\0')) {
129 *dst = '\0';
130 mblen = 0;
131 }
132 }
133
134 // check if it is empty
135 if(unlikely(*dst == '\0')) {
136 strncpyz((char *)dst, empty, dst_size);
137 dst[dst_size - 1] = '\0';
138 mblen = strlen((char *)dst);
139 if(multibyte_length) *multibyte_length = mblen;
140 return mblen;
141 }
142
143 if(multibyte_length) *multibyte_length = mblen;
144
145 return d - dst;
146 }