1 /**
2  * Copyright (c) 2014, Timothy Stack
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * * Redistributions of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  * * Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation
13  * and/or other materials provided with the distribution.
14  * * Neither the name of Timothy Stack nor the names of its contributors
15  * may be used to endorse or promote products derived from this software
16  * without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
25  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  *
29  * @file ptimec.hh
30  */
31 
32 #ifndef pctimec_hh
33 #define pctimec_hh
34 
35 // XXX
36 #define __STDC_FORMAT_MACROS
37 #include <stdio.h>
38 #include <string.h>
39 #include <ctype.h>
40 #include <time.h>
41 #include <inttypes.h>
42 #include <sys/types.h>
43 
44 #include <cstdlib>
45 
46 #include "base/lnav_log.hh"
47 #include "base/time_util.hh"
48 
49 #define PTIME_CONSUME(amount, block) \
50     if ((off_inout + (amount)) > len) { \
51         return false; \
52     } \
53     \
54     block \
55     \
56     off_inout += (amount);
57 
58 #define PTIME_APPEND(ch) \
59     if ((off_inout + 2) >= len) { \
60         return; \
61     } \
62     dst[off_inout] = ch; \
63     off_inout += 1;
64 
65 #define ABR_TO_INT(a, b, c) \
66     (((a) << 24) | ((b) << 16) | ((c) << 8))
67 
68 inline
ptime_upto(char ch,const char * str,off_t & off_inout,ssize_t len)69 bool ptime_upto(char ch, const char *str, off_t &off_inout, ssize_t len)
70 {
71     for (; off_inout < len; off_inout++) {
72         if (str[off_inout] == ch) {
73             return true;
74         }
75     }
76 
77     return false;
78 }
79 
80 inline
ptime_upto_end(const char * str,off_t & off_inout,ssize_t len)81 bool ptime_upto_end(const char *str, off_t &off_inout, ssize_t len)
82 {
83     off_inout = len;
84 
85     return true;
86 }
87 
88 bool ptime_b_slow(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len);
89 
ptime_b(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)90 inline bool ptime_b(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
91 {
92     if (off_inout + 3 < len) {
93         auto month_start = (unsigned char *) &str[off_inout];
94         uint32_t month_int =
95             ABR_TO_INT(month_start[0] & ~0x20UL,
96                        month_start[1] & ~0x20UL,
97                        month_start[2] & ~0x20UL);
98         int val;
99 
100         switch (month_int) {
101         case ABR_TO_INT('J', 'A', 'N'):
102             val = 0;
103             break;
104         case ABR_TO_INT('F', 'E', 'B'):
105             val = 1;
106             break;
107         case ABR_TO_INT('M', 'A', 'R'):
108             val = 2;
109             break;
110         case ABR_TO_INT('A', 'P', 'R'):
111             val = 3;
112             break;
113         case ABR_TO_INT('M', 'A', 'Y'):
114             val = 4;
115             break;
116         case ABR_TO_INT('J', 'U', 'N'):
117             val = 5;
118             break;
119         case ABR_TO_INT('J', 'U', 'L'):
120             val = 6;
121             break;
122         case ABR_TO_INT('A', 'U', 'G'):
123             val = 7;
124             break;
125         case ABR_TO_INT('S', 'E', 'P'):
126             val = 8;
127             break;
128         case ABR_TO_INT('O', 'C', 'T'):
129             val = 9;
130             break;
131         case ABR_TO_INT('N', 'O', 'V'):
132             val = 10;
133             break;
134         case ABR_TO_INT('D', 'E', 'C'):
135             val = 11;
136             break;
137         default:
138             val = -1;
139             break;
140         }
141         if (val >= 0) {
142             off_inout += 3;
143             dst->et_tm.tm_mon = val;
144             dst->et_flags |= ETF_MONTH_SET;
145             return true;
146         }
147     }
148 
149     return ptime_b_slow(dst, str, off_inout, len);
150 }
151 
ftime_a(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)152 inline void ftime_a(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
153 {
154     switch (tm.et_tm.tm_wday) {
155         case 0:
156             PTIME_APPEND('S');
157             PTIME_APPEND('u');
158             PTIME_APPEND('n');
159             break;
160         case 1:
161             PTIME_APPEND('M');
162             PTIME_APPEND('o');
163             PTIME_APPEND('n');
164             break;
165         case 2:
166             PTIME_APPEND('T');
167             PTIME_APPEND('u');
168             PTIME_APPEND('e');
169             break;
170         case 3:
171             PTIME_APPEND('W');
172             PTIME_APPEND('e');
173             PTIME_APPEND('d');
174             break;
175         case 4:
176             PTIME_APPEND('T');
177             PTIME_APPEND('h');
178             PTIME_APPEND('u');
179             break;
180         case 5:
181             PTIME_APPEND('F');
182             PTIME_APPEND('r');
183             PTIME_APPEND('i');
184             break;
185         case 6:
186             PTIME_APPEND('S');
187             PTIME_APPEND('a');
188             PTIME_APPEND('t');
189             break;
190         default:
191             PTIME_APPEND('X');
192             PTIME_APPEND('X');
193             PTIME_APPEND('X');
194             break;
195     }
196 }
197 
ftime_Z(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)198 inline void ftime_Z(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
199 {
200 }
201 
ftime_b(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)202 inline void ftime_b(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
203 {
204     switch (tm.et_tm.tm_mon) {
205         case 0:
206             PTIME_APPEND('J');
207             PTIME_APPEND('a');
208             PTIME_APPEND('n');
209             break;
210         case 1:
211             PTIME_APPEND('F');
212             PTIME_APPEND('e');
213             PTIME_APPEND('b');
214             break;
215         case 2:
216             PTIME_APPEND('M');
217             PTIME_APPEND('a');
218             PTIME_APPEND('r');
219             break;
220         case 3:
221             PTIME_APPEND('A');
222             PTIME_APPEND('p');
223             PTIME_APPEND('r');
224             break;
225         case 4:
226             PTIME_APPEND('M');
227             PTIME_APPEND('a');
228             PTIME_APPEND('y');
229             break;
230         case 5:
231             PTIME_APPEND('J');
232             PTIME_APPEND('u');
233             PTIME_APPEND('n');
234             break;
235         case 6:
236             PTIME_APPEND('J');
237             PTIME_APPEND('u');
238             PTIME_APPEND('l');
239             break;
240         case 7:
241             PTIME_APPEND('A');
242             PTIME_APPEND('u');
243             PTIME_APPEND('g');
244             break;
245         case 8:
246             PTIME_APPEND('S');
247             PTIME_APPEND('e');
248             PTIME_APPEND('p');
249             break;
250         case 9:
251             PTIME_APPEND('O');
252             PTIME_APPEND('c');
253             PTIME_APPEND('t');
254             break;
255         case 10:
256             PTIME_APPEND('N');
257             PTIME_APPEND('o');
258             PTIME_APPEND('v');
259             break;
260         case 11:
261             PTIME_APPEND('D');
262             PTIME_APPEND('e');
263             PTIME_APPEND('c');
264             break;
265         default:
266             PTIME_APPEND('X');
267             PTIME_APPEND('X');
268             PTIME_APPEND('X');
269             break;
270     }
271 }
272 
ptime_S(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)273 inline bool ptime_S(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
274 {
275     PTIME_CONSUME(2, {
276         if (str[off_inout + 1] > '9') {
277             return false;
278         }
279         dst->et_tm.tm_sec = (str[off_inout] - '0') * 10 + (str[off_inout + 1] - '0');
280     });
281 
282     return (dst->et_tm.tm_sec >= 0 && dst->et_tm.tm_sec <= 59);
283 }
284 
ftime_S(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)285 inline void ftime_S(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
286 {
287     PTIME_APPEND('0' + ((tm.et_tm.tm_sec / 10) % 10));
288     PTIME_APPEND('0' + ((tm.et_tm.tm_sec /  1) % 10));
289 }
290 
ptime_s(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)291 inline bool ptime_s(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
292 {
293     off_t off_start = off_inout;
294     lnav::time64_t epoch = 0;
295 
296     while (off_inout < len && isdigit(str[off_inout])) {
297         if ((off_inout - off_start) > 11) {
298             return false;
299         }
300 
301         epoch *= 10;
302         epoch += str[off_inout] - '0';
303         off_inout += 1;
304     }
305 
306     if (epoch >= MAX_TIME_T) {
307         return false;
308     }
309 
310     secs2tm(epoch, &dst->et_tm);
311     dst->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
312 
313     return (epoch > 0);
314 }
315 
ftime_s(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)316 inline void ftime_s(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
317 {
318     time_t t = tm2sec(&tm.et_tm);
319 
320     snprintf(&dst[off_inout], len - off_inout, "%ld", t);
321     off_inout = strlen(dst);
322 }
323 
ptime_q(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)324 inline bool ptime_q(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
325 {
326     off_t off_start = off_inout;
327     lnav::time64_t epoch = 0;
328 
329     while (off_inout < len && isxdigit(str[off_inout])) {
330         if ((off_inout - off_start) > 11) {
331             return false;
332         }
333 
334         epoch *= 16;
335         switch (tolower(str[off_inout])) {
336             case '0':
337             case '1':
338             case '2':
339             case '3':
340             case '4':
341             case '5':
342             case '6':
343             case '7':
344             case '8':
345             case '9':
346                 epoch += str[off_inout] - '0';
347                 break;
348             case 'a':
349             case 'b':
350             case 'c':
351             case 'd':
352             case 'e':
353             case 'f':
354                 epoch += str[off_inout] - 'a' + 10;
355                 break;
356         }
357         off_inout += 1;
358     }
359 
360     if (epoch >= MAX_TIME_T) {
361         return false;
362     }
363 
364     secs2tm(epoch, &dst->et_tm);
365     dst->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
366 
367     return (epoch > 0);
368 }
369 
ftime_q(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)370 inline void ftime_q(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
371 {
372     time_t t = tm2sec(&tm.et_tm);
373 
374     snprintf(&dst[off_inout], len - off_inout, "%lx", t);
375     off_inout = strlen(dst);
376 }
377 
ptime_L(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)378 inline bool ptime_L(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
379 {
380     int ms = 0;
381 
382     PTIME_CONSUME(3, {
383         char c0 = str[off_inout];
384         char c1 = str[off_inout + 1];
385         char c2 = str[off_inout + 2];
386         if (!isdigit(c0) || !isdigit(c1) || !isdigit(c2)) {
387             return false;
388         }
389         ms = (
390                 (str[off_inout    ] - '0') * 100 +
391                 (str[off_inout + 1] - '0') * 10 +
392                 (str[off_inout + 2] - '0'));
393     });
394 
395     if ((ms >= 0 && ms <= 999)) {
396         dst->et_nsec = ms * 1000000;
397         return true;
398     }
399     return false;
400 }
401 
ftime_L(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)402 inline void ftime_L(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
403 {
404     int millis = tm.et_nsec / 1000000;
405 
406     PTIME_APPEND('0' + ((millis / 100) % 10));
407     PTIME_APPEND('0' + ((millis /  10) % 10));
408     PTIME_APPEND('0' + ((millis /   1) % 10));
409 }
410 
ptime_M(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)411 inline bool ptime_M(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
412 {
413     PTIME_CONSUME(2, {
414         if (str[off_inout + 1] > '9') {
415             return false;
416         }
417         dst->et_tm.tm_min = (str[off_inout] - '0') * 10 + (str[off_inout + 1] - '0');
418     });
419 
420     return (dst->et_tm.tm_min >= 0 && dst->et_tm.tm_min <= 59);
421 }
422 
ftime_M(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)423 inline void ftime_M(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
424 {
425     PTIME_APPEND('0' + ((tm.et_tm.tm_min / 10) % 10));
426     PTIME_APPEND('0' + ((tm.et_tm.tm_min /  1) % 10));
427 }
428 
ptime_H(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)429 inline bool ptime_H(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
430 {
431     PTIME_CONSUME(2, {
432         if (str[off_inout + 1] > '9') {
433             return false;
434         }
435         dst->et_tm.tm_hour = (str[off_inout] - '0') * 10 + (str[off_inout + 1] - '0');
436     });
437 
438     return (dst->et_tm.tm_hour >= 0 && dst->et_tm.tm_hour <= 23);
439 }
440 
ftime_H(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)441 inline void ftime_H(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
442 {
443     PTIME_APPEND('0' + ((tm.et_tm.tm_hour / 10) % 10));
444     PTIME_APPEND('0' + ((tm.et_tm.tm_hour /  1) % 10));
445 }
446 
ptime_i(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)447 inline bool ptime_i(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
448 {
449     uint64_t epoch_ms = 0;
450     lnav::time64_t epoch;
451 
452     while (off_inout < len && isdigit(str[off_inout])) {
453         epoch_ms *= 10;
454         epoch_ms += str[off_inout] - '0';
455         off_inout += 1;
456     }
457 
458     dst->et_nsec = (epoch_ms % 1000ULL) * 1000000;
459     epoch = (epoch_ms / 1000ULL);
460 
461     if (epoch >= MAX_TIME_T) {
462         return false;
463     }
464 
465     secs2tm(epoch, &dst->et_tm);
466     dst->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
467 
468     return (epoch_ms > 0);
469 }
470 
ftime_i(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)471 inline void ftime_i(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
472 {
473     int64_t t = tm2sec(&tm.et_tm);
474 
475     t += tm.et_nsec / 1000000;
476     snprintf(&dst[off_inout], len - off_inout, "%" PRId64, t);
477     off_inout = strlen(dst);
478 }
479 
ptime_6(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)480 inline bool ptime_6(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
481 {
482     uint64_t epoch_us = 0;
483     lnav::time64_t epoch;
484 
485     while (off_inout < len && isdigit(str[off_inout])) {
486         epoch_us *= 10;
487         epoch_us += str[off_inout] - '0';
488         off_inout += 1;
489     }
490 
491     dst->et_nsec = (epoch_us % 1000000ULL) * 1000ULL;
492     epoch = (epoch_us / 1000000ULL);
493 
494     if (epoch >= MAX_TIME_T) {
495         return false;
496     }
497 
498     secs2tm(epoch, &dst->et_tm);
499     dst->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
500 
501     return (epoch_us > 0);
502 }
503 
ftime_6(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)504 inline void ftime_6(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
505 {
506     int64_t t = tm2sec(&tm.et_tm);
507 
508     t += tm.et_nsec / 1000;
509     snprintf(&dst[off_inout], len - off_inout, "%" PRId64, t);
510     off_inout = strlen(dst);
511 }
512 
ptime_I(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)513 inline bool ptime_I(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
514 {
515     PTIME_CONSUME(2, {
516         if (str[off_inout + 1] > '9') {
517             return false;
518         }
519         dst->et_tm.tm_hour = (str[off_inout] - '0') * 10 + (str[off_inout + 1] - '0');
520 
521         if (dst->et_tm.tm_hour < 1 || dst->et_tm.tm_hour > 12) {
522             return false;
523         }
524     });
525 
526     return true;
527 }
528 
ftime_I(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)529 inline void ftime_I(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
530 {
531     int hour = tm.et_tm.tm_hour;
532 
533     if (hour >= 12) {
534         hour -= 12;
535     }
536     if (hour == 0) {
537         hour = 12;
538     }
539 
540     PTIME_APPEND('0' + ((hour / 10) % 10));
541     PTIME_APPEND('0' + ((hour /  1) % 10));
542 }
543 
ptime_d(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)544 inline bool ptime_d(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
545 {
546     PTIME_CONSUME(2, {
547         if (str[off_inout] == ' ') {
548             dst->et_tm.tm_mday = 0;
549         }
550         else {
551             dst->et_tm.tm_mday = (str[off_inout] - '0') * 10;
552         }
553         if (str[off_inout + 1] > '9') {
554             return false;
555         }
556         dst->et_tm.tm_mday += (str[off_inout + 1] - '0');
557     });
558 
559     if (dst->et_tm.tm_mday >= 1 && dst->et_tm.tm_mday <= 31) {
560         dst->et_flags |= ETF_DAY_SET;
561         return true;
562     }
563     return false;
564 }
565 
ftime_d(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)566 inline void ftime_d(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
567 {
568     PTIME_APPEND('0' + ((tm.et_tm.tm_mday / 10) % 10));
569     PTIME_APPEND('0' + ((tm.et_tm.tm_mday /  1) % 10));
570 }
571 
ptime_e(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)572 inline bool ptime_e(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
573 {
574     dst->et_tm.tm_mday = 0;
575     PTIME_CONSUME(1, {
576         if (str[off_inout] < '1' || str[off_inout] > '9') {
577             return false;
578         }
579         dst->et_tm.tm_mday = str[off_inout] - '0';
580     });
581     if (off_inout + 1 < len) {
582         if (str[off_inout] >= '0' && str[off_inout] <= '9') {
583             dst->et_tm.tm_mday *= 10;
584             dst->et_tm.tm_mday += str[off_inout] - '0';
585             off_inout += 1;
586         }
587     }
588 
589     if (dst->et_tm.tm_mday >= 1 && dst->et_tm.tm_mday <= 31) {
590         dst->et_flags |= ETF_DAY_SET;
591         return true;
592     }
593     return false;
594 }
595 
ftime_e(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)596 inline void ftime_e(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
597 {
598     if (tm.et_tm.tm_mday < 10) {
599         PTIME_APPEND(' ');
600     }
601     else {
602         PTIME_APPEND('0' + ((tm.et_tm.tm_mday / 10) % 10));
603     }
604     PTIME_APPEND('0' + ((tm.et_tm.tm_mday /  1) % 10));
605 }
606 
ptime_m(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)607 inline bool ptime_m(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
608 {
609     off_t orig_off = off_inout;
610 
611     dst->et_tm.tm_mon = 0;
612     PTIME_CONSUME(1, {
613         if (str[off_inout] < '0' || str[off_inout] > '9') {
614             return false;
615         }
616         dst->et_tm.tm_mon = str[off_inout] - '0';
617     });
618     if (off_inout + 1 < len) {
619         if (str[off_inout] >= '0' && str[off_inout] <= '9') {
620             dst->et_tm.tm_mon *= 10;
621             dst->et_tm.tm_mon += str[off_inout] - '0';
622             off_inout += 1;
623         }
624     }
625 
626     dst->et_tm.tm_mon -= 1;
627 
628     if (dst->et_tm.tm_mon >= 0 && dst->et_tm.tm_mon <= 11) {
629         dst->et_flags |= ETF_MONTH_SET;
630         return true;
631     }
632 
633     off_inout = orig_off;
634     return false;
635 }
636 
ftime_m(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)637 inline void ftime_m(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
638 {
639     PTIME_APPEND('0' + (((tm.et_tm.tm_mon + 1) / 10) % 10));
640     PTIME_APPEND('0' + (((tm.et_tm.tm_mon + 1) /  1) % 10));
641 }
642 
ptime_k(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)643 inline bool ptime_k(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
644 {
645     dst->et_tm.tm_hour = 0;
646     PTIME_CONSUME(1, {
647         if (str[off_inout] < '0' || str[off_inout] > '9') {
648             return false;
649         }
650         dst->et_tm.tm_hour = str[off_inout] - '0';
651     });
652     if (off_inout + 1 < len) {
653         if (str[off_inout] >= '0' && str[off_inout] <= '9') {
654             dst->et_tm.tm_hour *= 10;
655             dst->et_tm.tm_hour += str[off_inout] - '0';
656             off_inout += 1;
657         }
658     }
659 
660     return (dst->et_tm.tm_hour >= 0 && dst->et_tm.tm_hour <= 23);
661 }
662 
ftime_k(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)663 inline void ftime_k(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
664 {
665     if (tm.et_tm.tm_hour < 10) {
666         PTIME_APPEND(' ');
667     }
668     else {
669         PTIME_APPEND('0' + ((tm.et_tm.tm_hour / 10) % 10));
670     }
671     PTIME_APPEND('0' + ((tm.et_tm.tm_hour /  1) % 10));
672 }
673 
ptime_l(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)674 inline bool ptime_l(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
675 {
676     off_t orig_off = off_inout;
677     bool consumed_space = false;
678 
679     dst->et_tm.tm_hour = 0;
680 
681     if ((off_inout + 1) > len) {
682         return false;
683     }
684 
685     if (str[off_inout] == ' ') {
686         consumed_space = true;
687         off_inout += 1;
688     }
689 
690     if ((off_inout + 1) > len) {
691         off_inout = orig_off;
692         return false;
693     }
694 
695     if (str[off_inout] < '1' || str[off_inout] > '9') {
696         off_inout = orig_off;
697         return false;
698     }
699 
700     dst->et_tm.tm_hour = str[off_inout] - '0';
701     off_inout += 1;
702 
703     if (consumed_space || str[off_inout] < '0' || str[off_inout] > '9') {
704         return true;
705     }
706 
707     dst->et_tm.tm_hour *= 10;
708     dst->et_tm.tm_hour += str[off_inout] - '0';
709     off_inout += 1;
710 
711     if (dst->et_tm.tm_hour >= 0 && dst->et_tm.tm_hour <= 23) {
712         return true;
713     }
714 
715     off_inout = orig_off;
716     return false;
717 }
718 
ftime_l(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)719 inline void ftime_l(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
720 {
721     int hour = tm.et_tm.tm_hour;
722 
723     if (hour >= 12) {
724         hour -= 12;
725     }
726     if (hour == 0) {
727         hour = 12;
728     }
729 
730     if (hour < 10) {
731         PTIME_APPEND(' ');
732     }
733     else {
734         PTIME_APPEND('0' + ((hour / 10) % 10));
735     }
736     PTIME_APPEND('0' + ((hour /  1) % 10));
737 }
738 
ptime_p(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)739 inline bool ptime_p(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
740 {
741     PTIME_CONSUME(2, {
742         char lead = str[off_inout];
743 
744         if ((str[off_inout + 1] & 0xdf) != 'M') {
745             return false;
746         }
747         else if ((lead & 0xdf) == 'A') {
748             if (dst->et_tm.tm_hour == 12) {
749                 dst->et_tm.tm_hour = 0;
750             }
751         }
752         else if ((lead & 0xdf) == 'P') {
753             if (dst->et_tm.tm_hour < 12) {
754                 dst->et_tm.tm_hour += 12;
755             }
756         }
757         else {
758             return false;
759         }
760     });
761 
762     return true;
763 }
764 
ftime_p(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)765 inline void ftime_p(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
766 {
767     if (tm.et_tm.tm_hour < 12) {
768         PTIME_APPEND('A');
769     }
770     else {
771         PTIME_APPEND('P');
772     }
773     PTIME_APPEND('M');
774 }
775 
ptime_Y(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)776 inline bool ptime_Y(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
777 {
778     PTIME_CONSUME(4, {
779         dst->et_tm.tm_year = (
780             (str[off_inout + 0] - '0') * 1000 +
781             (str[off_inout + 1] - '0') *  100 +
782             (str[off_inout + 2] - '0') *   10 +
783             (str[off_inout + 3] - '0') *    1) - 1900;
784 
785         if (dst->et_tm.tm_year < 0 || dst->et_tm.tm_year > 1100) {
786             return false;
787         }
788 
789         dst->et_flags |= ETF_YEAR_SET;
790     });
791 
792     return true;
793 }
794 
ftime_Y(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)795 inline void ftime_Y(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
796 {
797     int year = tm.et_tm.tm_year + 1900;
798 
799     PTIME_APPEND('0' + ((year / 1000) % 10));
800     PTIME_APPEND('0' + ((year /  100) % 10));
801     PTIME_APPEND('0' + ((year /   10) % 10));
802     PTIME_APPEND('0' + ((year /    1) % 10));
803 }
804 
ptime_y(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)805 inline bool ptime_y(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
806 {
807     PTIME_CONSUME(2, {
808         dst->et_tm.tm_year = (
809             (str[off_inout + 0] - '0') * 10 +
810             (str[off_inout + 1] - '0') *  1);
811     });
812 
813     if (dst->et_tm.tm_year >= 0 && dst->et_tm.tm_year < 100) {
814         if (dst->et_tm.tm_year < 69) {
815             dst->et_tm.tm_year += 100;
816         }
817 
818         dst->et_flags |= ETF_YEAR_SET;
819         return true;
820     }
821     return false;
822 }
823 
ftime_y(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)824 inline void ftime_y(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
825 {
826     int year = tm.et_tm.tm_year + 1900;
827 
828     PTIME_APPEND('0' + ((year /   10) % 10));
829     PTIME_APPEND('0' + ((year /    1) % 10));
830 }
831 
ptime_z(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)832 inline bool ptime_z(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
833 {
834     int consume_amount = 5;
835 
836     if ((off_inout + 6) <= len && str[off_inout + 3] == ':') {
837         consume_amount = 6;
838     }
839     PTIME_CONSUME(consume_amount, {
840         long sign;
841         long hours;
842         long mins;
843         int skip_colon = (consume_amount == 6) ? 1 : 0;
844 
845         if (str[off_inout] == '+') {
846             sign = 1;
847         }
848         else if (str[off_inout] == '-') {
849             sign = -1;
850         }
851         else {
852             return false;
853         }
854 
855         hours = (
856             (str[off_inout + 1] - '0') * 10 +
857             (str[off_inout + 2] - '0') *  1) * 60 * 60;
858         mins = (
859             (str[off_inout + skip_colon + 3] - '0') *   10 +
860             (str[off_inout + skip_colon + 4] - '0') *    1) * 60;
861         dst->et_gmtoff = sign * (hours + mins);
862 #ifdef HAVE_STRUCT_TM_TM_ZONE
863         dst->et_tm.tm_gmtoff = sign * (hours + mins);
864 #endif
865     });
866 
867     return true;
868 }
869 
ftime_z(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)870 inline void ftime_z(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
871 {
872     long gmtoff = std::abs(tm.et_gmtoff) / 60;
873 
874     if (tm.et_gmtoff < 0) {
875         PTIME_APPEND('-');
876     }
877     else {
878         PTIME_APPEND('+');
879     }
880 
881     long mins = gmtoff % 60;
882     long hours = gmtoff / 60;
883 
884     PTIME_APPEND('0' + ((hours / 10) % 10));
885     PTIME_APPEND('0' + ((hours /  1) % 10));
886     PTIME_APPEND('0' + ((mins  / 10) % 10));
887     PTIME_APPEND('0' + ((mins  /  1) % 10));
888 }
889 
ptime_f(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)890 inline bool ptime_f(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
891 {
892     PTIME_CONSUME(6, {
893         for (int lpc = 0; lpc < 6; lpc++) {
894             if (str[off_inout + lpc] < '0' || str[off_inout + lpc] > '9') {
895                 return false;
896             }
897         }
898         dst->et_nsec = (
899                 (str[off_inout + 0] - '0') * 100000 +
900                 (str[off_inout + 1] - '0') *  10000 +
901                 (str[off_inout + 2] - '0') *   1000 +
902                 (str[off_inout + 3] - '0') *    100 +
903                 (str[off_inout + 4] - '0') *     10 +
904                 (str[off_inout + 5] - '0') *      1) * 1000;
905     });
906 
907     return true;
908 }
909 
ftime_f(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)910 inline void ftime_f(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
911 {
912     uint32_t micros = tm.et_nsec / 1000;
913 
914     PTIME_APPEND('0' + ((micros / 100000) % 10));
915     PTIME_APPEND('0' + ((micros /  10000) % 10));
916     PTIME_APPEND('0' + ((micros /   1000) % 10));
917     PTIME_APPEND('0' + ((micros /    100) % 10));
918     PTIME_APPEND('0' + ((micros /     10) % 10));
919     PTIME_APPEND('0' + ((micros /      1) % 10));
920 }
921 
ptime_N(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)922 inline bool ptime_N(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
923 {
924     PTIME_CONSUME(9, {
925         for (int lpc = 0; lpc < 9; lpc++) {
926             if (str[off_inout + lpc] < '0' || str[off_inout + lpc] > '9') {
927                 return false;
928             }
929         }
930         dst->et_nsec = (
931             (str[off_inout + 0] - '0') * 100000000 +
932             (str[off_inout + 1] - '0') *  10000000 +
933             (str[off_inout + 2] - '0') *   1000000 +
934             (str[off_inout + 3] - '0') *    100000 +
935             (str[off_inout + 4] - '0') *     10000 +
936             (str[off_inout + 5] - '0') *      1000 +
937             (str[off_inout + 6] - '0') *       100 +
938             (str[off_inout + 7] - '0') *        10 +
939             (str[off_inout + 8] - '0') *         1);
940     });
941 
942     return true;
943 }
944 
ftime_N(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)945 inline void ftime_N(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
946 {
947     uint32_t nano = tm.et_nsec;
948 
949     PTIME_APPEND('0' + ((nano / 100000000) % 10));
950     PTIME_APPEND('0' + ((nano /  10000000) % 10));
951     PTIME_APPEND('0' + ((nano /   1000000) % 10));
952     PTIME_APPEND('0' + ((nano /    100000) % 10));
953     PTIME_APPEND('0' + ((nano /     10000) % 10));
954     PTIME_APPEND('0' + ((nano /      1000) % 10));
955     PTIME_APPEND('0' + ((nano /       100) % 10));
956     PTIME_APPEND('0' + ((nano /        10) % 10));
957     PTIME_APPEND('0' + ((nano /         1) % 10));
958 }
959 
ptime_char(char val,const char * str,off_t & off_inout,ssize_t len)960 inline bool ptime_char(char val, const char *str, off_t &off_inout, ssize_t len)
961 {
962     PTIME_CONSUME(1, {
963         if (str[off_inout] != val) {
964             return false;
965         }
966     });
967 
968     return true;
969 }
970 
ftime_char(char * dst,off_t & off_inout,ssize_t len,char ch)971 inline void ftime_char(char *dst, off_t &off_inout, ssize_t len, char ch)
972 {
973     PTIME_APPEND(ch);
974 }
975 
976 template<typename T>
ptime_hex_to_quad(T & value_inout,const char quad)977 inline bool ptime_hex_to_quad(T &value_inout, const char quad)
978 {
979     value_inout <<= 4;
980     if ('0' <= quad && quad <= '9') {
981         value_inout |= ((quad - '0') & 0x0f);
982     }
983     else if ('a' <= quad && quad <= 'f') {
984         value_inout |= 10 + ((quad - 'a') & 0x0f);
985     }
986     else if ('A' <= quad && quad <= 'F') {
987         value_inout |= 10 + ((quad - 'A') & 0x0f);
988     }
989     else {
990         return false;
991     }
992 
993     return true;
994 }
995 
ptime_at(struct exttm * dst,const char * str,off_t & off_inout,ssize_t len)996 inline bool ptime_at(struct exttm *dst, const char *str, off_t &off_inout, ssize_t len)
997 {
998     PTIME_CONSUME(16, {
999         int64_t secs = 0;
1000 
1001         for (int lpc = 0; lpc < 16; lpc++) {
1002             char quad = str[off_inout + lpc];
1003 
1004             if (!ptime_hex_to_quad(secs, quad)) {
1005                 return false;
1006             }
1007         }
1008         dst->et_nsec = 0;
1009 
1010         lnav::time64_t small_secs = secs - 4611686018427387914ULL;
1011 
1012         if (small_secs >= MAX_TIME_T) {
1013             return false;
1014         }
1015 
1016         secs2tm(small_secs, &dst->et_tm);
1017     });
1018 
1019     if ((len - off_inout) == 8) {
1020         PTIME_CONSUME(8, {
1021             for (int lpc = 0; lpc < 8; lpc++) {
1022                 char quad = str[off_inout + lpc];
1023 
1024                 if (!ptime_hex_to_quad(dst->et_nsec, quad)) {
1025                     return false;
1026                 }
1027             }
1028         });
1029     }
1030 
1031     dst->et_flags |= ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
1032 
1033     return true;
1034 }
1035 
ftime_at(char * dst,off_t & off_inout,ssize_t len,const struct exttm & tm)1036 inline void ftime_at(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
1037 {
1038 
1039 }
1040 
1041 typedef bool (*ptime_func)(struct exttm *dst, const char *str, off_t &off, ssize_t len);
1042 typedef void (*ftime_func)(char *dst, off_t &off_inout, size_t len, const struct exttm &tm);
1043 
1044 bool ptime_fmt(const char *fmt, struct exttm *dst, const char *str, off_t &off, ssize_t len);
1045 size_t ftime_fmt(char *dst, size_t len, const char *fmt, const struct exttm &tm);
1046 
1047 struct ptime_fmt {
1048     const char *pf_fmt;
1049     ptime_func pf_func;
1050     ftime_func pf_ffunc;
1051 };
1052 
1053 extern struct ptime_fmt PTIMEC_FORMATS[];
1054 
1055 extern const char *PTIMEC_FORMAT_STR[];
1056 
1057 #endif
1058