/src/resiprocate/rutil/ParseBuffer.cxx
Line | Count | Source (jump to first uncovered line) |
1 | | #include "rutil/ResipAssert.h" |
2 | | |
3 | | #include "rutil/Logger.hxx" |
4 | | #include "rutil/ParseBuffer.hxx" |
5 | | #include "rutil/ParseException.hxx" |
6 | | #include "rutil/DataStream.hxx" |
7 | | #include "rutil/WinLeakCheck.hxx" |
8 | | |
9 | | using namespace resip; |
10 | | |
11 | | #define RESIPROCATE_SUBSYSTEM Subsystem::SIP |
12 | | |
13 | | const char* ParseBuffer::ParamTerm = ";?"; // maybe include "@>,"? |
14 | | const char* ParseBuffer::Whitespace = " \t\r\n"; |
15 | | const Data ParseBuffer::Pointer::msg("dereferenced ParseBuffer eof"); |
16 | | |
17 | | ParseBuffer::ParseBuffer(const char* buff, size_t len, |
18 | | const Data& errorContext) |
19 | | : mBuff(buff), |
20 | | mPosition(buff), |
21 | | mEnd(buff+len), |
22 | | mErrorContext(errorContext) |
23 | 159k | {} |
24 | | |
25 | | ParseBuffer::ParseBuffer(const char* buff, |
26 | | const Data& errorContext) |
27 | | : mBuff(buff), |
28 | | mPosition(buff), |
29 | | mEnd(buff+strlen(buff)), |
30 | | mErrorContext(errorContext) |
31 | 0 | {} |
32 | | |
33 | | ParseBuffer::ParseBuffer(const Data& data, |
34 | | const Data& errorContext) |
35 | | : mBuff(data.data()), |
36 | | mPosition(mBuff), |
37 | | mEnd(mBuff + data.size()), |
38 | | mErrorContext(errorContext) |
39 | 693 | {} |
40 | | |
41 | | ParseBuffer::ParseBuffer(const ParseBuffer& rhs) |
42 | | : mBuff(rhs.mBuff), |
43 | | mPosition(rhs.mPosition), |
44 | | mEnd(rhs.mEnd), |
45 | | mErrorContext(rhs.mErrorContext) |
46 | 0 | {} |
47 | | |
48 | | ParseBuffer& |
49 | | ParseBuffer::operator=(const ParseBuffer& rhs) |
50 | 0 | { |
51 | 0 | if (&rhs != this) |
52 | 0 | { |
53 | 0 | mBuff = rhs.mBuff; |
54 | 0 | mPosition = rhs.mPosition; |
55 | 0 | mEnd = rhs.mEnd; |
56 | 0 | } |
57 | |
|
58 | 0 | return *this; |
59 | 0 | } |
60 | | |
61 | | ParseBuffer::CurrentPosition |
62 | | ParseBuffer::skipChar(char c) |
63 | 5.04M | { |
64 | 5.04M | if (eof()) |
65 | 2.80k | { |
66 | 2.80k | fail(__FILE__, __LINE__,"skipped over eof"); |
67 | 2.80k | } |
68 | 5.04M | if (*mPosition != c) |
69 | 17.7k | { |
70 | 17.7k | Data msg("expected '"); |
71 | 17.7k | msg += c; |
72 | 17.7k | msg += "'"; |
73 | 17.7k | fail(__FILE__, __LINE__,msg); |
74 | 17.7k | } |
75 | 5.04M | ++mPosition; |
76 | 5.04M | return CurrentPosition(*this); |
77 | 5.04M | } |
78 | | |
79 | | ParseBuffer::CurrentPosition |
80 | | ParseBuffer::skipChars(const char* cs) |
81 | 6.45k | { |
82 | 6.45k | const char* match = cs; |
83 | 12.9k | while (*match != 0) |
84 | 6.47k | { |
85 | 6.47k | if (eof() || (*match != *mPosition)) |
86 | 6.45k | { |
87 | 6.45k | Data msg("Expected \""); |
88 | 6.45k | msg += cs; |
89 | 6.45k | msg += "\""; |
90 | 6.45k | fail(__FILE__, __LINE__,msg); |
91 | 6.45k | } |
92 | 6.47k | match++; |
93 | 6.47k | mPosition++; |
94 | 6.47k | } |
95 | 6.45k | return CurrentPosition(*this); |
96 | 6.45k | } |
97 | | |
98 | | ParseBuffer::CurrentPosition |
99 | | ParseBuffer::skipChars(const Data& cs) |
100 | 0 | { |
101 | 0 | const char* match = cs.data(); |
102 | 0 | for(Data::size_type i = 0; i < cs.size(); i++) |
103 | 0 | { |
104 | 0 | if (eof() || (*match != *mPosition)) |
105 | 0 | { |
106 | 0 | Data msg( "Expected \""); |
107 | 0 | msg += cs; |
108 | 0 | msg += "\""; |
109 | 0 | fail(__FILE__, __LINE__,msg); |
110 | 0 | } |
111 | 0 | match++; |
112 | 0 | mPosition++; |
113 | 0 | } |
114 | 0 | return CurrentPosition(*this); |
115 | 0 | } |
116 | | |
117 | | ParseBuffer::CurrentPosition |
118 | | ParseBuffer::skipNonWhitespace() |
119 | 9.45k | { |
120 | 9.45k | assertNotEof(); |
121 | 52.1M | while (mPosition < mEnd) |
122 | 52.1M | { |
123 | 52.1M | switch (*mPosition) |
124 | 52.1M | { |
125 | 1.12k | case ' ' : |
126 | 1.33k | case '\t' : |
127 | 1.97k | case '\r' : |
128 | 5.27k | case '\n' : |
129 | 5.27k | return CurrentPosition(*this); |
130 | 52.1M | default : |
131 | 52.1M | mPosition++; |
132 | 52.1M | } |
133 | 52.1M | } |
134 | 4.18k | return CurrentPosition(*this); |
135 | 9.45k | } |
136 | | |
137 | | ParseBuffer::CurrentPosition |
138 | | ParseBuffer::skipWhitespace() |
139 | 53.8M | { |
140 | 53.9M | while (mPosition < mEnd) |
141 | 53.8M | { |
142 | 53.8M | switch (*mPosition) |
143 | 53.8M | { |
144 | 6.39k | case ' ' : |
145 | 11.4k | case '\t' : |
146 | 19.4k | case '\r' : |
147 | 45.0k | case '\n' : |
148 | 45.0k | { |
149 | 45.0k | mPosition++; |
150 | 45.0k | break; |
151 | 19.4k | } |
152 | 53.8M | default : |
153 | 53.8M | return CurrentPosition(*this); |
154 | 53.8M | } |
155 | 53.8M | } |
156 | 19.1k | return CurrentPosition(*this); |
157 | 53.8M | } |
158 | | |
159 | | // "SIP header field values can be folded onto multiple lines if the |
160 | | // continuation line begins with a space or horizontal tab" |
161 | | |
162 | | // CR can be quote with \ within "" and comments -- treat \CR as whitespace |
163 | | ParseBuffer::CurrentPosition |
164 | | ParseBuffer::skipLWS() |
165 | 0 | { |
166 | 0 | enum State {WS, CR, LF}; |
167 | 0 | State state = WS; |
168 | 0 | while (mPosition < mEnd) |
169 | 0 | { |
170 | 0 | char c = *mPosition++; |
171 | 0 | if (c == '\\') |
172 | 0 | { |
173 | | // treat escaped CR and LF as space |
174 | 0 | c = *mPosition++; |
175 | 0 | if (c == '\r' || c == '\n') |
176 | 0 | { |
177 | 0 | c = ' '; |
178 | 0 | } |
179 | 0 | } |
180 | 0 | switch (*mPosition++) |
181 | 0 | { |
182 | 0 | case ' ' : |
183 | 0 | case '\t' : |
184 | 0 | { |
185 | 0 | state = WS; |
186 | 0 | break; |
187 | 0 | } |
188 | 0 | case '\r' : |
189 | 0 | { |
190 | 0 | state = CR; |
191 | 0 | break; |
192 | 0 | } |
193 | 0 | case '\n' : |
194 | 0 | { |
195 | 0 | if (state == CR) |
196 | 0 | { |
197 | 0 | state = LF; |
198 | 0 | } |
199 | 0 | else |
200 | 0 | { |
201 | 0 | state = WS; |
202 | 0 | } |
203 | 0 | break; |
204 | 0 | } |
205 | 0 | default : |
206 | 0 | { |
207 | | // terminating CRLF not skipped |
208 | 0 | if (state == LF) |
209 | 0 | { |
210 | 0 | mPosition -= 3; |
211 | 0 | } |
212 | 0 | else |
213 | 0 | { |
214 | 0 | mPosition--; |
215 | 0 | } |
216 | 0 | return CurrentPosition(*this); |
217 | 0 | } |
218 | 0 | } |
219 | 0 | } |
220 | 0 | return CurrentPosition(*this); |
221 | 0 | } |
222 | | |
223 | | static Data CRLF("\r\n"); |
224 | | ParseBuffer::CurrentPosition |
225 | | ParseBuffer::skipToTermCRLF() |
226 | 0 | { |
227 | 0 | while (mPosition < mEnd) |
228 | 0 | { |
229 | 0 | skipToChars(CRLF); |
230 | 0 | mPosition += 2; |
231 | 0 | if ((*mPosition != ' ' && |
232 | 0 | *mPosition != '\t' && |
233 | | // check for \CRLF -- not terminating |
234 | | // \\CRLF -- terminating |
235 | 0 | ((mPosition-3 < mBuff || *(mPosition-3) != '\\') || |
236 | 0 | (mPosition-4 > mBuff && *(mPosition-4) == '\\')))) |
237 | 0 | { |
238 | 0 | mPosition -= 2; |
239 | 0 | return CurrentPosition(*this); |
240 | 0 | } |
241 | 0 | } |
242 | 0 | return CurrentPosition(*this); |
243 | 0 | } |
244 | | |
245 | | ParseBuffer::CurrentPosition |
246 | | ParseBuffer::skipToChars(const char* cs) |
247 | 6.64k | { |
248 | 6.64k | resip_assert(cs); |
249 | 6.64k | unsigned int l = (unsigned int)strlen(cs); |
250 | | |
251 | 6.64k | const char* rpos; |
252 | 6.64k | const char* cpos; |
253 | | // Checking mPosition >= mEnd - l +1 is unnecessary because there won't be |
254 | | // enough bytes left to find [cs]. |
255 | 46.3M | while (mPosition < mEnd - l + 1) |
256 | 46.3M | { |
257 | 46.3M | rpos = mPosition; |
258 | 46.3M | cpos = cs; |
259 | 46.3M | for (unsigned int i = 0; i < l; i++) |
260 | 46.3M | { |
261 | 46.3M | if (*cpos++ != *rpos++) |
262 | 46.3M | { |
263 | 46.3M | mPosition++; |
264 | 46.3M | goto skip; |
265 | 46.3M | } |
266 | 46.3M | } |
267 | 4.92k | return CurrentPosition(*this); |
268 | 46.3M | skip: ; |
269 | 46.3M | } |
270 | | // Advance to the end since we didn't find a match. |
271 | 1.72k | mPosition = mEnd; |
272 | 1.72k | return CurrentPosition(*this); |
273 | 6.64k | } |
274 | | |
275 | | ParseBuffer::CurrentPosition |
276 | | ParseBuffer::skipToChars(const Data& sub) |
277 | 6.56k | { |
278 | 6.56k | const char* begSub = sub.mBuf; |
279 | 6.56k | const char* endSub = sub.mBuf + sub.mSize; |
280 | 6.56k | if(begSub == endSub) |
281 | 0 | { |
282 | 0 | fail(__FILE__, __LINE__, "ParseBuffer::skipToChars() called with an " |
283 | 0 | "empty string. Don't do this!"); |
284 | 0 | } |
285 | | |
286 | 6.56k | while (true) |
287 | 6.56k | { |
288 | 100M | next: |
289 | 100M | const char* searchPos = mPosition; |
290 | 100M | const char* subPos = sub.mBuf; |
291 | | |
292 | 100M | while (subPos != endSub) |
293 | 100M | { |
294 | 100M | if (searchPos == mEnd) |
295 | 6.46k | { |
296 | | // nope |
297 | 6.46k | mPosition = mEnd; |
298 | 6.46k | return CurrentPosition(*this); |
299 | 6.46k | } |
300 | 100M | if (*subPos++ != *searchPos++) |
301 | 100M | { |
302 | | // nope, but try the next position |
303 | 100M | ++mPosition; |
304 | 100M | goto next; |
305 | 100M | } |
306 | 100M | } |
307 | | // found a match |
308 | 101 | return CurrentPosition(*this); |
309 | 100M | } |
310 | 6.56k | } |
311 | | |
312 | | bool |
313 | | ParseBuffer::oneOf(char c, const char* cs) |
314 | 303M | { |
315 | 1.02G | while (*cs) |
316 | 726M | { |
317 | 726M | if (c == *(cs++)) |
318 | 1.81M | { |
319 | 1.81M | return true; |
320 | 1.81M | } |
321 | 726M | } |
322 | 302M | return false; |
323 | 303M | } |
324 | | |
325 | | bool |
326 | | ParseBuffer::oneOf(char c, const Data& cs) |
327 | 0 | { |
328 | 0 | for (Data::size_type i = 0; i < cs.size(); i++) |
329 | 0 | { |
330 | 0 | if (c == cs[i]) |
331 | 0 | { |
332 | 0 | return true; |
333 | 0 | } |
334 | 0 | } |
335 | 0 | return false; |
336 | 0 | } |
337 | | |
338 | | ParseBuffer::CurrentPosition |
339 | | ParseBuffer::skipToOneOf(const char* cs) |
340 | 40.3k | { |
341 | 124M | while (mPosition < mEnd) |
342 | 124M | { |
343 | 124M | if (oneOf(*mPosition, cs)) |
344 | 30.3k | { |
345 | 30.3k | return CurrentPosition(*this); |
346 | 30.3k | } |
347 | 124M | else |
348 | 124M | { |
349 | 124M | mPosition++; |
350 | 124M | } |
351 | 124M | } |
352 | 10.0k | return CurrentPosition(*this); |
353 | 40.3k | } |
354 | | |
355 | | ParseBuffer::CurrentPosition |
356 | | ParseBuffer::skipToOneOf(const char* cs1, |
357 | | const char* cs2) |
358 | 1.79M | { |
359 | 90.5M | while (mPosition < mEnd) |
360 | 90.5M | { |
361 | 90.5M | if (oneOf(*mPosition, cs1) || |
362 | 90.5M | oneOf(*mPosition, cs2)) |
363 | 1.78M | { |
364 | 1.78M | return CurrentPosition(*this); |
365 | 1.78M | } |
366 | 88.8M | else |
367 | 88.8M | { |
368 | 88.8M | mPosition++; |
369 | 88.8M | } |
370 | 90.5M | } |
371 | 4.87k | return CurrentPosition(*this); |
372 | 1.79M | } |
373 | | |
374 | | ParseBuffer::CurrentPosition |
375 | | ParseBuffer::skipToOneOf(const Data& cs) |
376 | 0 | { |
377 | 0 | while (mPosition < mEnd) |
378 | 0 | { |
379 | 0 | if (oneOf(*mPosition, cs)) |
380 | 0 | { |
381 | 0 | return CurrentPosition(*this); |
382 | 0 | } |
383 | 0 | else |
384 | 0 | { |
385 | 0 | mPosition++; |
386 | 0 | } |
387 | 0 | } |
388 | 0 | return CurrentPosition(*this); |
389 | 0 | } |
390 | | |
391 | | ParseBuffer::CurrentPosition |
392 | | ParseBuffer::skipToOneOf(const Data& cs1, |
393 | | const Data& cs2) |
394 | 0 | { |
395 | 0 | while (mPosition < mEnd) |
396 | 0 | { |
397 | 0 | if (oneOf(*mPosition, cs1) || |
398 | 0 | oneOf(*mPosition, cs2)) |
399 | 0 | { |
400 | 0 | return CurrentPosition(*this); |
401 | 0 | } |
402 | 0 | else |
403 | 0 | { |
404 | 0 | mPosition++; |
405 | 0 | } |
406 | 0 | } |
407 | 0 | return CurrentPosition(*this); |
408 | 0 | } |
409 | | |
410 | | const char* |
411 | | ParseBuffer::skipToEndQuote(char quote) |
412 | 3.96k | { |
413 | 9.38M | while (mPosition < mEnd) |
414 | 9.38M | { |
415 | | // !dlb! mark character encoding |
416 | 9.38M | if (*mPosition == '\\') |
417 | 2.69k | { |
418 | 2.69k | mPosition += 2; |
419 | 2.69k | } |
420 | 9.38M | else if (*mPosition == quote) |
421 | 3.47k | { |
422 | 3.47k | return mPosition; |
423 | 3.47k | } |
424 | 9.37M | else |
425 | 9.37M | { |
426 | 9.37M | mPosition++; |
427 | 9.37M | } |
428 | 9.38M | } |
429 | | |
430 | 492 | { |
431 | 492 | Data msg("Missing '"); |
432 | 492 | msg += quote; |
433 | 492 | msg += "'"; |
434 | 492 | fail(__FILE__,__LINE__,msg); |
435 | 492 | } |
436 | 492 | return 0; |
437 | 3.96k | } |
438 | | |
439 | | const char* |
440 | | ParseBuffer::skipBackChar() |
441 | 0 | { |
442 | 0 | if (bof()) |
443 | 0 | { |
444 | 0 | fail(__FILE__, __LINE__,"backed over beginning of buffer"); |
445 | 0 | } |
446 | 0 | mPosition--; |
447 | 0 | return mPosition; |
448 | 0 | } |
449 | | |
450 | | const char* |
451 | | ParseBuffer::skipBackWhitespace() |
452 | 300 | { |
453 | 1.08k | while (!bof()) |
454 | 1.08k | { |
455 | 1.08k | switch (*(--mPosition)) |
456 | 1.08k | { |
457 | 202 | case ' ' : |
458 | 396 | case '\t' : |
459 | 590 | case '\r' : |
460 | 784 | case '\n' : |
461 | 784 | { |
462 | 784 | break; |
463 | 590 | } |
464 | 300 | default : |
465 | 300 | return ++mPosition; |
466 | 1.08k | } |
467 | 1.08k | } |
468 | 0 | return mBuff; |
469 | 300 | } |
470 | | |
471 | | // abcde |
472 | | // ^ |
473 | | // skipBackChar('d'); |
474 | | // abcde |
475 | | // ^ |
476 | | // skipChar('d'); |
477 | | // abcde |
478 | | // ^ |
479 | | const char* |
480 | | ParseBuffer::skipBackChar(char c) |
481 | 0 | { |
482 | 0 | if (bof()) |
483 | 0 | { |
484 | 0 | fail(__FILE__, __LINE__,"backed over beginning of buffer"); |
485 | 0 | } |
486 | 0 | if (*(--mPosition) != c) |
487 | 0 | { |
488 | 0 | Data msg( "Expected '"); |
489 | 0 | msg += c; |
490 | 0 | msg += "'"; |
491 | 0 | fail(__FILE__, __LINE__,msg); |
492 | 0 | } |
493 | 0 | return mPosition; |
494 | 0 | } |
495 | | |
496 | | // abcde |
497 | | // ^ |
498 | | // skipBackToChar('c'); |
499 | | // abcde |
500 | | // ^ |
501 | | const char* |
502 | | ParseBuffer::skipBackToChar(char c) |
503 | 4 | { |
504 | 38 | while (!bof()) |
505 | 38 | { |
506 | 38 | if (*(--mPosition) == c) |
507 | 4 | { |
508 | 4 | return ++mPosition; |
509 | 4 | } |
510 | 38 | } |
511 | 0 | return mBuff; |
512 | 4 | } |
513 | | |
514 | | const char* |
515 | | ParseBuffer::skipBackToOneOf(const char* cs) |
516 | 0 | { |
517 | 0 | while (!bof()) |
518 | 0 | { |
519 | 0 | if (oneOf(*(--mPosition),cs)) |
520 | 0 | { |
521 | 0 | return ++mPosition; |
522 | 0 | } |
523 | 0 | } |
524 | 0 | return mBuff; |
525 | 0 | } |
526 | | |
527 | | void |
528 | | ParseBuffer::data(Data& data, const char* start) const |
529 | 3.21M | { |
530 | 3.21M | if (!(mBuff <= start && start <= mPosition)) |
531 | 0 | { |
532 | 0 | fail(__FILE__, __LINE__,"Bad anchor position"); |
533 | 0 | } |
534 | | |
535 | 3.21M | if (data.mShareEnum == Data::Take) |
536 | 0 | { |
537 | 0 | delete[] data.mBuf; |
538 | 0 | } |
539 | 3.21M | data.mSize = (unsigned int)(mPosition - start); |
540 | 3.21M | data.mBuf = const_cast<char*>(start); |
541 | 3.21M | data.mCapacity = data.mSize; |
542 | 3.21M | data.mShareEnum = Data::Share; |
543 | 3.21M | } |
544 | | |
545 | | static const unsigned char hexToByte[256] = |
546 | | { |
547 | | // 0 1 2 3 4 5 6 7 8 9 a b c d e f |
548 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//0 |
549 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//1 |
550 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//2 |
551 | | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'k','k','k','k','k','k', //3 |
552 | | 'k',0xA,0xB,0xC,0xD,0xE,0xF,'k','k','k','k','k','k','k','k','k', //4 |
553 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//5 |
554 | | 'k',0xA,0xB,0xC,0xD,0xE,0xF,'k','k','k','k','k','k','k','k','k', //6 |
555 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//8 |
556 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//9 |
557 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//a |
558 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//b |
559 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//c |
560 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//d |
561 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k',//e |
562 | | 'k','k','k','k','k','k','k','k','k','k','k','k','k','k','k','k' //f |
563 | | |
564 | | }; |
565 | | |
566 | | void |
567 | | ParseBuffer::dataUnescaped(Data& dataToUse, const char* start) const |
568 | 924 | { |
569 | 924 | if (!(mBuff <= start && start <= mPosition)) |
570 | 0 | { |
571 | 0 | fail(__FILE__, __LINE__, "Bad anchor position"); |
572 | 0 | } |
573 | | |
574 | 924 | { |
575 | 924 | const char* current = start; |
576 | 5.86M | while (current < mPosition) |
577 | 5.86M | { |
578 | 5.86M | if (*current == '%') |
579 | 362 | { |
580 | | // needs to be unencoded |
581 | 362 | goto copy; |
582 | 362 | } |
583 | 5.86M | current++; |
584 | 5.86M | } |
585 | | // can use an overlay |
586 | 562 | data(dataToUse, start); |
587 | 562 | return; |
588 | 924 | } |
589 | | |
590 | 362 | copy: |
591 | 362 | if ((size_t)(mPosition-start) > dataToUse.mCapacity) |
592 | 184 | { |
593 | 184 | dataToUse.resize(mPosition-start, false); |
594 | 184 | } |
595 | | |
596 | 362 | char* target = dataToUse.mBuf; |
597 | 362 | const char* current = start; |
598 | 2.85M | while (current < mPosition) |
599 | 2.85M | { |
600 | 2.85M | if (*current == '%') |
601 | 1.22k | { |
602 | 1.22k | current++; |
603 | 1.22k | if (mPosition - current < 2) |
604 | 59 | { |
605 | 59 | fail(__FILE__, __LINE__,"Illegal escaping"); |
606 | 59 | } |
607 | 1.22k | const char high = hexToByte[(unsigned char)*current]; |
608 | 1.22k | const char low = hexToByte[(unsigned char)*(current + 1)]; |
609 | 1.22k | if (high!='k' && low!='k') |
610 | 1.03k | { |
611 | 1.03k | unsigned char escaped = 0; |
612 | 1.03k | escaped = high << 4 | low; |
613 | | // !bwc! I think this check is bogus, especially the ':' (58) check |
614 | | // You could maybe argue that the point of %-escaping is to allow |
615 | | // the use of UTF-8 data (including ASCII that is not allowed in an |
616 | | // on-the-wire representation of whatever it is we're unescaping), |
617 | | // and not unprintable characters (the unprintable codes are not |
618 | | // used by UTF-8). |
619 | 1.03k | if (escaped > 31 && escaped != 127 && escaped != 58) |
620 | 279 | { |
621 | 279 | *target++ = escaped; |
622 | 279 | current+= 2; |
623 | 279 | } |
624 | 757 | else |
625 | 757 | { |
626 | 757 | *target++ = '%'; |
627 | 757 | *target++ = *current++; |
628 | 757 | *target++ = *current++; |
629 | 757 | } |
630 | 1.03k | } |
631 | 184 | else |
632 | 184 | { |
633 | 184 | fail(__FILE__, __LINE__,"Illegal escaping, not hex"); |
634 | 184 | } |
635 | 1.22k | } |
636 | 2.85M | else |
637 | 2.85M | { |
638 | 2.85M | *target++ = *current++; |
639 | 2.85M | } |
640 | 2.85M | } |
641 | 362 | *target = 0; |
642 | 362 | dataToUse.mSize = target - dataToUse.mBuf; |
643 | 362 | } |
644 | | |
645 | | Data |
646 | | ParseBuffer::data(const char* start) const |
647 | 1.53M | { |
648 | 1.53M | if (!(mBuff <= start && start <= mPosition)) |
649 | 0 | { |
650 | | |
651 | 0 | fail(__FILE__, __LINE__,"Bad anchor position"); |
652 | 0 | } |
653 | | |
654 | 1.53M | Data data(start, mPosition - start); |
655 | 1.53M | return data; |
656 | 1.53M | } |
657 | | |
658 | | int |
659 | | ParseBuffer::integer() |
660 | 160k | { |
661 | 160k | if (this->eof()) |
662 | 405 | { |
663 | 405 | fail(__FILE__, __LINE__,"Expected a digit, got eof "); |
664 | 405 | } |
665 | | |
666 | 160k | bool negative = false; |
667 | 160k | if (*mPosition == '-') |
668 | 745 | { |
669 | 745 | negative = true; |
670 | 745 | ++mPosition; |
671 | 745 | assertNotEof(); |
672 | 745 | } |
673 | 159k | else if (*mPosition == '+') |
674 | 292 | { |
675 | 292 | ++mPosition; |
676 | 292 | assertNotEof(); |
677 | 292 | } |
678 | | |
679 | 160k | if (!isdigit(*mPosition)) |
680 | 12.1k | { |
681 | 12.1k | Data msg("Expected a digit, got: "); |
682 | 12.1k | msg += Data(mPosition, (mEnd - mPosition)); |
683 | 12.1k | fail(__FILE__, __LINE__,msg); |
684 | 12.1k | } |
685 | | |
686 | | #ifdef _MSC_VER |
687 | | #pragma warning( push ) |
688 | | #pragma warning( disable : 4146) |
689 | | #endif |
690 | | // The absolute value limit depending on the detected sign |
691 | 160k | const unsigned int absoluteLimit = negative ? -(unsigned int)INT_MIN : INT_MAX; |
692 | | // maximum value for full number except last digit |
693 | 160k | const unsigned int border = absoluteLimit / 10; |
694 | | // value the last digit must not exceed |
695 | 160k | const unsigned int digitLimit = absoluteLimit % 10; |
696 | | |
697 | 160k | unsigned int num = 0; |
698 | 318k | while (!eof() && isdigit(*mPosition)) |
699 | 158k | { |
700 | 158k | const unsigned int c = *mPosition++ - '0'; |
701 | 158k | if (num > border || (num == border && c > digitLimit)) { |
702 | 128 | fail(__FILE__, __LINE__,"Overflow detected."); |
703 | 128 | } |
704 | 158k | num *= 10; |
705 | 158k | num += c; |
706 | 158k | } |
707 | 160k | if (negative) |
708 | 705 | { |
709 | 705 | num = -num; |
710 | 705 | } |
711 | 160k | return num; |
712 | | #ifdef _MSC_VER |
713 | | #pragma warning( pop ) |
714 | | #endif |
715 | 160k | } |
716 | | |
717 | | uint8_t |
718 | | ParseBuffer::uInt8() |
719 | 0 | { |
720 | 0 | const char* begin=mPosition; |
721 | 0 | uint8_t num = 0; |
722 | 0 | uint8_t last = 0; |
723 | 0 | while (!eof() && isdigit(*mPosition)) |
724 | 0 | { |
725 | 0 | last = num; |
726 | 0 | num = num*10 + (*mPosition-'0'); |
727 | 0 | if(last>num) |
728 | 0 | { |
729 | 0 | fail(__FILE__, __LINE__,"Overflow detected."); |
730 | 0 | } |
731 | 0 | ++mPosition; |
732 | 0 | } |
733 | | |
734 | 0 | if(mPosition==begin) |
735 | 0 | { |
736 | 0 | fail(__FILE__, __LINE__,"Expected a digit"); |
737 | 0 | } |
738 | 0 | return num; |
739 | 0 | } |
740 | | |
741 | | |
742 | | //!dcm! -- merge these, ask about length checks |
743 | | uint32_t |
744 | | ParseBuffer::uInt32() |
745 | 27.6k | { |
746 | 27.6k | const char* begin=mPosition; |
747 | 27.6k | uint32_t num = 0; |
748 | 59.7k | while (!eof() && isdigit(*mPosition)) |
749 | 32.1k | { |
750 | 32.1k | num = num*10 + (*mPosition-'0'); |
751 | 32.1k | ++mPosition; |
752 | 32.1k | } |
753 | | |
754 | 27.6k | switch(mPosition-begin) |
755 | 27.6k | { |
756 | 19.2k | case 0: |
757 | 19.2k | fail(__FILE__, __LINE__,"Expected a digit"); |
758 | 24.2k | case 1: |
759 | 24.5k | case 2: |
760 | 24.8k | case 3: |
761 | 25.0k | case 4: |
762 | 25.2k | case 5: |
763 | 25.4k | case 6: |
764 | 25.7k | case 7: |
765 | 25.9k | case 8: |
766 | 26.1k | case 9: |
767 | 26.1k | break; |
768 | 1.14k | case 10: |
769 | 1.14k | if(*begin<'4') |
770 | 455 | { |
771 | 455 | break; |
772 | 455 | } |
773 | 689 | else if(*begin=='4' && num >= 4000000000UL) |
774 | 247 | { |
775 | 247 | break; |
776 | 247 | } |
777 | 784 | default: |
778 | 784 | fail(__FILE__, __LINE__,"Overflow detected"); |
779 | 27.6k | } |
780 | | |
781 | 7.62k | return num; |
782 | 27.6k | } |
783 | | |
784 | | uint64_t |
785 | | ParseBuffer::uInt64() |
786 | 5.09k | { |
787 | 5.09k | const char* begin=mPosition; |
788 | 5.09k | uint64_t num = 0; |
789 | 7.26k | while (!eof() && isdigit(*mPosition)) |
790 | 2.16k | { |
791 | 2.16k | num = num*10 + (*mPosition-'0'); |
792 | 2.16k | ++mPosition; |
793 | 2.16k | } |
794 | | |
795 | 5.09k | switch(mPosition-begin) |
796 | 5.09k | { |
797 | 4.94k | case 0: |
798 | 4.94k | fail(__FILE__, __LINE__,"Expected a digit"); |
799 | 4.96k | case 1: |
800 | 4.96k | case 2: |
801 | 4.96k | case 3: |
802 | 4.97k | case 4: |
803 | 4.97k | case 5: |
804 | 4.97k | case 6: |
805 | 4.98k | case 7: |
806 | 4.98k | case 8: |
807 | 5.00k | case 9: |
808 | 5.00k | case 10: |
809 | 5.00k | case 11: |
810 | 5.01k | case 12: |
811 | 5.01k | case 13: |
812 | 5.01k | case 14: |
813 | 5.02k | case 15: |
814 | 5.02k | case 16: |
815 | 5.02k | case 17: |
816 | 5.02k | case 18: |
817 | 5.03k | case 19: |
818 | 5.03k | break; |
819 | 61 | case 20: |
820 | 61 | if(*begin=='1' && num >= 10000000000000000000ULL) |
821 | 46 | { |
822 | 46 | break; |
823 | 46 | } |
824 | 20 | default: |
825 | 20 | fail(__FILE__, __LINE__,"Overflow detected"); |
826 | 5.09k | } |
827 | | |
828 | 129 | return num; |
829 | 5.09k | } |
830 | | |
831 | | #ifndef RESIP_FIXED_POINT |
832 | | float |
833 | | ParseBuffer::floatVal() |
834 | 0 | { |
835 | 0 | const char* s = mPosition; |
836 | 0 | try |
837 | 0 | { |
838 | 0 | float mant = 0.0; |
839 | 0 | int num = integer(); |
840 | |
|
841 | 0 | if (*mPosition == '.') |
842 | 0 | { |
843 | 0 | skipChar(); |
844 | 0 | const char* pos = mPosition; |
845 | 0 | mant = float(integer()); |
846 | 0 | int s = int(mPosition - pos); |
847 | 0 | while (s--) |
848 | 0 | { |
849 | 0 | mant /= 10.0; |
850 | 0 | } |
851 | 0 | } |
852 | 0 | return num + mant; |
853 | 0 | } |
854 | 0 | catch (ParseException&) |
855 | 0 | { |
856 | 0 | Data msg("Expected a floating point value, got: "); |
857 | 0 | msg += Data(s, mPosition - s); |
858 | 0 | fail(__FILE__, __LINE__,msg); |
859 | 0 | return 0.0; |
860 | 0 | } |
861 | 0 | } |
862 | | #endif |
863 | | |
864 | | int |
865 | | ParseBuffer::qVal() |
866 | 6.20k | { |
867 | | // parse a qvalue into an integer between 0 and 1000 (ex: 1.0 -> 1000, 0.8 -> 800, 0.05 -> 50) |
868 | 6.20k | const char* s = mPosition; |
869 | 6.20k | try |
870 | 6.20k | { |
871 | 6.20k | int num = integer(); |
872 | 6.20k | if (num == 1) |
873 | 1.88k | { |
874 | 1.88k | num = 1000; |
875 | 1.88k | } |
876 | 4.32k | else if (num != 0) |
877 | 2.90k | { |
878 | | // error: qvalue must start with 1 or 0 |
879 | 2.90k | return 0; |
880 | 2.90k | } |
881 | | |
882 | 3.29k | if (!eof() && *mPosition == '.') |
883 | 1.03k | { |
884 | 1.03k | skipChar(); |
885 | | |
886 | 1.03k | int i = 100; |
887 | 2.50k | while(!eof() && isdigit(*mPosition) && i) |
888 | 1.46k | { |
889 | 1.46k | num += (*mPosition-'0') * i; |
890 | 1.46k | i /= 10; |
891 | 1.46k | skipChar(); |
892 | 1.46k | } |
893 | 1.03k | } |
894 | 3.29k | return num; |
895 | 6.20k | } |
896 | 6.20k | catch (ParseException&) |
897 | 6.20k | { |
898 | 225 | Data msg("Expected a floating point value, got: "); |
899 | 225 | msg += Data(s, mPosition - s); |
900 | 225 | fail(__FILE__, __LINE__,msg); |
901 | 225 | return 0; |
902 | 225 | } |
903 | 6.20k | } |
904 | | |
905 | | |
906 | | Data |
907 | | spaces(unsigned int numSpaces) |
908 | 78.2k | { |
909 | 78.2k | Data sps(numSpaces, Data::Preallocate); |
910 | 158M | for (unsigned int i = 0; i < numSpaces; i++) |
911 | 158M | { |
912 | 158M | sps += ' '; |
913 | 158M | } |
914 | 78.2k | return sps; |
915 | 78.2k | } |
916 | | |
917 | | Data |
918 | | escapeAndAnnotate(const char* buffer, |
919 | | Data::size_type size, |
920 | | const char* position) |
921 | 84.7k | { |
922 | 84.7k | Data ret(2*size+16, Data::Preallocate); |
923 | | |
924 | 84.7k | const char* lastReturn = buffer; |
925 | 84.7k | int lineCount = 0; |
926 | 84.7k | bool doneAt = false; |
927 | | |
928 | 84.7k | const char* p = buffer; |
929 | 1.14G | for (unsigned int i = 0; i < size; i++) |
930 | 1.14G | { |
931 | 1.14G | unsigned char c = *p++; |
932 | | |
933 | 1.14G | switch (c) |
934 | 1.14G | { |
935 | 205k | case 0x0D: // CR |
936 | 205k | { |
937 | 205k | continue; |
938 | 0 | } |
939 | 4.98M | case 0x0A: // LF |
940 | 4.98M | { |
941 | 4.98M | if (!doneAt && p >= position) |
942 | 35.3k | { |
943 | 35.3k | ret += "[CRLF]\n"; |
944 | 35.3k | ret += spaces((unsigned int)(position - lastReturn)); |
945 | 35.3k | ret += "^[CRLF]\n"; |
946 | 35.3k | doneAt = true; |
947 | 35.3k | } |
948 | 4.95M | else |
949 | 4.95M | { |
950 | 4.95M | lastReturn = p; |
951 | 4.95M | ret += c; |
952 | 4.95M | } |
953 | 4.98M | lineCount++; |
954 | 4.98M | continue; |
955 | 0 | } |
956 | 1.14G | } |
957 | | |
958 | 1.14G | if (iscntrl(c) || (c >= 0x7F)) |
959 | 557M | { |
960 | 557M | ret +='*'; // indicates unprintable character |
961 | 557M | continue; |
962 | 557M | } |
963 | | |
964 | 582M | ret += c; |
965 | 582M | } |
966 | 84.7k | if (!doneAt && p >= position) |
967 | 42.8k | { |
968 | 42.8k | ret += "\n"; |
969 | 42.8k | ret += spaces((unsigned int)(position - lastReturn)); |
970 | 42.8k | ret += "^\n"; |
971 | 42.8k | } |
972 | | |
973 | 84.7k | return ret; |
974 | 84.7k | } |
975 | | |
976 | | void |
977 | | ParseBuffer::fail(const char* file, unsigned int line, const Data& detail) const |
978 | 84.7k | { |
979 | 84.7k | Data errmsg; |
980 | 84.7k | { |
981 | 84.7k | DataStream ds(errmsg); |
982 | 84.7k | ds << file << ":" << line |
983 | 84.7k | << ", Parse failed "; |
984 | | |
985 | 84.7k | if (detail != Data::Empty) ds << detail << ' ' ; |
986 | | |
987 | 84.7k | ds << "in context: " << mErrorContext |
988 | 84.7k | << std::endl |
989 | 84.7k | << escapeAndAnnotate(mBuff, mEnd - mBuff, mPosition); |
990 | | |
991 | 84.7k | ds.flush(); |
992 | 84.7k | } |
993 | 84.7k | DebugLog(<<errmsg); |
994 | 84.7k | throw ParseException(errmsg, mErrorContext, file, line); |
995 | 84.7k | } |
996 | | |
997 | | ParseBuffer::Pointer::Pointer(const ParseBuffer& pb, |
998 | | const char* position, |
999 | | bool atEof) |
1000 | | : mPb(pb), |
1001 | | mPosition(position), |
1002 | | mIsValid(!atEof) |
1003 | 5.84k | {} |
1004 | | |
1005 | | ParseBuffer::Pointer::Pointer(const CurrentPosition& pos) : |
1006 | | mPb(pos.mPb), |
1007 | | mPosition(pos), |
1008 | | mIsValid(pos.mPb.valid()) |
1009 | 0 | {} |
1010 | | |
1011 | | const char& |
1012 | | ParseBuffer::Pointer::operator*() const |
1013 | 0 | { |
1014 | 0 | if (mIsValid) |
1015 | 0 | { |
1016 | 0 | return *mPosition; |
1017 | 0 | } |
1018 | 0 | else |
1019 | 0 | { |
1020 | 0 | throw ParseException(msg, mPb.getContext(), __FILE__, __LINE__); |
1021 | 0 | } |
1022 | 0 | } |
1023 | | |
1024 | | /* ==================================================================== |
1025 | | * The Vovida Software License, Version 1.0 |
1026 | | * |
1027 | | * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved. |
1028 | | * |
1029 | | * Redistribution and use in source and binary forms, with or without |
1030 | | * modification, are permitted provided that the following conditions |
1031 | | * are met: |
1032 | | * |
1033 | | * 1. Redistributions of source code must retain the above copyright |
1034 | | * notice, this list of conditions and the following disclaimer. |
1035 | | * |
1036 | | * 2. Redistributions in binary form must reproduce the above copyright |
1037 | | * notice, this list of conditions and the following disclaimer in |
1038 | | * the documentation and/or other materials provided with the |
1039 | | * distribution. |
1040 | | * |
1041 | | * 3. The names "VOCAL", "Vovida Open Communication Application Library", |
1042 | | * and "Vovida Open Communication Application Library (VOCAL)" must |
1043 | | * not be used to endorse or promote products derived from this |
1044 | | * software without prior written permission. For written |
1045 | | * permission, please contact vocal@vovida.org. |
1046 | | * |
1047 | | * 4. Products derived from this software may not be called "VOCAL", nor |
1048 | | * may "VOCAL" appear in their name, without prior written |
1049 | | * permission of Vovida Networks, Inc. |
1050 | | * |
1051 | | * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED |
1052 | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
1053 | | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND |
1054 | | * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA |
1055 | | * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES |
1056 | | * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL, |
1057 | | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
1058 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
1059 | | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
1060 | | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
1061 | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE |
1062 | | * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
1063 | | * DAMAGE. |
1064 | | * |
1065 | | * ==================================================================== |
1066 | | * |
1067 | | * This software consists of voluntary contributions made by Vovida |
1068 | | * Networks, Inc. and many individuals on behalf of Vovida Networks, |
1069 | | * Inc. For more information on Vovida Networks, Inc., please see |
1070 | | * <http://www.vovida.org/>. |
1071 | | * |
1072 | | */ |