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