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