Line data Source code
1 : /*
2 : * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 : *
4 : * Use of this source code is governed by a BSD-style license
5 : * that can be found in the LICENSE file in the root of the source
6 : * tree. An additional intellectual property rights grant can be found
7 : * in the file PATENTS. All contributing project authors may
8 : * be found in the AUTHORS file in the root of the source tree.
9 : */
10 :
11 : #include "webrtc/base/stringencode.h"
12 :
13 : #include <stdio.h>
14 : #include <stdlib.h>
15 :
16 : #include "webrtc/base/checks.h"
17 : #include "webrtc/base/stringutils.h"
18 :
19 : namespace rtc {
20 :
21 : /////////////////////////////////////////////////////////////////////////////
22 : // String Encoding Utilities
23 : /////////////////////////////////////////////////////////////////////////////
24 :
25 0 : size_t escape(char * buffer, size_t buflen,
26 : const char * source, size_t srclen,
27 : const char * illegal, char escape) {
28 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
29 0 : if (buflen <= 0)
30 0 : return 0;
31 :
32 0 : size_t srcpos = 0, bufpos = 0;
33 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
34 0 : char ch = source[srcpos++];
35 0 : if ((ch == escape) || ::strchr(illegal, ch)) {
36 0 : if (bufpos + 2 >= buflen)
37 0 : break;
38 0 : buffer[bufpos++] = escape;
39 : }
40 0 : buffer[bufpos++] = ch;
41 : }
42 :
43 0 : buffer[bufpos] = '\0';
44 0 : return bufpos;
45 : }
46 :
47 0 : size_t unescape(char * buffer, size_t buflen,
48 : const char * source, size_t srclen,
49 : char escape) {
50 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
51 0 : if (buflen <= 0)
52 0 : return 0;
53 :
54 0 : size_t srcpos = 0, bufpos = 0;
55 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
56 0 : char ch = source[srcpos++];
57 0 : if ((ch == escape) && (srcpos < srclen)) {
58 0 : ch = source[srcpos++];
59 : }
60 0 : buffer[bufpos++] = ch;
61 : }
62 0 : buffer[bufpos] = '\0';
63 0 : return bufpos;
64 : }
65 :
66 0 : size_t encode(char * buffer, size_t buflen,
67 : const char * source, size_t srclen,
68 : const char * illegal, char escape) {
69 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
70 0 : if (buflen <= 0)
71 0 : return 0;
72 :
73 0 : size_t srcpos = 0, bufpos = 0;
74 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
75 0 : char ch = source[srcpos++];
76 0 : if ((ch != escape) && !::strchr(illegal, ch)) {
77 0 : buffer[bufpos++] = ch;
78 0 : } else if (bufpos + 3 >= buflen) {
79 0 : break;
80 : } else {
81 0 : buffer[bufpos+0] = escape;
82 0 : buffer[bufpos+1] = hex_encode((static_cast<unsigned char>(ch) >> 4) & 0xF);
83 0 : buffer[bufpos+2] = hex_encode((static_cast<unsigned char>(ch) ) & 0xF);
84 0 : bufpos += 3;
85 : }
86 : }
87 0 : buffer[bufpos] = '\0';
88 0 : return bufpos;
89 : }
90 :
91 0 : size_t decode(char * buffer, size_t buflen,
92 : const char * source, size_t srclen,
93 : char escape) {
94 0 : if (buflen <= 0)
95 0 : return 0;
96 :
97 : unsigned char h1, h2;
98 0 : size_t srcpos = 0, bufpos = 0;
99 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
100 0 : char ch = source[srcpos++];
101 0 : if ((ch == escape)
102 0 : && (srcpos + 1 < srclen)
103 0 : && hex_decode(source[srcpos], &h1)
104 0 : && hex_decode(source[srcpos+1], &h2)) {
105 0 : buffer[bufpos++] = (h1 << 4) | h2;
106 0 : srcpos += 2;
107 : } else {
108 0 : buffer[bufpos++] = ch;
109 : }
110 : }
111 0 : buffer[bufpos] = '\0';
112 0 : return bufpos;
113 : }
114 :
115 0 : const char* unsafe_filename_characters() {
116 : // It might be better to have a single specification which is the union of
117 : // all operating systems, unless one system is overly restrictive.
118 : #if defined(WEBRTC_WIN)
119 : return "\\/:*?\"<>|";
120 : #else // !WEBRTC_WIN
121 : // TODO(grunell): Should this never be reached?
122 0 : RTC_NOTREACHED();
123 0 : return "";
124 : #endif // !WEBRTC_WIN
125 : }
126 :
127 : const unsigned char URL_UNSAFE = 0x1; // 0-33 "#$%&+,/:;<=>?@[\]^`{|} 127
128 : const unsigned char XML_UNSAFE = 0x2; // "&'<>
129 : const unsigned char HTML_UNSAFE = 0x2; // "&'<>
130 :
131 : // ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 6 5 7 8 9 : ; < = > ?
132 : //@ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _
133 : //` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~
134 :
135 : const unsigned char ASCII_CLASS[128] = {
136 : 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
137 : 1,0,3,1,1,1,3,2,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,3,1,3,1,
138 : 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,
139 : 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,
140 : };
141 :
142 0 : size_t url_encode(char * buffer, size_t buflen,
143 : const char * source, size_t srclen) {
144 0 : if (NULL == buffer)
145 0 : return srclen * 3 + 1;
146 0 : if (buflen <= 0)
147 0 : return 0;
148 :
149 0 : size_t srcpos = 0, bufpos = 0;
150 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
151 0 : unsigned char ch = source[srcpos++];
152 0 : if ((ch < 128) && (ASCII_CLASS[ch] & URL_UNSAFE)) {
153 0 : if (bufpos + 3 >= buflen) {
154 0 : break;
155 : }
156 0 : buffer[bufpos+0] = '%';
157 0 : buffer[bufpos+1] = hex_encode((ch >> 4) & 0xF);
158 0 : buffer[bufpos+2] = hex_encode((ch ) & 0xF);
159 0 : bufpos += 3;
160 : } else {
161 0 : buffer[bufpos++] = ch;
162 : }
163 : }
164 0 : buffer[bufpos] = '\0';
165 0 : return bufpos;
166 : }
167 :
168 0 : size_t url_decode(char * buffer, size_t buflen,
169 : const char * source, size_t srclen) {
170 0 : if (NULL == buffer)
171 0 : return srclen + 1;
172 0 : if (buflen <= 0)
173 0 : return 0;
174 :
175 : unsigned char h1, h2;
176 0 : size_t srcpos = 0, bufpos = 0;
177 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
178 0 : unsigned char ch = source[srcpos++];
179 0 : if (ch == '+') {
180 0 : buffer[bufpos++] = ' ';
181 0 : } else if ((ch == '%')
182 0 : && (srcpos + 1 < srclen)
183 0 : && hex_decode(source[srcpos], &h1)
184 0 : && hex_decode(source[srcpos+1], &h2))
185 : {
186 0 : buffer[bufpos++] = (h1 << 4) | h2;
187 0 : srcpos += 2;
188 : } else {
189 0 : buffer[bufpos++] = ch;
190 : }
191 : }
192 0 : buffer[bufpos] = '\0';
193 0 : return bufpos;
194 : }
195 :
196 0 : size_t utf8_decode(const char* source, size_t srclen, unsigned long* value) {
197 0 : const unsigned char* s = reinterpret_cast<const unsigned char*>(source);
198 0 : if ((s[0] & 0x80) == 0x00) { // Check s[0] == 0xxxxxxx
199 0 : *value = s[0];
200 0 : return 1;
201 : }
202 0 : if ((srclen < 2) || ((s[1] & 0xC0) != 0x80)) { // Check s[1] != 10xxxxxx
203 0 : return 0;
204 : }
205 : // Accumulate the trailer byte values in value16, and combine it with the
206 : // relevant bits from s[0], once we've determined the sequence length.
207 0 : unsigned long value16 = (s[1] & 0x3F);
208 0 : if ((s[0] & 0xE0) == 0xC0) { // Check s[0] == 110xxxxx
209 0 : *value = ((s[0] & 0x1F) << 6) | value16;
210 0 : return 2;
211 : }
212 0 : if ((srclen < 3) || ((s[2] & 0xC0) != 0x80)) { // Check s[2] != 10xxxxxx
213 0 : return 0;
214 : }
215 0 : value16 = (value16 << 6) | (s[2] & 0x3F);
216 0 : if ((s[0] & 0xF0) == 0xE0) { // Check s[0] == 1110xxxx
217 0 : *value = ((s[0] & 0x0F) << 12) | value16;
218 0 : return 3;
219 : }
220 0 : if ((srclen < 4) || ((s[3] & 0xC0) != 0x80)) { // Check s[3] != 10xxxxxx
221 0 : return 0;
222 : }
223 0 : value16 = (value16 << 6) | (s[3] & 0x3F);
224 0 : if ((s[0] & 0xF8) == 0xF0) { // Check s[0] == 11110xxx
225 0 : *value = ((s[0] & 0x07) << 18) | value16;
226 0 : return 4;
227 : }
228 0 : return 0;
229 : }
230 :
231 0 : size_t utf8_encode(char* buffer, size_t buflen, unsigned long value) {
232 0 : if ((value <= 0x7F) && (buflen >= 1)) {
233 0 : buffer[0] = static_cast<unsigned char>(value);
234 0 : return 1;
235 : }
236 0 : if ((value <= 0x7FF) && (buflen >= 2)) {
237 0 : buffer[0] = 0xC0 | static_cast<unsigned char>(value >> 6);
238 0 : buffer[1] = 0x80 | static_cast<unsigned char>(value & 0x3F);
239 0 : return 2;
240 : }
241 0 : if ((value <= 0xFFFF) && (buflen >= 3)) {
242 0 : buffer[0] = 0xE0 | static_cast<unsigned char>(value >> 12);
243 0 : buffer[1] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
244 0 : buffer[2] = 0x80 | static_cast<unsigned char>(value & 0x3F);
245 0 : return 3;
246 : }
247 0 : if ((value <= 0x1FFFFF) && (buflen >= 4)) {
248 0 : buffer[0] = 0xF0 | static_cast<unsigned char>(value >> 18);
249 0 : buffer[1] = 0x80 | static_cast<unsigned char>((value >> 12) & 0x3F);
250 0 : buffer[2] = 0x80 | static_cast<unsigned char>((value >> 6) & 0x3F);
251 0 : buffer[3] = 0x80 | static_cast<unsigned char>(value & 0x3F);
252 0 : return 4;
253 : }
254 0 : return 0;
255 : }
256 :
257 0 : size_t html_encode(char * buffer, size_t buflen,
258 : const char * source, size_t srclen) {
259 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
260 0 : if (buflen <= 0)
261 0 : return 0;
262 :
263 0 : size_t srcpos = 0, bufpos = 0;
264 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
265 0 : unsigned char ch = source[srcpos];
266 0 : if (ch < 128) {
267 0 : srcpos += 1;
268 0 : if (ASCII_CLASS[ch] & HTML_UNSAFE) {
269 0 : const char * escseq = 0;
270 0 : size_t esclen = 0;
271 0 : switch (ch) {
272 0 : case '<': escseq = "<"; esclen = 4; break;
273 0 : case '>': escseq = ">"; esclen = 4; break;
274 0 : case '\'': escseq = "'"; esclen = 5; break;
275 0 : case '\"': escseq = """; esclen = 6; break;
276 0 : case '&': escseq = "&"; esclen = 5; break;
277 0 : default: RTC_NOTREACHED();
278 : }
279 0 : if (bufpos + esclen >= buflen) {
280 0 : break;
281 : }
282 0 : memcpy(buffer + bufpos, escseq, esclen);
283 0 : bufpos += esclen;
284 : } else {
285 0 : buffer[bufpos++] = ch;
286 : }
287 : } else {
288 : // Largest value is 0x1FFFFF => � (10 characters)
289 0 : const size_t kEscseqSize = 11;
290 : char escseq[kEscseqSize];
291 : unsigned long val;
292 0 : if (size_t vallen = utf8_decode(&source[srcpos], srclen - srcpos, &val)) {
293 0 : srcpos += vallen;
294 : } else {
295 : // Not a valid utf8 sequence, just use the raw character.
296 0 : val = static_cast<unsigned char>(source[srcpos++]);
297 : }
298 0 : size_t esclen = sprintfn(escseq, kEscseqSize, "&#%lu;", val);
299 0 : if (bufpos + esclen >= buflen) {
300 0 : break;
301 : }
302 0 : memcpy(buffer + bufpos, escseq, esclen);
303 0 : bufpos += esclen;
304 : }
305 : }
306 0 : buffer[bufpos] = '\0';
307 0 : return bufpos;
308 : }
309 :
310 0 : size_t html_decode(char * buffer, size_t buflen,
311 : const char * source, size_t srclen) {
312 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
313 0 : return xml_decode(buffer, buflen, source, srclen);
314 : }
315 :
316 0 : size_t xml_encode(char * buffer, size_t buflen,
317 : const char * source, size_t srclen) {
318 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
319 0 : if (buflen <= 0)
320 0 : return 0;
321 :
322 0 : size_t srcpos = 0, bufpos = 0;
323 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
324 0 : unsigned char ch = source[srcpos++];
325 0 : if ((ch < 128) && (ASCII_CLASS[ch] & XML_UNSAFE)) {
326 0 : const char * escseq = 0;
327 0 : size_t esclen = 0;
328 0 : switch (ch) {
329 0 : case '<': escseq = "<"; esclen = 4; break;
330 0 : case '>': escseq = ">"; esclen = 4; break;
331 0 : case '\'': escseq = "'"; esclen = 6; break;
332 0 : case '\"': escseq = """; esclen = 6; break;
333 0 : case '&': escseq = "&"; esclen = 5; break;
334 0 : default: RTC_NOTREACHED();
335 : }
336 0 : if (bufpos + esclen >= buflen) {
337 0 : break;
338 : }
339 0 : memcpy(buffer + bufpos, escseq, esclen);
340 0 : bufpos += esclen;
341 : } else {
342 0 : buffer[bufpos++] = ch;
343 : }
344 : }
345 0 : buffer[bufpos] = '\0';
346 0 : return bufpos;
347 : }
348 :
349 0 : size_t xml_decode(char * buffer, size_t buflen,
350 : const char * source, size_t srclen) {
351 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
352 0 : if (buflen <= 0)
353 0 : return 0;
354 :
355 0 : size_t srcpos = 0, bufpos = 0;
356 0 : while ((srcpos < srclen) && (bufpos + 1 < buflen)) {
357 0 : unsigned char ch = source[srcpos++];
358 0 : if (ch != '&') {
359 0 : buffer[bufpos++] = ch;
360 0 : } else if ((srcpos + 2 < srclen)
361 0 : && (memcmp(source + srcpos, "lt;", 3) == 0)) {
362 0 : buffer[bufpos++] = '<';
363 0 : srcpos += 3;
364 0 : } else if ((srcpos + 2 < srclen)
365 0 : && (memcmp(source + srcpos, "gt;", 3) == 0)) {
366 0 : buffer[bufpos++] = '>';
367 0 : srcpos += 3;
368 0 : } else if ((srcpos + 4 < srclen)
369 0 : && (memcmp(source + srcpos, "apos;", 5) == 0)) {
370 0 : buffer[bufpos++] = '\'';
371 0 : srcpos += 5;
372 0 : } else if ((srcpos + 4 < srclen)
373 0 : && (memcmp(source + srcpos, "quot;", 5) == 0)) {
374 0 : buffer[bufpos++] = '\"';
375 0 : srcpos += 5;
376 0 : } else if ((srcpos + 3 < srclen)
377 0 : && (memcmp(source + srcpos, "amp;", 4) == 0)) {
378 0 : buffer[bufpos++] = '&';
379 0 : srcpos += 4;
380 0 : } else if ((srcpos < srclen) && (source[srcpos] == '#')) {
381 0 : int int_base = 10;
382 0 : if ((srcpos + 1 < srclen) && (source[srcpos+1] == 'x')) {
383 0 : int_base = 16;
384 0 : srcpos += 1;
385 : }
386 : char * ptr;
387 : // TODO(grunell): Fix hack (ptr may go past end of data)
388 0 : unsigned long val = strtoul(source + srcpos + 1, &ptr, int_base);
389 0 : if ((static_cast<size_t>(ptr - source) < srclen) && (*ptr == ';')) {
390 0 : srcpos = ptr - source + 1;
391 : } else {
392 : // Not a valid escape sequence.
393 : break;
394 : }
395 0 : if (size_t esclen = utf8_encode(buffer + bufpos, buflen - bufpos, val)) {
396 0 : bufpos += esclen;
397 : } else {
398 : // Not enough room to encode the character, or illegal character
399 0 : break;
400 : }
401 : } else {
402 : // Unrecognized escape sequence.
403 : break;
404 : }
405 : }
406 0 : buffer[bufpos] = '\0';
407 0 : return bufpos;
408 : }
409 :
410 : static const char HEX[] = "0123456789abcdef";
411 :
412 0 : char hex_encode(unsigned char val) {
413 0 : RTC_DCHECK_LT(val, 16);
414 0 : return (val < 16) ? HEX[val] : '!';
415 : }
416 :
417 0 : bool hex_decode(char ch, unsigned char* val) {
418 0 : if ((ch >= '0') && (ch <= '9')) {
419 0 : *val = ch - '0';
420 0 : } else if ((ch >= 'A') && (ch <= 'Z')) {
421 0 : *val = (ch - 'A') + 10;
422 0 : } else if ((ch >= 'a') && (ch <= 'z')) {
423 0 : *val = (ch - 'a') + 10;
424 : } else {
425 0 : return false;
426 : }
427 0 : return true;
428 : }
429 :
430 0 : size_t hex_encode(char* buffer, size_t buflen,
431 : const char* csource, size_t srclen) {
432 0 : return hex_encode_with_delimiter(buffer, buflen, csource, srclen, 0);
433 : }
434 :
435 0 : size_t hex_encode_with_delimiter(char* buffer, size_t buflen,
436 : const char* csource, size_t srclen,
437 : char delimiter) {
438 0 : RTC_DCHECK(buffer); // TODO(grunell): estimate output size
439 0 : if (buflen == 0)
440 0 : return 0;
441 :
442 : // Init and check bounds.
443 : const unsigned char* bsource =
444 0 : reinterpret_cast<const unsigned char*>(csource);
445 0 : size_t srcpos = 0, bufpos = 0;
446 0 : size_t needed = delimiter ? (srclen * 3) : (srclen * 2 + 1);
447 0 : if (buflen < needed)
448 0 : return 0;
449 :
450 0 : while (srcpos < srclen) {
451 0 : unsigned char ch = bsource[srcpos++];
452 0 : buffer[bufpos ] = hex_encode((ch >> 4) & 0xF);
453 0 : buffer[bufpos+1] = hex_encode((ch ) & 0xF);
454 0 : bufpos += 2;
455 :
456 : // Don't write a delimiter after the last byte.
457 0 : if (delimiter && (srcpos < srclen)) {
458 0 : buffer[bufpos] = delimiter;
459 0 : ++bufpos;
460 : }
461 : }
462 :
463 : // Null terminate.
464 0 : buffer[bufpos] = '\0';
465 0 : return bufpos;
466 : }
467 :
468 0 : std::string hex_encode(const std::string& str) {
469 0 : return hex_encode(str.c_str(), str.size());
470 : }
471 :
472 0 : std::string hex_encode(const char* source, size_t srclen) {
473 0 : return hex_encode_with_delimiter(source, srclen, 0);
474 : }
475 :
476 0 : std::string hex_encode_with_delimiter(const char* source, size_t srclen,
477 : char delimiter) {
478 0 : const size_t kBufferSize = srclen * 3;
479 0 : char* buffer = STACK_ARRAY(char, kBufferSize);
480 0 : size_t length = hex_encode_with_delimiter(buffer, kBufferSize,
481 0 : source, srclen, delimiter);
482 0 : RTC_DCHECK(srclen == 0 || length > 0);
483 0 : return std::string(buffer, length);
484 : }
485 :
486 0 : size_t hex_decode(char * cbuffer, size_t buflen,
487 : const char * source, size_t srclen) {
488 0 : return hex_decode_with_delimiter(cbuffer, buflen, source, srclen, 0);
489 : }
490 :
491 0 : size_t hex_decode_with_delimiter(char* cbuffer, size_t buflen,
492 : const char* source, size_t srclen,
493 : char delimiter) {
494 0 : RTC_DCHECK(cbuffer); // TODO(grunell): estimate output size
495 0 : if (buflen == 0)
496 0 : return 0;
497 :
498 : // Init and bounds check.
499 0 : unsigned char* bbuffer = reinterpret_cast<unsigned char*>(cbuffer);
500 0 : size_t srcpos = 0, bufpos = 0;
501 0 : size_t needed = (delimiter) ? (srclen + 1) / 3 : srclen / 2;
502 0 : if (buflen < needed)
503 0 : return 0;
504 :
505 0 : while (srcpos < srclen) {
506 0 : if ((srclen - srcpos) < 2) {
507 : // This means we have an odd number of bytes.
508 0 : return 0;
509 : }
510 :
511 : unsigned char h1, h2;
512 0 : if (!hex_decode(source[srcpos], &h1) ||
513 0 : !hex_decode(source[srcpos + 1], &h2))
514 0 : return 0;
515 :
516 0 : bbuffer[bufpos++] = (h1 << 4) | h2;
517 0 : srcpos += 2;
518 :
519 : // Remove the delimiter if needed.
520 0 : if (delimiter && (srclen - srcpos) > 1) {
521 0 : if (source[srcpos] != delimiter)
522 0 : return 0;
523 0 : ++srcpos;
524 : }
525 : }
526 :
527 0 : return bufpos;
528 : }
529 :
530 0 : size_t hex_decode(char* buffer, size_t buflen, const std::string& source) {
531 0 : return hex_decode_with_delimiter(buffer, buflen, source, 0);
532 : }
533 0 : size_t hex_decode_with_delimiter(char* buffer, size_t buflen,
534 : const std::string& source, char delimiter) {
535 0 : return hex_decode_with_delimiter(buffer, buflen,
536 0 : source.c_str(), source.length(), delimiter);
537 : }
538 :
539 0 : size_t transform(std::string& value, size_t maxlen, const std::string& source,
540 : Transform t) {
541 0 : char* buffer = STACK_ARRAY(char, maxlen + 1);
542 0 : size_t length = t(buffer, maxlen + 1, source.data(), source.length());
543 0 : value.assign(buffer, length);
544 0 : return length;
545 : }
546 :
547 0 : std::string s_transform(const std::string& source, Transform t) {
548 : // Ask transformation function to approximate the destination size (returns upper bound)
549 0 : size_t maxlen = t(NULL, 0, source.data(), source.length());
550 0 : char * buffer = STACK_ARRAY(char, maxlen);
551 0 : size_t len = t(buffer, maxlen, source.data(), source.length());
552 0 : std::string result(buffer, len);
553 0 : return result;
554 : }
555 :
556 0 : size_t tokenize(const std::string& source, char delimiter,
557 : std::vector<std::string>* fields) {
558 0 : fields->clear();
559 0 : size_t last = 0;
560 0 : for (size_t i = 0; i < source.length(); ++i) {
561 0 : if (source[i] == delimiter) {
562 0 : if (i != last) {
563 0 : fields->push_back(source.substr(last, i - last));
564 : }
565 0 : last = i + 1;
566 : }
567 : }
568 0 : if (last != source.length()) {
569 0 : fields->push_back(source.substr(last, source.length() - last));
570 : }
571 0 : return fields->size();
572 : }
573 :
574 0 : size_t tokenize_with_empty_tokens(const std::string& source,
575 : char delimiter,
576 : std::vector<std::string>* fields) {
577 0 : fields->clear();
578 0 : size_t last = 0;
579 0 : for (size_t i = 0; i < source.length(); ++i) {
580 0 : if (source[i] == delimiter) {
581 0 : fields->push_back(source.substr(last, i - last));
582 0 : last = i + 1;
583 : }
584 : }
585 0 : fields->push_back(source.substr(last, source.length() - last));
586 0 : return fields->size();
587 : }
588 :
589 0 : size_t tokenize_append(const std::string& source, char delimiter,
590 : std::vector<std::string>* fields) {
591 0 : if (!fields) return 0;
592 :
593 0 : std::vector<std::string> new_fields;
594 0 : tokenize(source, delimiter, &new_fields);
595 0 : fields->insert(fields->end(), new_fields.begin(), new_fields.end());
596 0 : return fields->size();
597 : }
598 :
599 0 : size_t tokenize(const std::string& source, char delimiter, char start_mark,
600 : char end_mark, std::vector<std::string>* fields) {
601 0 : if (!fields) return 0;
602 0 : fields->clear();
603 :
604 0 : std::string remain_source = source;
605 0 : while (!remain_source.empty()) {
606 0 : size_t start_pos = remain_source.find(start_mark);
607 0 : if (std::string::npos == start_pos) break;
608 0 : std::string pre_mark;
609 0 : if (start_pos > 0) {
610 0 : pre_mark = remain_source.substr(0, start_pos - 1);
611 : }
612 :
613 0 : ++start_pos;
614 0 : size_t end_pos = remain_source.find(end_mark, start_pos);
615 0 : if (std::string::npos == end_pos) break;
616 :
617 : // We have found the matching marks. First tokenize the pre-mask. Then add
618 : // the marked part as a single field. Finally, loop back for the post-mark.
619 0 : tokenize_append(pre_mark, delimiter, fields);
620 0 : fields->push_back(remain_source.substr(start_pos, end_pos - start_pos));
621 0 : remain_source = remain_source.substr(end_pos + 1);
622 : }
623 :
624 0 : return tokenize_append(remain_source, delimiter, fields);
625 : }
626 :
627 0 : bool tokenize_first(const std::string& source,
628 : const char delimiter,
629 : std::string* token,
630 : std::string* rest) {
631 : // Find the first delimiter
632 0 : size_t left_pos = source.find(delimiter);
633 0 : if (left_pos == std::string::npos) {
634 0 : return false;
635 : }
636 :
637 : // Look for additional occurrances of delimiter.
638 0 : size_t right_pos = left_pos + 1;
639 0 : while (source[right_pos] == delimiter) {
640 0 : right_pos++;
641 : }
642 :
643 0 : *token = source.substr(0, left_pos);
644 0 : *rest = source.substr(right_pos);
645 0 : return true;
646 : }
647 :
648 0 : size_t split(const std::string& source, char delimiter,
649 : std::vector<std::string>* fields) {
650 0 : RTC_DCHECK(fields);
651 0 : fields->clear();
652 0 : size_t last = 0;
653 0 : for (size_t i = 0; i < source.length(); ++i) {
654 0 : if (source[i] == delimiter) {
655 0 : fields->push_back(source.substr(last, i - last));
656 0 : last = i + 1;
657 : }
658 : }
659 0 : fields->push_back(source.substr(last, source.length() - last));
660 0 : return fields->size();
661 : }
662 :
663 0 : char make_char_safe_for_filename(char c) {
664 0 : if (c < 32)
665 0 : return '_';
666 :
667 0 : switch (c) {
668 : case '<':
669 : case '>':
670 : case ':':
671 : case '"':
672 : case '/':
673 : case '\\':
674 : case '|':
675 : case '*':
676 : case '?':
677 0 : return '_';
678 :
679 : default:
680 0 : return c;
681 : }
682 : }
683 :
684 : /*
685 : void sprintf(std::string& value, size_t maxlen, const char * format, ...) {
686 : char * buffer = STACK_ARRAY(char, maxlen + 1);
687 : va_list args;
688 : va_start(args, format);
689 : value.assign(buffer, vsprintfn(buffer, maxlen + 1, format, args));
690 : va_end(args);
691 : }
692 : */
693 :
694 : /////////////////////////////////////////////////////////////////////////////
695 :
696 : } // namespace rtc
|