maint: update copyright
[gnulib.git] / lib / posixtm.c
1 /* Parse dates for touch and date.
2
3    Copyright (C) 1989-1991, 1998, 2000-2014 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 }