summaryrefslogtreecommitdiff
path: root/src/lib/evil
diff options
context:
space:
mode:
authorVincent Torri <vincent.torri@gmail.com>2014-09-28 15:28:25 +0200
committerzmike <michael.blumenkrantz@gmail.com>2014-09-28 12:29:54 -0400
commit6c2ec01d39800ac0160ec30bb06c47174baf8c18 (patch)
treedbb2ba414cd7b7e4ea81893711d06d38b7c41465 /src/lib/evil
parent0af6fcbc2a781c40b8dfca026852dc80bd1f7a90 (diff)
Evil: add strptime()
@feature
Diffstat (limited to 'src/lib/evil')
-rw-r--r--src/lib/evil/evil_time.c580
-rw-r--r--src/lib/evil/evil_time.h19
2 files changed, 599 insertions, 0 deletions
diff --git a/src/lib/evil/evil_time.c b/src/lib/evil/evil_time.c
index d590b8b2bd..9bb1ebdf85 100644
--- a/src/lib/evil/evil_time.c
+++ b/src/lib/evil/evil_time.c
@@ -2,6 +2,12 @@
2# include "config.h" 2# include "config.h"
3#endif /* HAVE_CONFIG_H */ 3#endif /* HAVE_CONFIG_H */
4 4
5#include <strings.h>
6#include <inttypes.h>
7#include <ctype.h>
8#define _POSIX /* FIXME: to be removed when mingw-w64 will be fixed */
9#include <time.h>
10
5#include "Evil.h" 11#include "Evil.h"
6#include "evil_private.h" 12#include "evil_private.h"
7 13
@@ -33,3 +39,577 @@ localtime_r(const time_t *timep, struct tm *result)
33} 39}
34 40
35#endif /* localtime_r */ 41#endif /* localtime_r */
42
43/*
44 * strptime
45 * based on http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/time/strptime.c?rev=HEAD
46 * BSD licence
47 */
48
49#define TM_YEAR_BASE 1900
50
51/*
52 * We do not implement alternate representations. However, we always
53 * check whether a given modifier is allowed for a certain conversion.
54 */
55#define ALT_E 0x01
56#define ALT_O 0x02
57#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; }
58
59
60static const char *day[7] =
61{
62 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
63};
64
65static const char *abday[7] =
66{
67 "Sun","Mon","Tue","Wed","Thu","Fri","Sat"
68};
69
70static const char *mon[12] =
71{
72 "January", "February", "March", "April", "May", "June", "July",
73 "August", "September", "October", "November", "December"
74};
75
76static const char *abmon[12] =
77{
78 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
79 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
80};
81
82static const char *am_pm[2] =
83{
84 "AM", "PM"
85};
86
87static char gmt[] = { "GMT" };
88
89#ifdef TM_ZONE
90static char utc[] = { "UTC" };
91#endif
92
93/* RFC-822/RFC-2822 */
94static const char * const nast[5] = {
95 "EST", "CST", "MST", "PST", "\0\0\0"
96};
97
98static const char * const nadt[5] = {
99 "EDT", "CDT", "MDT", "PDT", "\0\0\0"
100};
101
102static const unsigned char *
103find_string(const unsigned char *bp, int *tgt,
104 const char *const *n1, const char *const *n2,
105 int c)
106{
107 size_t len;
108 int i;
109
110 /* check full name - then abbreviated ones */
111 for (; n1 != NULL; n1 = n2, n2 = NULL)
112 {
113 for (i = 0; i < c; i++, n1++)
114 {
115 len = strlen(*n1);
116 if (strncasecmp(*n1, (const char *)bp, len) == 0)
117 {
118 *tgt = i;
119 return bp + len;
120 }
121 }
122 }
123
124 /* Nothing matched */
125 return NULL;
126}
127
128static const unsigned char *
129conv_num(const unsigned char *buf, int *dest, unsigned int llim, unsigned int ulim)
130{
131 unsigned int result = 0;
132 unsigned char ch;
133
134 /* The limit also determines the number of valid digits. */
135 unsigned int rulim = ulim;
136
137 ch = *buf;
138 if (ch < '0' || ch > '9')
139 return NULL;
140
141 do {
142 result *= 10;
143 result += ch - '0';
144 rulim /= 10;
145 ch = *++buf;
146 } while ((result * 10 <= ulim) && rulim && (ch >= '0') && (ch <= '9'));
147
148 if ((result < llim) || (result > ulim))
149 return NULL;
150
151 *dest = result;
152 return buf;
153}
154
155char *
156strptime(const char *buf, const char *fmt, struct tm *tm)
157{
158 unsigned char c;
159 const unsigned char *bp, *ep;
160 int alt_format, i, split_year = 0, neg = 0, offs;
161 const char *new_fmt;
162
163 bp = (const unsigned char *)buf;
164
165 while (bp != NULL && (c = *fmt++) != '\0')
166 {
167 /* Clear `alternate' modifier prior to new conversion. */
168 alt_format = 0;
169 i = 0;
170
171 /* Eat up white-space. */
172 if (isspace(c))
173 {
174 while (isspace(*bp))
175 bp++;
176 continue;
177 }
178
179 if (c != '%')
180 goto literal;
181
182 again:
183 switch (c = *fmt++)
184 {
185 case '%': /* "%%" is converted to "%". */
186 literal:
187 if (c != *bp++)
188 return NULL;
189 LEGAL_ALT(0);
190 continue;
191
192 /*
193 * "Alternative" modifiers. Just set the appropriate flag
194 * and start over again.
195 */
196 case 'E': /* "%E?" alternative conversion modifier. */
197 LEGAL_ALT(0);
198 alt_format |= ALT_E;
199 goto again;
200
201 case 'O': /* "%O?" alternative conversion modifier. */
202 LEGAL_ALT(0);
203 alt_format |= ALT_O;
204 goto again;
205
206 /*
207 * "Complex" conversion rules, implemented through recursion.
208 */
209 /* case 'c': /\* Date and time, using the locale's format. *\/ */
210 /* new_fmt = _TIME_LOCALE(loc)->d_t_fmt; */
211 /* goto recurse; */
212
213 case 'D': /* The date as "%m/%d/%y". */
214 new_fmt = "%m/%d/%y";
215 LEGAL_ALT(0);
216 goto recurse;
217
218 case 'F': /* The date as "%Y-%m-%d". */
219 new_fmt = "%Y-%m-%d";
220 LEGAL_ALT(0);
221 goto recurse;
222
223 case 'R': /* The time as "%H:%M". */
224 new_fmt = "%H:%M";
225 LEGAL_ALT(0);
226 goto recurse;
227
228 /* case 'r': /\* The time in 12-hour clock representation. *\/ */
229 /* new_fmt = _TIME_LOCALE(loc)->t_fmt_ampm; */
230 /* LEGAL_ALT(0); */
231 /* goto recurse; */
232
233 case 'T': /* The time as "%H:%M:%S". */
234 new_fmt = "%H:%M:%S";
235 LEGAL_ALT(0);
236 goto recurse;
237
238 /* case 'X': /\* The time, using the locale's format. *\/ */
239 /* new_fmt = _TIME_LOCALE(loc)->t_fmt; */
240 /* goto recurse; */
241
242 /* case 'x': /\* The date, using the locale's format. *\/ */
243 /* new_fmt = _TIME_LOCALE(loc)->d_fmt; */
244 recurse:
245 bp = (const unsigned char *)strptime((const char *)bp,
246 new_fmt, tm);
247 LEGAL_ALT(ALT_E);
248 continue;
249
250 /*
251 * "Elementary" conversion rules.
252 */
253 case 'A': /* The day of week, using the locale's form. */
254 case 'a':
255 bp = find_string(bp, &tm->tm_wday, day, abday, 7);
256 LEGAL_ALT(0);
257 continue;
258
259 case 'B': /* The month, using the locale's form. */
260 case 'b':
261 case 'h':
262 bp = find_string(bp, &tm->tm_mon, mon, abmon, 12);
263 LEGAL_ALT(0);
264 continue;
265
266 case 'C': /* The century number. */
267 i = 20;
268 bp = conv_num(bp, &i, 0, 99);
269
270 i = i * 100 - TM_YEAR_BASE;
271 if (split_year)
272 i += tm->tm_year % 100;
273 split_year = 1;
274 tm->tm_year = i;
275 LEGAL_ALT(ALT_E);
276 continue;
277
278 case 'd': /* The day of month. */
279 case 'e':
280 bp = conv_num(bp, &tm->tm_mday, 1, 31);
281 LEGAL_ALT(ALT_O);
282 continue;
283
284 case 'k': /* The hour (24-hour clock representation). */
285 LEGAL_ALT(0);
286 /* FALLTHROUGH */
287 case 'H':
288 bp = conv_num(bp, &tm->tm_hour, 0, 23);
289 LEGAL_ALT(ALT_O);
290 continue;
291
292 case 'l': /* The hour (12-hour clock representation). */
293 LEGAL_ALT(0);
294 /* FALLTHROUGH */
295 case 'I':
296 bp = conv_num(bp, &tm->tm_hour, 1, 12);
297 if (tm->tm_hour == 12)
298 tm->tm_hour = 0;
299 LEGAL_ALT(ALT_O);
300 continue;
301
302 case 'j': /* The day of year. */
303 i = 1;
304 bp = conv_num(bp, &i, 1, 366);
305 tm->tm_yday = i - 1;
306 LEGAL_ALT(0);
307 continue;
308
309 case 'M': /* The minute. */
310 bp = conv_num(bp, &tm->tm_min, 0, 59);
311 LEGAL_ALT(ALT_O);
312 continue;
313
314 case 'm': /* The month. */
315 i = 1;
316 bp = conv_num(bp, &i, 1, 12);
317 tm->tm_mon = i - 1;
318 LEGAL_ALT(ALT_O);
319 continue;
320
321 case 'p': /* The locale's equivalent of AM/PM. */
322 bp = find_string(bp, &i, am_pm, NULL, 2);
323 if (tm->tm_hour > 11)
324 return NULL;
325 tm->tm_hour += i * 12;
326 LEGAL_ALT(0);
327 continue;
328
329 case 'S': /* The seconds. */
330 bp = conv_num(bp, &tm->tm_sec, 0, 61);
331 LEGAL_ALT(ALT_O);
332 continue;
333
334#ifndef TIME_MAX
335# define TIME_MAX INT64_MAX
336#endif
337 case 's': /* seconds since the epoch */
338 {
339 time_t sse = 0;
340 __int64 rulim = TIME_MAX;
341
342 if (*bp < '0' || *bp > '9')
343 {
344 bp = NULL;
345 continue;
346 }
347
348 do
349 {
350 sse *= 10;
351 sse += *bp++ - '0';
352 rulim /= 10;
353 } while ((sse * 10 <= TIME_MAX) &&
354 rulim && *bp >= '0' && *bp <= '9');
355
356 if (sse < 0 || (__int64)sse > TIME_MAX)
357 {
358 bp = NULL;
359 continue;
360 }
361
362 if (localtime_r(&sse, tm) == NULL)
363 bp = NULL;
364 }
365 continue;
366
367 case 'U': /* The week of year, beginning on sunday. */
368 case 'W': /* The week of year, beginning on monday. */
369 /*
370 * XXX This is bogus, as we can not assume any valid
371 * information present in the tm structure at this
372 * point to calculate a real value, so just check the
373 * range for now.
374 */
375 bp = conv_num(bp, &i, 0, 53);
376 LEGAL_ALT(ALT_O);
377 continue;
378
379 case 'w': /* The day of week, beginning on sunday. */
380 bp = conv_num(bp, &tm->tm_wday, 0, 6);
381 LEGAL_ALT(ALT_O);
382 continue;
383
384 case 'u': /* The day of week, monday = 1. */
385 bp = conv_num(bp, &i, 1, 7);
386 tm->tm_wday = i % 7;
387 LEGAL_ALT(ALT_O);
388 continue;
389
390 case 'g': /* The year corresponding to the ISO week
391 * number but without the century.
392 */
393 bp = conv_num(bp, &i, 0, 99);
394 continue;
395
396 case 'G': /* The year corresponding to the ISO week
397 * number with century.
398 */
399 do
400 bp++;
401 while (isdigit(*bp));
402 continue;
403
404 case 'V': /* The ISO 8601:1988 week number as decimal */
405 bp = conv_num(bp, &i, 0, 53);
406 continue;
407
408 case 'Y': /* The year. */
409 i = TM_YEAR_BASE; /* just for data sanity... */
410 bp = conv_num(bp, &i, 0, 9999);
411 tm->tm_year = i - TM_YEAR_BASE;
412 LEGAL_ALT(ALT_E);
413 continue;
414
415 case 'y': /* The year within 100 years of the epoch. */
416 /* LEGAL_ALT(ALT_E | ALT_O); */
417 bp = conv_num(bp, &i, 0, 99);
418
419 if (split_year)
420 /* preserve century */
421 i += (tm->tm_year / 100) * 100;
422 else {
423 split_year = 1;
424 if (i <= 68)
425 i = i + 2000 - TM_YEAR_BASE;
426 else
427 i = i + 1900 - TM_YEAR_BASE;
428 }
429 tm->tm_year = i;
430 continue;
431
432 case 'Z':
433 tzset();
434 if (strncmp((const char *)bp, gmt, 3) == 0) {
435 tm->tm_isdst = 0;
436#ifdef TM_GMTOFF
437 tm->TM_GMTOFF = 0;
438#endif
439#ifdef TM_ZONE
440 tm->TM_ZONE = gmt;
441#endif
442 bp += 3;
443 }
444 else
445 {
446 ep = find_string(bp, &i,
447 (const char * const *)tzname,
448 NULL, 2);
449 if (ep != NULL)
450 {
451 tm->tm_isdst = i;
452#ifdef TM_GMTOFF
453 tm->TM_GMTOFF = -(timezone);
454#endif
455#ifdef TM_ZONE
456 tm->TM_ZONE = tzname[i];
457#endif
458 }
459 bp = ep;
460 }
461 continue;
462
463 case 'z':
464 /*
465 * We recognize all ISO 8601 formats:
466 * Z = Zulu time/UTC
467 * [+-]hhmm
468 * [+-]hh:mm
469 * [+-]hh
470 * We recognize all RFC-822/RFC-2822 formats:
471 * UT|GMT
472 * North American : UTC offsets
473 * E[DS]T = Eastern : -4 | -5
474 * C[DS]T = Central : -5 | -6
475 * M[DS]T = Mountain: -6 | -7
476 * P[DS]T = Pacific : -7 | -8
477 * Military
478 * [A-IL-M] = -1 ... -9 (J not used)
479 * [N-Y] = +1 ... +12
480 */
481 while (isspace(*bp))
482 bp++;
483
484 switch (*bp++)
485 {
486 case 'G':
487 if (*bp++ != 'M')
488 return NULL;
489 /*FALLTHROUGH*/
490 case 'U':
491 if (*bp++ != 'T')
492 return NULL;
493 /*FALLTHROUGH*/
494 case 'Z':
495 tm->tm_isdst = 0;
496#ifdef TM_GMTOFF
497 tm->TM_GMTOFF = 0;
498#endif
499#ifdef TM_ZONE
500 tm->TM_ZONE = utc;
501#endif
502 continue;
503 case '+':
504 neg = 0;
505 break;
506 case '-':
507 neg = 1;
508 break;
509 default:
510 --bp;
511 ep = find_string(bp, &i, nast, NULL, 4);
512 if (ep != NULL) {
513#ifdef TM_GMTOFF
514 tm->TM_GMTOFF = -5 - i;
515#endif
516#ifdef TM_ZONE
517 tm->TM_ZONE = __UNCONST(nast[i]);
518#endif
519 bp = ep;
520 continue;
521 }
522 ep = find_string(bp, &i, nadt, NULL, 4);
523 if (ep != NULL)
524 {
525 tm->tm_isdst = 1;
526#ifdef TM_GMTOFF
527 tm->TM_GMTOFF = -4 - i;
528#endif
529#ifdef TM_ZONE
530 tm->TM_ZONE = __UNCONST(nadt[i]);
531#endif
532 bp = ep;
533 continue;
534 }
535
536 if ((*bp >= 'A' && *bp <= 'I') ||
537 (*bp >= 'L' && *bp <= 'Y'))
538 {
539#ifdef TM_GMTOFF
540 /* Argh! No 'J'! */
541 if (*bp >= 'A' && *bp <= 'I')
542 tm->TM_GMTOFF =
543 ('A' - 1) - (int)*bp;
544 else if (*bp >= 'L' && *bp <= 'M')
545 tm->TM_GMTOFF = 'A' - (int)*bp;
546 else if (*bp >= 'N' && *bp <= 'Y')
547 tm->TM_GMTOFF = (int)*bp - 'M';
548#endif
549#ifdef TM_ZONE
550 tm->TM_ZONE = NULL; /* XXX */
551#endif
552 bp++;
553 continue;
554 }
555 return NULL;
556 }
557 offs = 0;
558 for (i = 0; i < 4; )
559 {
560 if (isdigit(*bp))
561 {
562 offs = offs * 10 + (*bp++ - '0');
563 i++;
564 continue;
565 }
566 if (i == 2 && *bp == ':')
567 {
568 bp++;
569 continue;
570 }
571 break;
572 }
573 switch (i)
574 {
575 case 2:
576 offs *= 100;
577 break;
578 case 4:
579 i = offs % 100;
580 if (i >= 60)
581 return NULL;
582 /* Convert minutes into decimal */
583 offs = (offs / 100) * 100 + (i * 50) / 30;
584 break;
585 default:
586 return NULL;
587 }
588 if (neg)
589 offs = -offs;
590 tm->tm_isdst = 0; /* XXX */
591#ifdef TM_GMTOFF
592 tm->TM_GMTOFF = offs;
593#endif
594#ifdef TM_ZONE
595 tm->TM_ZONE = NULL; /* XXX */
596#endif
597 continue;
598
599 /*
600 * Miscellaneous conversions.
601 */
602 case 'n': /* Any kind of white-space. */
603 case 't':
604 while (isspace(*bp))
605 bp++;
606 LEGAL_ALT(0);
607 continue;
608
609 default: /* Unknown/unsupported conversion. */
610 return NULL;
611 }
612 }
613
614 return (char *)bp;
615}
diff --git a/src/lib/evil/evil_time.h b/src/lib/evil/evil_time.h
index da9d9ea9af..e137cbdf0b 100644
--- a/src/lib/evil/evil_time.h
+++ b/src/lib/evil/evil_time.h
@@ -38,6 +38,25 @@ EAPI struct tm *localtime_r(const time_t *timep, struct tm *result);
38 38
39#endif /* localtime_r */ 39#endif /* localtime_r */
40 40
41/**
42 * @brief Convert a string representation of time to a time tm structure .
43 *
44 * @param buf The string to convert.
45 * @param fmt The representation of time.
46 * @aram tm The time tm structure.
47 * @return The first character not processed in this function call.
48 *
49 * This function converts the string @p s to a time tm structure and
50 * fill the buffer @p tm. The format of the time is specified by
51 * @p format. on success, this function returns the first character
52 * not processed in this function call, @c NULL otherwise.
53 *
54 * Conformity: Non applicable.
55 *
56 * Supported OS: Windows XP.
57 */
58EAPI char *strptime(const char *buf, const char *fmt, struct tm *tm);
59
41 60
42/** 61/**
43 * @} 62 * @}