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