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