stdio: Don't break attribute 'scanf' on clang.
[gnulib.git] / lib / posixtm.c
blob5b88b170fa573e0c037e3a8d6b15c616af36161b
1 /* Parse dates for touch and date.
3 Copyright (C) 1989-1991, 1998, 2000-2020 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
19 Rewritten by Jim Meyering. */
21 #include <config.h>
23 #include "posixtm.h"
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <string.h>
30 #if USE_UNLOCKED_IO
31 # include "unlocked-io.h"
32 #endif
34 /* ISDIGIT differs from isdigit, as follows:
35 - Its arg may be any int or unsigned int; it need not be an unsigned char
36 or EOF.
37 - It's typically faster.
38 POSIX says that only '0' through '9' are digits. Prefer ISDIGIT to
39 isdigit unless it's important to use the locale's definition
40 of "digit" even when the host does not conform to POSIX. */
41 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
44 POSIX requires:
46 touch -t [[CC]YY]mmddhhmm[.ss] FILE...
47 8, 10, or 12 digits, followed by optional .ss
48 (PDS_CENTURY | PDS_SECONDS)
50 touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
51 8 or 10 digits, YY (if present) must be in the range 69-99
52 (PDS_TRAILING_YEAR | PDS_PRE_2000)
54 date mmddhhmm[[CC]YY]
55 8, 10, or 12 digits
56 (PDS_TRAILING_YEAR | PDS_CENTURY)
60 static bool
61 year (struct tm *tm, const int *digit_pair, size_t n, unsigned int syntax_bits)
63 switch (n)
65 case 1:
66 tm->tm_year = *digit_pair;
67 /* Deduce the century based on the year.
68 POSIX requires that 00-68 be interpreted as 2000-2068,
69 and that 69-99 be interpreted as 1969-1999. */
70 if (digit_pair[0] <= 68)
72 if (syntax_bits & PDS_PRE_2000)
73 return false;
74 tm->tm_year += 100;
76 break;
78 case 2:
79 if (! (syntax_bits & PDS_CENTURY))
80 return false;
81 tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
82 break;
84 case 0:
86 time_t now;
87 struct tm *tmp;
89 /* Use current year. */
90 time (&now);
91 tmp = localtime (&now);
92 if (! tmp)
93 return false;
94 tm->tm_year = tmp->tm_year;
96 break;
98 default:
99 abort ();
102 return true;
105 static bool
106 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
108 const char *dot = NULL;
109 int pair[6];
110 int *p;
111 size_t i;
113 size_t s_len = strlen (s);
114 size_t len = s_len;
116 if (syntax_bits & PDS_SECONDS)
118 dot = strchr (s, '.');
119 if (dot)
121 len = dot - s;
122 if (s_len - len != 3)
123 return false;
127 if (! (8 <= len && len <= 12 && len % 2 == 0))
128 return false;
130 for (i = 0; i < len; i++)
131 if (!ISDIGIT (s[i]))
132 return false;
134 len /= 2;
135 for (i = 0; i < len; i++)
136 pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
138 p = pair;
139 if (! (syntax_bits & PDS_TRAILING_YEAR))
141 if (! year (tm, p, len - 4, syntax_bits))
142 return false;
143 p += len - 4;
144 len = 4;
147 /* Handle 8 digits worth of 'MMDDhhmm'. */
148 tm->tm_mon = *p++ - 1;
149 tm->tm_mday = *p++;
150 tm->tm_hour = *p++;
151 tm->tm_min = *p++;
152 len -= 4;
154 /* Handle any trailing year. */
155 if (syntax_bits & PDS_TRAILING_YEAR)
157 if (! year (tm, p, len, syntax_bits))
158 return false;
161 /* Handle seconds. */
162 if (!dot)
163 tm->tm_sec = 0;
164 else if (ISDIGIT (dot[1]) && ISDIGIT (dot[2]))
165 tm->tm_sec = 10 * (dot[1] - '0') + dot[2] - '0';
166 else
167 return false;
169 return true;
172 /* Parse a POSIX-style date, returning true if successful. */
174 bool
175 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
177 struct tm tm0;
178 struct tm tm1;
179 time_t t;
181 if (! posix_time_parse (&tm0, s, syntax_bits))
182 return false;
184 tm1.tm_sec = tm0.tm_sec;
185 tm1.tm_min = tm0.tm_min;
186 tm1.tm_hour = tm0.tm_hour;
187 tm1.tm_mday = tm0.tm_mday;
188 tm1.tm_mon = tm0.tm_mon;
189 tm1.tm_year = tm0.tm_year;
190 tm1.tm_wday = -1;
191 tm1.tm_isdst = -1;
192 t = mktime (&tm1);
194 if (tm1.tm_wday < 0)
195 return false;
197 /* Reject dates like "September 31" and times like "25:61".
198 However, allow a seconds count of 60 even in time zones that do
199 not support leap seconds, treating it as the following second;
200 POSIX requires this. */
201 if ((tm0.tm_year ^ tm1.tm_year)
202 | (tm0.tm_mon ^ tm1.tm_mon)
203 | (tm0.tm_mday ^ tm1.tm_mday)
204 | (tm0.tm_hour ^ tm1.tm_hour)
205 | (tm0.tm_min ^ tm1.tm_min)
206 | (tm0.tm_sec ^ tm1.tm_sec))
208 /* Any mismatch without 60 in the tm_sec field is invalid. */
209 if (tm0.tm_sec != 60)
210 return false;
213 /* Allow times like 01:35:60 or 23:59:60. */
214 time_t dummy;
215 char buf[16];
216 char *b = stpcpy (buf, s);
217 strcpy (b - 2, "59");
218 if (!posixtime (&dummy, buf, syntax_bits))
219 return false;
223 *p = t;
224 return true;