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