(jm_FILE_SYSTEM_USAGE): Add `[]' between use of
[coreutils.git] / lib / posixtm.c
blob2c392e40124dda4fb2de2fc740b1babe45494b85
1 /* Parse dates for touch and date.
2 Copyright (C) 1989, 1990, 1991, 1998, 2000 Free Software Foundation Inc.
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.
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.
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. */
18 /* Yacc-based version written by Jim Kingdon and David MacKenzie.
19 Rewritten by Jim Meyering. */
21 #ifdef HAVE_CONFIG_H
22 # include <config.h>
23 #endif
25 #include <stdio.h>
26 #if HAVE_STDLIB_H
27 # include <stdlib.h>
28 #endif
29 #include <sys/types.h>
30 #if HAVE_STRING_H
31 # include <string.h>
32 #else
33 # include <strings.h>
34 #endif
36 #ifdef TM_IN_SYS_TIME
37 # include <sys/time.h>
38 #else
39 # include <time.h>
40 #endif
42 #include "posixtm.h"
44 /* ISDIGIT differs from isdigit, as follows:
45 - Its arg may be any int or unsigned int; it need not be an unsigned char.
46 - It's guaranteed to evaluate its argument exactly once.
47 - It's typically faster.
48 Posix 1003.2-1992 section 2.5.2.1 page 50 lines 1556-1558 says that
49 only '0' through '9' are digits. Prefer ISDIGIT to isdigit unless
50 it's important to use the locale's definition of `digit' even when the
51 host does not conform to Posix. */
52 #define ISDIGIT(c) ((unsigned) (c) - '0' <= 9)
54 /* The return value. */
55 static struct tm t;
57 time_t mktime ();
60 POSIX requires:
62 touch -t [[CC]YY]mmddhhmm[.ss] FILE...
63 8, 10, or 12 digits, followed by optional .ss
64 (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS)
66 touch mmddhhmm[YY] FILE... (obsolescent)
67 8 or 10 digits
68 (PDS_TRAILING_YEAR)
70 date mmddhhmm[[CC]YY]
71 8, 10, or 12 digits
72 (PDS_TRAILING_YEAR | PDS_CENTURY)
76 static int
77 year (const int *digit_pair, size_t n, int allow_century)
79 switch (n)
81 case 1:
82 t.tm_year = *digit_pair;
83 /* Deduce the century based on the year.
84 See POSIX.2 section 4.63.3. */
85 if (digit_pair[0] <= 68)
86 t.tm_year += 100;
87 break;
89 case 2:
90 if (!allow_century)
91 return 1;
92 t.tm_year = digit_pair[0] * 100 + digit_pair[1];
93 if (t.tm_year < 1900)
94 return 1;
95 t.tm_year -= 1900;
96 break;
98 case 0:
100 time_t now;
101 struct tm *tmp;
103 /* Use current year. */
104 time (&now);
105 tmp = localtime (&now);
106 t.tm_year = tmp->tm_year;
108 break;
110 default:
111 abort ();
114 return 0;
117 static int
118 posix_time_parse (const char *s, unsigned int syntax_bits)
120 const char *dot = NULL;
121 int pair[6];
122 int *p;
123 unsigned int i;
125 size_t s_len = strlen (s);
126 size_t len = (((syntax_bits & PDS_SECONDS) && (dot = strchr (s, '.')))
127 ? dot - s
128 : s_len);
130 if (len != 8 && len != 10 && len != 12)
131 return 1;
133 if (dot)
135 if (!(syntax_bits & PDS_SECONDS))
136 return 1;
138 if (s_len - len != 3)
139 return 1;
142 for (i = 0; i < len; i++)
143 if (!ISDIGIT (s[i]))
144 return 1;
146 len /= 2;
147 for (i = 0; i < len; i++)
148 pair[i] = 10 * (s[2*i] - '0') + s[2*i + 1] - '0';
150 p = pair;
151 if (syntax_bits & PDS_LEADING_YEAR)
153 if (year (p, len - 4, syntax_bits & PDS_CENTURY))
154 return 1;
155 p += len - 4;
156 len = 4;
159 /* Handle 8 digits worth of `MMDDhhmm'. */
160 if (*p < 1 || *p > 12)
161 return 1;
162 t.tm_mon = *p - 1;
163 ++p; --len;
165 if (*p < 1 || *p > 31)
166 return 1;
167 t.tm_mday = *p;
168 ++p; --len;
170 if (*p < 0 || *p > 23)
171 return 1;
172 t.tm_hour = *p;
173 ++p; --len;
175 if (*p < 0 || *p > 59)
176 return 1;
177 t.tm_min = *p;
178 ++p; --len;
180 /* Handle any trailing year. */
181 if (syntax_bits & PDS_TRAILING_YEAR)
183 if (year (p, len, syntax_bits & PDS_CENTURY))
184 return 1;
187 /* Handle seconds. */
188 if (!dot)
190 t.tm_sec = 0;
192 else
194 int seconds;
196 ++dot;
197 if (!ISDIGIT (dot[0]) || !ISDIGIT (dot[1]))
198 return 1;
199 seconds = 10 * (dot[0] - '0') + dot[1] - '0';
201 if (seconds < 0 || seconds > 61)
202 return 1;
203 t.tm_sec = seconds;
206 return 0;
209 /* Parse a POSIX-style date and return it, or (time_t)-1 for an error. */
211 time_t
212 posixtime (const char *s, unsigned int syntax_bits)
214 t.tm_isdst = -1;
215 if (posix_time_parse (s, syntax_bits))
216 return (time_t)-1;
217 else
218 return mktime (&t);
221 /* Parse a POSIX-style date and return it, or NULL for an error. */
223 struct tm *
224 posixtm (const char *s, unsigned int syntax_bits)
226 if (posixtime (s, syntax_bits) == -1)
227 return NULL;
228 return &t;
231 #ifdef TEST_POSIXTIME
233 Test mainly with syntax_bits == 13
234 (aka: (PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
236 BEGIN-DATA
237 1112131415 13 1323807300 Tue Dec 13 14:15:00 2011
238 1112131415.16 13 1323807316 Tue Dec 13 14:15:16 2011
239 201112131415.16 13 1323807316 Tue Dec 13 14:15:16 2011
240 191112131415.16 13 -1 ***
241 203712131415.16 13 2144348116 Sun Dec 13 14:15:16 2037
242 3712131415.16 13 2144348116 Sun Dec 13 14:15:16 2037
243 6812131415.16 13 -1 ***
244 6912131415.16 13 -1 ***
245 7012131415.16 13 29967316 Sun Dec 13 14:15:16 1970
246 12131415.16 13 913580116 Sun Dec 13 14:15:16 1998
248 1213141599 2 945116100 Mon Dec 13 14:15:00 1999
249 1213141500 2 976738500 Wed Dec 13 14:15:00 2000
250 END-DATA
254 # define MAX_BUFF_LEN 1024
257 main ()
259 char buff[MAX_BUFF_LEN + 1];
261 buff[MAX_BUFF_LEN] = 0;
262 while (fgets (buff, MAX_BUFF_LEN, stdin) && buff[0])
264 char time_str[MAX_BUFF_LEN];
265 unsigned int syntax_bits;
266 time_t t;
267 char *result;
268 sscanf (buff, "%s %u", time_str, &syntax_bits);
269 t = posixtime (time_str, syntax_bits);
270 result = (t == (time_t) -1 ? "***" : ctime (&t));
271 printf ("%d %s\n", (int) t, result);
273 exit (0);
276 #endif