1 /* Parse dates for touch and date.
2 
3    Copyright (C) 1989, 1990, 1991, 1998, 2000, 2001, 2002, 2003, 2004,
4    2005, 2006, 2007 Free Software Foundation Inc.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software Foundation,
18    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19 
20 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
21    Rewritten by Jim Meyering.  */
22 
23 #include <config.h>
24 
25 #include "posixtm.h"
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <string.h>
31 
32 #if USE_UNLOCKED_IO
33 # include "unlocked-io.h"
34 #endif
35 
36 /* ISDIGIT differs from isdigit, as follows:
37    - Its arg may be any int or unsigned int; it need not be an unsigned char
38      or EOF.
39    - It's typically faster.
40    POSIX says that only '0' through '9' are digits.  Prefer ISDIGIT to
41    isdigit unless it's important to use the locale's definition
42    of `digit' even when the host does not conform to POSIX.  */
43 #define ISDIGIT(c) ((unsigned int) (c) - '0' <= 9)
44 
45 time_t mktime ();
46 
47 /*
48   POSIX requires:
49 
50   touch -t [[CC]YY]mmddhhmm[.ss] FILE...
51     8, 10, or 12 digits, followed by optional .ss
52     (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
53 
54   touch mmddhhmm[YY] FILE... (obsoleted by POSIX 1003.1-2001)
55     8 or 10 digits, YY (if present) must be in the range 69-99
56     (PDS_TRAILING_YEAR | PDS_PRE_2000)
57 
58   date mmddhhmm[[CC]YY]
59     8, 10, or 12 digits
60     (PDS_TRAILING_YEAR | PDS_CENTURY)
61 
62 */
63 
64 static int
year(struct tm * tm,const int * digit_pair,size_t n,unsigned int syntax_bits)65 year (struct tm *tm, const int *digit_pair, size_t n, unsigned int syntax_bits)
66 {
67   switch (n)
68     {
69     case 1:
70       tm->tm_year = *digit_pair;
71       /* Deduce the century based on the year.
72 	 POSIX requires that 00-68 be interpreted as 2000-2068,
73 	 and that 69-99 be interpreted as 1969-1999.  */
74       if (digit_pair[0] <= 68)
75 	{
76 	  if (syntax_bits & PDS_PRE_2000)
77 	    return 1;
78 	  tm->tm_year += 100;
79 	}
80       break;
81 
82     case 2:
83       if (! (syntax_bits & PDS_CENTURY))
84 	return 1;
85       tm->tm_year = digit_pair[0] * 100 + digit_pair[1] - 1900;
86       break;
87 
88     case 0:
89       {
90 	time_t now;
91 	struct tm *tmp;
92 
93 	/* Use current year.  */
94 	time (&now);
95 	tmp = localtime (&now);
96 	if (! tmp)
97 	  return 1;
98 	tm->tm_year = tmp->tm_year;
99       }
100       break;
101 
102     default:
103       abort ();
104     }
105 
106   return 0;
107 }
108 
109 static int
posix_time_parse(struct tm * tm,const char * s,unsigned int syntax_bits)110 posix_time_parse (struct tm *tm, const char *s, unsigned int syntax_bits)
111 {
112   const char *dot = NULL;
113   int pair[6];
114   int *p;
115   size_t i;
116 
117   size_t s_len = strlen (s);
118   size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
119 		? (size_t) (dot - s)
120 		: s_len);
121 
122   if (len != 8 && len != 10 && len != 12)
123     return 1;
124 
125   if (dot)
126     {
127       if (!(syntax_bits & PDS_SECONDS))
128 	return 1;
129 
130       if (s_len - len != 3)
131 	return 1;
132     }
133 
134   for (i = 0; i < len; i++)
135     if (!ISDIGIT (s[i]))
136       return 1;
137 
138   len /= 2;
139   for (i = 0; i < len; i++)
140     pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
141 
142   p = pair;
143   if (syntax_bits & PDS_LEADING_YEAR)
144     {
145       if (year (tm, p, len - 4, syntax_bits))
146 	return 1;
147       p += len - 4;
148       len = 4;
149     }
150 
151   /* Handle 8 digits worth of `MMDDhhmm'.  */
152   tm->tm_mon = *p++ - 1;
153   tm->tm_mday = *p++;
154   tm->tm_hour = *p++;
155   tm->tm_min = *p++;
156   len -= 4;
157 
158   /* Handle any trailing year.  */
159   if (syntax_bits & PDS_TRAILING_YEAR)
160     {
161       if (year (tm, p, len, syntax_bits))
162 	return 1;
163     }
164 
165   /* Handle seconds.  */
166   if (!dot)
167     {
168       tm->tm_sec = 0;
169     }
170   else
171     {
172       int seconds;
173 
174       ++dot;
175       if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
176 	return 1;
177       seconds = 10 * (dot[0] - '0') + dot[1] - '0';
178 
179       tm->tm_sec = seconds;
180     }
181 
182   return 0;
183 }
184 
185 /* Parse a POSIX-style date, returning true if successful.  */
186 
187 bool
posixtime(time_t * p,const char * s,unsigned int syntax_bits)188 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
189 {
190   struct tm tm0
191 #ifdef lint
192   /* Placate gcc-4's -Wuninitialized.
193      posix_time_parse fails to set all of tm0 only when it returns
194      nonzero (due to year() returning nonzero), and in that case,
195      this code doesn't use the tm0 at all.  */
196     = { 0, }
197 #endif
198     ;
199   struct tm tm1;
200   struct tm const *tm;
201   time_t t;
202 
203   if (posix_time_parse (&tm0, s, syntax_bits))
204     return false;
205 
206   tm1 = tm0;
207   tm1.tm_isdst = -1;
208   t = mktime (&tm1);
209 
210   if (t != (time_t) -1)
211     tm = &tm1;
212   else
213     {
214       /* mktime returns -1 for errors, but -1 is also a valid time_t
215 	 value.  Check whether an error really occurred.  */
216       tm = localtime (&t);
217       if (! tm)
218 	return false;
219     }
220 
221   /* Reject dates like "September 31" and times like "25:61".  */
222   if ((tm0.tm_year ^ tm->tm_year)
223       | (tm0.tm_mon ^ tm->tm_mon)
224       | (tm0.tm_mday ^ tm->tm_mday)
225       | (tm0.tm_hour ^ tm->tm_hour)
226       | (tm0.tm_min ^ tm->tm_min)
227       | (tm0.tm_sec ^ tm->tm_sec))
228     return false;
229 
230   *p = t;
231   return true;
232 }
233 
234 #ifdef TEST_POSIXTIME
235 /*
236     Test mainly with syntax_bits == 13
237     (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
238 
239     This test data assumes Universal Time, e.g., TZ="UTC0".
240 
241     This test data also assumes that time_t is signed and is at least
242     39 bits wide, so that it can represent all years from 0000 through
243     9999.  A host with 32-bit signed time_t can represent only time
244     stamps in the range 1901-12-13 20:45:52 through 2038-01-18
245     03:14:07 UTC, assuming POSIX time_t with no leap seconds, so test
246     cases outside this range will not work on such a host.
247 
248     Also, the first two lines of test data assume that the current
249     year is 2002.
250 
251 BEGIN-DATA
252 12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
253 12131415.16     13   1039788916 Fri Dec 13 14:15:16 2002
254 000001010000.00 13 -62167132800 Sun Jan  1 00:00:00 0000
255 190112132045.52 13  -2147483648 Fri Dec 13 20:45:52 1901
256 190112132045.53 13  -2147483647 Fri Dec 13 20:45:53 1901
257 190112132046.52 13  -2147483588 Fri Dec 13 20:46:52 1901
258 190112132145.52 13  -2147480048 Fri Dec 13 21:45:52 1901
259 190112142045.52 13  -2147397248 Sat Dec 14 20:45:52 1901
260 190201132045.52 13  -2144805248 Mon Jan 13 20:45:52 1902
261 196912312359.59 13           -1 Wed Dec 31 23:59:59 1969
262 197001010000.00 13            0 Thu Jan  1 00:00:00 1970
263 197001010000.01 13            1 Thu Jan  1 00:00:01 1970
264 197001010001.00 13           60 Thu Jan  1 00:01:00 1970
265 197001010100.00 13         3600 Thu Jan  1 01:00:00 1970
266 197001020000.00 13        86400 Fri Jan  2 00:00:00 1970
267 197002010000.00 13      2678400 Sun Feb  1 00:00:00 1970
268 197101010000.00 13     31536000 Fri Jan  1 00:00:00 1971
269 197001000000.00 13            * *
270 197000010000.00 13            * *
271 197001010000.60 13            * *
272 197001010060.00 13            * *
273 197001012400.00 13            * *
274 197001320000.00 13            * *
275 197013010000.00 13            * *
276 203801190314.06 13   2147483646 Tue Jan 19 03:14:06 2038
277 203801190314.07 13   2147483647 Tue Jan 19 03:14:07 2038
278 203801190314.08 13   2147483648 Tue Jan 19 03:14:08 2038
279 999912312359.59 13 253402300799 Fri Dec 31 23:59:59 9999
280 1112131415      13   1323785700 Tue Dec 13 14:15:00 2011
281 1112131415.16   13   1323785716 Tue Dec 13 14:15:16 2011
282 201112131415.16 13   1323785716 Tue Dec 13 14:15:16 2011
283 191112131415.16 13  -1831974284 Wed Dec 13 14:15:16 1911
284 203712131415.16 13   2144326516 Sun Dec 13 14:15:16 2037
285 3712131415.16   13   2144326516 Sun Dec 13 14:15:16 2037
286 6812131415.16   13   3122633716 Thu Dec 13 14:15:16 2068
287 6912131415.16   13     -1590284 Sat Dec 13 14:15:16 1969
288 7012131415.16   13     29945716 Sun Dec 13 14:15:16 1970
289 1213141599       2    945094500 Mon Dec 13 14:15:00 1999
290 1213141500       2    976716900 Wed Dec 13 14:15:00 2000
291 END-DATA
292 
293 */
294 
295 # define MAX_BUFF_LEN 1024
296 
297 int
main(void)298 main (void)
299 {
300   char buff[MAX_BUFF_LEN + 1];
301 
302   buff[MAX_BUFF_LEN] = 0;
303   while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
304     {
305       char time_str[MAX_BUFF_LEN];
306       unsigned int syntax_bits;
307       time_t t;
308       if (sscanf (buff, "%s %u", time_str, &syntax_bits) != 2)
309 	printf ("*\n");
310       else
311 	{
312 	  printf ("%-15s %2u ", time_str, syntax_bits);
313 	  if (posixtime (&t, time_str, syntax_bits))
314 	    printf ("%12ld %s", (long int) t, ctime (&t));
315 	  else
316 	    printf ("%12s %s", "*", "*\n");
317 	}
318     }
319   exit (0);
320 
321 }
322 #endif
323 
324 /*
325 Local Variables:
326 compile-command: "gcc -DTEST_POSIXTIME -g -O -Wall -W posixtm.c"
327 End:
328 */
329