1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright 2010 - 2015, Göteborg Bit Factory.
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included
13 // in all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 // SOFTWARE.
22 //
23 // http://www.opensource.org/licenses/mit-license.php
24 //
25 ////////////////////////////////////////////////////////////////////////////////
26
27 #include <cmake.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <inttypes.h>
33 #include <Nibbler.h>
34 #ifdef NIBBLER_FEATURE_DATE
35 #include <Date.h>
36 #endif
37 #ifdef NIBBLER_FEATURE_REGEX
38 #include <RX.h>
39 #endif
40 #include <util.h>
41
42 static const char* _uuid_pattern = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
43 static const unsigned int _uuid_min_length = 8;
44
45 ////////////////////////////////////////////////////////////////////////////////
Nibbler()46 Nibbler::Nibbler ()
47 : _input ("")
48 , _length (0)
49 , _cursor (0)
50 , _saved (0)
51 {
52 }
53
54 ////////////////////////////////////////////////////////////////////////////////
Nibbler(const std::string & input)55 Nibbler::Nibbler (const std::string& input)
56 : _input (input)
57 , _length (input.length ())
58 , _cursor (0)
59 {
60 }
61
62 ////////////////////////////////////////////////////////////////////////////////
Nibbler(const Nibbler & other)63 Nibbler::Nibbler (const Nibbler& other)
64 : _input (other._input)
65 , _length (other._length)
66 , _cursor (other._cursor)
67 {
68 }
69
70 ////////////////////////////////////////////////////////////////////////////////
operator =(const Nibbler & other)71 Nibbler& Nibbler::operator= (const Nibbler& other)
72 {
73 if (this != &other)
74 {
75 _input = other._input;
76 _length = other._length;
77 _cursor = other._cursor;
78 }
79
80 return *this;
81 }
82
83 ////////////////////////////////////////////////////////////////////////////////
~Nibbler()84 Nibbler::~Nibbler ()
85 {
86 }
87
88 ////////////////////////////////////////////////////////////////////////////////
89 // Extract up until the next c (but not including) or EOS.
getUntil(char c,std::string & result)90 bool Nibbler::getUntil (char c, std::string& result)
91 {
92 if (_cursor < _length)
93 {
94 std::string::size_type i = _input.find (c, _cursor);
95 if (i != std::string::npos)
96 {
97 result = _input.substr (_cursor, i - _cursor);
98 _cursor = i;
99 }
100 else
101 {
102 result = _input.substr (_cursor);
103 _cursor = _length;
104 }
105
106 return true;
107 }
108
109 return false;
110 }
111
112 ////////////////////////////////////////////////////////////////////////////////
getUntil(const std::string & terminator,std::string & result)113 bool Nibbler::getUntil (const std::string& terminator, std::string& result)
114 {
115 if (_cursor < _length)
116 {
117 std::string::size_type i = _input.find (terminator, _cursor);
118 if (i != std::string::npos)
119 {
120 result = _input.substr (_cursor, i - _cursor);
121 _cursor = i;
122 }
123 else
124 {
125 result = _input.substr (_cursor);
126 _cursor = _length;
127 }
128
129 return true;
130 }
131
132 return false;
133 }
134
135 ////////////////////////////////////////////////////////////////////////////////
136 #ifdef NIBBLER_FEATURE_REGEX
getUntilRx(const std::string & regex,std::string & result)137 bool Nibbler::getUntilRx (const std::string& regex, std::string& result)
138 {
139 if (_cursor < _length)
140 {
141 RX r (regex, true);
142 std::vector <int> start;
143 std::vector <int> end;
144 if (r.match (start, end, _input.substr (_cursor)))
145 {
146 result = _input.substr (_cursor, start[0]);
147 _cursor += start[0];
148 }
149 else
150 {
151 result = _input.substr (_cursor);
152 _cursor = _length;
153 }
154
155 return true;
156 }
157
158 return false;
159 }
160 #endif
161
162 ////////////////////////////////////////////////////////////////////////////////
getUntilOneOf(const std::string & chars,std::string & result)163 bool Nibbler::getUntilOneOf (const std::string& chars, std::string& result)
164 {
165 if (_cursor < _length)
166 {
167 std::string::size_type i = _input.find_first_of (chars, _cursor);
168 if (i != std::string::npos)
169 {
170 result = _input.substr (_cursor, i - _cursor);
171 _cursor = i;
172 }
173 else
174 {
175 result = _input.substr (_cursor);
176 _cursor = _length;
177 }
178
179 return true;
180 }
181
182 return false;
183 }
184
185 ////////////////////////////////////////////////////////////////////////////////
getUntilWS(std::string & result)186 bool Nibbler::getUntilWS (std::string& result)
187 {
188 return this->getUntilOneOf (" \t\r\n\f", result);
189 }
190
191 ////////////////////////////////////////////////////////////////////////////////
getUntilEOL(std::string & result)192 bool Nibbler::getUntilEOL (std::string& result)
193 {
194 return getUntil ('\n', result);
195 }
196
197 ////////////////////////////////////////////////////////////////////////////////
getUntilEOS(std::string & result)198 bool Nibbler::getUntilEOS (std::string& result)
199 {
200 if (_cursor < _length)
201 {
202 result = _input.substr (_cursor);
203 _cursor = _length;
204 return true;
205 }
206
207 return false;
208 }
209
210 ////////////////////////////////////////////////////////////////////////////////
getN(const int quantity,std::string & result)211 bool Nibbler::getN (const int quantity, std::string& result)
212 {
213 if (_cursor + quantity <= _length)
214 {
215 result = _input.substr (_cursor, quantity);
216 _cursor += quantity;
217 return true;
218 }
219
220 return false;
221 }
222
223 ////////////////////////////////////////////////////////////////////////////////
getQuoted(char c,std::string & result,bool quote)224 bool Nibbler::getQuoted (
225 char c,
226 std::string& result,
227 bool quote /* = false */)
228 {
229 bool inquote = false;
230 bool inescape = false;
231 char previous = 0;
232 char current = 0;
233 result = "";
234
235 if (_cursor >= _length ||
236 _input[_cursor] != c)
237 {
238 return false;
239 }
240
241 for (std::string::size_type i = _cursor; i < _length; ++i)
242 {
243 current = _input[i];
244
245 if (current == '\\' && !inescape)
246 {
247 inescape = true;
248 previous = current;
249 continue;
250 }
251
252 if (current == c && !inescape)
253 {
254 if (quote)
255 result += current;
256
257 if (!inquote)
258 {
259 inquote = true;
260 }
261 else
262 {
263 _cursor = i + 1;
264 return true;
265 }
266 }
267 else
268 {
269 if (previous)
270 {
271 result += previous;
272 previous = 0;
273 }
274
275 result += current;
276 inescape = false;
277 }
278 }
279
280 return false;
281 }
282
283 ////////////////////////////////////////////////////////////////////////////////
getDigit(int & result)284 bool Nibbler::getDigit (int& result)
285 {
286 if (_cursor < _length &&
287 isdigit (_input[_cursor]))
288 {
289 result = _input[_cursor++] - '0';
290 return true;
291 }
292
293 return false;
294 }
295
296 ////////////////////////////////////////////////////////////////////////////////
getDigit6(int & result)297 bool Nibbler::getDigit6 (int& result)
298 {
299 std::string::size_type i = _cursor;
300 if (i < _length &&
301 _length - i >= 6)
302 {
303 if (isdigit (_input[i + 0]) &&
304 isdigit (_input[i + 1]) &&
305 isdigit (_input[i + 2]) &&
306 isdigit (_input[i + 3]) &&
307 isdigit (_input[i + 4]) &&
308 isdigit (_input[i + 5]))
309 {
310 result = strtoimax (_input.substr (_cursor, 6).c_str (), NULL, 10);
311 _cursor += 6;
312 return true;
313 }
314 }
315
316 return false;
317 }
318
319 ////////////////////////////////////////////////////////////////////////////////
getDigit4(int & result)320 bool Nibbler::getDigit4 (int& result)
321 {
322 std::string::size_type i = _cursor;
323 if (i < _length &&
324 _length - i >= 4)
325 {
326 if (isdigit (_input[i + 0]) &&
327 isdigit (_input[i + 1]) &&
328 isdigit (_input[i + 2]) &&
329 isdigit (_input[i + 3]))
330 {
331 result = strtoimax (_input.substr (_cursor, 4).c_str (), NULL, 10);
332 _cursor += 4;
333 return true;
334 }
335 }
336
337 return false;
338 }
339
340 ////////////////////////////////////////////////////////////////////////////////
getDigit3(int & result)341 bool Nibbler::getDigit3 (int& result)
342 {
343 std::string::size_type i = _cursor;
344 if (i < _length &&
345 _length - i >= 3)
346 {
347 if (isdigit (_input[i + 0]) &&
348 isdigit (_input[i + 1]) &&
349 isdigit (_input[i + 2]))
350 {
351 result = strtoimax (_input.substr (_cursor, 3).c_str (), NULL, 10);
352 _cursor += 3;
353 return true;
354 }
355 }
356
357 return false;
358 }
359
360 ////////////////////////////////////////////////////////////////////////////////
getDigit2(int & result)361 bool Nibbler::getDigit2 (int& result)
362 {
363 std::string::size_type i = _cursor;
364 if (i < _length &&
365 _length - i >= 2)
366 {
367 if (isdigit (_input[i + 0]) &&
368 isdigit (_input[i + 1]))
369 {
370 result = strtoimax (_input.substr (_cursor, 2).c_str (), NULL, 10);
371 _cursor += 2;
372 return true;
373 }
374 }
375
376 return false;
377 }
378
379 ////////////////////////////////////////////////////////////////////////////////
getInt(int & result)380 bool Nibbler::getInt (int& result)
381 {
382 std::string::size_type i = _cursor;
383
384 if (i < _length)
385 {
386 if (_input[i] == '-')
387 ++i;
388 else if (_input[i] == '+')
389 ++i;
390 }
391
392 // TODO Potential for use of find_first_not_of
393 while (i < _length && isdigit (_input[i]))
394 ++i;
395
396 if (i > _cursor)
397 {
398 result = strtoimax (_input.substr (_cursor, i - _cursor).c_str (), NULL, 10);
399 _cursor = i;
400 return true;
401 }
402
403 return false;
404 }
405
406 ////////////////////////////////////////////////////////////////////////////////
getUnsignedInt(int & result)407 bool Nibbler::getUnsignedInt (int& result)
408 {
409 std::string::size_type i = _cursor;
410 // TODO Potential for use of find_first_not_of
411 while (i < _length && isdigit (_input[i]))
412 ++i;
413
414 if (i > _cursor)
415 {
416 result = strtoimax (_input.substr (_cursor, i - _cursor).c_str (), NULL, 10);
417 _cursor = i;
418 return true;
419 }
420
421 return false;
422 }
423
424 ////////////////////////////////////////////////////////////////////////////////
425 // number:
426 // int frac? exp?
427 //
428 // int:
429 // (-|+)? digit+
430 //
431 // frac:
432 // . digit+
433 //
434 // exp:
435 // e digit+
436 //
437 // e:
438 // e|E (+|-)?
439 //
getNumber(std::string & result)440 bool Nibbler::getNumber (std::string& result)
441 {
442 std::string::size_type i = _cursor;
443
444 // [+-]?
445 if (i < _length && (_input[i] == '-' || _input[i] == '+'))
446 ++i;
447
448 // digit+
449 if (i < _length && isdigit (_input[i]))
450 {
451 ++i;
452
453 while (i < _length && isdigit (_input[i]))
454 ++i;
455
456 // ( . digit+ )?
457 if (i < _length && _input[i] == '.')
458 {
459 ++i;
460
461 while (i < _length && isdigit (_input[i]))
462 ++i;
463 }
464
465 // ( [eE] [+-]? digit+ )?
466 if (i < _length && (_input[i] == 'e' || _input[i] == 'E'))
467 {
468 ++i;
469
470 if (i < _length && (_input[i] == '+' || _input[i] == '-'))
471 ++i;
472
473 if (i < _length && isdigit (_input[i]))
474 {
475 ++i;
476
477 while (i < _length && isdigit (_input[i]))
478 ++i;
479
480 result = _input.substr (_cursor, i - _cursor);
481 _cursor = i;
482 return true;
483 }
484
485 return false;
486 }
487
488 result = _input.substr (_cursor, i - _cursor);
489 _cursor = i;
490 return true;
491 }
492
493 return false;
494 }
495
496 ////////////////////////////////////////////////////////////////////////////////
getNumber(double & result)497 bool Nibbler::getNumber (double &result)
498 {
499 bool isnumber;
500 std::string s;
501
502 isnumber = getNumber (s);
503 if (isnumber)
504 {
505 result = strtof (s.c_str (), NULL);
506 }
507 return isnumber;
508 }
509
510 ////////////////////////////////////////////////////////////////////////////////
511 // number:
512 // int frac? exp?
513 //
514 // int:
515 // digit+
516 //
517 // frac:
518 // . digit+
519 //
520 // exp:
521 // e digit+
522 //
523 // e:
524 // e|E (+|-)?
525 //
getUnsignedNumber(double & result)526 bool Nibbler::getUnsignedNumber (double& result)
527 {
528 std::string::size_type i = _cursor;
529
530 // digit+
531 if (i < _length && isdigit (_input[i]))
532 {
533 ++i;
534
535 while (i < _length && isdigit (_input[i]))
536 ++i;
537
538 // ( . digit+ )?
539 if (i < _length && _input[i] == '.')
540 {
541 ++i;
542
543 while (i < _length && isdigit (_input[i]))
544 ++i;
545 }
546
547 // ( [eE] [+-]? digit+ )?
548 if (i < _length && (_input[i] == 'e' || _input[i] == 'E'))
549 {
550 ++i;
551
552 if (i < _length && (_input[i] == '+' || _input[i] == '-'))
553 ++i;
554
555 if (i < _length && isdigit (_input[i]))
556 {
557 ++i;
558
559 while (i < _length && isdigit (_input[i]))
560 ++i;
561
562 result = strtof (_input.substr (_cursor, i - _cursor).c_str (), NULL);
563 _cursor = i;
564 return true;
565 }
566
567 return false;
568 }
569
570 result = strtof (_input.substr (_cursor, i - _cursor).c_str (), NULL);
571 _cursor = i;
572 return true;
573 }
574
575 return false;
576 }
577
578 ////////////////////////////////////////////////////////////////////////////////
getLiteral(const std::string & literal)579 bool Nibbler::getLiteral (const std::string& literal)
580 {
581 if (_cursor < _length &&
582 _input.find (literal, _cursor) == _cursor)
583 {
584 _cursor += literal.length ();
585 return true;
586 }
587
588 return false;
589 }
590
591 ////////////////////////////////////////////////////////////////////////////////
592 #ifdef NIBBLER_FEATURE_REGEX
getRx(const std::string & regex,std::string & result)593 bool Nibbler::getRx (const std::string& regex, std::string& result)
594 {
595 if (_cursor < _length)
596 {
597 // Regex may be anchored to the beginning and include capturing parentheses,
598 // otherwise they are added.
599 std::string modified_regex;
600 if (regex.substr (0, 2) != "^(")
601 modified_regex = "^(" + regex + ")";
602 else
603 modified_regex = regex;
604
605 RX r (modified_regex, true);
606 std::vector <std::string> results;
607 if (r.match (results, _input.substr (_cursor)))
608 {
609 result = results[0];
610 _cursor += result.length ();
611 return true;
612 }
613 }
614
615 return false;
616 }
617 #endif
618
619 ////////////////////////////////////////////////////////////////////////////////
getUUID(std::string & result)620 bool Nibbler::getUUID (std::string& result)
621 {
622 std::string::size_type i = _cursor;
623
624 if (i < _length &&
625 _length - i >= 36)
626 {
627 // 88888888-4444-4444-4444-cccccccccccc
628 if (isxdigit (_input[i + 0]) &&
629 isxdigit (_input[i + 1]) &&
630 isxdigit (_input[i + 2]) &&
631 isxdigit (_input[i + 3]) &&
632 isxdigit (_input[i + 4]) &&
633 isxdigit (_input[i + 5]) &&
634 isxdigit (_input[i + 6]) &&
635 isxdigit (_input[i + 7]) &&
636 _input[i + 8] == '-' &&
637 isxdigit (_input[i + 9]) &&
638 isxdigit (_input[i + 10]) &&
639 isxdigit (_input[i + 11]) &&
640 isxdigit (_input[i + 12]) &&
641 _input[i + 13] == '-' &&
642 isxdigit (_input[i + 14]) &&
643 isxdigit (_input[i + 15]) &&
644 isxdigit (_input[i + 16]) &&
645 isxdigit (_input[i + 17]) &&
646 _input[i + 18] == '-' &&
647 isxdigit (_input[i + 19]) &&
648 isxdigit (_input[i + 20]) &&
649 isxdigit (_input[i + 21]) &&
650 isxdigit (_input[i + 22]) &&
651 _input[i + 23] == '-' &&
652 isxdigit (_input[i + 24]) &&
653 isxdigit (_input[i + 25]) &&
654 isxdigit (_input[i + 26]) &&
655 isxdigit (_input[i + 27]) &&
656 isxdigit (_input[i + 28]) &&
657 isxdigit (_input[i + 29]) &&
658 isxdigit (_input[i + 30]) &&
659 isxdigit (_input[i + 31]) &&
660 isxdigit (_input[i + 32]) &&
661 isxdigit (_input[i + 33]) &&
662 isxdigit (_input[i + 34]) &&
663 isxdigit (_input[i + 35]))
664 {
665 result = _input.substr (_cursor, 36);
666 _cursor = i + 36;
667 return true;
668 }
669 }
670
671 return false;
672 }
673
674 ////////////////////////////////////////////////////////////////////////////////
getPartialUUID(std::string & result)675 bool Nibbler::getPartialUUID (std::string& result)
676 {
677 std::string::size_type i;
678 for (i = 0; i < 36 && i < (_length - _cursor); i++)
679 {
680 if (_uuid_pattern[i] == 'x' && !isxdigit (_input[_cursor + i]))
681 break;
682
683 else if (_uuid_pattern[i] == '-' && _input[_cursor + i] != '-')
684 break;
685 }
686
687 // If the partial match found is long enough, consider it a match.
688 if (i >= _uuid_min_length)
689 {
690 // Fail if there is another hex digit.
691 if (_cursor + i < _length &&
692 isxdigit (_input[_cursor + i]))
693 return false;
694
695 result = _input.substr (_cursor, i);
696 _cursor += i;
697
698 return true;
699 }
700
701 return false;
702 }
703
704 ////////////////////////////////////////////////////////////////////////////////
705 // 19980119T070000Z = YYYYMMDDThhmmssZ
getDateISO(time_t & t)706 bool Nibbler::getDateISO (time_t& t)
707 {
708 std::string::size_type i = _cursor;
709
710 if (i < _length &&
711 _length - i >= 16)
712 {
713 if (isdigit (_input[i + 0]) &&
714 isdigit (_input[i + 1]) &&
715 isdigit (_input[i + 2]) &&
716 isdigit (_input[i + 3]) &&
717 isdigit (_input[i + 4]) &&
718 isdigit (_input[i + 5]) &&
719 isdigit (_input[i + 6]) &&
720 isdigit (_input[i + 7]) &&
721 _input[i + 8] == 'T' &&
722 isdigit (_input[i + 9]) &&
723 isdigit (_input[i + 10]) &&
724 isdigit (_input[i + 11]) &&
725 isdigit (_input[i + 12]) &&
726 isdigit (_input[i + 13]) &&
727 isdigit (_input[i + 14]) &&
728 _input[i + 15] == 'Z')
729 {
730 _cursor += 16;
731
732 int year = (_input[i + 0] - '0') * 1000
733 + (_input[i + 1] - '0') * 100
734 + (_input[i + 2] - '0') * 10
735 + (_input[i + 3] - '0');
736
737 int month = (_input[i + 4] - '0') * 10
738 + (_input[i + 5] - '0');
739
740 int day = (_input[i + 6] - '0') * 10
741 + (_input[i + 7] - '0');
742
743 int hour = (_input[i + 9] - '0') * 10
744 + (_input[i + 10] - '0');
745
746 int minute = (_input[i + 11] - '0') * 10
747 + (_input[i + 12] - '0');
748
749 int second = (_input[i + 13] - '0') * 10
750 + (_input[i + 14] - '0');
751
752 // Convert to epoch.
753 struct tm tms = {0};
754 tms.tm_isdst = -1; // Requests that mktime determine summer time effect.
755 tms.tm_mon = month - 1;
756 tms.tm_mday = day;
757 tms.tm_year = year - 1900;
758 tms.tm_hour = hour;
759 tms.tm_min = minute;
760 tms.tm_sec = second;
761 #ifdef HAVE_TM_GMTOFF
762 tms.tm_gmtoff = 0;
763 #endif
764
765 t = timegm (&tms);
766 return true;
767 }
768 }
769
770 return false;
771 }
772
773 ////////////////////////////////////////////////////////////////////////////////
774 // Parse the longest integer using the next 'limit' characters of 'result'
775 // following position 'i' (when strict is true, the number of digits must be
776 // equal to limit).
parseDigits(std::string::size_type & i,int & result,unsigned int limit,bool strict)777 bool Nibbler::parseDigits(std::string::size_type& i,
778 int& result,
779 unsigned int limit,
780 bool strict /* = true */)
781 {
782 // If the result has already been set
783 if (result != -1)
784 return false;
785 for (unsigned int f = limit; f > 0; --f)
786 {
787 // Check that the nibbler has enough unparsed characters
788 if (i + f <= _length)
789 {
790 // Check that 'f' of them are digits
791 unsigned int g;
792 for (g = 0; g < f; g++)
793 if (! isdigit (_input[i + g]))
794 break;
795 // Parse the integer when it is the case
796 if (g == f)
797 {
798 if (f == 1)
799 result = _input[i] - '0';
800 else
801 result = atoi (_input.substr (i, f).c_str ());
802 // Update the global cursor before returning
803 i += f;
804 return true;
805 }
806 }
807 // Do not try smaller limits if the option is strict on the size
808 if (strict)
809 break;
810 }
811 return false;
812 }
813
814 ////////////////////////////////////////////////////////////////////////////////
815 #ifdef NIBBLER_FEATURE_DATE
getDate(const std::string & format,time_t & t)816 bool Nibbler::getDate (const std::string& format, time_t& t)
817 {
818 std::string::size_type i = _cursor;
819
820 int month = -1; // So we can check later.
821 int day = -1;
822 int year = -1;
823 int hour = -1;
824 int minute = -1;
825 int second = -1;
826
827 // For parsing, unused.
828 int wday = -1;
829 int week = -1;
830
831 for (unsigned int f = 0; f < format.length (); ++f)
832 {
833 switch (format[f])
834 {
835 case 'm':
836 case 'M':
837 if (! parseDigits(i, month, 2, format[f] == 'M'))
838 return false;
839 break;
840
841 case 'd':
842 case 'D':
843 if (! parseDigits(i, day, 2, format[f] == 'D'))
844 return false;
845 break;
846
847 case 'y':
848 case 'Y':
849 if (! parseDigits(i, year, format[f] == 'y' ? 2 : 4))
850 return false;
851 if (format[f] == 'y')
852 year += 2000;
853 break;
854
855 case 'h':
856 case 'H':
857 if (! parseDigits(i, hour, 2, format[f] == 'H'))
858 return false;
859 break;
860
861 case 'n':
862 case 'N':
863 if (! parseDigits(i, minute, 2, format[f] == 'N'))
864 return false;
865 break;
866
867 case 's':
868 case 'S':
869 if (! parseDigits(i, second, 2, format[f] == 'S'))
870 return false;
871 break;
872
873 // Merely parse, not extract.
874 case 'v':
875 case 'V':
876 if (! parseDigits(i, week, 2, format[f] == 'V'))
877 return false;
878 break;
879
880 // Merely parse, not extract.
881 case 'a':
882 case 'A':
883 if (i + 3 <= _length &&
884 ! isdigit (_input[i + 0]) &&
885 ! isdigit (_input[i + 1]) &&
886 ! isdigit (_input[i + 2]))
887 {
888 wday = Date::dayOfWeek (_input.substr (i, 3).c_str ());
889 i += (format[f] == 'a') ? 3 : Date::dayName (wday).size ();
890 }
891 else
892 return false;
893 break;
894
895 case 'b':
896 case 'B':
897 if (i + 3 <= _length &&
898 ! isdigit (_input[i + 0]) &&
899 ! isdigit (_input[i + 1]) &&
900 ! isdigit (_input[i + 2]))
901 {
902 if (month != -1)
903 return false;
904 month = Date::monthOfYear (_input.substr (i, 3).c_str());
905 i += (format[f] == 'b') ? 3 : Date::monthName (month).size ();
906 }
907 else
908 return false;
909 break;
910
911 default:
912 if (i + 1 <= _length &&
913 _input[i] == format[f])
914 ++i;
915 else
916 return false;
917 break;
918 }
919 }
920
921 // By default, the most global date variables that are undefined are put to
922 // the current date (for instance, the year to the current year for formats
923 // that lack Y/y). If even 'second' is undefined, then the date is parsed as
924 // now.
925 if (year == -1)
926 {
927 Date now = Date ();
928 year = now.year ();
929 if (month == -1)
930 {
931 month = now.month ();
932 if (day == -1)
933 {
934 day = now.day ();
935 if (hour == -1)
936 {
937 hour = now.hour ();
938 if (minute == -1)
939 {
940 minute = now.minute ();
941 if (second == -1)
942 second = now.second ();
943 }
944 }
945 }
946 }
947 }
948
949 // Put all remaining undefined date variables to their default values (0 or
950 // 1).
951 month = (month == -1) ? 1 : month;
952 day = (day == -1) ? 1 : day;
953 hour = (hour == -1) ? 0 : hour;
954 minute = (minute == -1) ? 0 : minute;
955 second = (second == -1) ? 0 : second;
956
957 // Check that values are correct
958 if (! Date::valid (month, day, year, hour, minute, second))
959 return false;
960
961 // Convert to epoch.
962 struct tm tms = {0};
963 tms.tm_isdst = -1; // Requests that mktime determine summer time effect.
964 tms.tm_mon = month - 1;
965 tms.tm_mday = day;
966 tms.tm_year = year - 1900;
967 tms.tm_hour = hour;
968 tms.tm_min = minute;
969 tms.tm_sec = second;
970
971 t = mktime (&tms);
972 _cursor = i;
973 return true;
974 }
975 #endif
976
977 ////////////////////////////////////////////////////////////////////////////////
978 // Assumes that the options are sorted by decreasing length, so that if the
979 // options contain 'fourteen' and 'four', the stream is first matched against
980 // the longer entry.
getOneOf(const std::vector<std::string> & options,std::string & found)981 bool Nibbler::getOneOf (
982 const std::vector <std::string>& options,
983 std::string& found)
984 {
985 std::vector <std::string>::const_iterator option;
986 for (option = options.begin (); option != options.end (); ++option)
987 {
988 if (getLiteral (*option))
989 {
990 found = *option;
991 return true;
992 }
993 }
994
995 return false;
996 }
997
998 ////////////////////////////////////////////////////////////////////////////////
999 // A name is a string of alpha-numeric characters.
getName(std::string & result)1000 bool Nibbler::getName (std::string& result)
1001 {
1002 std::string::size_type i = _cursor;
1003
1004 if (i < _length)
1005 {
1006 if (! isdigit (_input[i]) &&
1007 ! ispunct (_input[i]) &&
1008 ! isspace (_input[i]))
1009 {
1010 ++i;
1011 while (i < _length &&
1012 (_input[i] == '_' || ! ispunct (_input[i])) &&
1013 ! isspace (_input[i]))
1014 {
1015 ++i;
1016 }
1017 }
1018
1019 if (i > _cursor)
1020 {
1021 result = _input.substr (_cursor, i - _cursor);
1022 _cursor = i;
1023 return true;
1024 }
1025 }
1026
1027 return false;
1028 }
1029
1030 ////////////////////////////////////////////////////////////////////////////////
1031 // A word is a contiguous string of non-space, non-digit, non-punct characters.
getWord(std::string & result)1032 bool Nibbler::getWord (std::string& result)
1033 {
1034 std::string::size_type i = _cursor;
1035
1036 if (i < _length)
1037 {
1038 while (!isdigit (_input[i]) &&
1039 !isPunctuation (_input[i]) &&
1040 !isspace (_input[i]))
1041 {
1042 ++i;
1043 }
1044
1045 if (i > _cursor)
1046 {
1047 result = _input.substr (_cursor, i - _cursor);
1048 _cursor = i;
1049 return true;
1050 }
1051 }
1052
1053 return false;
1054 }
1055
1056 ////////////////////////////////////////////////////////////////////////////////
skipN(const int quantity)1057 bool Nibbler::skipN (const int quantity /* = 1 */)
1058 {
1059 if (_cursor < _length &&
1060 _cursor <= _length - quantity)
1061 {
1062 _cursor += quantity;
1063 return true;
1064 }
1065
1066 return false;
1067 }
1068
1069 ////////////////////////////////////////////////////////////////////////////////
skip(char c)1070 bool Nibbler::skip (char c)
1071 {
1072 if (_cursor < _length &&
1073 _input[_cursor] == c)
1074 {
1075 ++_cursor;
1076 return true;
1077 }
1078
1079 return false;
1080 }
1081
1082 ////////////////////////////////////////////////////////////////////////////////
skipAll(char c)1083 bool Nibbler::skipAll (char c)
1084 {
1085 if (_cursor < _length)
1086 {
1087 std::string::size_type i = _input.find_first_not_of (c, _cursor);
1088 if (i == _cursor)
1089 return false;
1090
1091 if (i == std::string::npos)
1092 _cursor = _length; // Yes, off the end.
1093 else
1094 _cursor = i;
1095
1096 return true;
1097 }
1098
1099 return false;
1100 }
1101
1102 ////////////////////////////////////////////////////////////////////////////////
skipWS()1103 bool Nibbler::skipWS ()
1104 {
1105 return this->skipAllOneOf (" \t\n\r\f");
1106 }
1107
1108 ////////////////////////////////////////////////////////////////////////////////
1109 #ifdef NIBBLER_FEATURE_REGEX
skipRx(const std::string & regex)1110 bool Nibbler::skipRx (const std::string& regex)
1111 {
1112 if (_cursor < _length)
1113 {
1114 // Regex may be anchored to the beginning and include capturing parentheses,
1115 // otherwise they are added.
1116 std::string modified_regex;
1117 if (regex.substr (0, 2) != "^(")
1118 modified_regex = "^(" + regex + ")";
1119 else
1120 modified_regex = regex;
1121
1122 RX r (modified_regex, true);
1123 std::vector <std::string> results;
1124 if (r.match (results, _input.substr (_cursor)))
1125 {
1126 _cursor += results[0].length ();
1127 return true;
1128 }
1129 }
1130
1131 return false;
1132 }
1133 #endif
1134
1135 ////////////////////////////////////////////////////////////////////////////////
backN(const int quantity)1136 bool Nibbler::backN (const int quantity /*= 1*/)
1137 {
1138 if (_cursor >= (unsigned) quantity)
1139 {
1140 _cursor -= (unsigned) quantity;
1141 return true;
1142 }
1143
1144 return false;
1145 }
1146
1147 ////////////////////////////////////////////////////////////////////////////////
getRemainder(std::string & result)1148 void Nibbler::getRemainder (std::string& result)
1149 {
1150 if (_cursor < _length)
1151 result = _input.substr (_cursor);
1152 }
1153
1154
1155 ////////////////////////////////////////////////////////////////////////////////
skipAllOneOf(const std::string & chars)1156 bool Nibbler::skipAllOneOf (const std::string& chars)
1157 {
1158 if (_cursor < _length)
1159 {
1160 std::string::size_type i = _input.find_first_not_of (chars, _cursor);
1161 if (i == _cursor)
1162 return false;
1163
1164 if (i == std::string::npos)
1165 _cursor = _length; // Yes, off the end.
1166 else
1167 _cursor = i;
1168
1169 return true;
1170 }
1171
1172 return false;
1173 }
1174
1175 ////////////////////////////////////////////////////////////////////////////////
1176 // Peeks ahead - does not move cursor.
next()1177 char Nibbler::next ()
1178 {
1179 if (_cursor < _length)
1180 return _input[_cursor];
1181
1182 return '\0';
1183 }
1184
1185 ////////////////////////////////////////////////////////////////////////////////
cursor()1186 std::string::size_type Nibbler::cursor ()
1187 {
1188 return _cursor;
1189 }
1190
1191 ////////////////////////////////////////////////////////////////////////////////
1192 // Peeks ahead - does not move cursor.
next(const int quantity)1193 std::string Nibbler::next (const int quantity)
1194 {
1195 if ( _cursor < _length &&
1196 (unsigned) quantity <= _length &&
1197 _cursor <= _length - quantity)
1198 return _input.substr (_cursor, quantity);
1199
1200 return "";
1201 }
1202
1203 ////////////////////////////////////////////////////////////////////////////////
save()1204 std::string::size_type Nibbler::save ()
1205 {
1206 return _saved = _cursor;
1207 }
1208
1209 ////////////////////////////////////////////////////////////////////////////////
restore()1210 std::string::size_type Nibbler::restore ()
1211 {
1212 return _cursor = _saved;
1213 }
1214
1215 ////////////////////////////////////////////////////////////////////////////////
str() const1216 const std::string& Nibbler::str () const
1217 {
1218 return _input;
1219 }
1220
1221 ////////////////////////////////////////////////////////////////////////////////
depleted()1222 bool Nibbler::depleted ()
1223 {
1224 if (_cursor >= _length)
1225 return true;
1226
1227 return false;
1228 }
1229
1230 ////////////////////////////////////////////////////////////////////////////////
1231 // Override of ispunct, that considers #, $, _ and @ not to be punctuation.
1232 //
1233 // ispunct: ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
1234 // Punctuation: ! " % & ' ( ) * + , - . / : ; < = > ? [ \ ] ^ _ ` { | } ~
1235 // delta: # $ @
1236 //
isPunctuation(char c)1237 bool Nibbler::isPunctuation (char c)
1238 {
1239 if (c == '@' || c == '#' || c == '$' || c == '_')
1240 return false;
1241
1242 return ispunct (c);
1243 }
1244
1245 ////////////////////////////////////////////////////////////////////////////////
dump()1246 std::string Nibbler::dump ()
1247 {
1248 return std::string ("Nibbler ‹")
1249 + _input.substr (_cursor)
1250 + "›";
1251 }
1252
1253 ////////////////////////////////////////////////////////////////////////////////
1254