rewrite from yacc-based version
[gnulib.git] / lib / posixtm.c
1 /* Parse dates for touch.
2    Copyright (C) 1989, 1990, 1991, 1998 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 <stdio.h>
26 #include <sys/types.h>
27
28 #ifdef TM_IN_SYS_TIME
29 # include <sys/time.h>
30 #else
31 # include <time.h>
32 #endif
33
34 /* The return value. */
35 static struct tm t;
36
37 time_t mktime ();
38
39 /*
40   POSIX requires:
41
42   touch -t [[CC]YY]mmddhhmm[.ss] FILE...
43     8, 10, or 12 digits, followed by optional .ss
44     (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
45
46   touch mmddhhmm[YY] FILE...
47     8 or 10 digits
48     (PDS_TRAILING_YEAR)
49
50   date mmddhhmm[[CC]YY]
51     8, 10, or 12 digits
52     (PDS_TRAILING_YEAR | PDS_CENTURY)
53
54 */
55
56 /* FIXME: put these in posixtm.h */
57 /* POSIX Date Syntax flags.  */
58 #define PDS_LEADING_YEAR 1
59 #define PDS_TRAILING_YEAR 2
60 #define PDS_CENTURY 4
61 #define PDS_SECONDS 8
62
63 static int
64 year (const int *digit_pair, size_t n, int allow_century)
65 {
66   switch (n)
67     {
68     case 1:
69       t.tm_year = *digit_pair;
70       /* Deduce the century based on the year.
71          See POSIX.2 section 4.63.3.  */
72       if (digit_pair[0] <= 68)
73         t.tm_year += 100;
74       break;
75
76     case 2:
77       if (!allow_century)
78         return 1;
79       t.tm_year = digit_pair[0] * 100 + digit_pair[1];
80       if (t.tm_year < 1900)
81         return 1;
82       t.tm_year -= 1900;
83       break;
84
85     case 0:
86       {
87         time_t now;
88         struct tm *tmp;
89
90         /* Use current year.  */
91         time (&now);
92         tmp = localtime (&now);
93         t.tm_year = tmp->tm_year;
94       }
95       break;
96
97     default:
98       abort ();
99     }
100
101   return 0;
102 }
103
104 static int
105 posix_time_parse (const char *s, unsigned int syntax_bits)
106 {
107   const char *dot;
108   int pair[6];
109   int *p;
110   int i;
111
112   size_t s_len = strlen (s);
113   size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
114                 ? dot - s
115                 : s_len);
116
117   if (len != 8 && len != 10 && len != 12)
118     return 1;
119
120   if (dot)
121     {
122       if (!(syntax_bits & PDS_SECONDS))
123         return 1;
124
125       if (s_len - len != 3)
126         return 1;
127     }
128
129   for (i = 0; i < len; i++)
130     if (!ISDIGIT (s[i]))
131       return 1;
132
133   len /= 2;
134   for (i = 0; i < len; i++)
135     pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
136
137   p = pair;
138   if (syntax_bits & PDS_LEADING_YEAR)
139     {
140       if (year (p, len - 4, syntax_bits & PDS_CENTURY))
141         return 1;
142       p += len - 4;
143       len = 4;
144     }
145
146   /* Handle 8 digits worth of `MMDDhhmm'.  */
147   if (*p < 1 || *p > 12)
148     return 1;
149   t.tm_mon = *p - 1;
150
151   ++p; --len;
152   if (*p < 1 || *p > 31)
153     return 1;
154   t.tm_mday = *p;
155
156   ++p; --len;
157   if (*p < 0 || *p > 23)
158     return 1;
159   t.tm_hour = *p;
160
161   ++p; --len;
162   if (*p < 0 || *p > 59)
163     return 1;
164   t.tm_min = *p;
165
166   /* Handle any trailing year.  */
167   if (syntax_bits & PDS_TRAILING_YEAR)
168     {
169       if (year (p, len, syntax_bits & PDS_CENTURY))
170         return 1;
171     }
172
173   /* Handle seconds.  */
174   if (!dot)
175     {
176       t.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       if (seconds < 0 || seconds > 61)
188         return 1;
189       t.tm_sec = seconds;
190     }
191
192   return 0;
193 }
194
195 /* Parse a POSIX-style date and return it, or (time_t)-1 for an error.  */
196
197 struct tm *
198 posixtime (const char *s)
199 {
200   t.tm_isdst = -1;
201   if (posix_time_parse (s))
202     return (time_t)-1;
203   else
204     return mktime (&t);
205 }
206
207 /* Parse a POSIX-style date and return it, or NULL for an error.  */
208
209 struct tm *
210 posixtm (const char *s)
211 {
212   if (posixtime (s) == -1)
213     return NULL;
214   return &t;
215 }