Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
strptime.c
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2016-2017 Peter Budai.
4 Copyright (C) 2020 Pascal Obry.
5 Copyright (C) 2021 Miloš Komarčević.
6 Copyright (C) 2022 Martin Bařinka.
7
8 darktable is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 darktable is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with darktable. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22/* $NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $ */
23
24/*-
25 * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.
26 * All rights reserved.
27 *
28 * This code was contributed to The NetBSD Foundation by Klaus Klein.
29 * Heavily optimised by David Laight
30 *
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
33 * are met:
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 *
40 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
41 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
42 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
44 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
45 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
46 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
47 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
48 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
50 * POSSIBILITY OF SUCH DAMAGE.
51 */
52/*
53#include <sys/cdefs.h>
54#if defined(LIBC_SCCS) && !defined(lint)
55__RCSID("$NetBSD: strptime.c,v 1.36 2012/03/13 21:13:48 christos Exp $");
56#endif
57#include "namespace.h"
58#include <sys/localedef.h>
59*/
60#include "common/darktable.h"
61#include <ctype.h>
62#include <locale.h>
63#include <string.h>
64#include <time.h>
65#include <stdint.h>
66/*
67#include <tzfile.h>
68#include "private.h"
69#ifdef __weak_alias
70__weak_alias(strptime,_strptime)
71#endif
72*/
73typedef unsigned char u_char;
74typedef unsigned int uint;
75typedef unsigned __int64 uint64_t;
76
77#define _ctloc(x) (_CurrentTimeLocale->x)
78
79/*
80 * We do not implement alternate representations. However, we always
81 * check whether a given modifier is allowed for a certain conversion.
82 */
83#define ALT_E 0x01
84#define ALT_O 0x02
85#define LEGAL_ALT(x) \
86 { \
87 if(alt_format & ~(x)) return NULL; \
88 }
89
90static int TM_YEAR_BASE = 1900;
91static char gmt[] = { "GMT" };
92static char utc[] = { "UTC" };
93/* RFC-822/RFC-2822 */
94static const char *const nast[5] = { "EST", "CST", "MST", "PST", "\0\0\0" };
95static const char *const nadt[5] = { "EDT", "CDT", "MDT", "PDT", "\0\0\0" };
96static const char *const am_pm[2] = { "am", "pm" };
97static const char *const day[7] = { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" };
98static const char *const abday[7] = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
99static const char *const mon[12] = { "january", "february", "march", "april", "may", "june",
100 "july", "august", "september", "october", "november", "december" };
101static const char *const abmon[12]
102 = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
103
104static const u_char *conv_num(const unsigned char *, int *, uint, uint);
105static const u_char *find_string(const u_char *, int *, const char *const *, const char *const *, int);
106
107char *strptime(const char *buf, const char *fmt, struct tm *tm)
108{
109 unsigned char c;
110 const unsigned char *bp, *ep;
111 int alt_format, i, split_year = 0, neg = 0, offs;
112 const char *new_fmt;
113
114 bp = (const u_char *)buf;
115
116 while(!IS_NULL_PTR(bp) && (c = *fmt++) != '\0')
117 {
118 /* Clear `alternate' modifier prior to new conversion. */
119 alt_format = 0;
120 i = 0;
121
122 /* Eat up white-space. */
123 if(isspace(c))
124 {
125 while(isspace(*bp)) bp++;
126 continue;
127 }
128
129 if(c != '%') goto literal;
130
131
132 again:
133 switch(c = *fmt++)
134 {
135 case '%': /* "%%" is converted to "%". */
136 literal:
137 if(c != *bp++) return NULL;
138 LEGAL_ALT(0);
139 continue;
140
141 /*
142 * "Alternative" modifiers. Just set the appropriate flag
143 * and start over again.
144 */
145 case 'E': /* "%E?" alternative conversion modifier. */
146 LEGAL_ALT(0);
147 alt_format |= ALT_E;
148 goto again;
149
150 case 'O': /* "%O?" alternative conversion modifier. */
151 LEGAL_ALT(0);
152 alt_format |= ALT_O;
153 goto again;
154
155 /*
156 * "Complex" conversion rules, implemented through recursion.
157 */
158 /* we do not need 'c'
159 case 'c': Date and time, using the locale's format.
160 new_fmt = _ctloc(d_t_fmt);
161 goto recurse;
162 */
163
164 case 'D': /* The date as "%m/%d/%y". */
165 new_fmt = "%m/%d/%y";
166 LEGAL_ALT(0);
167 goto recurse;
168
169 case 'F': /* The date as "%Y-%m-%d". */
170 new_fmt = "%Y-%m-%d";
171 LEGAL_ALT(0);
172 goto recurse;
173
174 case 'R': /* The time as "%H:%M". */
175 new_fmt = "%H:%M";
176 LEGAL_ALT(0);
177 goto recurse;
178
179 case 'r': /* The time in 12-hour clock representation. */
180 new_fmt = "%I:%M:S %p"; //_ctloc(t_fmt_ampm);
181 LEGAL_ALT(0);
182 goto recurse;
183
184 case 'T': /* The time as "%H:%M:%S". */
185 new_fmt = "%H:%M:%S";
186 LEGAL_ALT(0);
187 goto recurse;
188
189 /* we don't use 'X'
190 case 'X': The time, using the locale's format.
191 new_fmt =_ctloc(t_fmt);
192 goto recurse;
193 */
194
195 /* we do not need 'x'
196 case 'x': The date, using the locale's format.
197 new_fmt =_ctloc(d_fmt);*/
198 recurse:
199 bp = (const u_char *)strptime((const char *)bp, new_fmt, tm);
201 continue;
202
203 /*
204 * "Elementary" conversion rules.
205 */
206 case 'A': /* The day of week, using the locale's form. */
207 case 'a':
208 bp = find_string(bp, &tm->tm_wday, day, abday, 7);
209 LEGAL_ALT(0);
210 continue;
211
212 case 'B': /* The month, using the locale's form. */
213 case 'b':
214 case 'h':
215 bp = find_string(bp, &tm->tm_mon, mon, abmon, 12);
216 LEGAL_ALT(0);
217 continue;
218
219 case 'C': /* The century number. */
220 i = 20;
221 bp = conv_num(bp, &i, 0, 99);
222
223 i = i * 100 - TM_YEAR_BASE;
224 if(split_year) i += tm->tm_year % 100;
225 split_year = 1;
226 tm->tm_year = i;
228 continue;
229
230 case 'd': /* The day of month. */
231 case 'e':
232 bp = conv_num(bp, &tm->tm_mday, 1, 31);
234 continue;
235
236 case 'k': /* The hour (24-hour clock representation). */
237 LEGAL_ALT(0);
238 /* FALLTHROUGH */
239 case 'H':
240 bp = conv_num(bp, &tm->tm_hour, 0, 23);
242 continue;
243
244 case 'l': /* The hour (12-hour clock representation). */
245 LEGAL_ALT(0);
246 /* FALLTHROUGH */
247 case 'I':
248 bp = conv_num(bp, &tm->tm_hour, 1, 12);
249 if(tm->tm_hour == 12) tm->tm_hour = 0;
251 continue;
252
253 case 'j': /* The day of year. */
254 i = 1;
255 bp = conv_num(bp, &i, 1, 366);
256 tm->tm_yday = i - 1;
257 LEGAL_ALT(0);
258 continue;
259
260 case 'M': /* The minute. */
261 bp = conv_num(bp, &tm->tm_min, 0, 59);
263 continue;
264
265 case 'm': /* The month. */
266 i = 1;
267 bp = conv_num(bp, &i, 1, 12);
268 tm->tm_mon = i - 1;
270 continue;
271
272 case 'p': /* The locale's equivalent of AM/PM. */
273 bp = find_string(bp, &i, am_pm, NULL, 2);
274 if(tm->tm_hour > 11) return NULL;
275 tm->tm_hour += i * 12;
276 LEGAL_ALT(0);
277 continue;
278
279 case 'S': /* The seconds. */
280 bp = conv_num(bp, &tm->tm_sec, 0, 61);
282 continue;
283
284#ifndef TIME_MAX
285#define TIME_MAX INT64_MAX
286#endif
287 case 's': /* seconds since the epoch */
288 {
289 time_t sse = 0;
290 uint64_t rulim = TIME_MAX;
291
292 if(*bp < '0' || *bp > '9')
293 {
294 bp = NULL;
295 continue;
296 }
297
298 do
299 {
300 sse *= 10;
301 sse += *bp++ - '0';
302 rulim /= 10;
303 } while((sse * 10 <= TIME_MAX) && rulim && *bp >= '0' && *bp <= '9');
304
305 if(sse < 0 || (uint64_t)sse > TIME_MAX)
306 {
307 bp = NULL;
308 continue;
309 }
310
311 tm = localtime(&sse);
312 if(IS_NULL_PTR(tm)) bp = NULL;
313 }
314 continue;
315
316 case 'U': /* The week of year, beginning on sunday. */
317 case 'W': /* The week of year, beginning on monday. */
318 /*
319 * XXX This is bogus, as we can not assume any valid
320 * information present in the tm structure at this
321 * point to calculate a real value, so just check the
322 * range for now.
323 */
324 bp = conv_num(bp, &i, 0, 53);
326 continue;
327
328 case 'w': /* The day of week, beginning on sunday. */
329 bp = conv_num(bp, &tm->tm_wday, 0, 6);
331 continue;
332
333 case 'u': /* The day of week, monday = 1. */
334 bp = conv_num(bp, &i, 1, 7);
335 tm->tm_wday = i % 7;
337 continue;
338
339 case 'g': /* The year corresponding to the ISO week
340 * number but without the century.
341 */
342 bp = conv_num(bp, &i, 0, 99);
343 continue;
344
345 case 'G': /* The year corresponding to the ISO week
346 * number with century.
347 */
348 do
349 bp++;
350 while(isdigit(*bp));
351 continue;
352
353 case 'V': /* The ISO 8601:1988 week number as decimal */
354 bp = conv_num(bp, &i, 0, 53);
355 continue;
356
357 case 'Y': /* The year. */
358 i = TM_YEAR_BASE; /* just for data sanity... */
359 bp = conv_num(bp, &i, 0, 9999);
360 tm->tm_year = i - TM_YEAR_BASE;
362 continue;
363
364 case 'y': /* The year within 100 years of the epoch. */
365 /* LEGAL_ALT(ALT_E | ALT_O); */
366 bp = conv_num(bp, &i, 0, 99);
367
368 if(split_year) /* preserve century */
369 i += (tm->tm_year / 100) * 100;
370 else
371 {
372 split_year = 1;
373 if(i <= 68)
374 i = i + 2000 - TM_YEAR_BASE;
375 else
376 i = i + 1900 - TM_YEAR_BASE;
377 }
378 tm->tm_year = i;
379 continue;
380
381 case 'Z':
382 _tzset();
383 if(strncasecmp((const char *)bp, gmt, 3) == 0 || strncasecmp((const char *)bp, utc, 3) == 0)
384 {
385 tm->tm_isdst = 0;
386#ifdef TM_GMTOFF
387 tm->TM_GMTOFF = 0;
388#endif
389#ifdef TM_ZONE
390 tm->TM_ZONE = gmt;
391#endif
392 bp += 3;
393 }
394 else
395 {
396 ep = find_string(bp, &i, (const char *const *)_tzname, NULL, 2);
397 if(!IS_NULL_PTR(ep))
398 {
399 tm->tm_isdst = i;
400#ifdef TM_GMTOFF
401 tm->TM_GMTOFF = -(_timezone);
402#endif
403#ifdef TM_ZONE
404 tm->TM_ZONE = _tzname[i];
405#endif
406 }
407 bp = ep;
408 }
409 continue;
410
411 case 'z':
412 /*
413 * We recognize all ISO 8601 formats:
414 * Z = Zulu time/UTC
415 * [+-]hhmm
416 * [+-]hh:mm
417 * [+-]hh
418 * We recognize all RFC-822/RFC-2822 formats:
419 * UT|GMT
420 * North American : UTC offsets
421 * E[DS]T = Eastern : -4 | -5
422 * C[DS]T = Central : -5 | -6
423 * M[DS]T = Mountain: -6 | -7
424 * P[DS]T = Pacific : -7 | -8
425 * Military
426 * [A-IL-M] = -1 ... -9 (J not used)
427 * [N-Y] = +1 ... +12
428 */
429 while(isspace(*bp)) bp++;
430
431 switch(*bp++)
432 {
433 case 'G':
434 if(*bp++ != 'M') return NULL;
435 /*FALLTHROUGH*/
436 case 'U':
437 if(*bp++ != 'T') return NULL;
438 /*FALLTHROUGH*/
439 case 'Z':
440 tm->tm_isdst = 0;
441#ifdef TM_GMTOFF
442 tm->TM_GMTOFF = 0;
443#endif
444#ifdef TM_ZONE
445 tm->TM_ZONE = utc;
446#endif
447 continue;
448 case '+':
449 neg = 0;
450 break;
451 case '-':
452 neg = 1;
453 break;
454 default:
455 --bp;
456 ep = find_string(bp, &i, nast, NULL, 4);
457 if(!IS_NULL_PTR(ep))
458 {
459#ifdef TM_GMTOFF
460 tm->TM_GMTOFF = -5 - i;
461#endif
462#ifdef TM_ZONE
463 tm->TM_ZONE = __UNCONST(nast[i]);
464#endif
465 bp = ep;
466 continue;
467 }
468 ep = find_string(bp, &i, nadt, NULL, 4);
469 if(!IS_NULL_PTR(ep))
470 {
471 tm->tm_isdst = 1;
472#ifdef TM_GMTOFF
473 tm->TM_GMTOFF = -4 - i;
474#endif
475#ifdef TM_ZONE
476 tm->TM_ZONE = __UNCONST(nadt[i]);
477#endif
478 bp = ep;
479 continue;
480 }
481
482 if((*bp >= 'A' && *bp <= 'I') || (*bp >= 'L' && *bp <= 'Y'))
483 {
484#ifdef TM_GMTOFF
485 /* Argh! No 'J'! */
486 if(*bp >= 'A' && *bp <= 'I')
487 tm->TM_GMTOFF = ('A' - 1) - (int)*bp;
488 else if(*bp >= 'L' && *bp <= 'M')
489 tm->TM_GMTOFF = 'A' - (int)*bp;
490 else if(*bp >= 'N' && *bp <= 'Y')
491 tm->TM_GMTOFF = (int)*bp - 'M';
492#endif
493#ifdef TM_ZONE
494 tm->TM_ZONE = NULL; /* XXX */
495#endif
496 bp++;
497 continue;
498 }
499 return NULL;
500 }
501 offs = 0;
502 for(i = 0; i < 4;)
503 {
504 if(isdigit(*bp))
505 {
506 offs = offs * 10 + (*bp++ - '0');
507 i++;
508 continue;
509 }
510 if(i == 2 && *bp == ':')
511 {
512 bp++;
513 continue;
514 }
515 break;
516 }
517 switch(i)
518 {
519 case 2:
520 offs *= 100;
521 break;
522 case 4:
523 i = offs % 100;
524 if(i >= 60) return NULL;
525 /* Convert minutes into decimal */
526 offs = (offs / 100) * 100 + (i * 50) / 30;
527 break;
528 default:
529 return NULL;
530 }
531 if(neg) offs = -offs;
532 tm->tm_isdst = 0; /* XXX */
533#ifdef TM_GMTOFF
534 tm->TM_GMTOFF = offs;
535#endif
536#ifdef TM_ZONE
537 tm->TM_ZONE = NULL; /* XXX */
538#endif
539 continue;
540
541 /*
542 * Miscellaneous conversions.
543 */
544 case 'n': /* Any kind of white-space. */
545 case 't':
546 while(isspace(*bp)) bp++;
547 LEGAL_ALT(0);
548 continue;
549
550
551 default: /* Unknown/unsupported conversion. */
552 return NULL;
553 }
554 }
555
556 return (char *)(bp);
557}
558
559
560static const u_char *conv_num(const unsigned char *buf, int *dest, uint llim, uint ulim)
561{
562 uint result = 0;
563 unsigned char ch;
564
565 /* The limit also determines the number of valid digits. */
566 uint rulim = ulim;
567
568 ch = *buf;
569 if(ch < '0' || ch > '9') return NULL;
570
571 do
572 {
573 result *= 10;
574 result += ch - '0';
575 rulim /= 10;
576 ch = *++buf;
577 } while((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');
578
579 if(result < llim || result > ulim) return NULL;
580
581 *dest = result;
582 return buf;
583}
584
585static const u_char *find_string(const u_char *bp, int *tgt, const char *const *n1, const char *const *n2, int c)
586{
587 int i;
588 size_t len;
589
590 /* check full name - then abbreviated ones */
591 for(; !IS_NULL_PTR(n1); n1 = n2, n2 = NULL)
592 {
593 for(i = 0; i < c; i++, n1++)
594 {
595 len = strlen(*n1);
596 if(strncasecmp(*n1, (const char *)bp, len) == 0)
597 {
598 *tgt = i;
599 return bp + len;
600 }
601 }
602 }
603
604 /* Nothing matched */
605 return NULL;
606}
607
608// clang-format off
609// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
610// vim: shiftwidth=2 expandtab tabstop=2 cindent
611// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
612// clang-format on
613
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
Definition darktable.h:281
float *const restrict const size_t const size_t ch
#define TIME_MAX
static const char *const nast[5]
Definition strptime.c:94
static int TM_YEAR_BASE
Definition strptime.c:90
static const char *const abday[7]
Definition strptime.c:98
static const char *const abmon[12]
Definition strptime.c:102
static const char *const mon[12]
Definition strptime.c:99
static const char *const day[7]
Definition strptime.c:97
static const u_char * find_string(const u_char *, int *, const char *const *, const char *const *, int)
Definition strptime.c:585
static const char *const am_pm[2]
Definition strptime.c:96
static char gmt[]
Definition strptime.c:91
#define ALT_E
Definition strptime.c:83
unsigned int uint
Definition strptime.c:74
#define LEGAL_ALT(x)
Definition strptime.c:85
static const u_char * conv_num(const unsigned char *, int *, uint, uint)
Definition strptime.c:560
static char utc[]
Definition strptime.c:92
char * strptime(const char *buf, const char *fmt, struct tm *tm)
Definition strptime.c:107
static const char *const nadt[5]
Definition strptime.c:95
unsigned char u_char
Definition strptime.c:73
unsigned __int64 uint64_t
Definition strptime.c:75
#define ALT_O
Definition strptime.c:84