1 /* Parse dates for touch and date.
2
3 Copyright (C) 1989-1991, 1998, 2000-2020 Free Software Foundation, Inc.
4
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.
9
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.
14
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/>. */
17
18 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
19 Rewritten by Jim Meyering. */
20
21 #include <config.h>
22
23 #include "posixtm.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <string.h>
29
30 #if USE_UNLOCKED_IO
31 # include "unlocked-io.h"
32 #endif
33
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)
42
43 /*
44 POSIX requires:
45
46 touch -t [[CC]YY]mmddhhmm[.ss] FILE...
47 8, 10, or 12 digits, followed by optional .ss
48 (PDS_CENTURY | PDS_SECONDS)
49
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)
53
54 date mmddhhmm[[CC]YY]
55 8, 10, or 12 digits
56 (PDS_TRAILING_YEAR | PDS_CENTURY)
57
58 */
59
60 static bool
year(struct tm * tm,const int * digit_pair,size_t n,unsigned int syntax_bits)61 year (struct tm *tm, const int *digit_pair, size_t n, unsigned int syntax_bits)
62 {
63 switch (n)
64 {
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)
71 {
72 if (syntax_bits & PDS_PRE_2000)
73 return false;
74 tm->tm_year += 100;
75 }
76 break;
77
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;
83
84 case 0:
85 {
86 time_t now;
87 struct tm *tmp;
88
89 /* Use current year. */
90 time (&now);
91 tmp = localtime (&now);
92 if (! tmp)
93 return false;
94 tm->tm_year = tmp->tm_year;
95 }
96 break;
97
98 default:
99 abort ();
100 }
101
102 return true;
103 }
104
105 static bool
posix_time_parse(struct tm * tm,const char * s,unsigned int syntax_bits)106 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
107 {
108 const char *dot = NULL;
109 int pair[6];
110 int *p;
111 size_t i;
112
113 size_t s_len = strlen (s);
114 size_t len = s_len;
115
116 if (syntax_bits & PDS_SECONDS)
117 {
118 dot = strchr (s, '.');
119 if (dot)
120 {
121 len = dot - s;
122 if (s_len - len != 3)
123 return false;
124 }
125 }
126
127 if (! (8 <= len && len <= 12 && len % 2 == 0))
128 return false;
129
130 for (i = 0; i < len; i++)
131 if (!ISDIGIT (s[i]))
132 return false;
133
134 len /= 2;
135 for (i = 0; i < len; i++)
136 pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
137
138 p = pair;
139 if (! (syntax_bits & PDS_TRAILING_YEAR))
140 {
141 if (! year (tm, p, len - 4, syntax_bits))
142 return false;
143 p += len - 4;
144 len = 4;
145 }
146
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;
153
154 /* Handle any trailing year. */
155 if (syntax_bits & PDS_TRAILING_YEAR)
156 {
157 if (! year (tm, p, len, syntax_bits))
158 return false;
159 }
160
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;
168
169 return true;
170 }
171
172 /* Parse a POSIX-style date, returning true if successful. */
173
174 bool
posixtime(time_t * p,const char * s,unsigned int syntax_bits)175 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
176 {
177 struct tm tm0;
178 struct tm tm1;
179 time_t t;
180
181 if (! posix_time_parse (&tm0, s, syntax_bits))
182 return false;
183
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);
193
194 if (tm1.tm_wday < 0)
195 return false;
196
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))
207 {
208 /* Any mismatch without 60 in the tm_sec field is invalid. */
209 if (tm0.tm_sec != 60)
210 return false;
211
212 {
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;
220 }
221 }
222
223 *p = t;
224 return true;
225 }
226