1/* Parse dates for touch and date. 2 3 Copyright (C) 1989-1991, 1998, 2000-2010 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 60static int 61year (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 105static int 106posix_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 183bool 184posixtime (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} 236