maint: update all copyright year number ranges
[gnulib.git] / lib / posixtm.c
1 /* Parse dates for touch and date.
2
3    Copyright (C) 1989-1991, 1998, 2000-2012 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 <http://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_LEADING_YEAR | 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 int
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 1;
74           tm->tm_year += 100;
75         }
76       break;
77
78     case 2:
79       if (! (syntax_bits & PDS_CENTURY))
80         return 1;
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 1;
94         tm->tm_year = tmp->tm_year;
95       }
96       break;
97
98     default:
99       abort ();
100     }
101
102   return 0;
103 }
104
105 static int
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 = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
115                 ? (size_t) (dot - s)
116                 : s_len);
117
118   if (len != 8 && len != 10 && len != 12)
119     return 1;
120
121   if (dot)
122     {
123       if (!(syntax_bits & PDS_SECONDS))
124         return 1;
125
126       if (s_len - len != 3)
127         return 1;
128     }
129
130   for (i = 0; i < len; i++)
131     if (!ISDIGIT (s[i]))
132       return 1;
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_LEADING_YEAR)
140     {
141       if (year (tm, p, len - 4, syntax_bits))
142         return 1;
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 1;
159     }
160
161   /* Handle seconds.  */
162   if (!dot)
163     {
164       tm->tm_sec = 0;
165     }
166   else
167     {
168       int seconds;
169
170       ++dot;
171       if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
172         return 1;
173       seconds = 10 * (dot[0] - '0') + dot[1] - '0';
174
175       tm->tm_sec = seconds;
176     }
177
178   return 0;
179 }
180
181 /* Parse a POSIX-style date, returning true if successful.  */
182
183 bool
184 posixtime (time_t *p, const char *s, unsigned int syntax_bits)
185 {
186   struct tm tm0;
187   struct tm tm1;
188   struct tm const *tm;
189   time_t t;
190
191   if (posix_time_parse (&tm0, s, syntax_bits))
192     return false;
193
194   tm1 = tm0;
195   tm1.tm_isdst = -1;
196   t = mktime (&tm1);
197
198   if (t != (time_t) -1)
199     tm = &tm1;
200   else
201     {
202       /* mktime returns -1 for errors, but -1 is also a valid time_t
203          value.  Check whether an error really occurred.  */
204       tm = localtime (&t);
205       if (! tm)
206         return false;
207     }
208
209   /* Reject dates like "September 31" and times like "25:61".
210      Do not reject times that specify "60" as the number of seconds.  */
211   if ((tm0.tm_year ^ tm->tm_year)
212       | (tm0.tm_mon ^ tm->tm_mon)
213       | (tm0.tm_mday ^ tm->tm_mday)
214       | (tm0.tm_hour ^ tm->tm_hour)
215       | (tm0.tm_min ^ tm->tm_min)
216       | (tm0.tm_sec ^ tm->tm_sec))
217     {
218       /* Any mismatch without 60 in the tm_sec field is invalid.  */
219       if (tm0.tm_sec != 60)
220         return false;
221
222       {
223         /* Allow times like 01:35:60 or 23:59:60.  */
224         time_t dummy;
225         char buf[16];
226         char *b = stpcpy (buf, s);
227         strcpy (b - 2, "59");
228         if (!posixtime (&dummy, buf, syntax_bits))
229           return false;
230       }
231     }
232
233   *p = t;
234   return true;
235 }